fuse-3.18.2/0000755000175000017500000000000015156613444011526 5ustar berndberndfuse-3.18.2/.ackrc0000644000175000017500000000005115156613252012603 0ustar berndbernd--ignore-dir=build --ignore-dir=doc/html fuse-3.18.2/.clang-format0000644000175000017500000000636415156613252014107 0ustar berndbernd# SPDX-License-Identifier: GPL-2.0 # # clang-format configuration file. Intended for clang-format >= 11. # # For more information, see: # # Documentation/process/clang-format.rst # https://clang.llvm.org/docs/ClangFormat.html # https://clang.llvm.org/docs/ClangFormatStyleOptions.html # --- AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: true AfterObjCDeclaration: false AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeComma BreakAfterJavaFieldAnnotations: false BreakStringLiterals: false ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 8 ContinuationIndentWidth: 8 Cpp11BracedListStyle: false DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: false IncludeBlocks: Preserve IncludeCategories: - Regex: '.*' Priority: 1 IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: false IndentGotoLabels: false IndentPPDirectives: None IndentWidth: 8 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 8 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true # Taken from git's rules PenaltyBreakAssignment: 10 PenaltyBreakBeforeFirstCallParameter: 30 PenaltyBreakComment: 10 PenaltyBreakFirstLessLess: 0 PenaltyBreakString: 10 PenaltyExcessCharacter: 100 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right ReflowComments: false SortIncludes: false SortUsingDeclarations: false SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatementsExceptForEachMacros SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp03 TabWidth: 8 UseTab: Always ... fuse-3.18.2/.codespellrc0000644000175000017500000000101515156613252014020 0ustar berndbernd[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 # - re-used: intentional hyphenation # - re-using: intentional hyphenation ignore-words-list = alse,siz,fiter,re-used,re-using fuse-3.18.2/.dir-locals.el0000644000175000017500000000523615156613252014162 0ustar berndbernd((python-mode . ((indent-tabs-mode . nil))) (c++-mode . ((c-file-style . "linux-kernel") (c-basic-offset . 8) (c-label-minimum-indentation . 0) (c-offsets-alist . ( (arglist-close . c-lineup-arglist-tabs-only) (arglist-cont-nonempty . (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) (arglist-intro . +) (brace-list-intro . +) (c . c-lineup-C-comments) (case-label . 0) (comment-intro . c-lineup-comment) (cpp-define-intro . +) (cpp-macro . -1000) (cpp-macro-cont . +) (defun-block-intro . +) (else-clause . 0) (func-decl-cont . +) (inclass . +) (inher-cont . c-lineup-multi-inher) (knr-argdecl-intro . 0) (label . -1000) (statement . 0) (statement-block-intro . +) (statement-case-intro . +) (statement-cont . +) (substatement . +) )) (indent-tabs-mode . t) (show-trailing-whitespace . t) )) (c-mode . ((c-file-style . "linux-kernel") (c-basic-offset . 8) (c-label-minimum-indentation . 0) (c-offsets-alist . ( (arglist-close . c-lineup-arglist-tabs-only) (arglist-cont-nonempty . (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) (arglist-intro . +) (brace-list-intro . +) (c . c-lineup-C-comments) (case-label . 0) (comment-intro . c-lineup-comment) (cpp-define-intro . +) (cpp-macro . -1000) (cpp-macro-cont . +) (defun-block-intro . +) (else-clause . 0) (func-decl-cont . +) (inclass . +) (inher-cont . c-lineup-multi-inher) (knr-argdecl-intro . 0) (label . -1000) (statement . 0) (statement-block-intro . +) (statement-case-intro . +) (statement-cont . +) (substatement . +) )) (indent-tabs-mode . t) (show-trailing-whitespace . t) )) ) fuse-3.18.2/AUTHORS0000644000175000017500000002470515156613252012603 0ustar berndberndCurrent Maintainers ------------------ Bernd Schubert Ashley Pittman Antonio SJ Musumeci Past Maintainers ---------------- Nikolaus Rath (until 02/2024) Miklos Szeredi (until 12/2015) Contributors ------------ CUSE has been written by Tejun Heo . Furthermore, the following people have contributed patches (autogenerated list): 1c7718e7 a1346054 <36859588+a1346054@users.noreply.github.com> admorgan Ahmed Masud AKowshik Alan Somers Albert Chen <58009229+hselin-kalista-io@users.noreply.github.com> Albert Chen Aleksandr Mikhailov Alexander alex Alex Richman Amir Goldstein amosonn Anatol Pomozov André Schröder Andrew Gaul Andrew Gaul Angelo G. Del Regno Anthony Rebello Antonio SJ Musumeci Arunav Sanyal asafkahlon <35964924+asafkahlon@users.noreply.github.com> Ashley Pittman AsumFace Banglang Baptiste Daroussin Benjamin Barenblat Bernd Schubert Bernd Schubert Bill Zissimooulos Bill Zissimopoulos bobrofon Brian Naylor Carl Edquist Carlos Maiolino Chad Austin Changli Gao Christian Menges Christopher Harrison Ciaran Consus Craig Chi Csaba Henk Csaba Henk cvs2git <> Dalvik Khertel Daniel Fullmer Daniel Thau David Galeano David McNab David Sheets dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Dharmendra singh Dharmendra Singh divinity76 DrDaveD <2129743+DrDaveD@users.noreply.github.com> Dr. David Alan Gilbert Emily Herbert Emmanuel Dreyfus Enke Chen Eric Engestrom Eric Wong Etienne Dublé Fabian Vogt Fabrice Bauzac Fabrice Fontaine Fedor Korotkov Feng Shuo ferivoz <72023087+ferivoz@users.noreply.github.com> Feverfew Fina Wilke Florian Weimer Forty-Bot Frank Dinoff Giulio Benetti Giuseppe Scrivano Goswin von Brederlow guraga HazelFZ Heiko Becker Hendrik Brueckner HereThereBeDragons Hookey human ikbenlike Ikey Doherty itsdeepak Jan Blumschein Jann Horn Jay Hankins Jean-Pierre André Jean-Yves VET Jérémie Galarneau Joachim Schiele Joachim Schiele Joerg Thalheim John Baber-Lucero John Muir Joseph Dodge Josh Soref Junichi Uekawa Junichi Uekawa Junichi Uekawa Kangjing "Chaser" Huang Ken Schalk Kevin Vigor Kirill Smelkov Kyle Lippincott Laszlo Boszormenyi (GCS) Laszlo Papp Laurent Bigonville Lilo Huang Liu Bo Li-Wen Hsu lixiaokeng <63774002+lixiaokeng@users.noreply.github.com> lixiaokeng Luis Henriques Madan Valluri Manuel Jacob Marcin Sulikowski Mark Glines Martin Blanchard Martin Pärtel Mateusz Urbańczyk Matthias Goergens Matthias Görgens Mattias Nissler maxice8 <30738253+maxice8@users.noreply.github.com> Maximilian Heinzler Max Krasnyansky Michael Forney Michael Grigoriev Mihail Konev Miklos Szeredi Miklos Szeredi Miklos Szeredi Miklos Szeredi Misono Tomohiro mkmm@gmx-topmail.de mrdvdrm Natanael Copa Niels de Vos Nikola Petrov <73067824+Petrov22Nikola@users.noreply.github.com> Nikolaus Rath Nozomi Miyamori <99280467+nm004@users.noreply.github.com> Oded Arbel Olivier Blin pablomh Pedro Kaj Kjellerup Nacht Pedro Nacht Peri Peter Lemenkov philmd Pierre Labastie Przemyslaw Pawelczyk Przemysław Pawełczyk psykose Ratna_Bolla@dell.com Rethan <359062468@qq.con> Reuben Hawkins rfjakob richardweinberger Richard W.M. Jones Riku Voipio Robo Shimmer Roland Bauerschmidt Roman Bogorodskiy Rosen Penev Rostislav Rostislav Skudnov Rudi Heitbaum Sam Huffman <40582525+samh-sifive@users.noreply.github.com> Sam James Sam Stuewe Sangwoo Moon Sarath Lakshman Sargun Dhillon scosu Scott Worley Sebastian Pipping Sergey Fedoseev Seunghoon Yeon Sławek Rudnicki Stefan Hajnoczi Stefan Hajnoczi Stephen Kitt Tej Chajed tenzap <46226844+tenzap@users.noreply.github.com> therealneworld@gmail.com Tobias Nießen Tofik Sonono Tomasz Kulasek <34129113+tkulasek@users.noreply.github.com> Tom Callaway Tom Callaway Tomohiro Kusumi userwithuid Valentin Plugaru Vivek Goyal Waldir Pimenta wdlkmpx William Woodruff Winfried Koehler winndows Xiubo Li Yaroslav Halchenko y Yuri Per Zhansong Gao Zhiqiang Liu zsugabubus # New authors since fuse-3.16.2 farlongsignal <141166749+farlongsignal@users.noreply.github.com> yangyun50 <149988609+yangyun50@users.noreply.github.com> bigbrotherwei <1965867461@qq.com> Caian Benedicto <2220062+Caian@users.noreply.github.com> desertwitch <24509509+desertwitch@users.noreply.github.com> SteveYang <40466358+SteveY4ng@users.noreply.github.com> FredyVia <942513309@qq.com> legezywzh <94814730+legezywzh@users.noreply.github.com> CismonX amitgeron Bernd Schubert Daniel Rosenberg Horst Birthelmer Joanne Koong Josef Bacik Matthew gandalfs_cat MJ Harvey Nils Norman Wilson leipeng Vladimir Serbinenko George Hilliard Tyler Hall yangyun Abhishek # New authors since fuse-3.17.1 Luis Henriques Zegang swj <1186093704@qq.com> Gleb Popov <6yearold@gmail.com> WekaJosh <80121792+WekaJosh@users.noreply.github.com> Alexander Monakov Ben Dooks Ben Linsay Dave Vasilevsky Darrick J. Wong Georgi Valkov Alik Aslanyan jnr0006 Jingbo Xu Long Li Maksim Harbachou Meng Lu Wang Vassili Tchersky Vassili Tchersky izxl007 Zeno Sebastian Endemann # New authors since fuse-3.18.1 Abhinav Agarwal fuse-3.18.2/ChangeLog.rst0000644000175000017500000010231715156613252014110 0ustar berndberndlibfuse 3.18.2 (2026-03-18) =========================== * Fix two io-uring issues that might be security critical * fuse-io-uring: Fix UAF and NULL deref in startup error path * fuse-io-uring: Fix NULL deref and memory leak in fuse_uring_init_queue libfuse 3.18.1 (2025-12-20) =========================== * Fix a critical ABI issue compared to libfuse-3.17.3+ * Note: This breaks ABI compatibility to libfuse-3.18.0 (given that 3.18.0 is out for 2 days only, probably the lesser evil) libfuse 3.18.0 (2025-12-18) =========================== New Features ------------ * fuse-over-io-uring communication * statx support * Request timeouts: Prevent hung operations * FUSE_NOTIFY_INC_EPOCH: New notification mechanism for epoch counters Important Fixes ---------------- * Fixed double unmount on FUSE_DESTROY * Fixed junk readdirplus results when filesystem doesn't fill stat info * Fixed memory deallocation in fuse_session_loop_remember * Fixed COPY_FILE_RANGE interface Platform Support ---------------- * Improved FreeBSD support (mount error reporting, test runner, build fixes) * Fixed 32-bit architecture builds * Fixed build with musl libc and older kernels (< 5.9) Other Improvements ------------------ * Added PanFS to fusermount whitelist * Thread naming support for easier debugging libfuse 3.17.4 (2025-08-19) =========================== - Try to detect mount-utils by checking for /run/mount/utab and don't try to update mtab if it does not exist - Fix a build warning when HAVE_BACKTRACE is undefined - fuse_loop_mt.c: fix close-on-exec flag on clone fd - Remove struct size assertions from fuse_common.h libfuse 3.17.3 (2025-07-16) =========================== * more conn->want / conn->want_ext conversion fixes * Fix feature detection for close_range * Avoid double unmount on FUSE_DESTROY libfuse 3.17.2 (2025-04-23) =========================== * Fixed uninitized bufsize value (compilation warning and real issue when HAVE_SPLICE was not defined) * Fixed initialization races related to buffer realocation when large buf sizes are used (/proc/sys/fs/fuse/max_pages_limit) * Fix build with kernel < 5.9 * Fix static_assert build failure with C++ version < 11 * Compilation fix (remove second fuse_main_real_versioned declaration) * Another conn.want flag conversion fix for high-level applications * Check if pthread_setname_np() exists before use it * fix example/memfs_ll rename deadlock error * signal handlers: Store fuse_session unconditionally and restore previous behavior that with multiple sessions the last session was used for the signal exist handler libfuse 3.17.1 (2025-03-24) =========================== * fuse: Fix want conn.want flag conversion * Prevent re-usage of stdio FDs for fusermount * PanFS added to fusermount whitelist libfuse 3.17.1-rc1 (2025-02-18) =============================== * several BSD fixes * x86 (32bit) build fixes * nested declarations moved out of the inlined functions to avoid build warnings * signify public key added for future 3.18 libfuse 3.17.1-rc0 (2025-02.10) =============================== * Fix libfuse build with FUSE_USE_VERSION 30 * Fix build of memfs_ll without manual meson reconfigure * Fix junk readdirplus results when filesystem not filling stat info * Fix conn.want_ext truncation to 32bit * Fix some build warnings with -Og * Fix fuse_main_real symbols * Several changes related to functions/symbols that added in the libfuse version in 3.17 * Add thread names to libfuse threads * With auto-umounts the FUSE_COMMFD2 (parent process fd is exported to be able to silence leak checkers libfuse 3.17 (2025-01-01, not officially releaesed) ================================================== * 3.11 and 3.14.2 introduced ABI incompatibilities, the ABI is restored to 3.10, .so version was increased since there were releases with the incompatible ABI * The libfuse version a program was compiled against is now encoded into that program, using inlined functions in fuse_lowlevel.h and fuse.h * Allows to handle fatal signals and to print a backtrace. New API function: fuse_set_fail_signal_handlers() * Allows fuse_log() messages to be send to syslog instead of stderr New API functions: fuse_log_enable_syslog() and fuse_log_close_syslog() * Handle buffer misalignment for FUSE_WRITE * Added support for filesystem passthrough read/write of files when FUSE_PASSTHROUGH capability is enabled New API functions: fuse_passthrough_open() and fuse_passthrough_close(), also see example/passthrough_hp.cc * Added fmask and dmask options to high-level API - dmask: umask applied to directories - fmask: umask applied to non-directories * Added FUSE_FILL_DIR_DEFAULTS enum to support C++ programs using fuse_fill_dir_t function * Added support for FUSE_CAP_HANDLE_KILLPRIV_V2 Fixes: * Fixed compilation failure on FreeBSD (mount_bsd.c now points to correct header) libfuse 3.16.2 (2023-10-10) =========================== * Various small fixes and improvements. 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) =========================== * Building of unit tests is now optional. * Fixed a test failure when running tests under XFS. * Fixed memory leaks in examples. * Minor documentation fixes. libfuse 3.10.3 (2021-04-12) =========================== * Fix returning d_ino and d_type from readdir(3) in non-plus mode libfuse 3.10.2 (2021-02-05) =========================== * Allow "nonempty" as a mount option, for backwards compatibility with fusermount 2. The option has no effect since mounting over non-empty directories is allowed by default. * Fix returning inode numbers from readdir() in offset==0 mode. * FUSE filesystems can now be mounted underneath EXFAT mountpoints. * Various minor bugfixes. libfuse 3.10.1 (2020-12-07) =========================== * Various minor bugfixes. libfuse 3.10.0 (2020-10-09) =========================== * Add FUSE_CAP_CACHE_SYMLINKS: allow caching symlinks in kernel page cache. * Various minor bugfixes and improvements. libfuse 3.9.4 (2020-08-09) ========================== This was an "accidental" release, it is equivalent to 3.9.3. libfuse 3.9.3 (2020-08-09) ========================== * Fixed compilation under OS X and µClibc. * Minor bugfixes and doc updates. libfuse 3.9.2 (2020-06-12) ========================== * Remove obsolete workarounds in examples. * Do not require C++ compiler for building. * Minor bugfixes. libfuse 3.9.1 (2020-03-19) =========================== * Fixed memory leak in fuse_session_new(). * Fixed an issue with the linker version script. * Make ioctl prototype conditional on FUSE_USE_VERSION. Define FUSE_USE_VERSION < 35 to get old ioctl prototype with int commands; define FUSE_USE_VERSION >= 35 to get new ioctl prototype with unsigned int commands. * Various small bugfixes. libfuse 3.9.0 (2019-12-14) ========================== * Added support for FUSE_EXPLICIT_INVAL_DATA to enable only invalidate cached pages on explicit request. libfuse 3.8.0 (2019-11-03) ========================== * Added support for FUSE_LSEEK operation which can be used to report holes in sparse files. libfuse 3.7.0 (2019-09-27) ========================== * Added UFSD to whitelist (so users can now mount FUSE filesystems on mountpoints within UFSD filesystems). * Added custom log message handler function support so that libfuse applications can direct messages to syslog(3) or other logging systems. stderr remains the default. See `fuse_log.h` for the new API. libfuse 3.6.2 (2019-07-09) ========================== * The init script is now installed to /etc/ rather than /usr/local/etc by default. libfuse 3.6.1 (2019-06-13) ========================== * Fixed version number (release 3.6.0 was shipped with a declared version of 3.0.0). libfuse 3.6.0 (2019-06-13) ========================== * Added a new example (passthrough_hp). The functionality is similar to passthrough_ll, but the implementation focuses on performance and correctness rather than simplicity. * Added support for fuse kernel feature `max_pages` which allows to increase the maximum number of pages that can be used per request. This feature was introduced in kernel 4.20. `max_pages` is set based on the value in `max_write`. By default `max_write` will be 1MiB now for kernels that support `max_pages`. If you want smaller buffers or writes you have to set `max_write` manually. libfuse 3.5.0 (2019-04-16) ========================== * Changed ioctl commands to "unsigned int" in order to support commands which do not fit into a signed int. Commands issued by applications are still truncated to 32 bits. * Added SMB2 to whitelist (so users can now mount FUSE filesystems on mountpoints within SMB 2.0 filesystems). * Added a new `cache_readdir` flag to `fuse_file_info` to enable caching of readdir results. Supported by kernels 4.20 and newer. * Add support and documentation for FUSE_CAP_NO_OPENDIR_SUPPORT. libfuse 3.4.2 (2019-03-09) ========================== * Fixed a memory leak in `examples/passthrough_ll.c`. * Added OpenAFS to whitelist (so users can now mount FUSE filesystems on mountpoints within OpenAFS filesystems). * Added HFS+ to whitelist (so users can now mount FUSE filesystems on mountpoints within HFS+ filesystems). * Documentation improvements. libfuse 3.4.1 (2018-12-22) ========================== * The `examples/passthrough_ll.c` example filesystem has been significantly extended. * Support for `copy_file_range` has been added. * Build system updates for non-Linux systems. libfuse 3.4.0 ============= * Add `copy_file_range()` to support efficient copying of data from one file to an other. libfuse 3.3.0 (2018-11-06) ========================== * The `auto_unmount` mode now works correctly in combination with autofs. * The FUSE_CAP_READDIRPLUS_AUTO capability is no longer enabled by default unless the file system defines both a readdir() and a readdirplus() handler. * The description of the FUSE_CAP_READDIRPLUS_AUTO flag has been improved. * Allow open `/dev/fuse` file descriptors to be passed via mountpoints of the special format `/dev/fd/%u`. This allows mounting to be handled by the parent so the FUSE filesystem process can run fully unprivileged. * Add a `drop_privileges` option to mount.fuse3 which causes it to open `/dev/fuse` and mount the file system itself, then run the FUSE file filesystem fully unprivileged and unable to re-acquire privilege via setuid, fscaps, etc. * Documented under which conditions the `fuse_lowlevel_notify_*` functions may block. libfuse 3.2.6 (2018-08-31) ========================== * The fuse_main() function now returns more fine-grained error codes. * FUSE filesystems may now be mounted on mountpoint within bcachefs, aufs and FAT filesystems. * libfuse may now be used as a Meson subproject. * Fix a few low-impact memory leaks. * The `fuse.conf` file is no longer looked for in `/etc`, but in the *sysconfdir* directory (which can be set with `meson configure`). By default, the location is thus `/usr/local/etc/fuse.conf`. libfuse 3.2.5 (2018-07-24) ========================== * SECURITY UPDATE: In previous versions of libfuse it was possible to for unprivileged users to specify the `allow_other` option even when this was forbidden in `/etc/fuse.conf`. The vulnerability is present only on systems where SELinux is active (including in permissive mode). * The fusermount binary has been hardened in several ways to reduce potential attack surface. Most importantly, mountpoints and mount options must now match a hard-coded whitelist. It is expected that this whitelist covers all regular use-cases. * Added a test of `seekdir` to test_syscalls. * Fixed `readdir` bug when non-zero offsets are given to filler and the filesystem client, after reading a whole directory, re-reads it from a non-zero offset e. g. by calling `seekdir` followed by `readdir`. libfuse 3.2.4 (2018-07-11) ========================== * Fixed `rename` deadlock on FreeBSD. libfuse 3.2.3 (2018-05-11) ========================== * Fixed a number of compiler warnings. libfuse 3.2.2 (2018-03-31) ========================== * Added example fuse.conf file. * Added "support" for -o nofail mount option (the option is accepted and ignored). * Various small bugfixes. libfuse 3.2.1 (2017-11-14) ========================== * Various small bugfixes. libfuse 3.2.0 (2017-09-12) ========================== * Support for building with autotools has been dropped. * Added new `fuse_invalidate_path()` routine for cache invalidation from the high-level FUSE API, along with an example and tests. * There's a new `printcap` example that can be used to determine the capabilities of the running kernel. * `fuse_loop_mt()` now returns the minus the actual errno if there was an error (instead of just -1). * `fuse_loop()` no longer returns a positive value if the filesystem loop was terminated without errors or signals. * Improved documentation of `fuse_lowlevel_notify_*` functions. * `fuse_lowlevel_notify_inval_inode()` and `fuse_lowlevel_notify_inval_entry()` now return -ENOSYS instead of an undefined error if the function is not supported by the kernel. * Documented the special meaning of the *zero* offset for the fuse_fill_dir_t function. * The `passthrough_fh` example now works under FreeBSD. * libfuse can now be build without libiconv. * Fixed support for `FUSE_CAP_POSIX_ACL`: setting this capability flag had no effect in the previous versions of libfuse 3.x; now ACLs should actually work. * Fixed a number of compilation problems under FreeBSD. * Fixed installation directory for udev rules. * Fixed compilation with LTO. libfuse 3.1.1 (2017-08-06) ========================== * Documentation: clarified how filesystems are supposed to process open() and create() flags (see include/fuse_lowlevel.h). * Fixed a compilation problem of the passthrough_ll example on 32 bit systems (wrong check and wrong error message). * pkg-config is now used to determine the proper directory for udev rules. * Fixed a symbol versioning problem that resulted in very strange failures (segfaults, unexpected behavior) in different situations. * Fixed a test failure when /tmp is on btrfs. * The maximum number of idle worker threads used by `fuse_loop_mt()` is now configurable. * `fuse_loop_mt()` and `fuse_session_loop_mt()` now take a `struct fuse_loop_config` parameter that supersedes the *clone_fd* parameter. * Incorporated several patches from the FreeBSD port. libfuse should now compile under FreeBSD without the need for patches. * The passthrough_ll example now supports writeback caching. libfuse 3.1.0 (2017-07-08) ========================== * Added new `fuse_lib_help()` function. File-systems that previously passed a ``--help`` option to `fuse_new()` must now process the ``--help`` option internally and call `fuse_lib_help()` to print the help for generic FUSE options. * Fixed description of the `fuse_conn_info->time_gran`. The default value of zero actually corresponds to full nanosecond resolution, not one second resolution. * The init script is now installed into the right location (``$DESTDIR/etc/init.d`` rather than ``$prefix/$sysconfdir/init.d``) * The `example/passthrough_ll` filesystem now supports creating and writing to files. * `fuse_main()` / `fuse_remove_signal_handlers()`: do not reset `SIGPIPE` handler to `SIG_DFL` if it was not set by us. * Documented the `RENAME_EXCHANGE` and `RENAME_NOREPLACE` flags that may be passed to the `rename` handler of both the high- and low-level API. Filesystem authors are strongly encouraged to check that these flags are handled correctly. libfuse 3.0.2 (2017-05-24) ========================== * Option parsing for the high-level API now works correctly (previously, default values would override specified values). * Tests should now build (and run) under FreeBSD. * Improved documentation of `struct fuse_context` * Internal: calculate request buffer size from page size and kernel page limit instead of using hardcoded 128 kB limit. libfuse 3.0.1 (2017-04-10) ========================== * Re-introduced *examples/null.c*. * Added experimental support for building with Meson. * Document that `-o auto_unmount` implies `-o nodev,nosuid`. * Document that the *use_ino* option of the high-level interface does not affect the inode that libfuse and the kernel use internally. * Fixed test cases for passthrough* examples (they weren't actually testing the examples). * Fixed several bugs in the passthrough* examples. libfuse 3.0.0 (2016-12-08) ========================== * NOTE TO PACKAGERS: libfuse 3 is designed to be co-installable with libfuse 2. However, some files will be installed by both libfuse 2 and libfuse 3 (e.g. /etc/fuse.conf, the udev and init scripts, and the mount.fuse(8) manpage). These files should be taken from libfuse 3. The format/content is guaranteed to remain backwards compatible with libfuse 2. We recommend to ship libfuse2 and libfuse3 in three separate packages: a libfuse-common package that contains files shared by libfuse 2+3 (taken from the libfuse3 tarball), and libfuse2 and libfuse3 packages that contain the shared library and helper programs for the respective version. * Fixed test errors when running tests as root. * Made check for util-linux version more robust. * Added documentation for all fuse capability flags (`FUSE_CAP_*`) and `struct fuse_conn_info` fields. * fuse_loop(), fuse_loop_mt(), fuse_session_loop() and fuse_session_loop_mt() now return more detailed error codes instead of just -1. See the documentation of fuse_session_loop() for details. * The FUSE main loop is now aborted if the file-system requests capabilities that are not supported by the kernel. In this case, the session loop is exited with a return code of ``-EPROTO``. * Most file-system capabilities that were opt-in in libfuse2 are now enabled by default. Filesystem developers are encouraged to review the documentation of the FUSE_CAP_* features to ensure that their filesystem is compatible with the new semantics. As before, a particular capability can still be disabled by unsetting the corresponding bit of `fuse_conn_info.wants` in the init() handler. * Added FUSE_CAP_PARALLEL_DIROPS and FUSE_CAP_POSIX_ACL, FUSE_HANDLE_KILLPRIV feature flags. * FUSE filesystems are now responsible for unsetting the setuid/setgid flags when a file is written, truncated, or its owner changed. Previously, this was handled by the kernel but subject to race conditions. * The fusermount and mount.fuse binaries have been renamed to fusermount3 and mount.fuse3 to allow co-installation of libfuse 2.x and 3.x * Added a `max_read` field to `struct fuse_conn_info`. For the time being, the maximum size of read requests has to be specified both there *and* passed to fuse_session_new() using the ``-o max_read=`` mount option. At some point in the future, specifying the mount option will no longer be necessary. * Documentation: clarified that the fuse_argv structure that is passed to `fuse_new()` and `fuse_lowlevel_new()` must always contain at least one element. * The high-level init() handler now receives an additional struct fuse_config pointer that can be used to adjust high-level API specific configuration options. * The `nopath_flag` field of struct fuse_operations has been removed. Instead, a new `nullpath_ok` flag can now be set in struct fuse_config. * File systems that use the low-level API and support lookup requests for '.' and '..' should continue make sure to set the FUSE_CAP_EXPORT_SUPPORT bit in fuse_conn_info->want. (This has actually always been the case, but was not very obvious from the documentation). * The help text generated by fuse_lowlevel_help(), fuse_new() (and indirectly fuse_main()) no longer includes options that are unlikely to be of interest to end-users. The full list of accepted options is now included in the respective function's documentation (located in the fuse.h/fuse_lowlevel.h and doc/html). * 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 ``-o large_read`` mount option has been dropped. Hopefully no one uses a Linux 2.4 kernel anymore. * The `-o nonempty` mount point has been removed, mounting over non-empty directories is now always allowed. This brings the behavior of FUSE file systems in-line with the behavior of the regular `mount` command. File systems that do not want to allow mounting to non-empty directories should perform this check themselves before handing control to libfuse. * The chmod, chown, truncate, utimens and getattr handlers of the high-level API now all receive an additional struct fuse_file_info pointer (which, however, may be NULL even if the file is currently open). The fgetattr and ftruncate handlers have become obsolete and have been removed. * 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. * For low-level file systems that implement the `write_buf` handler, the `splice_read` option is now enabled by default. As usual, this can be changed in the file system's `init` handler. * The treatment of low-level options has been made more consistent: Options that can be set in the init() handler (via the fuse_conn_info parameter) can now be set only here, i.e. fuse_session_new() no longer accepts arguments that change the fuse_conn_info object before or after the call do init(). As a side effect, this removes the ambiguity where some options can be overwritten by init(), while others overwrite the choices made by init(). For file systems that wish to offer command line options for these settings, the new fuse_parse_conn_info_opts() and fuse_apply_conn_info_opts() functions are available. Consequently, the fuse_lowlevel_help() method has been dropped. * The `async_read` field in `struct fuse_conn_info` has been removed. To determine if the kernel supports asynchronous reads, file systems should check the `FUSE_CAP_ASYNC_READ` bit of the `capable` field. To enable/disable asynchronous reads, file systems should set the flag in the `wanted` field. * The `fuse_parse_cmdline` function no longer prints out help when the ``--verbose`` or ``--help`` flags are given. This needs to be done by the file system (e.g. using the `fuse_cmdline_help()` and `fuse_lowlevel_help()` functions). * Added ``example/cuse_client.c`` to test ``example/cuse.c``. * Removed ``example/null.c``. This has not been working for a while for unknown reasons -- maybe because it tries to treat the mountpoint as a file rather than a directory? * There are several new examples that demonstrate the use of the ``fuse_lowlevel_notify_*`` functions: - ``example/notify_store_retrieve.c`` - ``example/notify_inval_inode.c`` - ``example/notify_inval_entry.c`` * The ``-o big_writes`` mount option has been removed. It is now always active. File systems that want to limit the size of write requests should use the ``-o max_write=`` option instead. * The `fuse_lowlevel_new` function has been renamed to `fuse_session_new` and no longer interprets the --version or --help options. To print help or version information, use the new `fuse_lowlevel_help` and `fuse_lowlevel_version` functions. * The ``allow_other`` and ``allow_root`` mount options (accepted by `fuse_session_new()`) may now be specified together. In this case, ``allow_root`` takes precedence. * There are new `fuse_session_unmount` and `fuse_session_mount` functions that should be used in the low-level API. The `fuse_mount` and `fuse_unmount` functions should be used with the high-level API only. * Neither `fuse_mount` nor `fuse_session_mount` take struct fuse_opts parameters anymore. Mount options are parsed by `fuse_new` (for the high-level API) and `fuse_session_new` (for the low-level API) instead. To print help or version information, use the new `fuse_mount_help` and `fuse_mount_version` functions. * The ``fuse_lowlevel_notify_*`` functions now all take a `struct fuse_session` parameter instead of a `struct fuse_chan`. * The channel interface (``fuse_chan_*`` functions) has been made private. As a result, the typical initialization sequence of a low-level file system has changed from :: ch = fuse_mount(mountpoint, &args); se = fuse_lowlevel_new(&args, &lo_oper, sizeof(lo_oper), &lo); fuse_set_signal_handlers(se); fuse_session_add_chan(se, ch); fuse_daemonize(fg); if (mt) fuse_session_loop_mt(se); else fuse_session_loop(se); fuse_remove_signal_handlers(se); fuse_session_remove_chan(ch); fuse_session_destroy(se); fuse_unmount(mountpoint, ch); to :: se = fuse_session_new(&args, &ll_ops, sizeof(ll_ops), NULL); fuse_set_signal_handlers(se); fuse_session_mount(se, mountpoint); fuse_daemonize(fg); if (mt) fuse_session_loop_mt(se); else fuse_session_loop(se); fuse_remove_signal_handlers(se); fuse_session_unmount(se); fuse_lowlevel_destroy(se); The typical high-level setup has changed from :: ch = fuse_mount(*mountpoint, &args); fuse = fuse_new(ch, &args, op, op_size, user_data); se = fuse_get_session(fuse); fuse_set_signal_handlers(se); fuse_daemonize(fg); if (mt) fuse_loop_mt(fuse); else fuse_loop(fuse); fuse_remove_signal_handlers(se); fuse_unmount(mountpoint, ch); fuse_destroy(fuse); to :: fuse = fuse_new(&args, op, op_size, user_data); se = fuse_get_session(fuse); fuse_set_signal_handlers(se); fuse_mount(fuse, mountpoint); fuse_daemonize(fg); if (mt) fuse_loop_mt(fuse); else fuse_loop(fuse); fuse_remove_signal_handlers(se); fuse_unmount(fuse); fuse_destroy(fuse); File systems that use `fuse_main` are not affected by this change. For integration with custom event loops, the new `fuse_session_fd` function provides the file descriptor that's used for communication with the kernel. * Added *clone_fd* option. This creates a separate device file descriptor for each processing thread, which might improve performance. * Added *writeback_cache* option. With kernel 3.14 and newer this enables write-back caching which can significantly improve performance. * Added *async_dio* option. With kernel 3.13 and newer, this allows direct I/O to be done asynchronously. * The (high- and low-level) `rename` handlers now takes a *flags* parameter (with values corresponding to the *renameat2* system call introduced in Linux 3.15). * The "ulockmgr_server" has been dropped. * There is a new (low-level) `readdirplus` handler, with a corresponding example in ``examples/fuse_lo-plus.c`` and a new `fuse_add_direntry_plus` API function. * The (high-level) `readdir` handler now takes a *flags* argument. * The (high-level) `filler` function passed to `readdir` now takes an additional *flags* argument. * The (high-level) `getdir` handler has been dropped. * The *flag_nullpath_ok* and *flag_utime_omit_ok* flags have been dropped. * The (high-level) *utime* handler has been dropped. * The `fuse_invalidate` function has been removed. * The `fuse_is_lib_option` function has been removed. * The *fh_old* member of `struct fuse_file_info` has been dropped. * The type of the *writepage* member of `struct fuse_file_info` was changed from *int* to *unsigned int*. * The `struct fuse_file_info` gained a new *poll_events* member. * There is a new `fuse_pkgversion` function. * 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*` changed from *unsigned* to *uint64_t*. * The (low-level) `setattr` handler gained a *FUSE_SET_ATTR_CTIME* bit *for its *to_set* parameter. * The `struct fuse_session_ops` data structure has been dropped. * The documentation has been clarified and improved in many places. FUSE 2.9.7 (2016-06-20) ======================= * Added SELinux support. * Fixed race-condition when session is terminated right after starting a FUSE file system. FUSE 2.9.6 (2016-04-23) ======================= * Tarball now includes documentation. * Shared-object version has now been bumped correctly. FUSE 2.9.5 (2016-01-14) ======================= * New maintainer: Nikolaus Rath . Many thanks to Miklos Szeredi for bringing FUSE to where it is now! * fix warning in mount.c:receive_fd(). Reported by Albert Berger * fix possible memory leak. Reported by Jose R. Guzman FUSE 2.9.4 (2015-05-22) ======================= * fix exec environment for mount and umount. Found by Tavis Ormandy (CVE-2015-3202). * fix fuse_remove_signal_handlers() to properly restore the default signal handler. Reported by: Chris Johnson * highlevel API: fix directory file handle passed to ioctl() method. Reported by Eric Biggers * libfuse: document deadlock avoidance for fuse_notify_inval_entry() and fuse_notify_delete() * fusermount, libfuse: send value as unsigned in "user_id=" and "group_id=" options. Uids/gids larger than 2147483647 would result in EINVAL when mounting the filesystem. This also needs a fix in the kernel. * Initialize stat buffer passed to ->getattr() and ->fgetattr() to zero in all cases. Reported by Daniel Iwan * libfuse: Add missing includes. This allows compiling fuse with musl. Patch by Daniel Thau Older Versions (before 2013-01-01) ================================== Please see Git history, e.g. at https://github.com/libfuse/libfuse/blob/fuse_2_9_3/ChangeLog. fuse-3.18.2/GPL2.txt0000644000175000017500000004325415156613252013000 0ustar berndbernd GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. fuse-3.18.2/LGPL2.txt0000644000175000017500000006364215156613252013117 0ustar berndbernd GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! fuse-3.18.2/LICENSE0000644000175000017500000000066315156613252012535 0ustar berndberndThe following files may be used under the terms of the GNU Lesser General Public License, version 2.1 ("LGPL"): - All files in the include/ directory. - All files in the lib/ directory. - meson.build The full terms of the LGPL can be found in the LGPL2.txt file. All other files may be used only under the terms of the GNU General Public License, version 2 ("GPL"). The full text of this license can be found in the GPL2.txt file. fuse-3.18.2/README.md0000644000175000017500000001446015156613252013007 0ustar berndberndlibfuse ======= About ----- FUSE (Filesystem in Userspace) is an interface for userspace programs to export a filesystem to the Linux kernel. The FUSE project consists of two components: the *fuse* kernel module (maintained in the regular kernel repositories) and the *libfuse* userspace library (maintained in this repository). libfuse provides the reference implementation for communicating with the FUSE kernel module. A FUSE file system is typically implemented as a standalone application that links with libfuse. libfuse provides functions to mount the file system, unmount it, read requests from the kernel, and send responses back. libfuse offers two APIs: a "high-level", synchronous API, and a "low-level" asynchronous API. In both cases, incoming requests from the kernel are passed to the main program using callbacks. When using the high-level API, the callbacks may work with file names and paths instead of inodes, and processing of a request finishes when the callback function returns. When using the low-level API, the callbacks must work with inodes and responses must be sent explicitly using a separate set of API functions. Development Status ------------------ libfuse is shipped by all major Linux distributions and has been in production use across a wide range of systems for many years. However, at present libfuse does not have any active, regular contributors. The current maintainer continues to apply pull requests and makes regular releases, but unfortunately has no capacity to do any development beyond addressing high-impact issues. When reporting bugs, please understand that unless you are including a pull request or are reporting a critical issue, you will probably not get a response. If you are using libfuse, please consider contributing to the project. Supported Platforms ------------------- * Linux (fully) * BSD (mostly/best-effort) * For OS-X, please use [OSXFUSE](https://osxfuse.github.io/) 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 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 setup .. Normally, the default build options will work fine. If you nevertheless want to adjust them, you can do so with the *meson configure* command: $ meson configure # list options $ meson configure -D disable-mtab=true # set an optionq $ # ensure all meson options are applied to the final build system $ meson setup --reconfigure ../ To build, test, and install libfuse, you then use Ninja: $ ninja $ sudo python3 -m pytest test/ $ sudo ninja install Running the tests requires the [py.test](http://www.pytest.org/) Python module. Instead of running the tests as root, the majority of tests can also be run as a regular user if *util/fusermount3* is made setuid root first: $ sudo chown root:root util/fusermount3 $ sudo chmod 4755 util/fusermount3 $ python3 -m pytest test/ Security implications --------------------- The *fusermount3* program is installed setuid root. This is done to allow normal users to mount their own filesystem implementations. To limit the harm that malicious users can do this way, *fusermount3* enforces the following limitations: - The user can only mount on a mountpoint for which they have write permission - The mountpoint must not be a sticky directory which isn't owned by the user (like /tmp usually is) - No other user (including root) can access the contents of the mounted filesystem (though this can be relaxed by allowing the use of the *allow_other* and *allow_root* mount options in */etc/fuse.conf*) If you intend to use the *allow_other* mount options, be aware that FUSE has an unresolved [security bug](https://github.com/libfuse/libfuse/issues/15): if the *default_permissions* mount option is not used, the results of the first permission check performed by the file system for a directory entry will be re-used for subsequent accesses as long as the inode of the accessed entry is present in the kernel cache - even if the permissions have since changed, and even if the subsequent access is made by a different user. This is of little concern if the filesystem is accessible only to the mounting user (which has full access to the filesystem anyway), but becomes a security issue when other users are allowed to access the filesystem (since they can exploit this to perform operations on the filesystem that they do not actually have permissions for). This bug needs to be fixed in the Linux kernel and has been known since 2006 but unfortunately no fix has been applied yet. If you depend on correct permission handling for FUSE file systems, the only workaround is to use `default_permissions` (which does not currently support ACLs), or to completely disable caching of directory entry attributes. Building your own filesystem ------------------------------ FUSE comes with several example file systems in the `example` directory. For example, the *passthrough* examples mirror the contents of the root directory under the mountpoint. Start from there and adapt the code! The documentation of the API functions and necessary callbacks is mostly contained in the files `include/fuse.h` (for the high-level API) and `include/fuse_lowlevel.h` (for the low-level API). An autogenerated html version of the API is available in the `doc/html` directory and at http://libfuse.github.io/doxygen. Getting Help ------------ If you need help, please ask on the mailing list (subscribe at https://lists.sourceforge.net/lists/listinfo/fuse-devel). Please report any bugs on the GitHub issue tracker at https://github.com/libfuse/libfuse/issues. fuse-3.18.2/SECURITY.md0000644000175000017500000000155615156613252013323 0ustar berndbernd# 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)).fuse-3.18.2/checkpatch.pl0000755000175000017500000072310015156613252014163 0ustar berndbernd#!/usr/bin/env perl # SPDX-License-Identifier: GPL-2.0 # # (c) 2001, Dave Jones. (the file handling bit) # (c) 2005, Joel Schopp (the ugly bit) # (c) 2007,2008, Andy Whitcroft (new conditions, test suite) # (c) 2008-2010 Andy Whitcroft # (c) 2010-2018 Joe Perches use strict; use warnings; use POSIX; use File::Basename; use Cwd 'abs_path'; use Term::ANSIColor qw(:constants); use Encode qw(decode encode); my $P = $0; my $D = dirname(abs_path($P)); my $V = '0.32'; use Getopt::Long qw(:config no_auto_abbrev); my $quiet = 0; my $verbose = 0; my %verbose_messages = (); my %verbose_emitted = (); my $tree = 1; my $chk_signoff = 1; my $chk_fixes_tag = 1; my $chk_patch = 1; my $tst_only; my $emacs = 0; my $terse = 0; my $showfile = 0; my $file = 0; my $git = 0; my %git_commits = (); my $check = 0; my $check_orig = 0; my $summary = 1; my $mailback = 0; my $summary_file = 0; my $show_types = 0; my $list_types = 0; my $fix = 0; my $fix_inplace = 0; my $root; my $gitroot = $ENV{'GIT_DIR'}; $gitroot = ".git" if !defined($gitroot); my %debug; my %camelcase = (); my %use_type = (); my @use = (); my %ignore_type = (); my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; my $max_line_length = 100; my $ignore_perl_version = 0; my $minimum_perl_version = 5.10.0; my $min_conf_desc_length = 4; my $spelling_file = "$D/spelling.txt"; my $codespell = 0; my $codespellfile = "/usr/share/codespell/dictionary.txt"; my $user_codespellfile = ""; my $conststructsfile = "$D/const_structs.checkpatch"; my $docsfile = "$D/../Documentation/dev-tools/checkpatch.rst"; my $typedefsfile; my $color = "auto"; my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE # git output parsing needs US English output, so first set backtick child process LANGUAGE my $git_command ='export LANGUAGE=en_US.UTF-8; git'; my $tabsize = 8; my ${CONFIG_} = "CONFIG_"; my %maybe_linker_symbol; # for externs in c exceptions, when seen in *vmlinux.lds.h sub help { my ($exitcode) = @_; print << "EOM"; Usage: $P [OPTION]... [FILE]... Version: $V Options: -q, --quiet quiet -v, --verbose verbose mode --no-tree run without a kernel tree --no-signoff do not check for 'Signed-off-by' line --no-fixes-tag do not check for 'Fixes:' tag --patch treat FILE as patchfile (default) --emacs emacs compile window format --terse one line per report --showfile emit diffed file position, not input file position -g, --git treat FILE as a single commit or git revision range single git commit with: ^ ~n multiple git commits with: .. ... - git merges are ignored -f, --file treat FILE as regular source file --subjective, --strict enable more subjective tests --list-types list the possible message types --types TYPE(,TYPE2...) show only these comma separated message types --ignore TYPE(,TYPE2...) ignore various comma separated message types --show-types show the specific message type in the output --max-line-length=n set the maximum line length, (default $max_line_length) if exceeded, warn on patches requires --strict for use with --file --min-conf-desc-length=n set the min description length, if shorter, warn --tab-size=n set the number of spaces for tab (default $tabsize) --root=PATH PATH to the kernel tree root --no-summary suppress the per-file summary --mailback only produce a report in case of warnings/errors --summary-file include the filename in summary --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of 'values', 'possible', 'type', and 'attr' (default is all off) --test-only=WORD report only warnings/errors containing WORD literally --fix EXPERIMENTAL - may create horrible results If correctable single-line errors exist, create ".EXPERIMENTAL-checkpatch-fixes" with potential errors corrected to the preferred checkpatch style --fix-inplace EXPERIMENTAL - may create horrible results Is the same as --fix, but overwrites the input file. It's your fault if there's no backup or git --ignore-perl-version override checking of perl version. expect runtime errors. --codespell Use the codespell dictionary for spelling/typos (default:$codespellfile) --codespellfile Use this codespell dictionary --typedefsfile Read additional types from this file --color[=WHEN] Use colors 'always', 'never', or only when output is a terminal ('auto'). Default is 'auto'. --kconfig-prefix=WORD use WORD as a prefix for Kconfig symbols (default ${CONFIG_}) -h, --help, --version display this help and exit When FILE is - read standard input. EOM exit($exitcode); } sub uniq { my %seen; return grep { !$seen{$_}++ } @_; } sub list_types { my ($exitcode) = @_; my $count = 0; local $/ = undef; open(my $script, '<', abs_path($P)) or die "$P: Can't read '$P' $!\n"; my $text = <$script>; close($script); my %types = (); # Also catch when type or level is passed through a variable while ($text =~ /(?:(\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) { if (defined($1)) { if (exists($types{$2})) { $types{$2} .= ",$1" if ($types{$2} ne $1); } else { $types{$2} = $1; } } else { $types{$2} = "UNDETERMINED"; } } print("#\tMessage type\n\n"); if ($color) { print(" ( Color coding: "); print(RED . "ERROR" . RESET); print(" | "); print(YELLOW . "WARNING" . RESET); print(" | "); print(GREEN . "CHECK" . RESET); print(" | "); print("Multiple levels / Undetermined"); print(" )\n\n"); } foreach my $type (sort keys %types) { my $orig_type = $type; if ($color) { my $level = $types{$type}; if ($level eq "ERROR") { $type = RED . $type . RESET; } elsif ($level eq "WARN") { $type = YELLOW . $type . RESET; } elsif ($level eq "CHK") { $type = GREEN . $type . RESET; } } print(++$count . "\t" . $type . "\n"); if ($verbose && exists($verbose_messages{$orig_type})) { my $message = $verbose_messages{$orig_type}; $message =~ s/\n/\n\t/g; print("\t" . $message . "\n\n"); } } exit($exitcode); } my $conf = which_conf($configuration_file); if (-f $conf) { my @conf_args; open(my $conffile, '<', "$conf") or warn "$P: Can't find a readable $configuration_file file $!\n"; while (<$conffile>) { my $line = $_; $line =~ s/\s*\n?$//g; $line =~ s/^\s*//g; $line =~ s/\s+/ /g; next if ($line =~ m/^\s*#/); next if ($line =~ m/^\s*$/); my @words = split(" ", $line); foreach my $word (@words) { last if ($word =~ m/^#/); push (@conf_args, $word); } } close($conffile); unshift(@ARGV, @conf_args) if @conf_args; } sub load_docs { open(my $docs, '<', "$docsfile") or warn "$P: Can't read the documentation file $docsfile $!\n"; my $type = ''; my $desc = ''; my $in_desc = 0; while (<$docs>) { chomp; my $line = $_; $line =~ s/\s+$//; if ($line =~ /^\s*\*\*(.+)\*\*$/) { if ($desc ne '') { $verbose_messages{$type} = trim($desc); } $type = $1; $desc = ''; $in_desc = 1; } elsif ($in_desc) { if ($line =~ /^(?:\s{4,}|$)/) { $line =~ s/^\s{4}//; $desc .= $line; $desc .= "\n"; } else { $verbose_messages{$type} = trim($desc); $type = ''; $desc = ''; $in_desc = 0; } } } if ($desc ne '') { $verbose_messages{$type} = trim($desc); } close($docs); } # Perl's Getopt::Long allows options to take optional arguments after a space. # Prevent --color by itself from consuming other arguments foreach (@ARGV) { if ($_ eq "--color" || $_ eq "-color") { $_ = "--color=$color"; } } GetOptions( 'q|quiet+' => \$quiet, 'v|verbose!' => \$verbose, 'tree!' => \$tree, 'signoff!' => \$chk_signoff, 'fixes-tag!' => \$chk_fixes_tag, 'patch!' => \$chk_patch, 'emacs!' => \$emacs, 'terse!' => \$terse, 'showfile!' => \$showfile, 'f|file!' => \$file, 'g|git!' => \$git, 'subjective!' => \$check, 'strict!' => \$check, 'ignore=s' => \@ignore, 'types=s' => \@use, 'show-types!' => \$show_types, 'list-types!' => \$list_types, 'max-line-length=i' => \$max_line_length, 'min-conf-desc-length=i' => \$min_conf_desc_length, 'tab-size=i' => \$tabsize, 'root=s' => \$root, 'summary!' => \$summary, 'mailback!' => \$mailback, 'summary-file!' => \$summary_file, 'fix!' => \$fix, 'fix-inplace!' => \$fix_inplace, 'ignore-perl-version!' => \$ignore_perl_version, 'debug=s' => \%debug, 'test-only=s' => \$tst_only, 'codespell!' => \$codespell, 'codespellfile=s' => \$user_codespellfile, 'typedefsfile=s' => \$typedefsfile, 'color=s' => \$color, 'no-color' => \$color, #keep old behaviors of -nocolor 'nocolor' => \$color, #keep old behaviors of -nocolor 'kconfig-prefix=s' => \${CONFIG_}, 'h|help' => \$help, 'version' => \$help ) or $help = 2; if ($user_codespellfile) { # Use the user provided codespell file unconditionally $codespellfile = $user_codespellfile; } elsif (!(-f $codespellfile)) { # If /usr/share/codespell/dictionary.txt is not present, try to find it # under codespell's install directory: /data/dictionary.txt if (($codespell || $help) && which("python3") ne "") { my $python_codespell_dict = << "EOF"; import os.path as op import codespell_lib codespell_dir = op.dirname(codespell_lib.__file__) codespell_file = op.join(codespell_dir, 'data', 'dictionary.txt') print(codespell_file, end='') EOF my $codespell_dict = `python3 -c "$python_codespell_dict" 2> /dev/null`; $codespellfile = $codespell_dict if (-f $codespell_dict); } } # $help is 1 if either -h, --help or --version is passed as option - exitcode: 0 # $help is 2 if invalid option is passed - exitcode: 1 help($help - 1) if ($help); die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix)); die "$P: --verbose cannot be used with --terse\n" if ($verbose && $terse); if ($color =~ /^[01]$/) { $color = !$color; } elsif ($color =~ /^always$/i) { $color = 1; } elsif ($color =~ /^never$/i) { $color = 0; } elsif ($color =~ /^auto$/i) { $color = (-t STDOUT); } else { die "$P: Invalid color mode: $color\n"; } load_docs() if ($verbose); list_types(0) if ($list_types); $fix = 1 if ($fix_inplace); $check_orig = $check; my $exit = 0; my $perl_version_ok = 1; if ($^V && $^V lt $minimum_perl_version) { $perl_version_ok = 0; printf "$P: requires at least perl version %vd\n", $minimum_perl_version; exit(1) if (!$ignore_perl_version); } #if no filenames are given, push '-' to read patch from stdin if ($#ARGV < 0) { push(@ARGV, '-'); } # skip TAB size 1 to avoid additional checks on $tabsize - 1 die "$P: Invalid TAB size: $tabsize\n" if ($tabsize < 2); sub hash_save_array_words { my ($hashRef, $arrayRef) = @_; my @array = split(/,/, join(',', @$arrayRef)); foreach my $word (@array) { $word =~ s/\s*\n?$//g; $word =~ s/^\s*//g; $word =~ s/\s+/ /g; $word =~ tr/[a-z]/[A-Z]/; next if ($word =~ m/^\s*#/); next if ($word =~ m/^\s*$/); $hashRef->{$word}++; } } sub hash_show_words { my ($hashRef, $prefix) = @_; if (keys %$hashRef) { print "\nNOTE: $prefix message types:"; foreach my $word (sort keys %$hashRef) { print " $word"; } print "\n"; } } hash_save_array_words(\%ignore_type, \@ignore); hash_save_array_words(\%use_type, \@use); my $dbg_values = 0; my $dbg_possible = 0; my $dbg_type = 0; my $dbg_attr = 0; for my $key (keys %debug) { ## no critic eval "\${dbg_$key} = '$debug{$key}';"; die "$@" if ($@); } my $rpt_cleaners = 0; if ($terse) { $emacs = 1; $quiet++; } if ($tree) { if (defined $root) { if (!top_of_kernel_tree($root)) { die "$P: $root: --root does not point at a valid tree\n"; } } else { if (top_of_kernel_tree('.')) { $root = '.'; } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && top_of_kernel_tree($1)) { $root = $1; } } if (!defined $root) { print "Must be run from the top-level dir. of a kernel tree\n"; exit(2); } } my $emitted_corrupt = 0; our $Ident = qr{ [A-Za-z_][A-Za-z\d_]* (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* }x; our $Storage = qr{extern|static|asmlinkage}; our $Sparse = qr{ __user| __kernel| __force| __iomem| __must_check| __kprobes| __ref| __refconst| __refdata| __rcu| __private }x; our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; # Notes to $Attribute: # We need \b after 'init' otherwise 'initconst' will cause a false positive in a check our $Attribute = qr{ const| volatile| __percpu| __nocast| __safe| __bitwise| __packed__| __packed2__| __naked| __maybe_unused| __always_unused| __noreturn| __used| __cold| __pure| __noclone| __deprecated| __read_mostly| __ro_after_init| __kprobes| $InitAttribute| __aligned\s*\(.*\)| ____cacheline_aligned| ____cacheline_aligned_in_smp| ____cacheline_internodealigned_in_smp| __weak| __alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) }x; our $Modifier; our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; our $Binary = qr{(?i)0b[01]+$Int_type?}; our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; our $Int = qr{[0-9]+$Int_type?}; our $Octal = qr{0[0-7]+$Int_type?}; our $String = qr{(?:\b[Lu])?"[X\t]*"}; our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; our $Float = qr{$Float_hex|$Float_dec|$Float_int}; our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; our $Compare = qr{<=|>=|==|!=|<|(?}; our $Arithmetic = qr{\+|-|\*|\/|%}; our $Operators = qr{ <=|>=|==|!=| =>|->|<<|>>|<|>|!|~| &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic }x; our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; our $BasicType; our $NonptrType; our $NonptrTypeMisordered; our $NonptrTypeWithAttr; our $Type; our $TypeMisordered; our $Declare; our $DeclareMisordered; our $NON_ASCII_UTF8 = qr{ [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 }x; our $UTF8 = qr{ [\x09\x0A\x0D\x20-\x7E] # ASCII | $NON_ASCII_UTF8 }x; our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; our $typeOtherOSTypedefs = qr{(?x: u_(?:char|short|int|long) | # bsd u(?:nchar|short|int|long) # sysv )}; our $typeKernelTypedefs = qr{(?x: (?:__)?(?:u|s|be|le)(?:8|16|32|64)| atomic_t )}; our $typeStdioTypedefs = qr{(?x: FILE )}; our $typeTypedefs = qr{(?x: $typeC99Typedefs\b| $typeOtherOSTypedefs\b| $typeKernelTypedefs\b| $typeStdioTypedefs\b )}; our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; our $logFunctions = qr{(?x: printk(?:_ratelimited|_once|_deferred_once|_deferred|)| (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| TP_printk| WARN(?:_RATELIMIT|_ONCE|)| panic| MODULE_[A-Z_]+| seq_vprintf|seq_printf|seq_puts )}; our $allocFunctions = qr{(?x: (?:(?:devm_)? (?:kv|k|v)[czm]alloc(?:_array)?(?:_node)? | kstrdup(?:_const)? | kmemdup(?:_nul)?) | (?:\w+)?alloc_skb(?:_ip_align)? | # dev_alloc_skb/netdev_alloc_skb, et al dma_alloc_coherent )}; our $signature_tags = qr{(?xi: Signed-off-by:| Co-developed-by:| Acked-by:| Tested-by:| Reviewed-by:| Reported-by:| Suggested-by:| To:| Cc: )}; our @link_tags = qw(Link Closes); #Create a search and print patterns for all these strings to be used directly below our $link_tags_search = ""; our $link_tags_print = ""; foreach my $entry (@link_tags) { if ($link_tags_search ne "") { $link_tags_search .= '|'; $link_tags_print .= ' or '; } $entry .= ':'; $link_tags_search .= $entry; $link_tags_print .= "'$entry'"; } $link_tags_search = "(?:${link_tags_search})"; our $tracing_logging_tags = qr{(?xi: [=-]*> | <[=-]* | \[ | \] | start | called | entered | entry | enter | in | inside | here | begin | exit | end | done | leave | completed | out | return | [\.\!:\s]* )}; sub edit_distance_min { my (@arr) = @_; my $len = scalar @arr; if ((scalar @arr) < 1) { # if underflow, return return; } my $min = $arr[0]; for my $i (0 .. ($len-1)) { if ($arr[$i] < $min) { $min = $arr[$i]; } } return $min; } sub get_edit_distance { my ($str1, $str2) = @_; $str1 = lc($str1); $str2 = lc($str2); $str1 =~ s/-//g; $str2 =~ s/-//g; my $len1 = length($str1); my $len2 = length($str2); # two dimensional array storing minimum edit distance my @distance; for my $i (0 .. $len1) { for my $j (0 .. $len2) { if ($i == 0) { $distance[$i][$j] = $j; } elsif ($j == 0) { $distance[$i][$j] = $i; } elsif (substr($str1, $i-1, 1) eq substr($str2, $j-1, 1)) { $distance[$i][$j] = $distance[$i - 1][$j - 1]; } else { my $dist1 = $distance[$i][$j - 1]; #insert distance my $dist2 = $distance[$i - 1][$j]; # remove my $dist3 = $distance[$i - 1][$j - 1]; #replace $distance[$i][$j] = 1 + edit_distance_min($dist1, $dist2, $dist3); } } } return $distance[$len1][$len2]; } sub find_standard_signature { my ($sign_off) = @_; my @standard_signature_tags = ( 'Signed-off-by:', 'Co-developed-by:', 'Acked-by:', 'Tested-by:', 'Reviewed-by:', 'Reported-by:', 'Suggested-by:' ); foreach my $signature (@standard_signature_tags) { return $signature if (get_edit_distance($sign_off, $signature) <= 2); } return ""; } our $obsolete_archives = qr{(?xi: \Qfreedesktop.org/archives/dri-devel\E | \Qlists.infradead.org\E | \Qlkml.org\E | \Qmail-archive.com\E | \Qmailman.alsa-project.org/pipermail\E | \Qmarc.info\E | \Qozlabs.org/pipermail\E | \Qspinics.net\E )}; our @typeListMisordered = ( qr{char\s+(?:un)?signed}, qr{int\s+(?:(?:un)?signed\s+)?short\s}, qr{int\s+short(?:\s+(?:un)?signed)}, qr{short\s+int(?:\s+(?:un)?signed)}, qr{(?:un)?signed\s+int\s+short}, qr{short\s+(?:un)?signed}, qr{long\s+int\s+(?:un)?signed}, qr{int\s+long\s+(?:un)?signed}, qr{long\s+(?:un)?signed\s+int}, qr{int\s+(?:un)?signed\s+long}, qr{int\s+(?:un)?signed}, qr{int\s+long\s+long\s+(?:un)?signed}, qr{long\s+long\s+int\s+(?:un)?signed}, qr{long\s+long\s+(?:un)?signed\s+int}, qr{long\s+long\s+(?:un)?signed}, qr{long\s+(?:un)?signed}, ); our @typeList = ( qr{void}, qr{(?:(?:un)?signed\s+)?char}, qr{(?:(?:un)?signed\s+)?short\s+int}, qr{(?:(?:un)?signed\s+)?short}, qr{(?:(?:un)?signed\s+)?int}, qr{(?:(?:un)?signed\s+)?long\s+int}, qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, qr{(?:(?:un)?signed\s+)?long\s+long}, qr{(?:(?:un)?signed\s+)?long}, qr{(?:un)?signed}, qr{float}, qr{double}, qr{bool}, qr{struct\s+$Ident}, qr{union\s+$Ident}, qr{enum\s+$Ident}, qr{${Ident}_t}, qr{${Ident}_handler}, qr{${Ident}_handler_fn}, @typeListMisordered, ); our $C90_int_types = qr{(?x: long\s+long\s+int\s+(?:un)?signed| long\s+long\s+(?:un)?signed\s+int| long\s+long\s+(?:un)?signed| (?:(?:un)?signed\s+)?long\s+long\s+int| (?:(?:un)?signed\s+)?long\s+long| int\s+long\s+long\s+(?:un)?signed| int\s+(?:(?:un)?signed\s+)?long\s+long| long\s+int\s+(?:un)?signed| long\s+(?:un)?signed\s+int| long\s+(?:un)?signed| (?:(?:un)?signed\s+)?long\s+int| (?:(?:un)?signed\s+)?long| int\s+long\s+(?:un)?signed| int\s+(?:(?:un)?signed\s+)?long| int\s+(?:un)?signed| (?:(?:un)?signed\s+)?int )}; our @typeListFile = (); our @typeListWithAttr = ( @typeList, qr{struct\s+$InitAttribute\s+$Ident}, qr{union\s+$InitAttribute\s+$Ident}, ); our @modifierList = ( qr{fastcall}, ); our @modifierListFile = (); our @mode_permission_funcs = ( ["module_param", 3], ["module_param_(?:array|named|string)", 4], ["module_param_array_named", 5], ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], ["proc_create(?:_data|)", 2], ["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2], ["IIO_DEV_ATTR_[A-Z_]+", 1], ["SENSOR_(?:DEVICE_|)ATTR_2", 2], ["SENSOR_TEMPLATE(?:_2|)", 3], ["__ATTR", 2], ); my $word_pattern = '\b[A-Z]?[a-z]{2,}\b'; #Create a search pattern for all these functions to speed up a loop below our $mode_perms_search = ""; foreach my $entry (@mode_permission_funcs) { $mode_perms_search .= '|' if ($mode_perms_search ne ""); $mode_perms_search .= $entry->[0]; } $mode_perms_search = "(?:${mode_perms_search})"; our %deprecated_apis = ( "synchronize_rcu_bh" => "synchronize_rcu", "synchronize_rcu_bh_expedited" => "synchronize_rcu_expedited", "call_rcu_bh" => "call_rcu", "rcu_barrier_bh" => "rcu_barrier", "synchronize_sched" => "synchronize_rcu", "synchronize_sched_expedited" => "synchronize_rcu_expedited", "call_rcu_sched" => "call_rcu", "rcu_barrier_sched" => "rcu_barrier", "get_state_synchronize_sched" => "get_state_synchronize_rcu", "cond_synchronize_sched" => "cond_synchronize_rcu", "kmap" => "kmap_local_page", "kunmap" => "kunmap_local", "kmap_atomic" => "kmap_local_page", "kunmap_atomic" => "kunmap_local", ); #Create a search pattern for all these strings to speed up a loop below our $deprecated_apis_search = ""; foreach my $entry (keys %deprecated_apis) { $deprecated_apis_search .= '|' if ($deprecated_apis_search ne ""); $deprecated_apis_search .= $entry; } $deprecated_apis_search = "(?:${deprecated_apis_search})"; our $mode_perms_world_writable = qr{ S_IWUGO | S_IWOTH | S_IRWXUGO | S_IALLUGO | 0[0-7][0-7][2367] }x; our %mode_permission_string_types = ( "S_IRWXU" => 0700, "S_IRUSR" => 0400, "S_IWUSR" => 0200, "S_IXUSR" => 0100, "S_IRWXG" => 0070, "S_IRGRP" => 0040, "S_IWGRP" => 0020, "S_IXGRP" => 0010, "S_IRWXO" => 0007, "S_IROTH" => 0004, "S_IWOTH" => 0002, "S_IXOTH" => 0001, "S_IRWXUGO" => 0777, "S_IRUGO" => 0444, "S_IWUGO" => 0222, "S_IXUGO" => 0111, ); #Create a search pattern for all these strings to speed up a loop below our $mode_perms_string_search = ""; foreach my $entry (keys %mode_permission_string_types) { $mode_perms_string_search .= '|' if ($mode_perms_string_search ne ""); $mode_perms_string_search .= $entry; } our $single_mode_perms_string_search = "(?:${mode_perms_string_search})"; our $multi_mode_perms_string_search = qr{ ${single_mode_perms_string_search} (?:\s*\|\s*${single_mode_perms_string_search})* }x; sub perms_to_octal { my ($string) = @_; return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/); my $val = ""; my $oval = ""; my $to = 0; my $curpos = 0; my $lastpos = 0; while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { $curpos = pos($string); my $match = $2; my $omatch = $1; last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); $lastpos = $curpos; $to |= $mode_permission_string_types{$match}; $val .= '\s*\|\s*' if ($val ne ""); $val .= $match; $oval .= $omatch; } $oval =~ s/^\s*\|\s*//; $oval =~ s/\s*\|\s*$//; return sprintf("%04o", $to); } our $allowed_asm_includes = qr{(?x: irq| memory| time| reboot )}; # memory.h: ARM has a custom one # Load common spelling mistakes and build regular expression list. my $misspellings; my %spelling_fix; if (open(my $spelling, '<', $spelling_file)) { while (<$spelling>) { my $line = $_; $line =~ s/\s*\n?$//g; $line =~ s/^\s*//g; next if ($line =~ m/^\s*#/); next if ($line =~ m/^\s*$/); my ($suspect, $fix) = split(/\|\|/, $line); $spelling_fix{$suspect} = $fix; } close($spelling); } else { warn "No typos will be found - file '$spelling_file': $!\n"; } if ($codespell) { if (open(my $spelling, '<', $codespellfile)) { while (<$spelling>) { my $line = $_; $line =~ s/\s*\n?$//g; $line =~ s/^\s*//g; next if ($line =~ m/^\s*#/); next if ($line =~ m/^\s*$/); next if ($line =~ m/, disabled/i); $line =~ s/,.*$//; my ($suspect, $fix) = split(/->/, $line); $spelling_fix{$suspect} = $fix; } close($spelling); } else { warn "No codespell typos will be found - file '$codespellfile': $!\n"; } } $misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; sub read_words { my ($wordsRef, $file) = @_; if (open(my $words, '<', $file)) { while (<$words>) { my $line = $_; $line =~ s/\s*\n?$//g; $line =~ s/^\s*//g; next if ($line =~ m/^\s*#/); next if ($line =~ m/^\s*$/); if ($line =~ /\s/) { print("$file: '$line' invalid - ignored\n"); next; } $$wordsRef .= '|' if (defined $$wordsRef); $$wordsRef .= $line; } close($file); return 1; } return 0; } my $const_structs; if (show_type("CONST_STRUCT")) { read_words(\$const_structs, $conststructsfile) or warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; } if (defined($typedefsfile)) { my $typeOtherTypedefs; read_words(\$typeOtherTypedefs, $typedefsfile) or warn "No additional types will be considered - file '$typedefsfile': $!\n"; $typeTypedefs .= '|' . $typeOtherTypedefs if (defined $typeOtherTypedefs); } sub build_types { my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; $BasicType = qr{ (?:$typeTypedefs\b)| (?:${all}\b) }x; $NonptrType = qr{ (?:$Modifier\s+|const\s+)* (?: (?:typeof|__typeof__)\s*\([^\)]*\)| (?:$typeTypedefs\b)| (?:${all}\b) ) (?:\s+$Modifier|\s+const)* }x; $NonptrTypeMisordered = qr{ (?:$Modifier\s+|const\s+)* (?: (?:${Misordered}\b) ) (?:\s+$Modifier|\s+const)* }x; $NonptrTypeWithAttr = qr{ (?:$Modifier\s+|const\s+)* (?: (?:typeof|__typeof__)\s*\([^\)]*\)| (?:$typeTypedefs\b)| (?:${allWithAttr}\b) ) (?:\s+$Modifier|\s+const)* }x; $Type = qr{ $NonptrType (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4} (?:\s+$Inline|\s+$Modifier)* }x; $TypeMisordered = qr{ $NonptrTypeMisordered (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4} (?:\s+$Inline|\s+$Modifier)* }x; $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; } build_types(); our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; # Using $balanced_parens, $LvalOrFunc, or $FuncArg # requires at least perl version v5.10.0 # Any use must be runtime checked with $^V our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; our $declaration_macros = qr{(?x: (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(| (?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\(| (?:$Storage\s+)?(?:XA_STATE|XA_STATE_ORDER)\s*\( )}; our %allow_repeated_words = ( add => '', added => '', bad => '', be => '', ); sub deparenthesize { my ($string) = @_; return "" if (!defined($string)); while ($string =~ /^\s*\(.*\)\s*$/) { $string =~ s@^\s*\(\s*@@; $string =~ s@\s*\)\s*$@@; } $string =~ s@\s+@ @g; return $string; } sub seed_camelcase_file { my ($file) = @_; return if (!(-f $file)); local $/; open(my $include_file, '<', "$file") or warn "$P: Can't read '$file' $!\n"; my $text = <$include_file>; close($include_file); my @lines = split('\n', $text); foreach my $line (@lines) { next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { $camelcase{$1} = 1; } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { $camelcase{$1} = 1; } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { $camelcase{$1} = 1; } } } our %maintained_status = (); sub is_maintained_obsolete { my ($filename) = @_; return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl")); if (!exists($maintained_status{$filename})) { $maintained_status{$filename} = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`; } return $maintained_status{$filename} =~ /obsolete/i; } sub is_SPDX_License_valid { my ($license) = @_; return 1 if (!$tree || which("python3") eq "" || !(-x "$root/scripts/spdxcheck.py") || !(-e "$gitroot")); my $root_path = abs_path($root); my $status = `cd "$root_path"; echo "$license" | scripts/spdxcheck.py -`; return 0 if ($status ne ""); return 1; } my $camelcase_seeded = 0; sub seed_camelcase_includes { return if ($camelcase_seeded); my $files; my $camelcase_cache = ""; my @include_files = (); $camelcase_seeded = 1; if (-e "$gitroot") { my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`; chomp $git_last_include_commit; $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; } else { my $last_mod_date = 0; $files = `find $root/include -name "*.h"`; @include_files = split('\n', $files); foreach my $file (@include_files) { my $date = POSIX::strftime("%Y%m%d%H%M", localtime((stat $file)[9])); $last_mod_date = $date if ($last_mod_date < $date); } $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; } if ($camelcase_cache ne "" && -f $camelcase_cache) { open(my $camelcase_file, '<', "$camelcase_cache") or warn "$P: Can't read '$camelcase_cache' $!\n"; while (<$camelcase_file>) { chomp; $camelcase{$_} = 1; } close($camelcase_file); return; } if (-e "$gitroot") { $files = `${git_command} ls-files "include/*.h"`; @include_files = split('\n', $files); } foreach my $file (@include_files) { seed_camelcase_file($file); } if ($camelcase_cache ne "") { unlink glob ".checkpatch-camelcase.*"; open(my $camelcase_file, '>', "$camelcase_cache") or warn "$P: Can't write '$camelcase_cache' $!\n"; foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { print $camelcase_file ("$_\n"); } close($camelcase_file); } } sub git_is_single_file { my ($filename) = @_; return 0 if ((which("git") eq "") || !(-e "$gitroot")); my $output = `${git_command} ls-files -- $filename 2>/dev/null`; my $count = $output =~ tr/\n//; return $count eq 1 && $output =~ m{^${filename}$}; } sub git_commit_info { my ($commit, $id, $desc) = @_; return ($id, $desc) if ((which("git") eq "") || !(-e "$gitroot")); my $output = `${git_command} log --no-color --format='%H %s' -1 $commit 2>&1`; $output =~ s/^\s*//gm; my @lines = split("\n", $output); return ($id, $desc) if ($#lines < 0); if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous/) { # Maybe one day convert this block of bash into something that returns # all matching commit ids, but it's very slow... # # echo "checking commits $1..." # git rev-list --remotes | grep -i "^$1" | # while read line ; do # git log --format='%H %s' -1 $line | # echo "commit $(cut -c 1-12,41-)" # done } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./ || $lines[0] =~ /^fatal: bad object $commit/) { $id = undef; } else { $id = substr($lines[0], 0, 12); $desc = substr($lines[0], 41); } return ($id, $desc); } $chk_signoff = 0 if ($file); $chk_fixes_tag = 0 if ($file); my @rawlines = (); my @lines = (); my @fixed = (); my @fixed_inserted = (); my @fixed_deleted = (); my $fixlinenr = -1; # If input is git commits, extract all commits from the commit expressions. # For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'. die "$P: No git repository found\n" if ($git && !-e "$gitroot"); if ($git) { my @commits = (); foreach my $commit_expr (@ARGV) { my $git_range; if ($commit_expr =~ m/^(.*)-(\d+)$/) { $git_range = "-$2 $1"; } elsif ($commit_expr =~ m/\.\./) { $git_range = "$commit_expr"; } else { $git_range = "-1 $commit_expr"; } my $lines = `${git_command} log --no-color --no-merges --pretty=format:'%H %s' $git_range`; foreach my $line (split(/\n/, $lines)) { $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; next if (!defined($1) || !defined($2)); my $sha1 = $1; my $subject = $2; unshift(@commits, $sha1); $git_commits{$sha1} = $subject; } } die "$P: no git commits after extraction!\n" if (@commits == 0); @ARGV = @commits; } my $vname; $allow_c99_comments = !defined $ignore_type{"C99_COMMENT_TOLERANCE"}; for my $filename (@ARGV) { my $FILE; my $is_git_file = git_is_single_file($filename); my $oldfile = $file; $file = 1 if ($is_git_file); if ($git) { open($FILE, '-|', "git format-patch -M --stdout -1 $filename") || die "$P: $filename: git format-patch failed - $!\n"; } elsif ($file) { open($FILE, '-|', "diff -u /dev/null $filename") || die "$P: $filename: diff failed - $!\n"; } elsif ($filename eq '-') { open($FILE, '<&STDIN'); } else { open($FILE, '<', "$filename") || die "$P: $filename: open failed - $!\n"; } if ($filename eq '-') { $vname = 'Your patch'; } elsif ($git) { $vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")'; } else { $vname = $filename; } while (<$FILE>) { chomp; push(@rawlines, $_); $vname = qq("$1") if ($filename eq '-' && $_ =~ m/^Subject:\s+(.+)/i); } close($FILE); if ($#ARGV > 0 && $quiet == 0) { print '-' x length($vname) . "\n"; print "$vname\n"; print '-' x length($vname) . "\n"; } if (!process($filename)) { $exit = 1; } @rawlines = (); @lines = (); @fixed = (); @fixed_inserted = (); @fixed_deleted = (); $fixlinenr = -1; @modifierListFile = (); @typeListFile = (); build_types(); $file = $oldfile if ($is_git_file); } if (!$quiet) { hash_show_words(\%use_type, "Used"); hash_show_words(\%ignore_type, "Ignored"); if (!$perl_version_ok) { print << "EOM" NOTE: perl $^V is not modern enough to detect all possible issues. An upgrade to at least perl $minimum_perl_version is suggested. EOM } if ($exit) { print << "EOM" NOTE: If any of the errors are false positives, please report them to the maintainer, see CHECKPATCH in MAINTAINERS. EOM } } exit($exit); sub top_of_kernel_tree { my ($root) = @_; my @tree_check = ( "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", "README", "Documentation", "arch", "include", "drivers", "fs", "init", "ipc", "kernel", "lib", "scripts", ); foreach my $check (@tree_check) { if (! -e $root . '/' . $check) { return 0; } } return 1; } sub parse_email { my ($formatted_email) = @_; my $name = ""; my $quoted = ""; my $name_comment = ""; my $address = ""; my $comment = ""; if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { $name = $1; $address = $2; $comment = $3 if defined $3; } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { $address = $1; $comment = $2 if defined $2; } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { $address = $1; $comment = $2 if defined $2; $formatted_email =~ s/\Q$address\E.*$//; $name = $formatted_email; $name = trim($name); $name =~ s/^\"|\"$//g; # If there's a name left after stripping spaces and # leading quotes, and the address doesn't have both # leading and trailing angle brackets, the address # is invalid. ie: # "joe smith joe@smith.com" bad # "joe smith ]+>$/) { $name = ""; $address = ""; $comment = ""; } } # Extract comments from names excluding quoted parts # "John D. (Doe)" - Do not extract if ($name =~ s/\"(.+)\"//) { $quoted = $1; } while ($name =~ s/\s*($balanced_parens)\s*/ /) { $name_comment .= trim($1); } $name =~ s/^[ \"]+|[ \"]+$//g; $name = trim("$quoted $name"); $address = trim($address); $address =~ s/^\<|\>$//g; $comment = trim($comment); if ($name =~ /[^\w \-]/i) { ##has "must quote" chars $name =~ s/(?"; } $formatted_email .= "$comment"; return $formatted_email; } sub reformat_email { my ($email) = @_; my ($email_name, $name_comment, $email_address, $comment) = parse_email($email); return format_email($email_name, $name_comment, $email_address, $comment); } sub same_email_addresses { my ($email1, $email2) = @_; my ($email1_name, $name1_comment, $email1_address, $comment1) = parse_email($email1); my ($email2_name, $name2_comment, $email2_address, $comment2) = parse_email($email2); return $email1_name eq $email2_name && $email1_address eq $email2_address && $name1_comment eq $name2_comment && $comment1 eq $comment2; } sub which { my ($bin) = @_; foreach my $path (split(/:/, $ENV{PATH})) { if (-e "$path/$bin") { return "$path/$bin"; } } return ""; } sub which_conf { my ($conf) = @_; foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { if (-e "$path/$conf") { return "$path/$conf"; } } return ""; } sub expand_tabs { my ($str) = @_; my $res = ''; my $n = 0; for my $c (split(//, $str)) { if ($c eq "\t") { $res .= ' '; $n++; for (; ($n % $tabsize) != 0; $n++) { $res .= ' '; } next; } $res .= $c; $n++; } return $res; } sub copy_spacing { (my $res = shift) =~ tr/\t/ /c; return $res; } sub line_stats { my ($line) = @_; # Drop the diff line leader and expand tabs $line =~ s/^.//; $line = expand_tabs($line); # Pick the indent from the front of the line. my ($white) = ($line =~ /^(\s*)/); return (length($line), length($white)); } my $sanitise_quote = ''; sub sanitise_line_reset { my ($in_comment) = @_; if ($in_comment) { $sanitise_quote = '*/'; } else { $sanitise_quote = ''; } } sub sanitise_line { my ($line) = @_; my $res = ''; my $l = ''; my $qlen = 0; my $off = 0; my $c; # Always copy over the diff marker. $res = substr($line, 0, 1); for ($off = 1; $off < length($line); $off++) { $c = substr($line, $off, 1); # Comments we are whacking completely including the begin # and end, all to $;. if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { $sanitise_quote = '*/'; substr($res, $off, 2, "$;$;"); $off++; next; } if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { $sanitise_quote = ''; substr($res, $off, 2, "$;$;"); $off++; next; } if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { $sanitise_quote = '//'; substr($res, $off, 2, $sanitise_quote); $off++; next; } # A \ in a string means ignore the next character. if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && $c eq "\\") { substr($res, $off, 2, 'XX'); $off++; next; } # Regular quotes. if ($c eq "'" || $c eq '"') { if ($sanitise_quote eq '') { $sanitise_quote = $c; substr($res, $off, 1, $c); next; } elsif ($sanitise_quote eq $c) { $sanitise_quote = ''; } } #print "c<$c> SQ<$sanitise_quote>\n"; if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { substr($res, $off, 1, $;); } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { substr($res, $off, 1, $;); } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { substr($res, $off, 1, 'X'); } else { substr($res, $off, 1, $c); } } if ($sanitise_quote eq '//') { $sanitise_quote = ''; } # The pathname on a #include may be surrounded by '<' and '>'. if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { my $clean = 'X' x length($1); $res =~ s@\<.*\>@<$clean>@; # The whole of a #error is a string. } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { my $clean = 'X' x length($1); $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; } if ($allow_c99_comments && $res =~ m@(//.*$)@) { my $match = $1; $res =~ s/\Q$match\E/"$;" x length($match)/e; } return $res; } sub get_quoted_string { my ($line, $rawline) = @_; return "" if (!defined($line) || !defined($rawline)); return "" if ($line !~ m/($String)/g); return substr($rawline, $-[0], $+[0] - $-[0]); } sub ctx_statement_block { my ($linenr, $remain, $off) = @_; my $line = $linenr - 1; my $blk = ''; my $soff = $off; my $coff = $off - 1; my $coff_set = 0; my $loff = 0; my $type = ''; my $level = 0; my @stack = (); my $p; my $c; my $len = 0; my $remainder; while (1) { @stack = (['', 0]) if ($#stack == -1); #warn "CSB: blk<$blk> remain<$remain>\n"; # If we are about to drop off the end, pull in more # context. if ($off >= $len) { for (; $remain > 0; $line++) { last if (!defined $lines[$line]); next if ($lines[$line] =~ /^-/); $remain--; $loff = $len; $blk .= $lines[$line] . "\n"; $len = length($blk); $line++; last; } # Bail if there is no further context. #warn "CSB: blk<$blk> off<$off> len<$len>\n"; if ($off >= $len) { last; } if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { $level++; $type = '#'; } } $p = $c; $c = substr($blk, $off, 1); $remainder = substr($blk, $off); #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; # Handle nested #if/#else. if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { push(@stack, [ $type, $level ]); } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { ($type, $level) = @{$stack[$#stack - 1]}; } elsif ($remainder =~ /^#\s*endif\b/) { ($type, $level) = @{pop(@stack)}; } # Statement ends at the ';' or a close '}' at the # outermost level. if ($level == 0 && $c eq ';') { last; } # An else is really a conditional as long as its not else if if ($level == 0 && $coff_set == 0 && (!defined($p) || $p =~ /(?:\s|\}|\+)/) && $remainder =~ /^(else)(?:\s|{)/ && $remainder !~ /^else\s+if\b/) { $coff = $off + length($1) - 1; $coff_set = 1; #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; } if (($type eq '' || $type eq '(') && $c eq '(') { $level++; $type = '('; } if ($type eq '(' && $c eq ')') { $level--; $type = ($level != 0)? '(' : ''; if ($level == 0 && $coff < $soff) { $coff = $off; $coff_set = 1; #warn "CSB: mark coff<$coff>\n"; } } if (($type eq '' || $type eq '{') && $c eq '{') { $level++; $type = '{'; } if ($type eq '{' && $c eq '}') { $level--; $type = ($level != 0)? '{' : ''; if ($level == 0) { if (substr($blk, $off + 1, 1) eq ';') { $off++; } last; } } # Preprocessor commands end at the newline unless escaped. if ($type eq '#' && $c eq "\n" && $p ne "\\") { $level--; $type = ''; $off++; last; } $off++; } # We are truly at the end, so shuffle to the next line. if ($off == $len) { $loff = $len + 1; $line++; $remain--; } my $statement = substr($blk, $soff, $off - $soff + 1); my $condition = substr($blk, $soff, $coff - $soff + 1); #warn "STATEMENT<$statement>\n"; #warn "CONDITION<$condition>\n"; #print "coff<$coff> soff<$off> loff<$loff>\n"; return ($statement, $condition, $line, $remain + 1, $off - $loff + 1, $level); } sub statement_lines { my ($stmt) = @_; # Strip the diff line prefixes and rip blank lines at start and end. $stmt =~ s/(^|\n)./$1/g; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; my @stmt_lines = ($stmt =~ /\n/g); return $#stmt_lines + 2; } sub statement_rawlines { my ($stmt) = @_; my @stmt_lines = ($stmt =~ /\n/g); return $#stmt_lines + 2; } sub statement_block_size { my ($stmt) = @_; $stmt =~ s/(^|\n)./$1/g; $stmt =~ s/^\s*{//; $stmt =~ s/}\s*$//; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; my @stmt_lines = ($stmt =~ /\n/g); my @stmt_statements = ($stmt =~ /;/g); my $stmt_lines = $#stmt_lines + 2; my $stmt_statements = $#stmt_statements + 1; if ($stmt_lines > $stmt_statements) { return $stmt_lines; } else { return $stmt_statements; } } sub ctx_statement_full { my ($linenr, $remain, $off) = @_; my ($statement, $condition, $level); my (@chunks); # Grab the first conditional/block pair. ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "F: c<$condition> s<$statement> remain<$remain>\n"; push(@chunks, [ $condition, $statement ]); if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { return ($level, $linenr, @chunks); } # Pull in the following conditional/block pairs and see if they # could continue the statement. for (;;) { ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "C: c<$condition> s<$statement> remain<$remain>\n"; last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); #print "C: push\n"; push(@chunks, [ $condition, $statement ]); } return ($level, $linenr, @chunks); } sub ctx_block_get { my ($linenr, $remain, $outer, $open, $close, $off) = @_; my $line; my $start = $linenr - 1; my $blk = ''; my @o; my @c; my @res = (); my $level = 0; my @stack = ($level); for ($line = $start; $remain > 0; $line++) { next if ($rawlines[$line] =~ /^-/); $remain--; $blk .= $rawlines[$line]; # Handle nested #if/#else. if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { push(@stack, $level); } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { $level = $stack[$#stack - 1]; } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { $level = pop(@stack); } foreach my $c (split(//, $lines[$line])) { ##print "C<$c>L<$level><$open$close>O<$off>\n"; if ($off > 0) { $off--; next; } if ($c eq $close && $level > 0) { $level--; last if ($level == 0); } elsif ($c eq $open) { $level++; } } if (!$outer || $level <= 1) { push(@res, $rawlines[$line]); } last if ($level == 0); } return ($level, @res); } sub ctx_block_outer { my ($linenr, $remain) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); return @r; } sub ctx_block { my ($linenr, $remain) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); return @r; } sub ctx_statement { my ($linenr, $remain, $off) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); return @r; } sub ctx_block_level { my ($linenr, $remain) = @_; return ctx_block_get($linenr, $remain, 0, '{', '}', 0); } sub ctx_statement_level { my ($linenr, $remain, $off) = @_; return ctx_block_get($linenr, $remain, 0, '(', ')', $off); } sub ctx_locate_comment { my ($first_line, $end_line) = @_; # If c99 comment on the current line, or the line before or after my ($current_comment) = ($rawlines[$end_line - 1] =~ m@^\+.*(//.*$)@); return $current_comment if (defined $current_comment); ($current_comment) = ($rawlines[$end_line - 2] =~ m@^[\+ ].*(//.*$)@); return $current_comment if (defined $current_comment); ($current_comment) = ($rawlines[$end_line] =~ m@^[\+ ].*(//.*$)@); return $current_comment if (defined $current_comment); # Catch a comment on the end of the line itself. ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); return $current_comment if (defined $current_comment); # Look through the context and try and figure out if there is a # comment. my $in_comment = 0; $current_comment = ''; for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { my $line = $rawlines[$linenr - 1]; #warn " $line\n"; if ($linenr == $first_line and $line =~ m@^.\s*\*@) { $in_comment = 1; } if ($line =~ m@/\*@) { $in_comment = 1; } if (!$in_comment && $current_comment ne '') { $current_comment = ''; } $current_comment .= $line . "\n" if ($in_comment); if ($line =~ m@\*/@) { $in_comment = 0; } } chomp($current_comment); return($current_comment); } sub ctx_has_comment { my ($first_line, $end_line) = @_; my $cmt = ctx_locate_comment($first_line, $end_line); ##print "LINE: $rawlines[$end_line - 1 ]\n"; ##print "CMMT: $cmt\n"; return ($cmt ne ''); } sub raw_line { my ($linenr, $cnt) = @_; my $offset = $linenr - 1; $cnt++; my $line; while ($cnt) { $line = $rawlines[$offset++]; next if (defined($line) && $line =~ /^-/); $cnt--; } return $line; } sub get_stat_real { my ($linenr, $lc) = @_; my $stat_real = raw_line($linenr, 0); for (my $count = $linenr + 1; $count <= $lc; $count++) { $stat_real = $stat_real . "\n" . raw_line($count, 0); } return $stat_real; } sub get_stat_here { my ($linenr, $cnt, $here) = @_; my $herectx = $here . "\n"; for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } return $herectx; } sub cat_vet { my ($vet) = @_; my ($res, $coded); $res = ''; while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { $res .= $1; if ($2 ne '') { $coded = sprintf("^%c", unpack('C', $2) + 64); $res .= $coded; } } $res =~ s/$/\$/; return $res; } my $av_preprocessor = 0; my $av_pending; my @av_paren_type; my $av_pend_colon; sub annotate_reset { $av_preprocessor = 0; $av_pending = '_'; @av_paren_type = ('E'); $av_pend_colon = 'O'; } sub annotate_values { my ($stream, $type) = @_; my $res; my $var = '_' x length($stream); my $cur = $stream; print "$stream\n" if ($dbg_values > 1); while (length($cur)) { @av_paren_type = ('E') if ($#av_paren_type < 0); print " <" . join('', @av_paren_type) . "> <$type> <$av_pending>" if ($dbg_values > 1); if ($cur =~ /^(\s+)/o) { print "WS($1)\n" if ($dbg_values > 1); if ($1 =~ /\n/ && $av_preprocessor) { $type = pop(@av_paren_type); $av_preprocessor = 0; } } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { print "CAST($1)\n" if ($dbg_values > 1); push(@av_paren_type, $type); $type = 'c'; } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { print "DECLARE($1)\n" if ($dbg_values > 1); $type = 'T'; } elsif ($cur =~ /^($Modifier)\s*/) { print "MODIFIER($1)\n" if ($dbg_values > 1); $type = 'T'; } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { print "DEFINE($1,$2)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); if ($2 ne '') { $av_pending = 'N'; } $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { print "UNDEF($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { print "PRE_START($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); push(@av_paren_type, $type); $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { print "PRE_RESTART($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $av_paren_type[$#av_paren_type]); $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:endif))/o) { print "PRE_END($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; # Assume all arms of the conditional end as this # one does, and continue as if the #endif was not here. pop(@av_paren_type); push(@av_paren_type, $type); $type = 'E'; } elsif ($cur =~ /^(\\\n)/o) { print "PRECONT($1)\n" if ($dbg_values > 1); } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { print "ATTR($1)\n" if ($dbg_values > 1); $av_pending = $type; $type = 'N'; } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { print "SIZEOF($1)\n" if ($dbg_values > 1); if (defined $2) { $av_pending = 'V'; } $type = 'N'; } elsif ($cur =~ /^(if|while|for)\b/o) { print "COND($1)\n" if ($dbg_values > 1); $av_pending = 'E'; $type = 'N'; } elsif ($cur =~/^(case)/o) { print "CASE($1)\n" if ($dbg_values > 1); $av_pend_colon = 'C'; $type = 'N'; } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { print "KEYWORD($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(\()/o) { print "PAREN('$1')\n" if ($dbg_values > 1); push(@av_paren_type, $av_pending); $av_pending = '_'; $type = 'N'; } elsif ($cur =~ /^(\))/o) { my $new_type = pop(@av_paren_type); if ($new_type ne '_') { $type = $new_type; print "PAREN('$1') -> $type\n" if ($dbg_values > 1); } else { print "PAREN('$1')\n" if ($dbg_values > 1); } } elsif ($cur =~ /^($Ident)\s*\(/o) { print "FUNC($1)\n" if ($dbg_values > 1); $type = 'V'; $av_pending = 'V'; } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { if (defined $2 && $type eq 'C' || $type eq 'T') { $av_pend_colon = 'B'; } elsif ($type eq 'E') { $av_pend_colon = 'L'; } print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); $type = 'V'; } elsif ($cur =~ /^($Ident|$Constant)/o) { print "IDENT($1)\n" if ($dbg_values > 1); $type = 'V'; } elsif ($cur =~ /^($Assignment)/o) { print "ASSIGN($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~/^(;|{|})/) { print "END($1)\n" if ($dbg_values > 1); $type = 'E'; $av_pend_colon = 'O'; } elsif ($cur =~/^(,)/) { print "COMMA($1)\n" if ($dbg_values > 1); $type = 'C'; } elsif ($cur =~ /^(\?)/o) { print "QUESTION($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(:)/o) { print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); substr($var, length($res), 1, $av_pend_colon); if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { $type = 'E'; } else { $type = 'N'; } $av_pend_colon = 'O'; } elsif ($cur =~ /^(\[)/o) { print "CLOSE($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { my $variant; print "OPV($1)\n" if ($dbg_values > 1); if ($type eq 'V') { $variant = 'B'; } else { $variant = 'U'; } substr($var, length($res), 1, $variant); $type = 'N'; } elsif ($cur =~ /^($Operators)/o) { print "OP($1)\n" if ($dbg_values > 1); if ($1 ne '++' && $1 ne '--') { $type = 'N'; } } elsif ($cur =~ /(^.)/o) { print "C($1)\n" if ($dbg_values > 1); } if (defined $1) { $cur = substr($cur, length($1)); $res .= $type x length($1); } } return ($res, $var); } sub possible { my ($possible, $line) = @_; my $notPermitted = qr{(?: ^(?: $Modifier| $Storage| $Type| DEFINE_\S+ )$| ^(?: goto| return| case| else| asm|__asm__| do| \#| \#\#| )(?:\s|$)| ^(?:typedef|struct|enum)\b )}x; warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); if ($possible !~ $notPermitted) { # Check for modifiers. $possible =~ s/\s*$Storage\s*//g; $possible =~ s/\s*$Sparse\s*//g; if ($possible =~ /^\s*$/) { } elsif ($possible =~ /\s/) { $possible =~ s/\s*$Type\s*//g; for my $modifier (split(' ', $possible)) { if ($modifier !~ $notPermitted) { warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); push(@modifierListFile, $modifier); } } } else { warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); push(@typeListFile, $possible); } build_types(); } else { warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); } } my $prefix = ''; sub show_type { my ($type) = @_; $type =~ tr/[a-z]/[A-Z]/; return defined $use_type{$type} if (scalar keys %use_type > 0); return !defined $ignore_type{$type}; } sub report { my ($level, $type, $msg) = @_; if (!show_type($type) || (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { return 0; } my $output = ''; if ($color) { if ($level eq 'ERROR') { $output .= RED; } elsif ($level eq 'WARNING') { $output .= YELLOW; } else { $output .= GREEN; } } $output .= $prefix . $level . ':'; if ($show_types) { $output .= BLUE if ($color); $output .= "$type:"; } $output .= RESET if ($color); $output .= ' ' . $msg . "\n"; if ($showfile) { my @lines = split("\n", $output, -1); splice(@lines, 1, 1); $output = join("\n", @lines); } if ($terse) { $output = (split('\n', $output))[0] . "\n"; } if ($verbose && exists($verbose_messages{$type}) && !exists($verbose_emitted{$type})) { $output .= $verbose_messages{$type} . "\n\n"; $verbose_emitted{$type} = 1; } push(our @report, $output); return 1; } sub report_dump { our @report; } sub fixup_current_range { my ($lineRef, $offset, $length) = @_; if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { my $o = $1; my $l = $2; my $no = $o + $offset; my $nl = $l + $length; $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; } } sub fix_inserted_deleted_lines { my ($linesRef, $insertedRef, $deletedRef) = @_; my $range_last_linenr = 0; my $delta_offset = 0; my $old_linenr = 0; my $new_linenr = 0; my $next_insert = 0; my $next_delete = 0; my @lines = (); my $inserted = @{$insertedRef}[$next_insert++]; my $deleted = @{$deletedRef}[$next_delete++]; foreach my $old_line (@{$linesRef}) { my $save_line = 1; my $line = $old_line; #don't modify the array if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename $delta_offset = 0; } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk $range_last_linenr = $new_linenr; fixup_current_range(\$line, $delta_offset, 0); } while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { $deleted = @{$deletedRef}[$next_delete++]; $save_line = 0; fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); } while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { push(@lines, ${$inserted}{'LINE'}); $inserted = @{$insertedRef}[$next_insert++]; $new_linenr++; fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); } if ($save_line) { push(@lines, $line); $new_linenr++; } $old_linenr++; } return @lines; } sub fix_insert_line { my ($linenr, $line) = @_; my $inserted = { LINENR => $linenr, LINE => $line, }; push(@fixed_inserted, $inserted); } sub fix_delete_line { my ($linenr, $line) = @_; my $deleted = { LINENR => $linenr, LINE => $line, }; push(@fixed_deleted, $deleted); } sub ERROR { my ($type, $msg) = @_; if (report("ERROR", $type, $msg)) { our $clean = 0; our $cnt_error++; return 1; } return 0; } sub WARN { my ($type, $msg) = @_; if (report("WARNING", $type, $msg)) { our $clean = 0; our $cnt_warn++; return 1; } return 0; } sub CHK { my ($type, $msg) = @_; if ($check && report("CHECK", $type, $msg)) { our $clean = 0; our $cnt_chk++; return 1; } return 0; } sub check_absolute_file { my ($absolute, $herecurr) = @_; my $file = $absolute; ##print "absolute<$absolute>\n"; # See if any suffix of this path is a path within the tree. while ($file =~ s@^[^/]*/@@) { if (-f "$root/$file") { ##print "file<$file>\n"; last; } } if (! -f _) { return 0; } # It is, so see if the prefix is acceptable. my $prefix = $absolute; substr($prefix, -length($file)) = ''; ##print "prefix<$prefix>\n"; if ($prefix ne ".../") { WARN("USE_RELATIVE_PATH", "use relative pathname instead of absolute in changelog text\n" . $herecurr); } } sub trim { my ($string) = @_; $string =~ s/^\s+|\s+$//g; return $string; } sub ltrim { my ($string) = @_; $string =~ s/^\s+//; return $string; } sub rtrim { my ($string) = @_; $string =~ s/\s+$//; return $string; } sub string_find_replace { my ($string, $find, $replace) = @_; $string =~ s/$find/$replace/g; return $string; } sub tabify { my ($leading) = @_; my $source_indent = $tabsize; my $max_spaces_before_tab = $source_indent - 1; my $spaces_to_tab = " " x $source_indent; #convert leading spaces to tabs 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; #Remove spaces before a tab 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; return "$leading"; } sub pos_last_openparen { my ($line) = @_; my $pos = 0; my $opens = $line =~ tr/\(/\(/; my $closes = $line =~ tr/\)/\)/; my $last_openparen = 0; if (($opens == 0) || ($closes >= $opens)) { return -1; } my $len = length($line); for ($pos = 0; $pos < $len; $pos++) { my $string = substr($line, $pos); if ($string =~ /^($FuncArg|$balanced_parens)/) { $pos += length($1) - 1; } elsif (substr($line, $pos, 1) eq '(') { $last_openparen = $pos; } elsif (index($string, '(') == -1) { last; } } return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; } sub get_raw_comment { my ($line, $rawline) = @_; my $comment = ''; for my $i (0 .. (length($line) - 1)) { if (substr($line, $i, 1) eq "$;") { $comment .= substr($rawline, $i, 1); } } return $comment; } sub exclude_global_initialisers { my ($realfile) = @_; # Do not check for BPF programs (tools/testing/selftests/bpf/progs/*.c, samples/bpf/*_kern.c, *.bpf.c). return $realfile =~ m@^tools/testing/selftests/bpf/progs/.*\.c$@ || $realfile =~ m@^samples/bpf/.*_kern\.c$@ || $realfile =~ m@/bpf/.*\.bpf\.c$@; } sub process { my $filename = shift; my $linenr=0; my $prevline=""; my $prevrawline=""; my $stashline=""; my $stashrawline=""; my $length; my $indent; my $previndent=0; my $stashindent=0; our $clean = 1; my $signoff = 0; my $fixes_tag = 0; my $is_revert = 0; my $needs_fixes_tag = ""; my $author = ''; my $authorsignoff = 0; my $author_sob = ''; my $is_patch = 0; my $is_binding_patch = -1; my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch my $has_patch_separator = 0; #Found a --- line my $has_commit_log = 0; #Encountered lines before patch my $commit_log_lines = 0; #Number of commit log lines my $commit_log_possible_stack_dump = 0; my $commit_log_long_line = 0; my $commit_log_has_diff = 0; my $reported_maintainer_file = 0; my $non_utf8_charset = 0; my $last_git_commit_id_linenr = -1; my $last_blank_line = 0; my $last_coalesced_string_linenr = -1; our @report = (); our $cnt_lines = 0; our $cnt_error = 0; our $cnt_warn = 0; our $cnt_chk = 0; # Trace the real file/line as we go. my $realfile = ''; my $realline = 0; my $realcnt = 0; my $here = ''; my $context_function; #undef'd unless there's a known function my $in_comment = 0; my $comment_edge = 0; my $first_line = 0; my $p1_prefix = ''; my $prev_values = 'E'; # suppression flags my %suppress_ifbraces; my %suppress_whiletrailers; my %suppress_export; my $suppress_statement = 0; my %signatures = (); # Pre-scan the patch sanitizing the lines. # Pre-scan the patch looking for any __setup documentation. # my @setup_docs = (); my $setup_docs = 0; my $camelcase_file_seeded = 0; my $checklicenseline = 1; sanitise_line_reset(); my $line; foreach my $rawline (@rawlines) { $linenr++; $line = $rawline; push(@fixed, $rawline) if ($fix); if ($rawline=~/^\+\+\+\s+(\S+)/) { $setup_docs = 0; if ($1 =~ m@Documentation/admin-guide/kernel-parameters.txt$@) { $setup_docs = 1; } #next; } if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $realline=$1-1; if (defined $2) { $realcnt=$3+1; } else { $realcnt=1+1; } $in_comment = 0; # Guestimate if this is a continuing comment. Run # the context looking for a comment "edge". If this # edge is a close comment then we must be in a comment # at context start. my $edge; my $cnt = $realcnt; for (my $ln = $linenr + 1; $cnt > 0; $ln++) { next if (defined $rawlines[$ln - 1] && $rawlines[$ln - 1] =~ /^-/); $cnt--; #print "RAW<$rawlines[$ln - 1]>\n"; last if (!defined $rawlines[$ln - 1]); if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { ($edge) = $1; last; } } if (defined $edge && $edge eq '*/') { $in_comment = 1; } # Guestimate if this is a continuing comment. If this # is the start of a diff block and this line starts # ' *' then it is very likely a comment. if (!defined $edge && $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) { $in_comment = 1; } ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; sanitise_line_reset($in_comment); } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { # Standardise the strings and chars within the input to # simplify matching -- only bother with positive lines. $line = sanitise_line($rawline); } push(@lines, $line); if ($realcnt > 1) { $realcnt-- if ($line =~ /^(?:\+| |$)/); } else { $realcnt = 0; } #print "==>$rawline\n"; #print "-->$line\n"; if ($setup_docs && $line =~ /^\+/) { push(@setup_docs, $line); } } $prefix = ''; $realcnt = 0; $linenr = 0; $fixlinenr = -1; foreach my $line (@lines) { $linenr++; $fixlinenr++; my $sline = $line; #copy of $line $sline =~ s/$;/ /g; #with comments as spaces my $rawline = $rawlines[$linenr - 1]; my $raw_comment = get_raw_comment($line, $rawline); # check if it's a mode change, rename or start of a patch if (!$in_commit_log && ($line =~ /^ mode change [0-7]+ => [0-7]+ \S+\s*$/ || ($line =~ /^rename (?:from|to) \S+\s*$/ || $line =~ /^diff --git a\/[\w\/\.\_\-]+ b\/\S+\s*$/))) { $is_patch = 1; } #extract the line range in the file after the patch is applied if (!$in_commit_log && $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) { my $context = $4; $is_patch = 1; $first_line = $linenr + 1; $realline=$1-1; if (defined $2) { $realcnt=$3+1; } else { $realcnt=1+1; } annotate_reset(); $prev_values = 'E'; %suppress_ifbraces = (); %suppress_whiletrailers = (); %suppress_export = (); $suppress_statement = 0; if ($context =~ /\b(\w+)\s*\(/) { $context_function = $1; } else { undef $context_function; } next; # track the line number as we move through the hunk, note that # new versions of GNU diff omit the leading space on completely # blank context lines so we need to count that too. } elsif ($line =~ /^( |\+|$)/) { $realline++; $realcnt-- if ($realcnt != 0); # Measure the line length and indent. ($length, $indent) = line_stats($rawline); # Track the previous line. ($prevline, $stashline) = ($stashline, $line); ($previndent, $stashindent) = ($stashindent, $indent); ($prevrawline, $stashrawline) = ($stashrawline, $rawline); #warn "line<$line>\n"; } elsif ($realcnt == 1) { $realcnt--; } my $hunk_line = ($realcnt != 0); $here = "#$linenr: " if (!$file); $here = "#$realline: " if ($file); my $found_file = 0; # extract the filename as it passes if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; $found_file = 1; } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; $p1_prefix = $1; if (!$file && $tree && $p1_prefix ne '' && -e "$root/$p1_prefix") { WARN("PATCH_PREFIX", "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); } if ($realfile =~ m@^include/asm/@) { ERROR("MODIFIED_INCLUDE_ASM", "do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); } $found_file = 1; } #make up the handle for any error we report on this line if ($showfile) { $prefix = "$realfile:$realline: " } elsif ($emacs) { if ($file) { $prefix = "$filename:$realline: "; } else { $prefix = "$filename:$linenr: "; } } if ($found_file) { if (is_maintained_obsolete($realfile)) { WARN("OBSOLETE", "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy. No unnecessary modifications please.\n"); } if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { $check = 1; } else { $check = $check_orig; } $checklicenseline = 1; if ($realfile !~ /^MAINTAINERS/) { my $last_binding_patch = $is_binding_patch; $is_binding_patch = () = $realfile =~ m@^(?:Documentation/devicetree/|include/dt-bindings/)@; if (($last_binding_patch != -1) && ($last_binding_patch ^ $is_binding_patch)) { WARN("DT_SPLIT_BINDING_PATCH", "DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.rst\n"); } } next; } $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); my $hereline = "$here\n$rawline\n"; my $herecurr = "$here\n$rawline\n"; my $hereprev = "$here\n$prevrawline\n$rawline\n"; $cnt_lines++ if ($realcnt != 0); # Verify the existence of a commit log if appropriate # 2 is used because a $signature is counted in $commit_log_lines if ($in_commit_log) { if ($line !~ /^\s*$/) { $commit_log_lines++; #could be a $signature } } elsif ($has_commit_log && $commit_log_lines < 2) { WARN("COMMIT_MESSAGE", "Missing commit description - Add an appropriate one\n"); $commit_log_lines = 2; #warn only once } # Check if the commit log has what seems like a diff which can confuse patch if ($in_commit_log && !$commit_log_has_diff && (($line =~ m@^\s+diff\b.*a/([\w/]+)@ && $line =~ m@^\s+diff\b.*a/[\w/]+\s+b/$1\b@) || $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { ERROR("DIFF_IN_COMMIT_MSG", "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); $commit_log_has_diff = 1; } # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; if ($realfile !~ m@scripts/@ && $realfile !~ /\.(py|pl|awk|sh)$/) { ERROR("EXECUTE_PERMISSIONS", "do not set execute permissions for source files\n" . $permhere); } } # Check the patch for a From: if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) { $author = $1; my $curline = $linenr; while(defined($rawlines[$curline]) && ($rawlines[$curline++] =~ /^[ \t]\s*(.*)/)) { $author .= $1; } $author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i); $author =~ s/"//g; $author = reformat_email($author); } # Check the patch for a signoff: if ($line =~ /^\s*signed-off-by:\s*(.*)/i) { $signoff++; $in_commit_log = 0; if ($author ne '' && $authorsignoff != 1) { if (same_email_addresses($1, $author)) { $authorsignoff = 1; } else { my $ctx = $1; my ($email_name, $email_comment, $email_address, $comment1) = parse_email($ctx); my ($author_name, $author_comment, $author_address, $comment2) = parse_email($author); if (lc $email_address eq lc $author_address && $email_name eq $author_name) { $author_sob = $ctx; $authorsignoff = 2; } elsif (lc $email_address eq lc $author_address) { $author_sob = $ctx; $authorsignoff = 3; } elsif ($email_name eq $author_name) { $author_sob = $ctx; $authorsignoff = 4; my $address1 = $email_address; my $address2 = $author_address; if ($address1 =~ /(\S+)\+\S+(\@.*)/) { $address1 = "$1$2"; } if ($address2 =~ /(\S+)\+\S+(\@.*)/) { $address2 = "$1$2"; } if ($address1 eq $address2) { $authorsignoff = 5; } } } } } # Check for patch separator if ($line =~ /^---$/) { $has_patch_separator = 1; $in_commit_log = 0; } # Check if MAINTAINERS is being updated. If so, there's probably no need to # emit the "does MAINTAINERS need updating?" message on file add/move/delete if ($line =~ /^\s*MAINTAINERS\s*\|/) { $reported_maintainer_file = 1; } # Check signature styles if (!$in_header_lines && $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { my $space_before = $1; my $sign_off = $2; my $space_after = $3; my $email = $4; my $ucfirst_sign_off = ucfirst(lc($sign_off)); if ($sign_off !~ /$signature_tags/) { my $suggested_signature = find_standard_signature($sign_off); if ($suggested_signature eq "") { WARN("BAD_SIGN_OFF", "Non-standard signature: $sign_off\n" . $herecurr); } else { if (WARN("BAD_SIGN_OFF", "Non-standard signature: '$sign_off' - perhaps '$suggested_signature'?\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/$sign_off/$suggested_signature/; } } } if (defined $space_before && $space_before ne "") { if (WARN("BAD_SIGN_OFF", "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "$ucfirst_sign_off $email"; } } if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { if (WARN("BAD_SIGN_OFF", "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "$ucfirst_sign_off $email"; } } if (!defined $space_after || $space_after ne " ") { if (WARN("BAD_SIGN_OFF", "Use a single space after $ucfirst_sign_off\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "$ucfirst_sign_off $email"; } } my ($email_name, $name_comment, $email_address, $comment) = parse_email($email); my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment)); if ($suggested_email eq "") { ERROR("BAD_SIGN_OFF", "Unrecognized email address: '$email'\n" . $herecurr); } else { my $dequoted = $suggested_email; $dequoted =~ s/^"//; $dequoted =~ s/" 1) { WARN("BAD_SIGN_OFF", "Use a single name comment in email: '$email'\n" . $herecurr); } # stable@vger.kernel.org or stable@kernel.org shouldn't # have an email name. In addition comments should strictly # begin with a # if ($email =~ /^.*stable\@(?:vger\.)?kernel\.org/i) { if (($comment ne "" && $comment !~ /^#.+/) || ($email_name ne "")) { my $cur_name = $email_name; my $new_comment = $comment; $cur_name =~ s/[a-zA-Z\s\-\"]+//g; # Remove brackets enclosing comment text # and # from start of comments to get comment text $new_comment =~ s/^\((.*)\)$/$1/; $new_comment =~ s/^\[(.*)\]$/$1/; $new_comment =~ s/^[\s\#]+|\s+$//g; $new_comment = trim("$new_comment $cur_name") if ($cur_name ne $new_comment); $new_comment = " # $new_comment" if ($new_comment ne ""); my $new_email = "$email_address$new_comment"; if (WARN("BAD_STABLE_ADDRESS_STYLE", "Invalid email format for stable: '$email', prefer '$new_email'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/; } } } elsif ($comment ne "" && $comment !~ /^(?:#.+|\(.+\))$/) { my $new_comment = $comment; # Extract comment text from within brackets or # c89 style /*...*/ comments $new_comment =~ s/^\[(.*)\]$/$1/; $new_comment =~ s/^\/\*(.*)\*\/$/$1/; $new_comment = trim($new_comment); $new_comment =~ s/^[^\w]$//; # Single lettered comment with non word character is usually a typo $new_comment = "($new_comment)" if ($new_comment ne ""); my $new_email = format_email($email_name, $name_comment, $email_address, $new_comment); if (WARN("BAD_SIGN_OFF", "Unexpected content after email: '$email', should be: '$new_email'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/; } } } # Check for duplicate signatures my $sig_nospace = $line; $sig_nospace =~ s/\s//g; $sig_nospace = lc($sig_nospace); if (defined $signatures{$sig_nospace}) { WARN("BAD_SIGN_OFF", "Duplicate signature\n" . $herecurr); } else { $signatures{$sig_nospace} = 1; } # Check Co-developed-by: immediately followed by Signed-off-by: with same name and email if ($sign_off =~ /^co-developed-by:$/i) { if ($email eq $author) { WARN("BAD_SIGN_OFF", "Co-developed-by: should not be used to attribute nominal patch author '$author'\n" . $herecurr); } if (!defined $lines[$linenr]) { WARN("BAD_SIGN_OFF", "Co-developed-by: must be immediately followed by Signed-off-by:\n" . $herecurr); } elsif ($rawlines[$linenr] !~ /^signed-off-by:\s*(.*)/i) { WARN("BAD_SIGN_OFF", "Co-developed-by: must be immediately followed by Signed-off-by:\n" . $herecurr . $rawlines[$linenr] . "\n"); } elsif ($1 ne $email) { WARN("BAD_SIGN_OFF", "Co-developed-by and Signed-off-by: name/email do not match\n" . $herecurr . $rawlines[$linenr] . "\n"); } } # check if Reported-by: is followed by a Closes: tag if ($sign_off =~ /^reported(?:|-and-tested)-by:$/i) { if (!defined $lines[$linenr]) { WARN("BAD_REPORTED_BY_LINK", "Reported-by: should be immediately followed by Closes: with a URL to the report\n" . $herecurr . "\n"); } elsif ($rawlines[$linenr] !~ /^closes:\s*/i) { WARN("BAD_REPORTED_BY_LINK", "Reported-by: should be immediately followed by Closes: with a URL to the report\n" . $herecurr . $rawlines[$linenr] . "\n"); } } } # These indicate a bug fix if (!$in_header_lines && !$is_patch && $line =~ /^This reverts commit/) { $is_revert = 1; } if (!$in_header_lines && !$is_patch && $line =~ /((?:(?:BUG: K.|UB)SAN: |Call Trace:|stable\@|syzkaller))/) { $needs_fixes_tag = $1; } # Check Fixes: styles is correct if (!$in_header_lines && $line =~ /^\s*(fixes:?)\s*(?:commit\s*)?([0-9a-f]{5,40})(?:\s*($balanced_parens))?/i) { my $tag = $1; my $orig_commit = $2; my $title; my $title_has_quotes = 0; $fixes_tag = 1; if (defined $3) { # Always strip leading/trailing parens then double quotes if existing $title = substr($3, 1, -1); if ($title =~ /^".*"$/) { $title = substr($title, 1, -1); $title_has_quotes = 1; } } else { $title = "commit title" } my $tag_case = not ($tag eq "Fixes:"); my $tag_space = not ($line =~ /^fixes:? [0-9a-f]{5,40} ($balanced_parens)/i); my $id_length = not ($orig_commit =~ /^[0-9a-f]{12}$/i); my $id_case = not ($orig_commit !~ /[A-F]/); my $id = "0123456789ab"; my ($cid, $ctitle) = git_commit_info($orig_commit, $id, $title); if ($ctitle ne $title || $tag_case || $tag_space || $id_length || $id_case || !$title_has_quotes) { if (WARN("BAD_FIXES_TAG", "Please use correct Fixes: style 'Fixes: <12 chars of sha1> (\"\")' - ie: 'Fixes: $cid (\"$ctitle\")'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "Fixes: $cid (\"$ctitle\")"; } } } # Check email subject for common tools that don't need to be mentioned if ($in_header_lines && $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { WARN("EMAIL_SUBJECT", "A patch subject line should describe the change not the tool that found it\n" . $herecurr); } # Check for Gerrit Change-Ids not in any patch context if ($realfile eq '' && !$has_patch_separator && $line =~ /^\s*change-id:/i) { if (ERROR("GERRIT_CHANGE_ID", "Remove Gerrit Change-Id's before submitting upstream\n" . $herecurr) && $fix) { fix_delete_line($fixlinenr, $rawline); } } # Check if the commit log is in a possible stack dump if ($in_commit_log && !$commit_log_possible_stack_dump && ($line =~ /^\s*(?:WARNING:|BUG:)/ || $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || # timestamp $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) || $line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ || $line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at [0-9a-fA-F]+/) { # stack dump address styles $commit_log_possible_stack_dump = 1; } # Check for line lengths > 75 in commit log, warn once if ($in_commit_log && !$commit_log_long_line && length($line) > 75 && !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || # file delta changes $line =~ /^\s*(?:[\w\.\-\+]*\/)++[\w\.\-\+]+:/ || # filename then : $line =~ /^\s*(?:Fixes:|$link_tags_search|$signature_tags)/i || # A Fixes:, link or signature tag line $commit_log_possible_stack_dump)) { WARN("COMMIT_LOG_LONG_LINE", "Prefer a maximum 75 chars per line (possible unwrapped commit description?)\n" . $herecurr); $commit_log_long_line = 1; } # Reset possible stack dump if a blank line is found if ($in_commit_log && $commit_log_possible_stack_dump && $line =~ /^\s*$/) { $commit_log_possible_stack_dump = 0; } # Check for odd tags before a URI/URL if ($in_commit_log && $line =~ /^\s*(\w+:)\s*http/ && $1 !~ /^$link_tags_search$/) { if ($1 =~ /^v(?:ersion)?\d+/i) { WARN("COMMIT_LOG_VERSIONING", "Patch version information should be after the --- line\n" . $herecurr); } else { WARN("COMMIT_LOG_USE_LINK", "Unknown link reference '$1', use $link_tags_print instead\n" . $herecurr); } } # Check for misuse of the link tags if ($in_commit_log && $line =~ /^\s*(\w+:)\s*(\S+)/) { my $tag = $1; my $value = $2; if ($tag =~ /^$link_tags_search$/ && $value !~ m{^https?://}) { WARN("COMMIT_LOG_WRONG_LINK", "'$tag' should be followed by a public http(s) link\n" . $herecurr); } } # Check for lines starting with a # if ($in_commit_log && $line =~ /^#/) { if (WARN("COMMIT_COMMENT_SYMBOL", "Commit log lines starting with '#' are dropped by git as comments\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^/ /; } } # Check for git id commit length and improperly formed commit descriptions # A correctly formed commit description is: # commit <SHA-1 hash length 12+ chars> ("Complete commit subject") # with the commit subject '("' prefix and '")' suffix # This is a fairly compilicated block as it tests for what appears to be # bare SHA-1 hash with minimum length of 5. It also avoids several types of # possible SHA-1 matches. # A commit match can span multiple lines so this block attempts to find a # complete typical commit on a maximum of 3 lines if ($perl_version_ok && $in_commit_log && !$commit_log_possible_stack_dump && $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink|base-commit):/i && $line !~ /^This reverts commit [0-9a-f]{7,40}/ && (($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || ($line =~ /\bcommit\s*$/i && defined($rawlines[$linenr]) && $rawlines[$linenr] =~ /^\s*[0-9a-f]{5,}\b/i)) || ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i && $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { my $init_char = "c"; my $orig_commit = ""; my $short = 1; my $long = 0; my $case = 1; my $space = 1; my $id = '0123456789ab'; my $orig_desc = "commit description"; my $description = ""; my $herectx = $herecurr; my $has_parens = 0; my $has_quotes = 0; my $input = $line; if ($line =~ /(?:\bcommit\s+[0-9a-f]{5,}|\bcommit\s*$)/i) { for (my $n = 0; $n < 2; $n++) { if ($input =~ /\bcommit\s+[0-9a-f]{5,}\s*($balanced_parens)/i) { $orig_desc = $1; $has_parens = 1; # Always strip leading/trailing parens then double quotes if existing $orig_desc = substr($orig_desc, 1, -1); if ($orig_desc =~ /^".*"$/) { $orig_desc = substr($orig_desc, 1, -1); $has_quotes = 1; } last; } last if ($#lines < $linenr + $n); $input .= " " . trim($rawlines[$linenr + $n]); $herectx .= "$rawlines[$linenr + $n]\n"; } $herectx = $herecurr if (!$has_parens); } if ($input =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { $init_char = $1; $orig_commit = lc($2); $short = 0 if ($input =~ /\bcommit\s+[0-9a-f]{12,40}/i); $long = 1 if ($input =~ /\bcommit\s+[0-9a-f]{41,}/i); $space = 0 if ($input =~ /\bcommit [0-9a-f]/i); $case = 0 if ($input =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); } elsif ($input =~ /\b([0-9a-f]{12,40})\b/i) { $orig_commit = lc($1); } ($id, $description) = git_commit_info($orig_commit, $id, $orig_desc); if (defined($id) && ($short || $long || $space || $case || ($orig_desc ne $description) || !$has_quotes) && $last_git_commit_id_linenr != $linenr - 1) { ERROR("GIT_COMMIT_ID", "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herectx); } #don't report the next line if this line ends in commit and the sha1 hash is the next line $last_git_commit_id_linenr = $linenr if ($line =~ /\bcommit\s*$/i); } # Check for mailing list archives other than lore.kernel.org if ($rawline =~ m{http.*\b$obsolete_archives}) { WARN("PREFER_LORE_ARCHIVE", "Use lore.kernel.org archive links when possible - see https://lore.kernel.org/lists.html\n" . $herecurr); } # Check for added, moved or deleted files if (!$reported_maintainer_file && !$in_commit_log && ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && (defined($1) || defined($2))))) { $is_patch = 1; $reported_maintainer_file = 1; WARN("FILE_PATH_CHANGES", "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); } # Check for adding new DT bindings not in schema format if (!$in_commit_log && ($line =~ /^new file mode\s*\d+\s*$/) && ($realfile =~ m@^Documentation/devicetree/bindings/.*\.txt$@)) { WARN("DT_SCHEMA_BINDING_PATCH", "DT bindings should be in DT schema format. See: Documentation/devicetree/bindings/writing-schema.rst\n"); } # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("CORRUPTED_PATCH", "patch seems to be corrupt (line wrapped?)\n" . $herecurr) if (!$emitted_corrupt++); } # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php if (($realfile =~ /^$/ || $line =~ /^\+/) && $rawline !~ m/^$UTF8*$/) { my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); my $blank = copy_spacing($rawline); my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; my $hereptr = "$hereline$ptr\n"; CHK("INVALID_UTF8", "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); } # Check if it's the start of a commit log # (not a header line and we haven't seen the patch filename) if ($in_header_lines && $realfile =~ /^$/ && !($rawline =~ /^\s+(?:\S|$)/ || $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) { $in_header_lines = 0; $in_commit_log = 1; $has_commit_log = 1; } # Check if there is UTF-8 in a commit log when a mail header has explicitly # declined it, i.e defined some charset where it is missing. if ($in_header_lines && $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && $1 !~ /utf-8/i) { $non_utf8_charset = 1; } if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && $rawline =~ /$NON_ASCII_UTF8/) { WARN("UTF8_BEFORE_PATCH", "8-bit UTF-8 used in possible commit log\n" . $herecurr); } # Check for absolute kernel paths in commit message if ($tree && $in_commit_log) { while ($line =~ m{(?:^|\s)(/\S*)}g) { my $file = $1; if ($file =~ m{^(.*?)(?::\d+)+:?$} && check_absolute_file($1, $herecurr)) { # } else { check_absolute_file($file, $herecurr); } } } # Check for various typo / spelling mistakes if (defined($misspellings) && ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { my $typo = $1; my $blank = copy_spacing($rawline); my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo); my $hereptr = "$hereline$ptr\n"; my $typo_fix = $spelling_fix{lc($typo)}; $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); my $msg_level = \&WARN; $msg_level = \&CHK if ($file); if (&{$msg_level}("TYPO_SPELLING", "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $hereptr) && $fix) { $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; } } } # check for invalid commit id if ($in_commit_log && $line =~ /(^fixes:|\bcommit)\s+([0-9a-f]{6,40})\b/i) { my $id; my $description; ($id, $description) = git_commit_info($2, undef, undef); if (!defined($id)) { WARN("UNKNOWN_COMMIT_ID", "Unknown commit id '$2', maybe rebased or not pulled?\n" . $herecurr); } } # check for repeated words separated by a single space # avoid false positive from list command eg, '-rw-r--r-- 1 root root' if (($rawline =~ /^\+/ || $in_commit_log) && $rawline !~ /[bcCdDlMnpPs\?-][rwxsStT-]{9}/) { pos($rawline) = 1 if (!$in_commit_log); while ($rawline =~ /\b($word_pattern) (?=($word_pattern))/g) { my $first = $1; my $second = $2; my $start_pos = $-[1]; my $end_pos = $+[2]; if ($first =~ /(?:struct|union|enum)/) { pos($rawline) += length($first) + length($second) + 1; next; } next if (lc($first) ne lc($second)); next if ($first eq 'long'); # check for character before and after the word matches my $start_char = ''; my $end_char = ''; $start_char = substr($rawline, $start_pos - 1, 1) if ($start_pos > ($in_commit_log ? 0 : 1)); $end_char = substr($rawline, $end_pos, 1) if ($end_pos < length($rawline)); next if ($start_char =~ /^\S$/); next if (index(" \t.,;?!", $end_char) == -1); # avoid repeating hex occurrences like 'ff ff fe 09 ...' if ($first =~ /\b[0-9a-f]{2,}\b/i) { next if (!exists($allow_repeated_words{lc($first)})); } if (WARN("REPEATED_WORD", "Possible repeated word: '$first'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b$first $second\b/$first/; } } # if it's a repeated word on consecutive lines in a comment block if ($prevline =~ /$;+\s*$/ && $prevrawline =~ /($word_pattern)\s*$/) { my $last_word = $1; if ($rawline =~ /^\+\s*\*\s*$last_word /) { if (WARN("REPEATED_WORD", "Possible repeated word: '$last_word'\n" . $hereprev) && $fix) { $fixed[$fixlinenr] =~ s/(\+\s*\*\s*)$last_word /$1/; } } } } # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); #trailing whitespace if ($line =~ /^\+.*\015/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (ERROR("DOS_LINE_ENDINGS", "DOS line endings\n" . $herevet) && $fix) { $fixed[$fixlinenr] =~ s/[\s\015]+$//; } } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (ERROR("TRAILING_WHITESPACE", "trailing whitespace\n" . $herevet) && $fix) { $fixed[$fixlinenr] =~ s/\s+$//; } $rpt_cleaners = 1; } # Check for FSF mailing addresses. if ($rawline =~ /\bwrite to the Free/i || $rawline =~ /\b675\s+Mass\s+Ave/i || $rawline =~ /\b59\s+Temple\s+Pl/i || $rawline =~ /\b51\s+Franklin\s+St/i) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; my $msg_level = \&ERROR; $msg_level = \&CHK if ($file); &{$msg_level}("FSF_MAILING_ADDRESS", "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) } # check for Kconfig help text having a real description # Only applies when adding the entry originally, after that we do not have # sufficient context to determine whether it is indeed long enough. if ($realfile =~ /Kconfig/ && # 'choice' is usually the last thing on the line (though # Kconfig supports named choices), so use a word boundary # (\b) rather than a whitespace character (\s) $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) { my $ln = $linenr; my $needs_help = 0; my $has_help = 0; my $help_length = 0; while (defined $lines[$ln]) { my $f = $lines[$ln++]; next if ($f =~ /^-/); last if ($f !~ /^[\+ ]/); # !patch context if ($f =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { $needs_help = 1; next; } if ($f =~ /^\+\s*help\s*$/) { $has_help = 1; next; } $f =~ s/^.//; # strip patch context [+ ] $f =~ s/#.*//; # strip # directives $f =~ s/^\s+//; # strip leading blanks next if ($f =~ /^$/); # skip blank lines # At the end of this Kconfig block: # This only checks context lines in the patch # and so hopefully shouldn't trigger false # positives, even though some of these are # common words in help texts if ($f =~ /^(?:config|menuconfig|choice|endchoice| if|endif|menu|endmenu|source)\b/x) { last; } $help_length++ if ($has_help); } if ($needs_help && $help_length < $min_conf_desc_length) { my $stat_real = get_stat_real($linenr, $ln - 1); WARN("CONFIG_DESCRIPTION", "please write a help paragraph that fully describes the config symbol\n" . "$here\n$stat_real\n"); } } # check MAINTAINERS entries if ($realfile =~ /^MAINTAINERS$/) { # check MAINTAINERS entries for the right form if ($rawline =~ /^\+[A-Z]:/ && $rawline !~ /^\+[A-Z]:\t\S/) { if (WARN("MAINTAINERS_STYLE", "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/; } } # check MAINTAINERS entries for the right ordering too my $preferred_order = 'MRLSWQBCPTFXNK'; if ($rawline =~ /^\+[A-Z]:/ && $prevrawline =~ /^[\+ ][A-Z]:/) { $rawline =~ /^\+([A-Z]):\s*(.*)/; my $cur = $1; my $curval = $2; $prevrawline =~ /^[\+ ]([A-Z]):\s*(.*)/; my $prev = $1; my $prevval = $2; my $curindex = index($preferred_order, $cur); my $previndex = index($preferred_order, $prev); if ($curindex < 0) { WARN("MAINTAINERS_STYLE", "Unknown MAINTAINERS entry type: '$cur'\n" . $herecurr); } else { if ($previndex >= 0 && $curindex < $previndex) { WARN("MAINTAINERS_STYLE", "Misordered MAINTAINERS entry - list '$cur:' before '$prev:'\n" . $hereprev); } elsif ((($prev eq 'F' && $cur eq 'F') || ($prev eq 'X' && $cur eq 'X')) && ($prevval cmp $curval) > 0) { WARN("MAINTAINERS_STYLE", "Misordered MAINTAINERS entry - list file patterns in alphabetic order\n" . $hereprev); } } } } if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { my $flag = $1; my $replacement = { 'EXTRA_AFLAGS' => 'asflags-y', 'EXTRA_CFLAGS' => 'ccflags-y', 'EXTRA_CPPFLAGS' => 'cppflags-y', 'EXTRA_LDFLAGS' => 'ldflags-y', }; WARN("DEPRECATED_VARIABLE", "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); } # check for DT compatible documentation if (defined $root && (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; my $dt_path = $root . "/Documentation/devicetree/bindings/"; my $vp_file = $dt_path . "vendor-prefixes.yaml"; foreach my $compat (@compats) { my $compat2 = $compat; $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/; my $compat3 = $compat; $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/; `grep -Erq "$compat|$compat2|$compat3" $dt_path`; if ( $? >> 8 ) { WARN("UNDOCUMENTED_DT_STRING", "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); } next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; my $vendor = $1; `grep -Eq "\\"\\^\Q$vendor\E,\\.\\*\\":" $vp_file`; if ( $? >> 8 ) { WARN("UNDOCUMENTED_DT_STRING", "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); } } } # check for using SPDX license tag at beginning of files if ($realline == $checklicenseline) { if ($rawline =~ /^[ \+]\s*\#\!\s*\//) { $checklicenseline = 2; } elsif ($rawline =~ /^\+/) { my $comment = ""; if ($realfile =~ /\.(h|s|S)$/) { $comment = '/*'; } elsif ($realfile =~ /\.(c|rs|dts|dtsi)$/) { $comment = '//'; } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) { $comment = '#'; } elsif ($realfile =~ /\.rst$/) { $comment = '..'; } # check SPDX comment style for .[chsS] files if ($realfile =~ /\.[chsS]$/ && $rawline =~ /SPDX-License-Identifier:/ && $rawline !~ m@^\+\s*\Q$comment\E\s*@) { WARN("SPDX_LICENSE_TAG", "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr); } if ($comment !~ /^$/ && $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) { WARN("SPDX_LICENSE_TAG", "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr); } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) { my $spdx_license = $1; if (!is_SPDX_License_valid($spdx_license)) { WARN("SPDX_LICENSE_TAG", "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr); } if ($realfile =~ m@^Documentation/devicetree/bindings/@ && $spdx_license !~ /GPL-2\.0(?:-only)? OR BSD-2-Clause/) { my $msg_level = \&WARN; $msg_level = \&CHK if ($file); if (&{$msg_level}("SPDX_LICENSE_TAG", "DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause)\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/SPDX-License-Identifier: .*/SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)/; } } if ($realfile =~ m@^include/dt-bindings/@ && $spdx_license !~ /GPL-2\.0(?:-only)? OR \S+/) { WARN("SPDX_LICENSE_TAG", "DT binding headers should be licensed (GPL-2.0-only OR .*)\n" . $herecurr); } } } } # check for embedded filenames if ($rawline =~ /^\+.*\b\Q$realfile\E\b/) { WARN("EMBEDDED_FILENAME", "It's generally not useful to have the filename in the file\n" . $herecurr); } # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|rs|s|S|sh|dtsi|dts)$/); # check for using SPDX-License-Identifier on the wrong line number if ($realline != $checklicenseline && $rawline =~ /\bSPDX-License-Identifier:/ && substr($line, @-, @+ - @-) eq "$;" x (@+ - @-)) { WARN("SPDX_LICENSE_TAG", "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr); } # line length limit (with some exclusions) # # There are a few types of lines that may extend beyond $max_line_length: # logging functions like pr_info that end in a string # lines with a single string # #defines that are a single string # lines with an RFC3986 like URL # # There are 3 different line length message types: # LONG_LINE_COMMENT a comment starts before but extends beyond $max_line_length # LONG_LINE_STRING a string starts before but extends beyond $max_line_length # LONG_LINE all other lines longer than $max_line_length # # if LONG_LINE is ignored, the other 2 types are also ignored # if ($line =~ /^\+/ && $length > $max_line_length) { my $msg_type = "LONG_LINE"; # Check the allowed long line types first # logging functions that end in a string that starts # before $max_line_length if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { $msg_type = ""; # lines with only strings (w/ possible termination) # #defines with only strings } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { $msg_type = ""; # More special cases } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ || $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) { $msg_type = ""; # URL ($rawline is used in case the URL is in a comment) } elsif ($rawline =~ /^\+.*\b[a-z][\w\.\+\-]*:\/\/\S+/i) { $msg_type = ""; # Otherwise set the alternate message types # a comment starts before $max_line_length } elsif ($line =~ /($;[\s$;]*)$/ && length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { $msg_type = "LONG_LINE_COMMENT" # a quoted string starts before $max_line_length } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { $msg_type = "LONG_LINE_STRING" } if ($msg_type ne "" && show_type("LONG_LINE") && show_type($msg_type)) { my $msg_level = \&WARN; $msg_level = \&CHK if ($file); &{$msg_level}($msg_type, "line length of $length exceeds $max_line_length columns\n" . $herecurr); } } # check for adding lines without a newline. if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { if (WARN("MISSING_EOF_NEWLINE", "adding a line without newline at end of file\n" . $herecurr) && $fix) { fix_delete_line($fixlinenr+1, "No newline at end of file"); } } # check for .L prefix local symbols in .S files if ($realfile =~ /\.S$/ && $line =~ /^\+\s*(?:[A-Z]+_)?SYM_[A-Z]+_(?:START|END)(?:_[A-Z_]+)?\s*\(\s*\.L/) { WARN("AVOID_L_PREFIX", "Avoid using '.L' prefixed local symbol names for denoting a range of code via 'SYM_*_START/END' annotations; see Documentation/core-api/asm-annotations.rst\n" . $herecurr); } # check we are in a valid source file C or perl if not then ignore this hunk next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); # at the beginning of a line any tabs must come first and anything # more than $tabsize must use tabs. if ($rawline =~ /^\+\s* \t\s*\S/ || $rawline =~ /^\+\s* \s*/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; $rpt_cleaners = 1; if (ERROR("CODE_INDENT", "code indent should use tabs where possible\n" . $herevet) && $fix) { $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; } } # check for space before tabs. if ($rawline =~ /^\+/ && $rawline =~ / \t/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (WARN("SPACE_BEFORE_TAB", "please, no space before tabs\n" . $herevet) && $fix) { while ($fixed[$fixlinenr] =~ s/(^\+.*) {$tabsize,$tabsize}\t/$1\t\t/) {} while ($fixed[$fixlinenr] =~ s/(^\+.*) +\t/$1\t/) {} } } # check for assignments on the start of a line if ($sline =~ /^\+\s+($Assignment)[^=]/) { my $operator = $1; if (CHK("ASSIGNMENT_CONTINUATIONS", "Assignment operator '$1' should be on the previous line\n" . $hereprev) && $fix && $prevrawline =~ /^\+/) { # add assignment operator to the previous line, remove from current line $fixed[$fixlinenr - 1] .= " $operator"; $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//; } } # check for && or || at the start of a line if ($rawline =~ /^\+\s*(&&|\|\|)/) { my $operator = $1; if (CHK("LOGICAL_CONTINUATIONS", "Logical continuations should be on the previous line\n" . $hereprev) && $fix && $prevrawline =~ /^\+/) { # insert logical operator at last non-comment, non-whitepsace char on previous line $prevline =~ /[\s$;]*$/; my $line_end = substr($prevrawline, $-[0]); $fixed[$fixlinenr - 1] =~ s/\Q$line_end\E$/ $operator$line_end/; $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//; } } # check indentation starts on a tab stop if ($perl_version_ok && $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) { my $indent = length($1); if ($indent % $tabsize) { if (WARN("TABSTOP", "Statements should start on a tabstop\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/$tabsize)@e; } } } # check multi-line statement indentation matches previous line if ($perl_version_ok && $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { $prevline =~ /^\+(\t*)(.*)$/; my $oldindent = $1; my $rest = $2; my $pos = pos_last_openparen($rest); if ($pos >= 0) { $line =~ /^(\+| )([ \t]*)/; my $newindent = $2; my $goodtabindent = $oldindent . "\t" x ($pos / $tabsize) . " " x ($pos % $tabsize); my $goodspaceindent = $oldindent . " " x $pos; if ($newindent ne $goodtabindent && $newindent ne $goodspaceindent) { if (CHK("PARENTHESIS_ALIGNMENT", "Alignment should match open parenthesis\n" . $hereprev) && $fix && $line =~ /^\+/) { $fixed[$fixlinenr] =~ s/^\+[ \t]*/\+$goodtabindent/; } } } } # check for space after cast like "(int) foo" or "(struct foo) bar" # avoid checking a few false positives: # "sizeof(<type>)" or "__alignof__(<type>)" # function pointer declarations like "(*foo)(int) = bar;" # structure definitions like "(struct foo) { 0 };" # multiline macros that define functions # known attributes or the __attribute__ keyword if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { if (CHK("SPACING", "No space is necessary after a cast\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(\(\s*$Type\s*\))[ \t]+/$1/; } } # Block comments use * on subsequent lines if ($prevline =~ /$;[ \t]*$/ && #ends in comment $prevrawline =~ /^\+.*?\/\*/ && #starting /* $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ $rawline =~ /^\+/ && #line is new $rawline !~ /^\+[ \t]*\*/) { #no leading * WARN("BLOCK_COMMENT_STYLE", "Block comments use * on subsequent lines\n" . $hereprev); } # Block comments use */ on trailing lines if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ WARN("BLOCK_COMMENT_STYLE", "Block comments use a trailing */ on a separate line\n" . $herecurr); } # Block comment * alignment if ($prevline =~ /$;[ \t]*$/ && #ends in comment $line =~ /^\+[ \t]*$;/ && #leading comment $rawline =~ /^\+[ \t]*\*/ && #leading * (($prevrawline =~ /^\+.*?\/\*/ && #leading /* $prevrawline !~ /\*\/[ \t]*$/) || #no trailing */ $prevrawline =~ /^\+[ \t]*\*/)) { #leading * my $oldindent; $prevrawline =~ m@^\+([ \t]*/?)\*@; if (defined($1)) { $oldindent = expand_tabs($1); } else { $prevrawline =~ m@^\+(.*/?)\*@; $oldindent = expand_tabs($1); } $rawline =~ m@^\+([ \t]*)\*@; my $newindent = $1; $newindent = expand_tabs($newindent); if (length($oldindent) ne length($newindent)) { WARN("BLOCK_COMMENT_STYLE", "Block comments should align the * on each line\n" . $hereprev); } } # check for missing blank lines after struct/union declarations # with exceptions for various attributes and macros if ($prevline =~ /^[\+ ]};?\s*$/ && $line =~ /^\+/ && !($line =~ /^\+\s*$/ || $line =~ /^\+\s*(?:EXPORT_SYMBOL|early_param|ALLOW_ERROR_INJECTION)/ || $line =~ /^\+\s*MODULE_/i || $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || $line =~ /^\+[a-z_]*init/ || $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || $line =~ /^\+\s*DECLARE/ || $line =~ /^\+\s*builtin_[\w_]*driver/ || $line =~ /^\+\s*__setup/)) { if (CHK("LINE_SPACING", "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && $fix) { fix_insert_line($fixlinenr, "\+"); } } # check for multiple consecutive blank lines if ($prevline =~ /^[\+ ]\s*$/ && $line =~ /^\+\s*$/ && $last_blank_line != ($linenr - 1)) { if (CHK("LINE_SPACING", "Please don't use multiple blank lines\n" . $hereprev) && $fix) { fix_delete_line($fixlinenr, $rawline); } $last_blank_line = $linenr; } # check for missing blank lines after declarations # (declarations must have the same indentation and not be at the start of line) if (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/) { # use temporaries my $sl = $sline; my $pl = $prevline; # remove $Attribute/$Sparse uses to simplify comparisons $sl =~ s/\b(?:$Attribute|$Sparse)\b//g; $pl =~ s/\b(?:$Attribute|$Sparse)\b//g; if (($pl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define $pl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros $pl =~ /^\+\s+$declaration_macros/) && # for "else if" which can look like "$Ident $Ident" !($pl =~ /^\+\s+$c90_Keywords\b/ || # other possible extensions of declaration lines $pl =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || # not starting a section or a macro "\" extended line $pl =~ /(?:\{\s*|\\)$/) && # looks like a declaration !($sl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define $sl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros $sl =~ /^\+\s+$declaration_macros/ || # start of struct or union or enum $sl =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ || # start or end of block or continuation of declaration $sl =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || # bitfield continuation $sl =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || # other possible extensions of declaration lines $sl =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/)) { if (WARN("LINE_SPACING", "Missing a blank line after declarations\n" . $hereprev) && $fix) { fix_insert_line($fixlinenr, "\+"); } } } # check for spaces at the beginning of a line. # Exceptions: # 1) within comments # 2) indented preprocessor commands # 3) hanging labels if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (WARN("LEADING_SPACE", "please, no spaces at the start of a line\n" . $herevet) && $fix) { $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; } } # check we are in a valid C source file if not then ignore this hunk next if ($realfile !~ /\.(h|c)$/); # check for unusual line ending [ or ( if ($line =~ /^\+.*([\[\(])\s*$/) { CHK("OPEN_ENDED_LINE", "Lines should not end with a '$1'\n" . $herecurr); } # check if this appears to be the start function declaration, save the name if ($sline =~ /^\+\{\s*$/ && $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) { $context_function = $1; } # check if this appears to be the end of function declaration if ($sline =~ /^\+\}\s*$/) { undef $context_function; } # check indentation of any line with a bare else # (but not if it is a multiple line "if (foo) return bar; else return baz;") # if the previous line is a break or return and is indented 1 tab more... if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { my $tabs = length($1) + 1; if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && defined $lines[$linenr] && $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { WARN("UNNECESSARY_ELSE", "else is not generally useful after a break or return\n" . $hereprev); } } # check indentation of a line with a break; # if the previous line is a goto, return or break # and is indented the same # of tabs if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { my $tabs = $1; if ($prevline =~ /^\+$tabs(goto|return|break)\b/) { if (WARN("UNNECESSARY_BREAK", "break is not useful after a $1\n" . $hereprev) && $fix) { fix_delete_line($fixlinenr, $rawline); } } } # check for RCS/CVS revision markers if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { WARN("CVS_KEYWORD", "CVS style keyword markers, these will _not_ be updated\n". $herecurr); } # check for old HOTPLUG __dev<foo> section markings if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { WARN("HOTPLUG_SECTION", "Using $1 is unnecessary\n" . $herecurr); } # Check for potential 'bare' types my ($stat, $cond, $line_nr_next, $remain_next, $off_next, $realline_next); #print "LINE<$line>\n"; if ($linenr > $suppress_statement && $realcnt && $sline =~ /.\s*\S/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0); $stat =~ s/\n./\n /g; $cond =~ s/\n./\n /g; #print "linenr<$linenr> <$stat>\n"; # If this statement has no statement boundaries within # it there is no point in retrying a statement scan # until we hit end of it. my $frag = $stat; $frag =~ s/;+\s*$//; if ($frag !~ /(?:{|;)/) { #print "skip<$line_nr_next>\n"; $suppress_statement = $line_nr_next; } # Find the real next line. $realline_next = $line_nr_next; if (defined $realline_next && (!defined $lines[$realline_next - 1] || substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { $realline_next++; } my $s = $stat; $s =~ s/{.*$//s; # Ignore goto labels. if ($s =~ /$Ident:\*$/s) { # Ignore functions being called } elsif ($s =~ /^.\s*$Ident\s*\(/s) { } elsif ($s =~ /^.\s*else\b/s) { # declarations always start with types } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { my $type = $1; $type =~ s/\s+/ /g; possible($type, "A:" . $s); # definitions in global scope can only start with types } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { possible($1, "B:" . $s); } # any (foo ... *) is a pointer cast, and foo is a type while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { possible($1, "C:" . $s); } # Check for any sort of function declaration. # int foo(something bar, other baz); # void (*store_gdt)(x86_descr_ptr *); if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { my ($name_len) = length($1); my $ctx = $s; substr($ctx, 0, $name_len + 1, ''); $ctx =~ s/\)[^\)]*$//; for my $arg (split(/\s*,\s*/, $ctx)) { if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { possible($1, "D:" . $s); } } } } # # Checks which may be anchored in the context. # # Check for switch () and associated case and default # statements should be at the same indent. if ($line=~/\bswitch\s*\(.*\)/) { my $err = ''; my $sep = ''; my @ctx = ctx_block_outer($linenr, $realcnt); shift(@ctx); for my $ctx (@ctx) { my ($clen, $cindent) = line_stats($ctx); if ($ctx =~ /^\+\s*(case\s+|default:)/ && $indent != $cindent) { $err .= "$sep$ctx\n"; $sep = ''; } else { $sep = "[...]\n"; } } if ($err ne '') { ERROR("SWITCH_CASE_INDENT_LEVEL", "switch and case should be at the same indent\n$hereline$err"); } } # if/while/etc brace do not go on next line, unless defining a do while loop, # or if that brace on the next line is for something else if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { my $pre_ctx = "$1$2"; my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); if ($line =~ /^\+\t{6,}/) { WARN("DEEP_INDENTATION", "Too many leading tabs - consider code refactoring\n" . $herecurr); } my $ctx_cnt = $realcnt - $#ctx - 1; my $ctx = join("\n", @ctx); my $ctx_ln = $linenr; my $ctx_skip = $realcnt; while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && defined $lines[$ctx_ln - 1] && $lines[$ctx_ln - 1] =~ /^-/)) { ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); $ctx_ln++; } #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { ERROR("OPEN_BRACE", "that open brace { should be on the previous line\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); } if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && $ctx =~ /\)\s*\;\s*$/ && defined $lines[$ctx_ln - 1]) { my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); if ($nindent > $indent) { WARN("TRAILING_SEMICOLON", "trailing semicolon indicates no statements, indent implies otherwise\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); } } } # Check relative indent for conditionals and blocks. if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0) if (!defined $stat); my ($s, $c) = ($stat, $cond); substr($s, 0, length($c), ''); # remove inline comments $s =~ s/$;/ /g; $c =~ s/$;/ /g; # Find out how long the conditional actually is. my @newlines = ($c =~ /\n/gs); my $cond_lines = 1 + $#newlines; # Make sure we remove the line prefixes as we have # none on the first line, and are going to readd them # where necessary. $s =~ s/\n./\n/gs; while ($s =~ /\n\s+\\\n/) { $cond_lines += $s =~ s/\n\s+\\\n/\n/g; } # We want to check the first line inside the block # starting at the end of the conditional, so remove: # 1) any blank line termination # 2) any opening brace { on end of the line # 3) any do (...) { my $continuation = 0; my $check = 0; $s =~ s/^.*\bdo\b//; $s =~ s/^\s*{//; if ($s =~ s/^\s*\\//) { $continuation = 1; } if ($s =~ s/^\s*?\n//) { $check = 1; $cond_lines++; } # Also ignore a loop construct at the end of a # preprocessor statement. if (($prevline =~ /^.\s*#\s*define\s/ || $prevline =~ /\\\s*$/) && $continuation == 0) { $check = 0; } my $cond_ptr = -1; $continuation = 0; while ($cond_ptr != $cond_lines) { $cond_ptr = $cond_lines; # If we see an #else/#elif then the code # is not linear. if ($s =~ /^\s*\#\s*(?:else|elif)/) { $check = 0; } # Ignore: # 1) blank lines, they should be at 0, # 2) preprocessor lines, and # 3) labels. if ($continuation || $s =~ /^\s*?\n/ || $s =~ /^\s*#\s*?/ || $s =~ /^\s*$Ident\s*:/) { $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; if ($s =~ s/^.*?\n//) { $cond_lines++; } } } my (undef, $sindent) = line_stats("+" . $s); my $stat_real = raw_line($linenr, $cond_lines); # Check if either of these lines are modified, else # this is not this patch's fault. if (!defined($stat_real) || $stat !~ /^\+/ && $stat_real !~ /^\+/) { $check = 0; } if (defined($stat_real) && $cond_lines > 1) { $stat_real = "[...]\n$stat_real"; } #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; if ($check && $s ne '' && (($sindent % $tabsize) != 0 || ($sindent < $indent) || ($sindent == $indent && ($s !~ /^\s*(?:\}|\{|else\b)/)) || ($sindent > $indent + $tabsize))) { WARN("SUSPECT_CODE_INDENT", "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); } } # Track the 'values' across context and added lines. my $opline = $line; $opline =~ s/^./ /; my ($curr_values, $curr_vars) = annotate_values($opline . "\n", $prev_values); $curr_values = $prev_values . $curr_values; if ($dbg_values) { my $outline = $opline; $outline =~ s/\t/ /g; print "$linenr > .$outline\n"; print "$linenr > $curr_values\n"; print "$linenr > $curr_vars\n"; } $prev_values = substr($curr_values, -1); #ignore lines not being added next if ($line =~ /^[^\+]/); # check for self assignments used to avoid compiler warnings # e.g.: int foo = foo, *bar = NULL; # struct foo bar = *(&(bar)); if ($line =~ /^\+\s*(?:$Declare)?([A-Za-z_][A-Za-z\d_]*)\s*=/) { my $var = $1; if ($line =~ /^\+\s*(?:$Declare)?$var\s*=\s*(?:$var|\*\s*\(?\s*&\s*\(?\s*$var\s*\)?\s*\)?)\s*[;,]/) { WARN("SELF_ASSIGNMENT", "Do not use self-assignments to avoid compiler warnings\n" . $herecurr); } } # check for dereferences that span multiple lines if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ && $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) { $prevline =~ /($Lval\s*(?:\.|->))\s*$/; my $ref = $1; $line =~ /^.\s*($Lval)/; $ref .= $1; $ref =~ s/\s//g; WARN("MULTILINE_DEREFERENCE", "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev); } # check for declarations of signed or unsigned without int while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { my $type = $1; my $var = $2; $var = "" if (!defined $var); if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { my $sign = $1; my $pointer = $2; $pointer = "" if (!defined $pointer); if (WARN("UNSPECIFIED_INT", "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && $fix) { my $decl = trim($sign) . " int "; my $comp_pointer = $pointer; $comp_pointer =~ s/\s//g; $decl .= $comp_pointer; $decl = rtrim($decl) if ($var eq ""); $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; } } } # TEST: allow direct testing of the type matcher. if ($dbg_type) { if ($line =~ /^.\s*$Declare\s*$/) { ERROR("TEST_TYPE", "TEST: is type\n" . $herecurr); } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { ERROR("TEST_NOT_TYPE", "TEST: is not type ($1 is)\n". $herecurr); } next; } # TEST: allow direct testing of the attribute matcher. if ($dbg_attr) { if ($line =~ /^.\s*$Modifier\s*$/) { ERROR("TEST_ATTR", "TEST: is attr\n" . $herecurr); } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { ERROR("TEST_NOT_ATTR", "TEST: is not attr ($1 is)\n". $herecurr); } next; } # check for initialisation to aggregates open brace on the next line if ($line =~ /^.\s*{/ && $prevline =~ /(?:^|[^=])=\s*$/) { if (ERROR("OPEN_BRACE", "that open brace { should be on the previous line\n" . $hereprev) && $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = $prevrawline; $fixedline =~ s/\s*=\s*$/ = {/; fix_insert_line($fixlinenr, $fixedline); $fixedline = $line; $fixedline =~ s/^(.\s*)\{\s*/$1/; fix_insert_line($fixlinenr, $fixedline); } } # # Checks which are anchored on the added line. # # check for malformed paths in #include statements (uses RAW line) if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { my $path = $1; if ($path =~ m{//}) { ERROR("MALFORMED_INCLUDE", "malformed #include filename\n" . $herecurr); } if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { ERROR("UAPI_INCLUDE", "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); } } # no C99 // comments if ($line =~ m{//}) { if (ERROR("C99_COMMENTS", "do not use C99 // comments\n" . $herecurr) && $fix) { my $line = $fixed[$fixlinenr]; if ($line =~ /\/\/(.*)$/) { my $comment = trim($1); $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; } } } # Remove C99 comments. $line =~ s@//.*@@; $opline =~ s@//.*@@; # EXPORT_SYMBOL should immediately follow the thing it is exporting, consider # the whole statement. #print "APW <$lines[$realline_next - 1]>\n"; if (defined $realline_next && exists $lines[$realline_next - 1] && !defined $suppress_export{$realline_next} && ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/)) { # Handle definitions which produce identifiers with # a prefix: # XXX(foo); # EXPORT_SYMBOL(something_foo); my $name = $1; $name =~ s/^\s*($Ident).*/$1/; if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && $name =~ /^${Ident}_$2/) { #print "FOO C name<$name>\n"; $suppress_export{$realline_next} = 1; } elsif ($stat !~ /(?: \n.}\s*$| ^.DEFINE_$Ident\(\Q$name\E\)| ^.DECLARE_$Ident\(\Q$name\E\)| ^.LIST_HEAD\(\Q$name\E\)| ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() )/x) { #print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; $suppress_export{$realline_next} = 2; } else { $suppress_export{$realline_next} = 1; } } if (!defined $suppress_export{$linenr} && $prevline =~ /^.\s*$/ && ($line =~ /EXPORT_SYMBOL.*\((.*)\)/)) { #print "FOO B <$lines[$linenr - 1]>\n"; $suppress_export{$linenr} = 2; } if (defined $suppress_export{$linenr} && $suppress_export{$linenr} == 2) { WARN("EXPORT_SYMBOL", "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); } # check for global initialisers. if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/ && !exclude_global_initialisers($realfile)) { if (ERROR("GLOBAL_INITIALISERS", "do not initialise globals to $1\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; } } # check for static initialisers. if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { if (ERROR("INITIALISED_STATIC", "do not initialise statics to $1\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; } } # check for misordered declarations of char/short/int/long with signed/unsigned while ($sline =~ m{(\b$TypeMisordered\b)}g) { my $tmp = trim($1); WARN("MISORDERED_TYPE", "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); } # check for unnecessary <signed> int declarations of short/long/long long while ($sline =~ m{\b($TypeMisordered(\s*\*)*|$C90_int_types)\b}g) { my $type = trim($1); next if ($type !~ /\bint\b/); next if ($type !~ /\b(?:short|long\s+long|long)\b/); my $new_type = $type; $new_type =~ s/\b\s*int\s*\b/ /; $new_type =~ s/\b\s*(?:un)?signed\b\s*/ /; $new_type =~ s/^const\s+//; $new_type = "unsigned $new_type" if ($type =~ /\bunsigned\b/); $new_type = "const $new_type" if ($type =~ /^const\b/); $new_type =~ s/\s+/ /g; $new_type = trim($new_type); if (WARN("UNNECESSARY_INT", "Prefer '$new_type' over '$type' as the int is unnecessary\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b\Q$type\E\b/$new_type/; } } # check for static const char * arrays. if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { WARN("STATIC_CONST_CHAR_ARRAY", "static const char * array should probably be static const char * const\n" . $herecurr); } # check for initialized const char arrays that should be static const if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) { if (WARN("STATIC_CONST_CHAR_ARRAY", "const array should probably be static const\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/; } } # check for static char foo[] = "bar" declarations. if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { WARN("STATIC_CONST_CHAR_ARRAY", "static char array declaration should probably be static const char\n" . $herecurr); } # check for const <foo> const where <foo> is not a pointer or array type if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { my $found = $1; if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { WARN("CONST_CONST", "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { WARN("CONST_CONST", "'const $found const' should probably be 'const $found'\n" . $herecurr); } } # check for const static or static <non ptr type> const declarations # prefer 'static const <foo>' over 'const static <foo>' and 'static <foo> const' if ($sline =~ /^\+\s*const\s+static\s+($Type)\b/ || $sline =~ /^\+\s*static\s+($BasicType)\s+const\b/) { if (WARN("STATIC_CONST", "Move const after static - use 'static const $1'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bconst\s+static\b/static const/; $fixed[$fixlinenr] =~ s/\bstatic\s+($BasicType)\s+const\b/static const $1/; } } # check for non-global char *foo[] = {"bar", ...} declarations. if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { WARN("STATIC_CONST_CHAR_ARRAY", "char * array declaration might be better as static const\n" . $herecurr); } # check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { my $array = $1; if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { my $array_div = $1; if (WARN("ARRAY_SIZE", "Prefer ARRAY_SIZE($array)\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; } } } # check for function declarations without arguments like "int foo()" if ($line =~ /(\b$Type\s*$Ident)\s*\(\s*\)/) { if (ERROR("FUNCTION_WITHOUT_ARGS", "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; } } # check for new typedefs, only function parameters and sparse annotations # make sense. if ($line =~ /\btypedef\s/ && $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && $line !~ /\b$typeTypedefs\b/ && $line !~ /\b__bitwise\b/) { WARN("NEW_TYPEDEFS", "do not add new typedefs\n" . $herecurr); } # * goes on variable not on type # (char*[ const]) while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { #print "AA<$1>\n"; my ($ident, $from, $to) = ($1, $2, $2); # Should start with a space. $to =~ s/^(\S)/ $1/; # Should not end with a space. $to =~ s/\s+$//; # '*'s should not have spaces between. while ($to =~ s/\*\s+\*/\*\*/) { } ## print "1: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to) { if (ERROR("POINTER_LOCATION", "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && $fix) { my $sub_from = $ident; my $sub_to = $ident; $sub_to =~ s/\Q$from\E/$to/; $fixed[$fixlinenr] =~ s@\Q$sub_from\E@$sub_to@; } } } while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { #print "BB<$1>\n"; my ($match, $from, $to, $ident) = ($1, $2, $2, $3); # Should start with a space. $to =~ s/^(\S)/ $1/; # Should not end with a space. $to =~ s/\s+$//; # '*'s should not have spaces between. while ($to =~ s/\*\s+\*/\*\*/) { } # Modifiers should have spaces. $to =~ s/(\b$Modifier$)/$1 /; ## print "2: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to && $ident !~ /^$Modifier$/) { if (ERROR("POINTER_LOCATION", "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && $fix) { my $sub_from = $match; my $sub_to = $match; $sub_to =~ s/\Q$from\E/$to/; $fixed[$fixlinenr] =~ s@\Q$sub_from\E@$sub_to@; } } } # do not use BUG() or variants if ($line =~ /\b(?!AA_|BUILD_|DCCP_|IDA_|KVM_|RWLOCK_|snd_|SPIN_)(?:[a-zA-Z_]*_)?BUG(?:_ON)?(?:_[A-Z_]+)?\s*\(/) { my $msg_level = \&WARN; $msg_level = \&CHK if ($file); &{$msg_level}("AVOID_BUG", "Do not crash the kernel unless it is absolutely unavoidable--use WARN_ON_ONCE() plus recovery code (if feasible) instead of BUG() or variants\n" . $herecurr); } # avoid LINUX_VERSION_CODE if ($line =~ /\bLINUX_VERSION_CODE\b/) { WARN("LINUX_VERSION_CODE", "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); } # check for uses of printk_ratelimit if ($line =~ /\bprintk_ratelimit\s*\(/) { WARN("PRINTK_RATELIMITED", "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); } # printk should use KERN_* levels if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) { WARN("PRINTK_WITHOUT_KERN_LEVEL", "printk() should include KERN_<LEVEL> facility level\n" . $herecurr); } # prefer variants of (subsystem|netdev|dev|pr)_<level> to printk(KERN_<LEVEL> if ($line =~ /\b(printk(_once|_ratelimited)?)\s*\(\s*KERN_([A-Z]+)/) { my $printk = $1; my $modifier = $2; my $orig = $3; $modifier = "" if (!defined($modifier)); my $level = lc($orig); $level = "warn" if ($level eq "warning"); my $level2 = $level; $level2 = "dbg" if ($level eq "debug"); $level .= $modifier; $level2 .= $modifier; WARN("PREFER_PR_LEVEL", "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to $printk(KERN_$orig ...\n" . $herecurr); } # prefer dev_<level> to dev_printk(KERN_<LEVEL> if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { my $orig = $1; my $level = lc($orig); $level = "warn" if ($level eq "warning"); $level = "dbg" if ($level eq "debug"); WARN("PREFER_DEV_LEVEL", "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); } # trace_printk should not be used in production code. if ($line =~ /\b(trace_printk|trace_puts|ftrace_vprintk)\s*\(/) { WARN("TRACE_PRINTK", "Do not use $1() in production code (this can be ignored if built only with a debug config option)\n" . $herecurr); } # ENOSYS means "bad syscall nr" and nothing else. This will have a small # number of false positives, but assembly files are not checked, so at # least the arch entry code will not trigger this warning. if ($line =~ /\bENOSYS\b/) { WARN("ENOSYS", "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); } # ENOTSUPP is not a standard error code and should be avoided in new patches. # Folks usually mean EOPNOTSUPP (also called ENOTSUP), when they type ENOTSUPP. # Similarly to ENOSYS warning a small number of false positives is expected. if (!$file && $line =~ /\bENOTSUPP\b/) { if (WARN("ENOTSUPP", "ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bENOTSUPP\b/EOPNOTSUPP/; } } # function brace can't be on same line, except for #defines of do while, # or if closed on same line if ($perl_version_ok && $sline =~ /$Type\s*$Ident\s*$balanced_parens\s*\{/ && $sline !~ /\#\s*define\b.*do\s*\{/ && $sline !~ /}/) { if (ERROR("OPEN_BRACE", "open brace '{' following function definitions go on the next line\n" . $herecurr) && $fix) { fix_delete_line($fixlinenr, $rawline); my $fixed_line = $rawline; $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*)\{(.*)$/; my $line1 = $1; my $line2 = $2; fix_insert_line($fixlinenr, ltrim($line1)); fix_insert_line($fixlinenr, "\+{"); if ($line2 !~ /^\s*$/) { fix_insert_line($fixlinenr, "\+\t" . trim($line2)); } } } # open braces for enum, union and struct go on the same line. if ($line =~ /^.\s*{/ && $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { if (ERROR("OPEN_BRACE", "open brace '{' following $1 go on the same line\n" . $hereprev) && $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = rtrim($prevrawline) . " {"; fix_insert_line($fixlinenr, $fixedline); $fixedline = $rawline; $fixedline =~ s/^(.\s*)\{\s*/$1\t/; if ($fixedline !~ /^\+\s*$/) { fix_insert_line($fixlinenr, $fixedline); } } } # missing space after union, struct or enum definition if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { if (WARN("SPACING", "missing space after $1 definition\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; } } # Function pointer declarations # check spacing between type, funcptr, and args # canonical declaration is "type (*funcptr)(args...)" if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { my $declare = $1; my $pre_pointer_space = $2; my $post_pointer_space = $3; my $funcname = $4; my $post_funcname_space = $5; my $pre_args_space = $6; # the $Declare variable will capture all spaces after the type # so check it for a missing trailing missing space but pointer return types # don't need a space so don't warn for those. my $post_declare_space = ""; if ($declare =~ /(\s+)$/) { $post_declare_space = $1; $declare = rtrim($declare); } if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { WARN("SPACING", "missing space after return type\n" . $herecurr); $post_declare_space = " "; } # unnecessary space "type (*funcptr)(args...)" # This test is not currently implemented because these declarations are # equivalent to # int foo(int bar, ...) # and this is form shouldn't/doesn't generate a checkpatch warning. # # elsif ($declare =~ /\s{2,}$/) { # WARN("SPACING", # "Multiple spaces after return type\n" . $herecurr); # } # unnecessary space "type ( *funcptr)(args...)" if (defined $pre_pointer_space && $pre_pointer_space =~ /^\s/) { WARN("SPACING", "Unnecessary space after function pointer open parenthesis\n" . $herecurr); } # unnecessary space "type (* funcptr)(args...)" if (defined $post_pointer_space && $post_pointer_space =~ /^\s/) { WARN("SPACING", "Unnecessary space before function pointer name\n" . $herecurr); } # unnecessary space "type (*funcptr )(args...)" if (defined $post_funcname_space && $post_funcname_space =~ /^\s/) { WARN("SPACING", "Unnecessary space after function pointer name\n" . $herecurr); } # unnecessary space "type (*funcptr) (args...)" if (defined $pre_args_space && $pre_args_space =~ /^\s/) { WARN("SPACING", "Unnecessary space before function pointer arguments\n" . $herecurr); } if (show_type("SPACING") && $fix) { $fixed[$fixlinenr] =~ s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; } } # check for spacing round square brackets; allowed: # 1. with a type on the left -- int [] a; # 2. at the beginning of a line for slice initialisers -- [0...10] = 5, # 3. inside a curly brace -- = { [0...10] = 5 } while ($line =~ /(.*?\s)\[/g) { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && $prefix !~ /[{,:]\s+$/) { if (ERROR("BRACKET_SPACE", "space prohibited before open square bracket '['\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(\+.*?)\s+\[/$1\[/; } } } # check for spaces between functions and their parentheses. while ($line =~ /($Ident)\s+\(/g) { my $name = $1; my $ctx_before = substr($line, 0, $-[1]); my $ctx = "$ctx_before$name"; # Ignore those directives where spaces _are_ permitted. if ($name =~ /^(?: if|for|while|switch|return|case| volatile|__volatile__| __attribute__|format|__extension__| asm|__asm__|scoped_guard)$/x) { # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open # parenthesis it is simply not a parameter group. } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { # cpp #elif statement condition may start with a ( } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { # If this whole things ends with a type its most # likely a typedef for a function. } elsif ($ctx =~ /$Type$/) { } else { if (WARN("SPACING", "space prohibited between function name and open parenthesis '('\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b$name\s+\(/$name\(/; } } } # Check operator spacing. if (!($line=~/\#\s*include/)) { my $fixed_line = ""; my $line_fixed = 0; my $ops = qr{ <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| =>|->|<<|>>|<|>|=|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| \?:|\?|: }x; my @elements = split(/($ops|;)/, $opline); ## print("element count: <" . $#elements . ">\n"); ## foreach my $el (@elements) { ## print("el: <$el>\n"); ## } my @fix_elements = (); my $off = 0; foreach my $el (@elements) { push(@fix_elements, substr($rawline, $off, length($el))); $off += length($el); } $off = 0; my $blank = copy_spacing($opline); my $last_after = -1; for (my $n = 0; $n < $#elements; $n += 2) { my $good = $fix_elements[$n] . $fix_elements[$n + 1]; ## print("n: <$n> good: <$good>\n"); $off += length($elements[$n]); # Pick up the preceding and succeeding characters. my $ca = substr($opline, 0, $off); my $cc = ''; if (length($opline) >= ($off + length($elements[$n + 1]))) { $cc = substr($opline, $off + length($elements[$n + 1])); } my $cb = "$ca$;$cc"; my $a = ''; $a = 'V' if ($elements[$n] ne ''); $a = 'W' if ($elements[$n] =~ /\s$/); $a = 'C' if ($elements[$n] =~ /$;$/); $a = 'B' if ($elements[$n] =~ /(\[|\()$/); $a = 'O' if ($elements[$n] eq ''); $a = 'E' if ($ca =~ /^\s*$/); my $op = $elements[$n + 1]; my $c = ''; if (defined $elements[$n + 2]) { $c = 'V' if ($elements[$n + 2] ne ''); $c = 'W' if ($elements[$n + 2] =~ /^\s/); $c = 'C' if ($elements[$n + 2] =~ /^$;/); $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); $c = 'O' if ($elements[$n + 2] eq ''); $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); } else { $c = 'E'; } my $ctx = "${a}x${c}"; my $at = "(ctx:$ctx)"; my $ptr = substr($blank, 0, $off) . "^"; my $hereptr = "$hereline$ptr\n"; # Pull out the value of this operator. my $op_type = substr($curr_values, $off + 1, 1); # Get the full operator variant. my $opv = $op . substr($curr_vars, $off, 1); # Ignore operators passed as parameters. if ($op_type ne 'V' && $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { # # Ignore comments # } elsif ($op =~ /^$;+$/) { # ; should have either the end of line or a space or \ after it } elsif ($op eq ';') { if ($ctx !~ /.x[WEBC]/ && $cc !~ /^\\/ && $cc !~ /^;/) { if (ERROR("SPACING", "space required after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; $line_fixed = 1; } } # // is a comment } elsif ($op eq '//') { # : when part of a bitfield } elsif ($opv eq ':B') { # skip the bitfield test for now # No spaces for: # -> } elsif ($op eq '->') { if ($ctx =~ /Wx.|.xW/) { if (ERROR("SPACING", "spaces prohibited around that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # , must not have a space before and must have a space on the right. } elsif ($op eq ',') { my $rtrim_before = 0; my $space_after = 0; if ($ctx =~ /Wx./) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { $line_fixed = 1; $rtrim_before = 1; } } if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { if (ERROR("SPACING", "space required after that '$op' $at\n" . $hereptr)) { $line_fixed = 1; $last_after = $n; $space_after = 1; } } if ($rtrim_before || $space_after) { if ($rtrim_before) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); } else { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); } if ($space_after) { $good .= " "; } } # '*' as part of a type definition -- reported already. } elsif ($opv eq '*_') { #warn "'*' is part of type\n"; # unary operators should have a space before and # none after. May be left adjacent to another # unary operator, or a cast } elsif ($op eq '!' || $op eq '~' || $opv eq '*U' || $opv eq '-U' || $opv eq '&U' || $opv eq '&&U') { if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { if (ERROR("SPACING", "space required before that '$op' $at\n" . $hereptr)) { if ($n != $last_after + 2) { $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); $line_fixed = 1; } } } if ($op eq '*' && $cc =~/\s*$Modifier\b/) { # A unary '*' may be const } elsif ($ctx =~ /.xW/) { if (ERROR("SPACING", "space prohibited after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # unary ++ and unary -- are allowed no space on one side. } elsif ($op eq '++' or $op eq '--') { if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { if (ERROR("SPACING", "space required one side of that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; $line_fixed = 1; } } if ($ctx =~ /Wx[BE]/ || ($ctx =~ /Wx./ && $cc =~ /^;/)) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); $line_fixed = 1; } } if ($ctx =~ /ExW/) { if (ERROR("SPACING", "space prohibited after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # << and >> may either have or not have spaces both sides } elsif ($op eq '<<' or $op eq '>>' or $op eq '&' or $op eq '^' or $op eq '|' or $op eq '+' or $op eq '-' or $op eq '*' or $op eq '/' or $op eq '%') { if ($check) { if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { if (CHK("SPACING", "spaces preferred around that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; $fix_elements[$n + 2] =~ s/^\s+//; $line_fixed = 1; } } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { if (CHK("SPACING", "space preferred before that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); $line_fixed = 1; } } } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { if (ERROR("SPACING", "need consistent spacing around '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # A colon needs no spaces before when it is # terminating a case value or a label. } elsif ($opv eq ':C' || $opv eq ':L') { if ($ctx =~ /Wx./ and $realfile !~ m@.*\.lds\.h$@) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); $line_fixed = 1; } } # All the others need spaces both sides. } elsif ($ctx !~ /[EWC]x[CWE]/) { my $ok = 0; # Ignore email addresses <foo@bar> if (($op eq '<' && $cc =~ /^\S+\@\S+>/) || ($op eq '>' && $ca =~ /<\S+\@\S+$/)) { $ok = 1; } # for asm volatile statements # ignore a colon with another # colon immediately before or after if (($op eq ':') && ($ca =~ /:$/ || $cc =~ /^:/)) { $ok = 1; } # messages are ERROR, but ?: are CHK if ($ok == 0) { my $msg_level = \&ERROR; $msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); if (&{$msg_level}("SPACING", "spaces required around that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } } $off += length($elements[$n + 1]); ## print("n: <$n> GOOD: <$good>\n"); $fixed_line = $fixed_line . $good; } if (($#elements % 2) == 0) { $fixed_line = $fixed_line . $fix_elements[$#elements]; } if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { $fixed[$fixlinenr] = $fixed_line; } } # check for whitespace before a non-naked semicolon if ($line =~ /^\+.*\S\s+;\s*$/) { if (WARN("SPACING", "space prohibited before semicolon\n" . $herecurr) && $fix) { 1 while $fixed[$fixlinenr] =~ s/^(\+.*\S)\s+;/$1;/; } } # check for multiple assignments if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { CHK("MULTIPLE_ASSIGNMENTS", "multiple assignments should be avoided\n" . $herecurr); } ## # check for multiple declarations, allowing for a function declaration ## # continuation. ## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && ## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { ## ## # Remove any bracketed sections to ensure we do not ## # falsely report the parameters of functions. ## my $ln = $line; ## while ($ln =~ s/\([^\(\)]*\)//g) { ## } ## if ($ln =~ /,/) { ## WARN("MULTIPLE_DECLARATION", ## "declaring multiple variables together should be avoided\n" . $herecurr); ## } ## } #need space before brace following if, while, etc if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || $line =~ /\b(?:else|do)\{/) { if (ERROR("SPACING", "space required before the open brace '{'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(\+.*(?:do|else|\)))\{/$1 {/; } } ## # check for blank lines before declarations ## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && ## $prevrawline =~ /^.\s*$/) { ## WARN("SPACING", ## "No blank lines before declarations\n" . $hereprev); ## } ## # closing brace should have a space following it when it has anything # on the line if ($line =~ /}(?!(?:,|;|\)|\}))\S/) { if (ERROR("SPACING", "space required after that close brace '}'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/}((?!(?:,|;|\)))\S)/} $1/; } } # check spacing on square brackets if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { if (ERROR("SPACING", "space prohibited after that open square bracket '['\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\[\s+/\[/; } } if ($line =~ /\s\]/) { if (ERROR("SPACING", "space prohibited before that close square bracket ']'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s+\]/\]/; } } # check spacing on parentheses if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && $line !~ /for\s*\(\s+;/) { if (ERROR("SPACING", "space prohibited after that open parenthesis '('\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\(\s+/\(/; } } if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && $line !~ /for\s*\(.*;\s+\)/ && $line !~ /:\s+\)/) { if (ERROR("SPACING", "space prohibited before that close parenthesis ')'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s+\)/\)/; } } # check unnecessary parentheses around addressof/dereference single $Lvals # ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { my $var = $1; if (CHK("UNNECESSARY_PARENTHESES", "Unnecessary parentheses around $var\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; } } # check for unnecessary parentheses around function pointer uses # ie: (foo->bar)(); should be foo->bar(); # but not "if (foo->bar) (" to avoid some false positives if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { my $var = $2; if (CHK("UNNECESSARY_PARENTHESES", "Unnecessary parentheses around function pointer $var\n" . $herecurr) && $fix) { my $var2 = deparenthesize($var); $var2 =~ s/\s//g; $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; } } # check for unnecessary parentheses around comparisons in if uses # when !drivers/staging or command-line uses --strict if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) && $perl_version_ok && defined($stat) && $stat =~ /(^.\s*if\s*($balanced_parens))/) { my $if_stat = $1; my $test = substr($2, 1, -1); my $herectx; while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) { my $match = $1; # avoid parentheses around potential macro args next if ($match =~ /^\s*\w+\s*$/); if (!defined($herectx)) { $herectx = $here . "\n"; my $cnt = statement_rawlines($if_stat); for (my $n = 0; $n < $cnt; $n++) { my $rl = raw_line($linenr, $n); $herectx .= $rl . "\n"; last if $rl =~ /^[ \+].*\{/; } } CHK("UNNECESSARY_PARENTHESES", "Unnecessary parentheses around '$match'\n" . $herectx); } } # check that goto labels aren't indented (allow a single space indentation) # and ignore bitfield definitions like foo:1 # Strictly, labels can have whitespace after the identifier and before the : # but this is not allowed here as many ?: uses would appear to be labels if ($sline =~ /^.\s+[A-Za-z_][A-Za-z\d_]*:(?!\s*\d+)/ && $sline !~ /^. [A-Za-z\d_][A-Za-z\d_]*:/ && $sline !~ /^.\s+default:/) { if (WARN("INDENTED_LABEL", "labels should not be indented\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(.)\s+/$1/; } } # check if a statement with a comma should be two statements like: # foo = bar(), /* comma should be semicolon */ # bar = baz(); if (defined($stat) && $stat =~ /^\+\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*,\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*;\s*$/) { my $cnt = statement_rawlines($stat); my $herectx = get_stat_here($linenr, $cnt, $here); WARN("SUSPECT_COMMA_SEMICOLON", "Possible comma where semicolon could be used\n" . $herectx); } # return is not a function if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { my $spacing = $1; if ($perl_version_ok && $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { my $value = $1; $value = deparenthesize($value); if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { ERROR("RETURN_PARENTHESES", "return is not a function, parentheses are not required\n" . $herecurr); } } elsif ($spacing !~ /\s+/) { ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr); } } # unnecessary return in a void function # at end-of-function, with the previous line a single leading tab, then return; # and the line before that not a goto label target like "out:" if ($sline =~ /^[ \+]}\s*$/ && $prevline =~ /^\+\treturn\s*;\s*$/ && $linenr >= 3 && $lines[$linenr - 3] =~ /^[ +]/ && $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { WARN("RETURN_VOID", "void function return statements are not generally useful\n" . $hereprev); } # if statements using unnecessary parentheses - ie: if ((foo == bar)) if ($perl_version_ok && $line =~ /\bif\s*((?:\(\s*){2,})/) { my $openparens = $1; my $count = $openparens =~ tr@\(@\(@; my $msg = ""; if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { my $comp = $4; #Not $1 because of $LvalOrFunc $msg = " - maybe == should be = ?" if ($comp eq "=="); WARN("UNNECESSARY_PARENTHESES", "Unnecessary parentheses$msg\n" . $herecurr); } } # comparisons with a constant or upper case identifier on the left # avoid cases like "foo + BAR < baz" # only fix matches surrounded by parentheses to avoid incorrect # conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" if ($perl_version_ok && $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { my $lead = $1; my $const = $2; my $comp = $3; my $to = $4; my $newcomp = $comp; if ($lead !~ /(?:$Operators|\.)\s*$/ && $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && WARN("CONSTANT_COMPARISON", "Comparisons should place the constant on the right side of the test\n" . $herecurr) && $fix) { if ($comp eq "<") { $newcomp = ">"; } elsif ($comp eq "<=") { $newcomp = ">="; } elsif ($comp eq ">") { $newcomp = "<"; } elsif ($comp eq ">=") { $newcomp = "<="; } $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; } } # Return of what appears to be an errno should normally be negative if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { my $name = $1; if ($name ne 'EOF' && $name ne 'ERROR' && $name !~ /^EPOLL/) { WARN("USE_NEGATIVE_ERRNO", "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); } } # Need a space before open parenthesis after if, while etc if ($line =~ /\b(if|while|for|switch)\(/) { if (ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b(if|while|for|switch)\(/$1 \(/; } } # Check for illegal assignment in if conditional -- and check for trailing # statements after the conditional. if ($line =~ /do\s*(?!{)/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0) if (!defined $stat); my ($stat_next) = ctx_statement_block($line_nr_next, $remain_next, $off_next); $stat_next =~ s/\n./\n /g; ##print "stat<$stat> stat_next<$stat_next>\n"; if ($stat_next =~ /^\s*while\b/) { # If the statement carries leading newlines, # then count those as offsets. my ($whitespace) = ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); my $offset = statement_rawlines($whitespace) - 1; $suppress_whiletrailers{$line_nr_next + $offset} = 1; } } if (!defined $suppress_whiletrailers{$linenr} && defined($stat) && defined($cond) && $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { my ($s, $c) = ($stat, $cond); my $fixed_assign_in_if = 0; if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { if (ERROR("ASSIGN_IN_IF", "do not use assignment in if condition\n" . $herecurr) && $fix && $perl_version_ok) { if ($rawline =~ /^\+(\s+)if\s*\(\s*(\!)?\s*\(\s*(($Lval)\s*=\s*$LvalOrFunc)\s*\)\s*(?:($Compare)\s*($FuncArg))?\s*\)\s*(\{)?\s*$/) { my $space = $1; my $not = $2; my $statement = $3; my $assigned = $4; my $test = $8; my $against = $9; my $brace = $15; fix_delete_line($fixlinenr, $rawline); fix_insert_line($fixlinenr, "$space$statement;"); my $newline = "${space}if ("; $newline .= '!' if defined($not); $newline .= '(' if (defined $not && defined($test) && defined($against)); $newline .= "$assigned"; $newline .= " $test $against" if (defined($test) && defined($against)); $newline .= ')' if (defined $not && defined($test) && defined($against)); $newline .= ')'; $newline .= " {" if (defined($brace)); fix_insert_line($fixlinenr + 1, $newline); $fixed_assign_in_if = 1; } } } # Find out what is on the end of the line after the # conditional. substr($s, 0, length($c), ''); $s =~ s/\n.*//g; $s =~ s/$;//g; # Remove any comments if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && $c !~ /}\s*while\s*/) { # Find out how long the conditional actually is. my @newlines = ($c =~ /\n/gs); my $cond_lines = 1 + $#newlines; my $stat_real = ''; $stat_real = raw_line($linenr, $cond_lines) . "\n" if ($cond_lines); if (defined($stat_real) && $cond_lines > 1) { $stat_real = "[...]\n$stat_real"; } if (ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr . $stat_real) && !$fixed_assign_in_if && $cond_lines == 0 && $fix && $perl_version_ok && $fixed[$fixlinenr] =~ /^\+(\s*)((?:if|while|for)\s*$balanced_parens)\s*(.*)$/) { my $indent = $1; my $test = $2; my $rest = rtrim($4); if ($rest =~ /;$/) { $fixed[$fixlinenr] = "\+$indent$test"; fix_insert_line($fixlinenr + 1, "$indent\t$rest"); } } } } # Check for bitwise tests written as boolean if ($line =~ / (?: (?:\[|\(|\&\&|\|\|) \s*0[xX][0-9]+\s* (?:\&\&|\|\|) | (?:\&\&|\|\|) \s*0[xX][0-9]+\s* (?:\&\&|\|\||\)|\]) )/x) { WARN("HEXADECIMAL_BOOLEAN_TEST", "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); } # if and else should not have general statements after it if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { my $s = $1; $s =~ s/$;//g; # Remove any comments if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr); } } # if should not continue a brace if ($line =~ /}\s*if\b/) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line (or did you mean 'else if'?)\n" . $herecurr); } # case and default should not have general statements after them if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && $line !~ /\G(?: (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| \s*return\s+ )/xg) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr); } # Check for }<nl>else {, these must be at the same # indent level to be relevant to each other. if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && $previndent == $indent) { if (ERROR("ELSE_AFTER_BRACE", "else should follow close brace '}'\n" . $hereprev) && $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = $prevrawline; $fixedline =~ s/}\s*$//; if ($fixedline !~ /^\+\s*$/) { fix_insert_line($fixlinenr, $fixedline); } $fixedline = $rawline; $fixedline =~ s/^(.\s*)else/$1} else/; fix_insert_line($fixlinenr, $fixedline); } } if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && $previndent == $indent) { my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); # Find out what is on the end of the line after the # conditional. substr($s, 0, length($c), ''); $s =~ s/\n.*//g; if ($s =~ /^\s*;/) { if (ERROR("WHILE_AFTER_BRACE", "while should follow close brace '}'\n" . $hereprev) && $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = $prevrawline; my $trailing = $rawline; $trailing =~ s/^\+//; $trailing = trim($trailing); $fixedline =~ s/}\s*$/} $trailing/; fix_insert_line($fixlinenr, $fixedline); } } } #Specific variable tests while ($line =~ m{($Constant|$Lval)}g) { my $var = $1; #CamelCase if ($var !~ /^$Constant$/ && $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && #Ignore some autogenerated defines and enum values $var !~ /^(?:[A-Z]+_){1,5}[A-Z]{1,3}[a-z]/ && #Ignore Page<foo> variants $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && #Ignore ETHTOOL_LINK_MODE_<foo> variants $var !~ /^ETHTOOL_LINK_MODE_/ && #Ignore SI style variants like nS, mV and dB #(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE) $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ && #Ignore some three character SI units explicitly, like MiB and KHz $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { while ($var =~ m{\b($Ident)}g) { my $word = $1; next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); if ($check) { seed_camelcase_includes(); if (!$file && !$camelcase_file_seeded) { seed_camelcase_file($realfile); $camelcase_file_seeded = 1; } } if (!defined $camelcase{$word}) { $camelcase{$word} = 1; CHK("CAMELCASE", "Avoid CamelCase: <$word>\n" . $herecurr); } } } } #no spaces allowed after \ in define if ($line =~ /\#\s*define.*\\\s+$/) { if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", "Whitespace after \\ makes next lines useless\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s+$//; } } # warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes # itself <asm/foo.h> (uses RAW line) if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { my $file = "$1.h"; my $checkfile = "include/linux/$file"; if (-f "$root/$checkfile" && $realfile ne $checkfile && $1 !~ /$allowed_asm_includes/) { my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; if ($asminclude > 0) { if ($realfile =~ m{^arch/}) { CHK("ARCH_INCLUDE_LINUX", "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); } else { WARN("INCLUDE_LINUX", "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); } } } } # multi-statement macros should be enclosed in a do while loop, grab the # first statement and ensure its the whole macro if its not enclosed # in a known good container if ($realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { my $ln = $linenr; my $cnt = $realcnt; my ($off, $dstat, $dcond, $rest); my $ctx = ''; my $has_flow_statement = 0; my $has_arg_concat = 0; ($dstat, $dcond, $ln, $cnt, $off) = ctx_statement_block($linenr, $realcnt, 0); $ctx = $dstat; #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); $dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//; my $define_args = $1; my $define_stmt = $dstat; my @def_args = (); if (defined $define_args && $define_args ne "") { $define_args = substr($define_args, 1, length($define_args) - 2); $define_args =~ s/\s*//g; $define_args =~ s/\\\+?//g; @def_args = split(",", $define_args); } $dstat =~ s/$;//g; $dstat =~ s/\\\n.//g; $dstat =~ s/^\s*//s; $dstat =~ s/\s*$//s; # Flatten any parentheses and braces while ($dstat =~ s/\([^\(\)]*\)/1u/ || $dstat =~ s/\{[^\{\}]*\}/1u/ || $dstat =~ s/.\[[^\[\]]*\]/1u/) { } # Flatten any obvious string concatenation. while ($dstat =~ s/($String)\s*$Ident/$1/ || $dstat =~ s/$Ident\s*($String)/$1/) { } # Make asm volatile uses seem like a generic function $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; my $exceptions = qr{ $Declare| module_param_named| MODULE_PARM_DESC| DECLARE_PER_CPU| DEFINE_PER_CPU| __typeof__\(| union| struct| \.$Ident\s*=\s*| ^\"|\"$| ^\[ }x; #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; $ctx =~ s/\n*$//; my $stmt_cnt = statement_rawlines($ctx); my $herectx = get_stat_here($linenr, $stmt_cnt, $here); if ($dstat ne '' && $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants $dstat !~ /$exceptions/ && $dstat !~ /^\.$Ident\s*=/ && # .foo = $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo $dstat !~ /^case\b/ && # case ... $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) $dstat !~ /^while\s*$Constant\s*$Constant\s*$/ && # while (...) {...} $dstat !~ /^for\s*$Constant$/ && # for (...) $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() $dstat !~ /^do\s*{/ && # do {... $dstat !~ /^\(\{/ && # ({... $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) { if ($dstat =~ /^\s*if\b/) { ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx"); } elsif ($dstat =~ /;/) { ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); } else { ERROR("COMPLEX_MACRO", "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); } } # Make $define_stmt single line, comment-free, etc my @stmt_array = split('\n', $define_stmt); my $first = 1; $define_stmt = ""; foreach my $l (@stmt_array) { $l =~ s/\\$//; if ($first) { $define_stmt = $l; $first = 0; } elsif ($l =~ /^[\+ ]/) { $define_stmt .= substr($l, 1); } } $define_stmt =~ s/$;//g; $define_stmt =~ s/\s+/ /g; $define_stmt = trim($define_stmt); # check if any macro arguments are reused (ignore '...' and 'type') foreach my $arg (@def_args) { next if ($arg =~ /\.\.\./); next if ($arg =~ /^type$/i); my $tmp_stmt = $define_stmt; $tmp_stmt =~ s/\b(__must_be_array|offsetof|sizeof|sizeof_field|__stringify|typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; $tmp_stmt =~ s/\#+\s*$arg\b//g; $tmp_stmt =~ s/\b$arg\s*\#\#//g; my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g; if ($use_cnt > 1) { CHK("MACRO_ARG_REUSE", "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx"); } # check if any macro arguments may have other precedence issues if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m && ((defined($1) && $1 ne ',') || (defined($2) && $2 ne ','))) { CHK("MACRO_ARG_PRECEDENCE", "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx"); } # check if this is an unused argument if ($define_stmt !~ /\b$arg\b/) { WARN("MACRO_ARG_UNUSED", "Argument '$arg' is not used in function-like macro\n" . "$herectx"); } } # check for macros with flow control, but without ## concatenation # ## concatenation is commonly a macro that defines a function so ignore those if ($has_flow_statement && !$has_arg_concat) { my $cnt = statement_rawlines($ctx); my $herectx = get_stat_here($linenr, $cnt, $here); WARN("MACRO_WITH_FLOW_CONTROL", "Macros with flow control statements should be avoided\n" . "$herectx"); } # check for line continuations outside of #defines, preprocessor #, and asm } elsif ($realfile =~ m@/vmlinux.lds.h$@) { $line =~ s/(\w+)/$maybe_linker_symbol{$1}++/ge; #print "REAL: $realfile\nln: $line\nkeys:", sort keys %maybe_linker_symbol; } else { if ($prevline !~ /^..*\\$/ && $line !~ /^\+\s*\#.*\\$/ && # preprocessor $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm $line =~ /^\+.*\\$/) { WARN("LINE_CONTINUATIONS", "Avoid unnecessary line continuations\n" . $herecurr); } } # do {} while (0) macro tests: # single-statement macros do not need to be enclosed in do while (0) loop, # macro should not end with a semicolon if ($perl_version_ok && $realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { my $ln = $linenr; my $cnt = $realcnt; my ($off, $dstat, $dcond, $rest); my $ctx = ''; ($dstat, $dcond, $ln, $cnt, $off) = ctx_statement_block($linenr, $realcnt, 0); $ctx = $dstat; $dstat =~ s/\\\n.//g; $dstat =~ s/$;/ /g; if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { my $stmts = $2; my $semis = $3; $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); my $herectx = get_stat_here($linenr, $cnt, $here); if (($stmts =~ tr/;/;/) == 1 && $stmts !~ /^\s*(if|while|for|switch)\b/) { WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); } if (defined $semis && $semis ne "") { WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); } } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); my $herectx = get_stat_here($linenr, $cnt, $here); WARN("TRAILING_SEMICOLON", "macros should not use a trailing semicolon\n" . "$herectx"); } } # check for redundant bracing round if etc if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { my ($level, $endln, @chunks) = ctx_statement_full($linenr, $realcnt, 1); #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; if ($#chunks > 0 && $level == 0) { my @allowed = (); my $allow = 0; my $seen = 0; my $herectx = $here . "\n"; my $ln = $linenr - 1; for my $chunk (@chunks) { my ($cond, $block) = @{$chunk}; # If the condition carries leading newlines, then count those as offsets. my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); my $offset = statement_rawlines($whitespace) - 1; $allowed[$allow] = 0; #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; # We have looked at and allowed this specific line. $suppress_ifbraces{$ln + $offset} = 1; $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; $ln += statement_rawlines($block) - 1; substr($block, 0, length($cond), ''); $seen++ if ($block =~ /^\s*{/); #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; if (statement_lines($cond) > 1) { #print "APW: ALLOWED: cond<$cond>\n"; $allowed[$allow] = 1; } if ($block =~/\b(?:if|for|while)\b/) { #print "APW: ALLOWED: block<$block>\n"; $allowed[$allow] = 1; } if (statement_block_size($block) > 1) { #print "APW: ALLOWED: lines block<$block>\n"; $allowed[$allow] = 1; } $allow++; } if ($seen) { my $sum_allowed = 0; foreach (@allowed) { $sum_allowed += $_; } if ($sum_allowed == 0) { WARN("BRACES", "braces {} are not necessary for any arm of this statement\n" . $herectx); } elsif ($sum_allowed != $allow && $seen != $allow) { CHK("BRACES", "braces {} should be used on all arms of this statement\n" . $herectx); } } } } if (!defined $suppress_ifbraces{$linenr - 1} && $line =~ /\b(if|while|for|else)\b/) { my $allowed = 0; # Check the pre-context. if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { #print "APW: ALLOWED: pre<$1>\n"; $allowed = 1; } my ($level, $endln, @chunks) = ctx_statement_full($linenr, $realcnt, $-[0]); # Check the condition. my ($cond, $block) = @{$chunks[0]}; #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; if (defined $cond) { substr($block, 0, length($cond), ''); } if (statement_lines($cond) > 1) { #print "APW: ALLOWED: cond<$cond>\n"; $allowed = 1; } if ($block =~/\b(?:if|for|while)\b/) { #print "APW: ALLOWED: block<$block>\n"; $allowed = 1; } if (statement_block_size($block) > 1) { #print "APW: ALLOWED: lines block<$block>\n"; $allowed = 1; } # Check the post-context. if (defined $chunks[1]) { my ($cond, $block) = @{$chunks[1]}; if (defined $cond) { substr($block, 0, length($cond), ''); } if ($block =~ /^\s*\{/) { #print "APW: ALLOWED: chunk-1 block<$block>\n"; $allowed = 1; } } if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { my $cnt = statement_rawlines($block); my $herectx = get_stat_here($linenr, $cnt, $here); WARN("BRACES", "braces {} are not necessary for single statement blocks\n" . $herectx); } } # check for single line unbalanced braces if ($sline =~ /^.\s*\}\s*else\s*$/ || $sline =~ /^.\s*else\s*\{\s*$/) { CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr); } # check for unnecessary blank lines around braces if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { if (CHK("BRACES", "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && $fix && $prevrawline =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); } } if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { if (CHK("BRACES", "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && $fix) { fix_delete_line($fixlinenr, $rawline); } } # no volatiles please my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { WARN("VOLATILE", "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr); } # Check for user-visible strings broken across lines, which breaks the ability # to grep for the string. Make exceptions when the previous string ends in a # newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' # (common in inline assembly) or is a octal \123 or hexadecimal \xaf value if ($line =~ /^\+\s*$String/ && $prevline =~ /"\s*$/ && $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { if (WARN("SPLIT_STRING", "quoted string split across lines\n" . $hereprev) && $fix && $prevrawline =~ /^\+.*"\s*$/ && $last_coalesced_string_linenr != $linenr - 1) { my $extracted_string = get_quoted_string($line, $rawline); my $comma_close = ""; if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { $comma_close = $1; } fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = $prevrawline; $fixedline =~ s/"\s*$//; $fixedline .= substr($extracted_string, 1) . trim($comma_close); fix_insert_line($fixlinenr - 1, $fixedline); $fixedline = $rawline; $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; if ($fixedline !~ /\+\s*$/) { fix_insert_line($fixlinenr, $fixedline); } $last_coalesced_string_linenr = $linenr; } } # check for missing a space in a string concatenation if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { WARN('MISSING_SPACE', "break quoted strings at a space character\n" . $hereprev); } # check for an embedded function name in a string when the function is known # This does not work very well for -f --file checking as it depends on patch # context providing the function name or a single line form for in-file # function declarations if ($line =~ /^\+.*$String/ && defined($context_function) && get_quoted_string($line, $rawline) =~ /\b$context_function\b/ && length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) { WARN("EMBEDDED_FUNCTION_NAME", "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr); } # check for unnecessary function tracing like uses # This does not use $logFunctions because there are many instances like # 'dprintk(FOO, "%s()\n", __func__);' which do not match $logFunctions if ($rawline =~ /^\+.*\([^"]*"$tracing_logging_tags{0,3}%s(?:\s*\(\s*\)\s*)?$tracing_logging_tags{0,3}(?:\\n)?"\s*,\s*__func__\s*\)\s*;/) { if (WARN("TRACING_LOGGING", "Unnecessary ftrace-like logging - prefer using ftrace\n" . $herecurr) && $fix) { fix_delete_line($fixlinenr, $rawline); } } # check for spaces before a quoted newline if ($rawline =~ /^.*\".*\s\\n/) { if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", "unnecessary whitespace before a quoted newline\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; } } # concatenated string without spaces between elements if ($line =~ /$String[A-Z_]/ || ($line =~ /([A-Za-z0-9_]+)$String/ && $1 !~ /^[Lu]$/)) { if (CHK("CONCATENATED_STRING", "Concatenated strings should use spaces between elements\n" . $herecurr) && $fix) { while ($line =~ /($String)/g) { my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); $fixed[$fixlinenr] =~ s/\Q$extracted_string\E([A-Za-z0-9_])/$extracted_string $1/; $fixed[$fixlinenr] =~ s/([A-Za-z0-9_])\Q$extracted_string\E/$1 $extracted_string/; } } } # uncoalesced string fragments if ($line =~ /$String\s*[Lu]?"/) { if (WARN("STRING_FRAGMENTS", "Consecutive strings are generally better as a single string\n" . $herecurr) && $fix) { while ($line =~ /($String)(?=\s*")/g) { my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); $fixed[$fixlinenr] =~ s/\Q$extracted_string\E\s*"/substr($extracted_string, 0, -1)/e; } } } # check for non-standard and hex prefixed decimal printf formats my $show_L = 1; #don't show the same defect twice my $show_Z = 1; while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { my $string = substr($rawline, $-[1], $+[1] - $-[1]); $string =~ s/%%/__/g; # check for %L if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) { WARN("PRINTF_L", "\%L$1 is non-standard C, use %ll$1\n" . $herecurr); $show_L = 0; } # check for %Z if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) { WARN("PRINTF_Z", "%Z$1 is non-standard C, use %z$1\n" . $herecurr); $show_Z = 0; } # check for 0x<decimal> if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) { ERROR("PRINTF_0XDECIMAL", "Prefixing 0x with decimal output is defective\n" . $herecurr); } } # check for line continuations in quoted strings with odd counts of " if ($rawline =~ /\\$/ && $sline =~ tr/"/"/ % 2) { WARN("LINE_CONTINUATIONS", "Avoid line continuations in quoted strings\n" . $herecurr); } # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { WARN("IF_0", "Consider removing the code enclosed by this #if 0 and its #endif\n" . $herecurr); } # warn about #if 1 if ($line =~ /^.\s*\#\s*if\s+1\b/) { WARN("IF_1", "Consider removing the #if 1 and its #endif\n" . $herecurr); } # check for needless "if (<foo>) fn(<foo>)" uses if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { my $tested = quotemeta($1); my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { my $func = $1; if (WARN('NEEDLESS_IF', "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && $fix) { my $do_fix = 1; my $leading_tabs = ""; my $new_leading_tabs = ""; if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { $leading_tabs = $1; } else { $do_fix = 0; } if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { $new_leading_tabs = $1; if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { $do_fix = 0; } } else { $do_fix = 0; } if ($do_fix) { fix_delete_line($fixlinenr - 1, $prevrawline); $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; } } } } # check for unnecessary "Out of Memory" messages if ($line =~ /^\+.*\b$logFunctions\s*\(/ && $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && (defined $1 || defined $3) && $linenr > 3) { my $testval = $2; my $testline = $lines[$linenr - 3]; my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); # print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*$allocFunctions\s*\(/ && $s !~ /\b__GFP_NOWARN\b/ ) { WARN("OOM_MESSAGE", "Possible unnecessary 'out of memory' message\n" . $hereprev); } } # check for logging functions with KERN_<LEVEL> if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { my $level = $1; if (WARN("UNNECESSARY_KERN_LEVEL", "Possible unnecessary $level\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s*$level\s*//; } } # check for logging continuations if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) { WARN("LOGGING_CONTINUATION", "Avoid logging continuation uses where feasible\n" . $herecurr); } # check for unnecessary use of %h[xudi] and %hh[xudi] in logging functions if (defined $stat && $line =~ /\b$logFunctions\s*\(/ && index($stat, '"') >= 0) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; my $stat_real = get_stat_real($linenr, $lc); pos($stat_real) = index($stat_real, '"'); while ($stat_real =~ /[^\"%]*(%[\#\d\.\*\-]*(h+)[idux])/g) { my $pspec = $1; my $h = $2; my $lineoff = substr($stat_real, 0, $-[1]) =~ tr@\n@@; if (WARN("UNNECESSARY_MODIFIER", "Integer promotion: Using '$h' in '$pspec' is unnecessary\n" . "$here\n$stat_real\n") && $fix && $fixed[$fixlinenr + $lineoff] =~ /^\+/) { my $nspec = $pspec; $nspec =~ s/h//g; $fixed[$fixlinenr + $lineoff] =~ s/\Q$pspec\E/$nspec/; } } } # check for mask then right shift without a parentheses if ($perl_version_ok && $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so WARN("MASK_THEN_SHIFT", "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); } # check for pointer comparisons to NULL if ($perl_version_ok) { while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { my $val = $1; my $equal = "!"; $equal = "" if ($4 eq "!="); if (CHK("COMPARISON_TO_NULL", "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; } } } # check for bad placement of section $InitAttribute (e.g.: __initdata) if ($line =~ /(\b$InitAttribute\b)/) { my $attr = $1; if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { my $ptr = $1; my $var = $2; if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && ERROR("MISPLACED_INIT", "$attr should be placed after $var\n" . $herecurr)) || ($ptr !~ /\b(union|struct)\s+$attr\b/ && WARN("MISPLACED_INIT", "$attr should be placed after $var\n" . $herecurr))) && $fix) { $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; } } } # check for $InitAttributeData (ie: __initdata) with const if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { my $attr = $1; $attr =~ /($InitAttributePrefix)(.*)/; my $attr_prefix = $1; my $attr_type = $2; if (ERROR("INIT_ATTRIBUTE", "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/$InitAttributeData/${attr_prefix}initconst/; } } # check for $InitAttributeConst (ie: __initconst) without const if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { my $attr = $1; if (ERROR("INIT_ATTRIBUTE", "Use of $attr requires a separate use of const\n" . $herecurr) && $fix) { my $lead = $fixed[$fixlinenr] =~ /(^\+\s*(?:static\s+))/; $lead = rtrim($1); $lead = "$lead " if ($lead !~ /^\+$/); $lead = "${lead}const "; $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; } } # check for __read_mostly with const non-pointer (should just be const) if ($line =~ /\b__read_mostly\b/ && $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { if (ERROR("CONST_READ_MOSTLY", "Invalid use of __read_mostly with const type\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; } } # don't use __constant_<foo> functions outside of include/uapi/ if ($realfile !~ m@^include/uapi/@ && $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { my $constant_func = $1; my $func = $constant_func; $func =~ s/^__constant_//; if (WARN("CONSTANT_CONVERSION", "$constant_func should be $func\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; } } # prefer usleep_range over udelay if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { my $delay = $1; # ignore udelay's < 10, however if (! ($delay < 10) ) { CHK("USLEEP_RANGE", "usleep_range is preferred over udelay; see function description of usleep_range() and udelay().\n" . $herecurr); } if ($delay > 2000) { WARN("LONG_UDELAY", "long udelay - prefer mdelay; see function description of mdelay().\n" . $herecurr); } } # warn about unexpectedly long msleep's if ($line =~ /\bmsleep\s*\((\d+)\);/) { if ($1 < 20) { WARN("MSLEEP", "msleep < 20ms can sleep for up to 20ms; see function description of msleep().\n" . $herecurr); } } # check for comparisons of jiffies if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { WARN("JIFFIES_COMPARISON", "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); } # check for comparisons of get_jiffies_64() if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { WARN("JIFFIES_COMPARISON", "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); } # warn about #ifdefs in C files # if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { # print "#ifdef in C files should be avoided\n"; # print "$herecurr"; # $clean = 0; # } # warn about spacing in #ifdefs if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { if (ERROR("SPACING", "exactly one space required after that #$1\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; } } # check for spinlock_t definitions without a comment. if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { my $which = $1; if (!ctx_has_comment($first_line, $linenr)) { CHK("UNCOMMENTED_DEFINITION", "$1 definition without comment\n" . $herecurr); } } # check for memory barriers without a comment. my $barriers = qr{ mb| rmb| wmb }x; my $barrier_stems = qr{ mb__before_atomic| mb__after_atomic| store_release| load_acquire| store_mb| (?:$barriers) }x; my $all_barriers = qr{ (?:$barriers)| smp_(?:$barrier_stems)| virt_(?:$barrier_stems) }x; if ($line =~ /\b(?:$all_barriers)\s*\(/) { if (!ctx_has_comment($first_line, $linenr)) { WARN("MEMORY_BARRIER", "memory barrier without comment\n" . $herecurr); } } my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; if ($realfile !~ m@^include/asm-generic/@ && $realfile !~ m@/barrier\.h$@ && $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { WARN("MEMORY_BARRIER", "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); } # check for waitqueue_active without a comment. if ($line =~ /\bwaitqueue_active\s*\(/) { if (!ctx_has_comment($first_line, $linenr)) { WARN("WAITQUEUE_ACTIVE", "waitqueue_active without comment\n" . $herecurr); } } # check for data_race without a comment. if ($line =~ /\bdata_race\s*\(/) { if (!ctx_has_comment($first_line, $linenr)) { WARN("DATA_RACE", "data_race without comment\n" . $herecurr); } } # check of hardware specific defines if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { CHK("ARCH_DEFINES", "architecture specific defines should be avoided\n" . $herecurr); } # check that the storage class is not after a type if ($line =~ /\b($Type)\s+($Storage)\b/) { WARN("STORAGE_CLASS", "storage class '$2' should be located before type '$1'\n" . $herecurr); } # Check that the storage class is at the beginning of a declaration if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage/ && $line =~ /^.\s*(.+?)\$Storage\s/ && $1 !~ /[\,\)]\s*$/) { WARN("STORAGE_CLASS", "storage class should be at the beginning of the declaration\n" . $herecurr); } # check the location of the inline attribute, that it is between # storage class and type. if ($line =~ /\b$Type\s+$Inline\b/ || $line =~ /\b$Inline\s+$Storage\b/) { ERROR("INLINE_LOCATION", "inline keyword should sit between storage class and type\n" . $herecurr); } # Check for __inline__ and __inline, prefer inline if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b(__inline__|__inline)\b/) { if (WARN("INLINE", "plain inline is preferred over $1\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; } } # Check for compiler attributes if ($realfile !~ m@\binclude/uapi/@ && $rawline =~ /\b__attribute__\s*\(\s*($balanced_parens)\s*\)/) { my $attr = $1; $attr =~ s/\s*\(\s*(.*)\)\s*/$1/; my %attr_list = ( "alias" => "__alias", "aligned" => "__aligned", "always_inline" => "__always_inline", "assume_aligned" => "__assume_aligned", "cold" => "__cold", "const" => "__attribute_const__", "copy" => "__copy", "designated_init" => "__designated_init", "externally_visible" => "__visible", "format" => "printf|scanf", "gnu_inline" => "__gnu_inline", "malloc" => "__malloc", "mode" => "__mode", "no_caller_saved_registers" => "__no_caller_saved_registers", "noclone" => "__noclone", "noinline" => "noinline", "nonstring" => "__nonstring", "noreturn" => "__noreturn", "packed" => "__packed", "pure" => "__pure", "section" => "__section", "used" => "__used", "weak" => "__weak" ); while ($attr =~ /\s*(\w+)\s*(${balanced_parens})?/g) { my $orig_attr = $1; my $params = ''; $params = $2 if defined($2); my $curr_attr = $orig_attr; $curr_attr =~ s/^[\s_]+|[\s_]+$//g; if (exists($attr_list{$curr_attr})) { my $new = $attr_list{$curr_attr}; if ($curr_attr eq "format" && $params) { $params =~ /^\s*\(\s*(\w+)\s*,\s*(.*)/; $new = "__$1\($2"; } else { $new = "$new$params"; } if (WARN("PREFER_DEFINED_ATTRIBUTE_MACRO", "Prefer $new over __attribute__(($orig_attr$params))\n" . $herecurr) && $fix) { my $remove = "\Q$orig_attr\E" . '\s*' . "\Q$params\E" . '(?:\s*,\s*)?'; $fixed[$fixlinenr] =~ s/$remove//; $fixed[$fixlinenr] =~ s/\b__attribute__/$new __attribute__/; $fixed[$fixlinenr] =~ s/\}\Q$new\E/} $new/; $fixed[$fixlinenr] =~ s/ __attribute__\s*\(\s*\(\s*\)\s*\)//; } } } # Check for __attribute__ unused, prefer __always_unused or __maybe_unused if ($attr =~ /^_*unused/) { WARN("PREFER_DEFINED_ATTRIBUTE_MACRO", "__always_unused or __maybe_unused is preferred over __attribute__((__unused__))\n" . $herecurr); } } # Check for __attribute__ weak, or __weak declarations (may have link issues) if ($perl_version_ok && $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || $line =~ /\b__weak\b/)) { ERROR("WEAK_DECLARATION", "Using weak declarations can have unintended link defects\n" . $herecurr); } # check for c99 types like uint8_t used outside of uapi/ and tools/ if ($realfile !~ m@\binclude/uapi/@ && $realfile !~ m@\btools/@ && $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { my $type = $1; if ($type =~ /\b($typeC99Typedefs)\b/) { $type = $1; my $kernel_type = 'u'; $kernel_type = 's' if ($type =~ /^_*[si]/); $type =~ /(\d+)/; $kernel_type .= $1; if (CHK("PREFER_KERNEL_TYPES", "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/; } } } # check for cast of C90 native int or longer types constants if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { my $cast = $1; my $const = $2; my $suffix = ""; my $newconst = $const; $newconst =~ s/${Int_type}$//; $suffix .= 'U' if ($cast =~ /\bunsigned\b/); if ($cast =~ /\blong\s+long\b/) { $suffix .= 'LL'; } elsif ($cast =~ /\blong\b/) { $suffix .= 'L'; } if (WARN("TYPECAST_INT_CONSTANT", "Unnecessary typecast of c90 int constant - '$cast$const' could be '$const$suffix'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; } } # check for sizeof(&) if ($line =~ /\bsizeof\s*\(\s*\&/) { WARN("SIZEOF_ADDRESS", "sizeof(& should be avoided\n" . $herecurr); } # check for sizeof without parenthesis if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { if (WARN("SIZEOF_PARENTHESIS", "sizeof $1 should be sizeof($1)\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; } } # check for struct spinlock declarations if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { WARN("USE_SPINLOCK_T", "struct spinlock should be spinlock_t\n" . $herecurr); } # check for seq_printf uses that could be seq_puts if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { my $fmt = get_quoted_string($line, $rawline); $fmt =~ s/%%//g; if ($fmt !~ /%/) { if (WARN("PREFER_SEQ_PUTS", "Prefer seq_puts to seq_printf\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; } } } # check for vsprintf extension %p<foo> misuses if ($perl_version_ok && defined $stat && $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s && $1 !~ /^_*volatile_*$/) { my $stat_real; my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; for (my $count = $linenr; $count <= $lc; $count++) { my $specifier; my $extension; my $qualifier; my $bad_specifier = ""; my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); $fmt =~ s/%%//g; while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) { $specifier = $1; $extension = $2; $qualifier = $3; if ($extension !~ /[4SsBKRraEehMmIiUDdgVCbGNOxtf]/ || ($extension eq "f" && defined $qualifier && $qualifier !~ /^w/) || ($extension eq "4" && defined $qualifier && $qualifier !~ /^cc/)) { $bad_specifier = $specifier; last; } if ($extension eq "x" && !defined($stat_real)) { if (!defined($stat_real)) { $stat_real = get_stat_real($linenr, $lc); } WARN("VSPRINTF_SPECIFIER_PX", "Using vsprintf specifier '\%px' potentially exposes the kernel memory layout, if you don't really need the address please consider using '\%p'.\n" . "$here\n$stat_real\n"); } } if ($bad_specifier ne "") { my $stat_real = get_stat_real($linenr, $lc); my $msg_level = \&WARN; my $ext_type = "Invalid"; my $use = ""; if ($bad_specifier =~ /p[Ff]/) { $use = " - use %pS instead"; $use =~ s/pS/ps/ if ($bad_specifier =~ /pf/); } elsif ($bad_specifier =~ /pA/) { $use = " - '%pA' is only intended to be used from Rust code"; $msg_level = \&ERROR; } &{$msg_level}("VSPRINTF_POINTER_EXTENSION", "$ext_type vsprintf pointer extension '$bad_specifier'$use\n" . "$here\n$stat_real\n"); } } } # Check for misused memsets if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { my $ms_addr = $2; my $ms_val = $7; my $ms_size = $12; if ($ms_size =~ /^(0x|)0$/i) { ERROR("MEMSET", "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); } elsif ($ms_size =~ /^(0x|)1$/i) { WARN("MEMSET", "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); } } # Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) # if ($perl_version_ok && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # if (WARN("PREFER_ETHER_ADDR_COPY", # "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && # $fix) { # $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; # } # } # Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) # if ($perl_version_ok && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # WARN("PREFER_ETHER_ADDR_EQUAL", # "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") # } # check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr # check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr # if ($perl_version_ok && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # # my $ms_val = $7; # # if ($ms_val =~ /^(?:0x|)0+$/i) { # if (WARN("PREFER_ETH_ZERO_ADDR", # "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && # $fix) { # $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; # } # } elsif ($ms_val =~ /^(?:0xff|255)$/i) { # if (WARN("PREFER_ETH_BROADCAST_ADDR", # "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && # $fix) { # $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; # } # } # } # strcpy uses that should likely be strscpy if ($line =~ /\bstrcpy\s*\(/) { WARN("STRCPY", "Prefer strscpy over strcpy - see: https://github.com/KSPP/linux/issues/88\n" . $herecurr); } # strlcpy uses that should likely be strscpy if ($line =~ /\bstrlcpy\s*\(/) { WARN("STRLCPY", "Prefer strscpy over strlcpy - see: https://github.com/KSPP/linux/issues/89\n" . $herecurr); } # strncpy uses that should likely be strscpy or strscpy_pad if ($line =~ /\bstrncpy\s*\(/) { WARN("STRNCPY", "Prefer strscpy, strscpy_pad, or __nonstring over strncpy - see: https://github.com/KSPP/linux/issues/90\n" . $herecurr); } # ethtool_sprintf uses that should likely be ethtool_puts if ($line =~ /\bethtool_sprintf\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { if (WARN("PREFER_ETHTOOL_PUTS", "Prefer ethtool_puts over ethtool_sprintf with only two arguments\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bethtool_sprintf\s*\(\s*($FuncArg)\s*,\s*($FuncArg)/ethtool_puts($1, $7)/; } } # use $rawline because $line loses %s via sanitization and thus we can't match against it. if ($rawline =~ /\bethtool_sprintf\s*\(\s*$FuncArg\s*,\s*\"\%s\"\s*,\s*$FuncArg\s*\)/) { if (WARN("PREFER_ETHTOOL_PUTS", "Prefer ethtool_puts over ethtool_sprintf with standalone \"%s\" specifier\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bethtool_sprintf\s*\(\s*($FuncArg)\s*,\s*"\%s"\s*,\s*($FuncArg)/ethtool_puts($1, $7)/; } } # typecasts on min/max could be min_t/max_t if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { if (defined $2 || defined $7) { my $call = $1; my $cast1 = deparenthesize($2); my $arg1 = $3; my $cast2 = deparenthesize($7); my $arg2 = $8; my $cast; if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { $cast = "$cast1 or $cast2"; } elsif ($cast1 ne "") { $cast = $cast1; } else { $cast = $cast2; } WARN("MINMAX", "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); } } # check usleep_range arguments if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { my $min = $1; my $max = $7; if ($min eq $max) { WARN("USLEEP_RANGE", "usleep_range should not use min == max args; see function description of usleep_range().\n" . "$here\n$stat\n"); } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && $min > $max) { WARN("USLEEP_RANGE", "usleep_range args reversed, use min then max; see function description of usleep_range().\n" . "$here\n$stat\n"); } } # check for naked sscanf if ($perl_version_ok && defined $stat && $line =~ /\bsscanf\b/ && ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; my $stat_real = get_stat_real($linenr, $lc); WARN("NAKED_SSCANF", "unchecked sscanf return value\n" . "$here\n$stat_real\n"); } # check for simple sscanf that should be kstrto<foo> if ($perl_version_ok && defined $stat && $line =~ /\bsscanf\b/) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; my $stat_real = get_stat_real($linenr, $lc); if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { my $format = $6; my $count = $format =~ tr@%@%@; if ($count == 1 && $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { WARN("SSCANF_TO_KSTRTO", "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); } } } # check for new externs in .h files. if ($realfile =~ /\.h$/ && $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { if (CHK("AVOID_EXTERNS", "extern prototypes should be avoided in .h files\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; } } # check for new externs in .c files. if ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) { my $function_name = $1; my $paren_space = $2; my $s = $stat; if (defined $cond) { substr($s, 0, length($cond), ''); } if ($s =~ /^\s*;/) { WARN("AVOID_EXTERNS", "externs should be avoided in .c files\n" . $herecurr); } if ($paren_space =~ /\n/) { WARN("FUNCTION_ARGUMENTS", "arguments for function declarations should follow identifier\n" . $herecurr); } } elsif ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^\+extern struct\s+(\w+)\s+(\w+)\[\];/) { my ($st_type, $st_name) = ($1, $2); for my $s (keys %maybe_linker_symbol) { #print "Linker symbol? $st_name : $s\n"; goto LIKELY_LINKER_SYMBOL if $st_name =~ /$s/; } WARN("AVOID_EXTERNS", "found a file-scoped extern type:$st_type name:$st_name in .c file\n" . "is this a linker symbol ?\n" . $herecurr); LIKELY_LINKER_SYMBOL: } elsif ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*extern\s+/) { WARN("AVOID_EXTERNS", "externs should be avoided in .c files\n" . $herecurr); } # check for function declarations that have arguments without identifier names if (defined $stat && $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s && $1 ne "void") { my $args = trim($1); while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) { my $arg = trim($1); if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) { WARN("FUNCTION_ARGUMENTS", "function definition argument '$arg' should also have an identifier name\n" . $herecurr); } } } # check for function definitions if ($perl_version_ok && defined $stat && $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) { $context_function = $1; # check for multiline function definition with misplaced open brace my $ok = 0; my $cnt = statement_rawlines($stat); my $herectx = $here . "\n"; for (my $n = 0; $n < $cnt; $n++) { my $rl = raw_line($linenr, $n); $herectx .= $rl . "\n"; $ok = 1 if ($rl =~ /^[ \+]\{/); $ok = 1 if ($rl =~ /\{/ && $n == 0); last if $rl =~ /^[ \+].*\{/; } if (!$ok) { ERROR("OPEN_BRACE", "open brace '{' following function definitions go on the next line\n" . $herectx); } } # checks for new __setup's if ($rawline =~ /\b__setup\("([^"]*)"/) { my $name = $1; if (!grep(/$name/, @setup_docs)) { CHK("UNDOCUMENTED_SETUP", "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.txt\n" . $herecurr); } } # check for pointless casting of alloc functions if ($line =~ /\*\s*\)\s*$allocFunctions\b/) { WARN("UNNECESSARY_CASTS", "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); } # alloc style # p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) if ($perl_version_ok && $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { CHK("ALLOC_SIZEOF_STRUCT", "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); } # check for (kv|k)[mz]alloc with multiplies that could be kmalloc_array/kvmalloc_array/kvcalloc/kcalloc if ($perl_version_ok && defined $stat && $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { my $oldfunc = $3; my $a1 = $4; my $a2 = $10; my $newfunc = "kmalloc_array"; $newfunc = "kvmalloc_array" if ($oldfunc eq "kvmalloc"); $newfunc = "kvcalloc" if ($oldfunc eq "kvzalloc"); $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); my $r1 = $a1; my $r2 = $a2; if ($a1 =~ /^sizeof\s*\S/) { $r1 = $a2; $r2 = $a1; } if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { my $cnt = statement_rawlines($stat); my $herectx = get_stat_here($linenr, $cnt, $here); if (WARN("ALLOC_WITH_MULTIPLY", "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) && $cnt == 1 && $fix) { $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; } } } # check for krealloc arg reuse if ($perl_version_ok && $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*($Lval)\s*,/ && $1 eq $3) { WARN("KREALLOC_ARG_REUSE", "Reusing the krealloc arg is almost always a bug\n" . $herecurr); } # check for alloc argument mismatch if ($line =~ /\b((?:devm_)?((?:k|kv)?(calloc|malloc_array)(?:_node)?))\s*\(\s*sizeof\b/) { WARN("ALLOC_ARRAY_ARGS", "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); } # check for multiple semicolons if ($line =~ /;\s*;\s*$/) { if (WARN("ONE_SEMICOLON", "Statements terminations use 1 semicolon\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; } } # check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi if ($realfile !~ m@^include/uapi/@ && $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { my $ull = ""; $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); if (CHK("BIT_MACRO", "Prefer using the BIT$ull macro\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; } } # check for IS_ENABLED() without CONFIG_<FOO> ($rawline for comments too) if ($rawline =~ /\bIS_ENABLED\s*\(\s*(\w+)\s*\)/ && $1 !~ /^${CONFIG_}/) { WARN("IS_ENABLED_CONFIG", "IS_ENABLED($1) is normally used as IS_ENABLED(${CONFIG_}$1)\n" . $herecurr); } # check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(${CONFIG_}[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { my $config = $1; if (WARN("PREFER_IS_ENABLED", "Prefer IS_ENABLED(<FOO>) to ${CONFIG_}<FOO> || ${CONFIG_}<FOO>_MODULE\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)"; } } # check for /* fallthrough */ like comment, prefer fallthrough; my @fallthroughs = ( 'fallthrough', '@fallthrough@', 'lint -fallthrough[ \t]*', 'intentional(?:ly)?[ \t]*fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)', '(?:else,?\s*)?FALL(?:S | |-)?THR(?:OUGH|U|EW)[ \t.!]*(?:-[^\n\r]*)?', 'Fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?', 'fall(?:s | |-)?thr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?', ); if ($raw_comment ne '') { foreach my $ft (@fallthroughs) { if ($raw_comment =~ /$ft/) { my $msg_level = \&WARN; $msg_level = \&CHK if ($file); &{$msg_level}("PREFER_FALLTHROUGH", "Prefer 'fallthrough;' over fallthrough comment\n" . $herecurr); last; } } } # check for switch/default statements without a break; if ($perl_version_ok && defined $stat && $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { my $cnt = statement_rawlines($stat); my $herectx = get_stat_here($linenr, $cnt, $here); WARN("DEFAULT_NO_BREAK", "switch default: should use break\n" . $herectx); } # check for gcc specific __FUNCTION__ if ($line =~ /\b__FUNCTION__\b/) { if (WARN("USE_FUNC", "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; } } # check for uses of __DATE__, __TIME__, __TIMESTAMP__ while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { ERROR("DATE_TIME", "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); } # check for use of yield() if ($line =~ /\byield\s*\(\s*\)/) { WARN("YIELD", "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); } # check for comparisons against true and false if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { my $lead = $1; my $arg = $2; my $test = $3; my $otype = $4; my $trail = $5; my $op = "!"; ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); my $type = lc($otype); if ($type =~ /^(?:true|false)$/) { if (("$test" eq "==" && "$type" eq "true") || ("$test" eq "!=" && "$type" eq "false")) { $op = ""; } CHK("BOOL_COMPARISON", "Using comparison to $otype is error prone\n" . $herecurr); ## maybe suggesting a correct construct would better ## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); } } # check for semaphores initialized locked if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { WARN("CONSIDER_COMPLETION", "consider using a completion\n" . $herecurr); } # recommend kstrto* over simple_strto* and strict_strto* if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { WARN("CONSIDER_KSTRTO", "$1 is obsolete, use k$3 instead\n" . $herecurr); } # check for __initcall(), use device_initcall() explicitly or more appropriate function please if ($line =~ /^.\s*__initcall\s*\(/) { WARN("USE_DEVICE_INITCALL", "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); } # check for spin_is_locked(), suggest lockdep instead if ($line =~ /\bspin_is_locked\(/) { WARN("USE_LOCKDEP", "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr); } # check for deprecated apis if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) { my $deprecated_api = $1; my $new_api = $deprecated_apis{$deprecated_api}; WARN("DEPRECATED_API", "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr); } # check for various structs that are normally const (ops, kgdb, device_tree) # and avoid what seem like struct definitions 'struct foo {' if (defined($const_structs) && $line !~ /\bconst\b/ && $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { WARN("CONST_STRUCT", "struct $1 should normally be const\n" . $herecurr); } # use of NR_CPUS is usually wrong # ignore definitions of NR_CPUS and usage to define arrays as likely right # ignore designated initializers using NR_CPUS if ($line =~ /\bNR_CPUS\b/ && $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/ && $line !~ /^.\s*\.\w+\s*=\s*.*\bNR_CPUS\b/) { WARN("NR_CPUS", "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); } # Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { ERROR("DEFINE_ARCH_HAS", "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); } # likely/unlikely comparisons similar to "(likely(foo) > 0)" if ($perl_version_ok && $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { WARN("LIKELY_MISUSE", "Using $1 should generally have parentheses around the comparison\n" . $herecurr); } # return sysfs_emit(foo, fmt, ...) fmt without newline if ($line =~ /\breturn\s+sysfs_emit\s*\(\s*$FuncArg\s*,\s*($String)/ && substr($rawline, $-[6], $+[6] - $-[6]) !~ /\\n"$/) { my $offset = $+[6] - 1; if (WARN("SYSFS_EMIT", "return sysfs_emit(...) formats should include a terminating newline\n" . $herecurr) && $fix) { substr($fixed[$fixlinenr], $offset, 0) = '\\n'; } } # check for array definition/declarations that should use flexible arrays instead if ($sline =~ /^[\+ ]\s*\}(?:\s*__packed)?\s*;\s*$/ && $prevline =~ /^\+\s*(?:\}(?:\s*__packed\s*)?|$Type)\s*$Ident\s*\[\s*(0|1)\s*\]\s*;\s*$/) { if (ERROR("FLEXIBLE_ARRAY", "Use C99 flexible arrays - see https://docs.kernel.org/process/deprecated.html#zero-length-and-one-element-arrays\n" . $hereprev) && $1 == '0' && $fix) { $fixed[$fixlinenr - 1] =~ s/\[\s*0\s*\]/[]/; } } # nested likely/unlikely calls if ($line =~ /\b(?:(?:un)?likely)\s*\(\s*!?\s*(IS_ERR(?:_OR_NULL|_VALUE)?|WARN)/) { WARN("LIKELY_MISUSE", "nested (un)?likely() calls, $1 already uses unlikely() internally\n" . $herecurr); } # whine mightly about in_atomic if ($line =~ /\bin_atomic\s*\(/) { if ($realfile =~ m@^drivers/@) { ERROR("IN_ATOMIC", "do not use in_atomic in drivers\n" . $herecurr); } elsif ($realfile !~ m@^kernel/@) { WARN("IN_ATOMIC", "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); } } # Complain about RCU Tasks Trace used outside of BPF (and of course, RCU). our $rcu_trace_funcs = qr{(?x: rcu_read_lock_trace | rcu_read_lock_trace_held | rcu_read_unlock_trace | call_rcu_tasks_trace | synchronize_rcu_tasks_trace | rcu_barrier_tasks_trace | rcu_request_urgent_qs_task )}; our $rcu_trace_paths = qr{(?x: kernel/bpf/ | include/linux/bpf | net/bpf/ | kernel/rcu/ | include/linux/rcu )}; if ($line =~ /\b($rcu_trace_funcs)\s*\(/) { if ($realfile !~ m{^$rcu_trace_paths}) { WARN("RCU_TASKS_TRACE", "use of RCU tasks trace is incorrect outside BPF or core RCU code\n" . $herecurr); } } # check for lockdep_set_novalidate_class if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || $line =~ /__lockdep_no_validate__\s*\)/ ) { if ($realfile !~ m@^kernel/lockdep@ && $realfile !~ m@^include/linux/lockdep@ && $realfile !~ m@^drivers/base/core@) { ERROR("LOCKDEP", "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); } } if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { WARN("EXPORTED_WORLD_WRITABLE", "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); } # check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO> # and whether or not function naming is typical and if # DEVICE_ATTR permissions uses are unusual too if ($perl_version_ok && defined $stat && $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) { my $var = $1; my $perms = $2; my $show = $3; my $store = $4; my $octal_perms = perms_to_octal($perms); if ($show =~ /^${var}_show$/ && $store =~ /^${var}_store$/ && $octal_perms eq "0644") { if (WARN("DEVICE_ATTR_RW", "Use DEVICE_ATTR_RW\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/; } } elsif ($show =~ /^${var}_show$/ && $store =~ /^NULL$/ && $octal_perms eq "0444") { if (WARN("DEVICE_ATTR_RO", "Use DEVICE_ATTR_RO\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/; } } elsif ($show =~ /^NULL$/ && $store =~ /^${var}_store$/ && $octal_perms eq "0200") { if (WARN("DEVICE_ATTR_WO", "Use DEVICE_ATTR_WO\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/; } } elsif ($octal_perms eq "0644" || $octal_perms eq "0444" || $octal_perms eq "0200") { my $newshow = "$show"; $newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show"); my $newstore = $store; $newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store"); my $rename = ""; if ($show ne $newshow) { $rename .= " '$show' to '$newshow'"; } if ($store ne $newstore) { $rename .= " '$store' to '$newstore'"; } WARN("DEVICE_ATTR_FUNCTIONS", "Consider renaming function(s)$rename\n" . $herecurr); } else { WARN("DEVICE_ATTR_PERMS", "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr); } } # Mode permission misuses where it seems decimal should be octal # This uses a shortcut match to avoid unnecessary uses of a slow foreach loop # o Ignore module_param*(...) uses with a decimal 0 permission as that has a # specific definition of not visible in sysfs. # o Ignore proc_create*(...) uses with a decimal 0 permission as that means # use the default permissions if ($perl_version_ok && defined $stat && $line =~ /$mode_perms_search/) { foreach my $entry (@mode_permission_funcs) { my $func = $entry->[0]; my $arg_pos = $entry->[1]; my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; my $stat_real = get_stat_real($linenr, $lc); my $skip_args = ""; if ($arg_pos > 1) { $arg_pos--; $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; } my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]"; if ($stat =~ /$test/) { my $val = $1; $val = $6 if ($skip_args ne ""); if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") && (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || ($val =~ /^$Octal$/ && length($val) ne 4))) { ERROR("NON_OCTAL_PERMISSIONS", "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real); } if ($val =~ /^$Octal$/ && (oct($val) & 02)) { ERROR("EXPORTED_WORLD_WRITABLE", "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real); } } } } # check for uses of S_<PERMS> that could be octal for readability while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) { my $oval = $1; my $octal = perms_to_octal($oval); if (WARN("SYMBOLIC_PERMS", "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/; } } # validate content of MODULE_LICENSE against list from include/linux/module.h if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { my $extracted_string = get_quoted_string($line, $rawline); my $valid_licenses = qr{ GPL| GPL\ v2| GPL\ and\ additional\ rights| Dual\ BSD/GPL| Dual\ MIT/GPL| Dual\ MPL/GPL| Proprietary }x; if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { WARN("MODULE_LICENSE", "unknown module license " . $extracted_string . "\n" . $herecurr); } if (!$file && $extracted_string eq '"GPL v2"') { if (WARN("MODULE_LICENSE", "Prefer \"GPL\" over \"GPL v2\" - see commit bf7fbeeae6db (\"module: Cure the MODULE_LICENSE \"GPL\" vs. \"GPL v2\" bogosity\")\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bMODULE_LICENSE\s*\(\s*"GPL v2"\s*\)/MODULE_LICENSE("GPL")/; } } } # check for sysctl duplicate constants if ($line =~ /\.extra[12]\s*=\s*&(zero|one|int_max)\b/) { WARN("DUPLICATED_SYSCTL_CONST", "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr); } } # If we have no input at all, then there is nothing to report on # so just keep quiet. if ($#rawlines == -1) { exit(0); } # In mailback mode only produce a report in the negative, for # things that appear to be patches. if ($mailback && ($clean == 1 || !$is_patch)) { exit(0); } # This is not a patch, and we are in 'no-patch' mode so # just keep quiet. if (!$chk_patch && !$is_patch) { exit(0); } if (!$is_patch && $filename !~ /cover-letter\.patch$/) { ERROR("NOT_UNIFIED_DIFF", "Does not appear to be a unified-diff format patch\n"); } if ($is_patch && $has_commit_log && $chk_fixes_tag) { if ($needs_fixes_tag ne "" && !$is_revert && !$fixes_tag) { WARN("MISSING_FIXES_TAG", "The commit message has '$needs_fixes_tag', perhaps it also needs a 'Fixes:' tag?\n"); } } if ($is_patch && $has_commit_log && $chk_signoff) { if ($signoff == 0) { ERROR("MISSING_SIGN_OFF", "Missing Signed-off-by: line(s)\n"); } elsif ($authorsignoff != 1) { # authorsignoff values: # 0 -> missing sign off # 1 -> sign off identical # 2 -> names and addresses match, comments mismatch # 3 -> addresses match, names different # 4 -> names match, addresses different # 5 -> names match, addresses excluding subaddress details (refer RFC 5233) match my $sob_msg = "'From: $author' != 'Signed-off-by: $author_sob'"; if ($authorsignoff == 0) { ERROR("NO_AUTHOR_SIGN_OFF", "Missing Signed-off-by: line by nominal patch author '$author'\n"); } elsif ($authorsignoff == 2) { CHK("FROM_SIGN_OFF_MISMATCH", "From:/Signed-off-by: email comments mismatch: $sob_msg\n"); } elsif ($authorsignoff == 3) { WARN("FROM_SIGN_OFF_MISMATCH", "From:/Signed-off-by: email name mismatch: $sob_msg\n"); } elsif ($authorsignoff == 4) { WARN("FROM_SIGN_OFF_MISMATCH", "From:/Signed-off-by: email address mismatch: $sob_msg\n"); } elsif ($authorsignoff == 5) { WARN("FROM_SIGN_OFF_MISMATCH", "From:/Signed-off-by: email subaddress mismatch: $sob_msg\n"); } } } print report_dump(); if ($summary && !($clean == 1 && $quiet == 1)) { print "$filename " if ($summary_file); print "total: $cnt_error errors, $cnt_warn warnings, " . (($check)? "$cnt_chk checks, " : "") . "$cnt_lines lines checked\n"; } if ($quiet == 0) { # If there were any defects found and not already fixing them if (!$clean and !$fix) { print << "EOM" NOTE: For some of the reported defects, checkpatch may be able to mechanically convert to the typical style using --fix or --fix-inplace. EOM } # If there were whitespace errors which cleanpatch can fix # then suggest that. if ($rpt_cleaners) { $rpt_cleaners = 0; print << "EOM" NOTE: Whitespace errors detected. You may wish to use scripts/cleanpatch or scripts/cleanfile EOM } } if ($clean == 0 && $fix && ("@rawlines" ne "@fixed" || $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { my $newfile = $filename; $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); my $linecount = 0; my $f; @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); open($f, '>', $newfile) or die "$P: Can't open $newfile for write\n"; foreach my $fixed_line (@fixed) { $linecount++; if ($file) { if ($linecount > 3) { $fixed_line =~ s/^\+//; print $f $fixed_line . "\n"; } } else { print $f $fixed_line . "\n"; } } close($f); if (!$quiet) { print << "EOM"; Wrote EXPERIMENTAL --fix correction(s) to '$newfile' Do _NOT_ trust the results written to this file. Do _NOT_ submit these changes without inspecting them for correctness. This EXPERIMENTAL file is simply a convenience to help rewrite patches. No warranties, expressed or implied... EOM } } if ($quiet == 0) { print "\n"; if ($clean == 1) { print "$vname has no obvious style problems and is ready for submission.\n"; } else { print "$vname has style problems, please review.\n"; } } return $clean; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/dev-docs/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�15156613252�013227� 5����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/dev-docs/extend-authors.sh��������������������������������������������������������������0000755�0001750�0001750�00000002222�15156613252�016536� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # Check if a starting tag is provided if [ $# -eq 0 ]; then echo "Usage: $0 <starting_tag>" echo "Example: $0 fuse-3.16.2" exit 1 fi START_TAG=$1 # Extract email addresses from git log git_emails=$(git log ${START_TAG}..HEAD --format='<%aE>' | sort -u | sed 's/^<//;s/>$//') # Extract email addresses from AUTHORS file authors_emails=$(grep -oP '(?<=<)[^>]+' AUTHORS | sort -u) # Find new email addresses (in git_emails but not in authors_emails) # -1 suppresses lines unique to AUTHORS, -3 suppresses lines common to both # Result: only lines unique to git_emails (i.e., new authors) new_emails=$(comm -1 -3 <(echo "$authors_emails") <(echo "$git_emails")) # If there are new email addresses, add corresponding authors to the AUTHORS file if [ -n "$new_emails" ]; then echo -e "\nNew authors to be added:" echo -e "\n# New authors since ${START_TAG}" >> AUTHORS for email in $new_emails; do author=$(git log -1 --format='%aN <%aE>' --author="$email") echo "$author" echo "$author" >> AUTHORS done echo "AUTHORS file has been updated." else echo "No new authors found since ${START_TAG}." fi ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/dev-docs/release-process.md�������������������������������������������������������������0000644�0001750�0001750�00000003472�15156613252�016653� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������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; git add signify/$P.pub` * Expire old release signing keys (keep one around just in case) * To update authors run : dev-docs/extend-authors.sh * `git commit --all -m "Released $TAG"` * `git tag $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` * `git -C ../libfuse.github.io add doxygen/` * `git -C ../libfuse.github.io commit --all -m "Re-generated doxygen documentation"` * `git -C ../libfuse.github.io push` * `git checkout master && 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 ``` ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/������������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�15156613444�012273� 5����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/Doxyfile����������������������������������������������������������������������������0000644�0001750�0001750�00000352153�15156613252�014007� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Doxyfile 1.9.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). # # Note: # # Use doxygen to compare the used configuration file with the template # configuration file: # doxygen -x [configFile] # Use doxygen to compare the used configuration file with the template # configuration file without replacing the environment variables or CMake type # replacement variables: # doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = libfuse # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format # and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to # control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO # Controls the number of sub-directories that will be created when # CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every # level increment doubles the number of directories, resulting in 4096 # directories at level 8 which is the default and also the maximum value. The # sub-directories are organized in 2 levels, the first level always has a fixed # number of 16 directories. # Minimum value: 0, maximum value: 8, default value: 8. # This tag requires that the tag CREATE_SUBDIRS is set to YES. CREATE_SUBDIRS_LEVEL = 8 # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, # Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English # (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, # Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with # English messages), Korean, Korean-en (Korean with English messages), Latvian, # Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, # Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, # Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". Note that you cannot put \n's in the value part of an alias # to insert newlines (in the resulting output). You can put ^^ in the value part # of an alias to insert a newline as if a physical newline was in the original # file. When you need a literal { or } or , in the value part of an alias you # have to escape them by means of a backslash (\), this can lead to conflicts # with the commands \{ and \} for these it is advised to use the version @{ and # @} or use a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, # VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # will also hide undocumented C++ concepts if enabled. This option has no effect # if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # Possible values are: SYSTEM, NO and YES. # The default value is: SYSTEM. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_HEADERFILE tag is set to YES then the documentation for a class # will show which file needs to be included to use the class. # The default value is: YES. SHOW_HEADERFILE = YES # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if <section_label> ... \endif and \cond <section_label> # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. See also section "Changing the # layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as documenting some parameters in # a documented function twice, or documenting parameters that don't exist or # using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete # function parameter documentation. If set to NO, doxygen will accept that some # parameters have no documentation without warning. # The default value is: YES. WARN_IF_INCOMPLETE_DOC = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong parameter # documentation, but not about the absence of documentation. If EXTRACT_ALL is # set to YES then this flag will automatically be disabled. See also # WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO # If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about # undocumented enumeration values. If set to NO, doxygen will accept # undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: NO. WARN_IF_UNDOC_ENUM_VAL = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # In the $text part of the WARN_FORMAT command it is possible that a reference # to a more specific place is given. To make it easier to jump to this place # (outside of doxygen) the user can define a custom "cut" / "paste" string. # Example: # WARN_LINE_FORMAT = "'vi $file +$line'" # See also: WARN_FORMAT # The default value is: at line $line of file $file. WARN_LINE_FORMAT = "at line $line of file $file" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). In case the file specified cannot be opened for writing the # warning and error messages are written to standard error. When as file - is # specified the warning and error messages are written to standard output # (stdout). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = . # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # This tag can be used to specify the character encoding of the source files # that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify # character encoding on a per file pattern basis. Doxygen will compare the file # name with each pattern and apply the encoding instead of the default # INPUT_ENCODING) if there is a match. The character encodings are a list of the # form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding # "INPUT_ENCODING" for further information on supported encodings. INPUT_FILE_ENCODING = # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, # *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C # comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h \ *.c \ *.h \ *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # ANamespace::AClass, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = example # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = *.c \ *.h # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # <filter> <input-file> # # where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that doxygen will use the data processed and written to standard output # for further processing, therefore nothing else, like debug statements or used # commands (so in case of a Windows batch file always use @echo OFF), should be # written to standard output. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common # extension is to allow longer lines before the automatic comment starts. The # setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can # be processed before the automatic comment starts. # Minimum value: 7, maximum value: 10000, default value: 72. FORTRAN_COMMENT_AFTER = 72 #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = NO # The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) # that should be ignored while generating the index headers. The IGNORE_PREFIX # tag works for classes, function and member names. The entity will be placed in # the alphabetical list under the first letter of the entity name that remains # after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). # Note: Since the styling of scrollbars can currently not be overruled in # Webkit/Chromium, the styling will be left out of the default doxygen.css if # one or more extra stylesheets have been specified. So if scrollbar # customization is desired it has to be added explicitly. For an example see the # documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = doc/fast17-vangoor.pdf # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # should be rendered with a dark or light theme. # Possible values are: LIGHT always generate light mode output, DARK always # generate dark mode output, AUTO_LIGHT automatically set the mode according to # the user preference, use light mode if no preference is set (the default), # AUTO_DARK automatically set the mode according to the user preference, use # dark mode if no preference is set and TOGGLE allow to user to switch between # light and dark mode via a button. # The default value is: AUTO_LIGHT. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE = AUTO_LIGHT # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag determines the URL of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDURL = # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # on Windows. In the beginning of 2021 Microsoft took the original page, with # a.o. the download links, offline the HTML help workshop was already many years # in maintenance mode). You can download the HTML help workshop from the web # archives at Installation executable (see: # http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo # ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine tune the look of the index (see "Fine-tuning the output"). As an # example, the default style sheet generated by doxygen has an example that # shows how to put an image at the root of the tree instead of the PROJECT_NAME. # Since the tree basically has the same information as the tab index, you could # consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the # FULL_SIDEBAR option determines if the side bar is limited to only the treeview # area (value NO) or if it should extend to the full height of the window (value # YES). Setting this to YES gives a layout similar to # https://docs.readthedocs.io with more room for contents, but less room for the # project logo, title, and description. If either GENERATE_TREEVIEW or # DISABLE_INDEX is set to NO, this option has no effect. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. FULL_SIDEBAR = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email # addresses. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. OBFUSCATE_EMAILS = YES # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # With MATHJAX_VERSION it is possible to specify the MathJax version to be used. # Note that the different versions of MathJax have different requirements with # regards to the different settings, so it is possible that also other MathJax # settings have to be changed when switching between the different MathJax # versions. # Possible values are: MathJax_2 and MathJax_3. # The default value is: MathJax_2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_VERSION = MathJax_2 # When MathJax is enabled you can set the default output format to be used for # the MathJax output. For more details about the output format see MathJax # version 2 (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 # (see: # http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best # compatibility. This is the name for Mathjax version 2, for MathJax version 3 # this will be translated into chtml), NativeMML (i.e. MathML. Only supported # for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This # is the name for Mathjax version 3, for MathJax version 2 this will be # translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. The default value is: # - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 # - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # for MathJax version 2 (see # https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # For example for MathJax version 3 (see # http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): # MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use <access key> + S # (what the <access key> is depends on the OS and browser, but it is typically # <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down # key> to jump into the search results window, the results can be navigated # using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel # the search. The filter options can be selected when the cursor is inside the # search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> # to select a filter and <Enter> or <escape> to activate or cancel the filter # option. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing # and searching needs to be provided by external tools. See the section # "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library # Xapian (see: # https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library # Xapian (see: # https://xapian.org/). See the section "External Indexing and Searching" for # details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. # The default file is: searchdata.xml. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. # This tag requires that the tag SEARCHENGINE is set to YES. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of # to a relative location where the documentation can be found. The format is: # EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... # This tag requires that the tag SEARCHENGINE is set to YES. EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: latex. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # # Note that when not enabling USE_PDFLATEX the default is latex when enabling # USE_PDFLATEX the default is pdflatex and when in the later case latex is # chosen this is overwritten by pdflatex. For specific output languages the # default can have been set differently, this depends on the implementation of # the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. # Note: This tag is used in the Makefile / make.bat. # See also: LATEX_MAKEINDEX_CMD for the part in the generated output file # (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex # The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to # generate index for LaTeX. In case there is no backslash (\) as first character # it will be automatically added in the LaTeX code. # Note: This tag is used in the generated output file (.tex). # See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. # The default value is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_MAKEINDEX_CMD = makeindex # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used by the # printer. # Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x # 14 inches) and executive (7.25 x 10.5 inches). # The default value is: a4. # This tag requires that the tag GENERATE_LATEX is set to YES. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names # that should be included in the LaTeX output. The package can be specified just # by its name or with the correct syntax as to be used with the LaTeX # \usepackage command. To get the times font for instance you can specify : # EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} # To use the option intlimits with the amsmath package you can specify: # EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for # the generated LaTeX document. The header should contain everything until the # first chapter. If it is left blank doxygen will generate a standard header. It # is highly recommended to start with a default header using # doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty # and then modify the file new_header.tex. See also section "Doxygen usage" for # information on how to generate the default header that doxygen normally uses. # # Note: Only use a user-defined header if you know what you are doing! # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. The following # commands have a special meaning inside the header (and footer): For a # description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for # the generated LaTeX document. The footer should contain everything after the # last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what # special commands can be used inside the footer. See also section "Doxygen # usage" for information on how to generate the default footer that doxygen # normally uses. Note: Only use a user-defined footer if you know what you are # doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created # by doxygen. Using this option one can overrule certain style aspects. Doxygen # will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_EXTRA_STYLESHEET = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or # markers available. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is # prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will # contain links (just like the HTML output) instead of page references. This # makes the output suitable for online browsing using a PDF viewer. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as # specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX # files. Set this option to YES, to get a higher quality PDF documentation. # # See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode # command to the generated LaTeX files. This will instruct LaTeX to keep running # if errors occur, instead of asking the user for help. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BATCHMODE = NO # If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the # index chapters (such as File Index, Compound Index, etc.) in the output. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HIDE_INDICES = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain # If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_TIMESTAMP = NO # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, # it will be relative to the LATEX_OUTPUT directory. If left blank the # LATEX_OUTPUT directory will be used. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_EMOJI_DIRECTORY = #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: rtf. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. # This tag requires that the tag GENERATE_RTF is set to YES. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will # contain hyperlink fields. The RTF file will contain links (just like the HTML # output) instead of page references. This makes the output suitable for online # browsing using Word or some other Word compatible readers that support those # fields. # # Note: WordPad (write) and others do not support links. # The default value is: NO. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # configuration file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is # similar to doxygen's configuration file. A template extensions file can be # generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for # classes and files. # The default value is: NO. GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. A directory man3 will be created inside the directory specified by # MAN_OUTPUT. # The default directory is: man. # This tag requires that the tag GENERATE_MAN is set to YES. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to the generated # man pages. In case the manual section does not start with a number, the number # 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is # optional. # The default value is: .3. # This tag requires that the tag GENERATE_MAN is set to YES. MAN_EXTENSION = .3 # The MAN_SUBDIR tag determines the name of the directory created within # MAN_OUTPUT in which the man pages are placed. If defaults to man followed by # MAN_EXTENSION with the initial . removed. # This tag requires that the tag GENERATE_MAN is set to YES. MAN_SUBDIR = # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without # them the man command would be unable to find the correct page. # The default value is: NO. # This tag requires that the tag GENERATE_MAN is set to YES. MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: xml. # This tag requires that the tag GENERATE_XML is set to YES. XML_OUTPUT = xml # If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. # The default value is: YES. # This tag requires that the tag GENERATE_XML is set to YES. XML_PROGRAMLISTING = YES # If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include # namespace members in file scope as well, matching the HTML output. # The default value is: NO. # This tag requires that the tag GENERATE_XML is set to YES. XML_NS_MEMB_FILE_SCOPE = NO #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- # If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. GENERATE_DOCBOOK = NO # The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in # front of it. # The default directory is: docbook. # This tag requires that the tag GENERATE_DOCBOOK is set to YES. DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an # AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. # This tag requires that the tag GENERATE_PERLMOD is set to YES. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to # understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. # This tag requires that the tag GENERATE_PERLMOD is set to YES. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file are # prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful # so different doxyrules.make files included by the same Makefile don't # overwrite each other's variables. # This tag requires that the tag GENERATE_PERLMOD is set to YES. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then # the macro expansion is limited to the macros specified with the PREDEFINED and # EXPAND_AS_DEFINED tags. # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the # preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of # RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will be # used. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. # gcc). The argument of the tag is a list of macros of the form: name or # name=definition (no spaces). If the definition and the "=" are omitted, "=1" # is assumed. To prevent a macro definition from being undefined via #undef or # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = FUSE_USE_VERSION=35 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The # macro definition that is found in the sources will be used. Use the PREDEFINED # tag if you want to use a different macro definition that overrules the # definition found in the source code. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have # an all uppercase name, and do not end with a semicolon. Such function macros # are typically used for boiler-plate code, and will confuse the parser if not # removed. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- # The TAGFILES tag can be used to specify one or more tag files. For each tag # file the location of the external documentation should be added. The format of # a tag file without this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where loc1 and loc2 can be relative or absolute paths or URLs. See the # section "Linking to external documentation" for more information about the use # of tag files. # Note: Each tag file must have a unique name (where the name does NOT include # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES, all external class will be listed in # the class index. If set to NO, only the inherited external classes will be # listed. # The default value is: NO. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed # in the modules index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. EXTERNAL_GROUPS = YES # If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. # If left empty dia is assumed to be found in the default search path. DIA_PATH = # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of # processors available in the system. You can set it explicitly to a value # larger than 0 to get control over the balance between CPU load and processing # speed. # Minimum value: 0, maximum value: 32, default value: 0. # This tag requires that the tag HAVE_DOT is set to YES. DOT_NUM_THREADS = 0 # DOT_COMMON_ATTR is common attributes for nodes, edges and labels of # subgraphs. When you want a differently looking font in the dot files that # doxygen generates you can specify fontname, fontcolor and fontsize attributes. # For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node, # Edge and Graph Attributes specification</a> You need to make sure dot is able # to find the font, which can be done by putting it in a standard location or by # setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. Default graphviz fontsize is 14. # The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" # DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can # add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a # href=https://graphviz.org/doc/info/arrows.html>Complete documentation about # arrows shapes.</a> # The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" # DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes # around nodes set 'shape=plain' or 'shape=plaintext' <a # href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a> # The default value is: shape=box,height=0.2,width=0.4. # This tag requires that the tag HAVE_DOT is set to YES. DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" # You can set the path where dot can find font specified with fontname in # DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = # If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a # graph for each documented class showing the direct and indirect inheritance # relations. In case HAVE_DOT is set as well dot will be used to draw the graph, # otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set # to TEXT the direct and indirect inheritance relations will be shown as texts / # links. # Possible values are: NO, YES, TEXT and GRAPH. # The default value is: YES. CLASS_GRAPH = TEXT # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the # class with other documented classes. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for # groups, showing the direct groups dependencies. See also the chapter Grouping # in the manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside the # class node. If there are many fields or methods and many nodes the graph may # become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the # number of items for each type to make the size more manageable. Set this to 0 # for no limit. Note that the threshold may be exceeded by 50% before the limit # is enforced. So when you set the threshold to 10, up to 15 fields may appear, # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. # This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 # If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and # methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS # tag is set to YES, doxygen will add type and arguments for attributes and # methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen # will not generate fields with class member information in the UML graphs. The # class diagrams will look similar to the default class diagrams but using UML # notation for the relationships. # Possible values are: NO, YES and NONE. # The default value is: NO. # This tag requires that the tag UML_LOOK is set to YES. DOT_UML_DETAILS = NO # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # to display on a single line. If the actual line length exceeds this threshold # significantly it will wrapped across multiple lines. Some heuristics are apply # to avoid ugly line breaks. # Minimum value: 0, maximum value: 1000, default value: 17. # This tag requires that the tag HAVE_DOT is set to YES. DOT_WRAP_THRESHOLD = 17 # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented # files. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented # files. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH tag is set to YES then doxygen will generate a call # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. Disabling a call graph can be # accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. CALL_GRAPH = NO # If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. Disabling a caller graph can be # accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical # hierarchy of all classes instead of a textual one. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the # files in the directories. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES # The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels # of child directories generated in directory dependency graphs by dot. # Minimum value: 1, maximum value: 25, default value: 1. # This tag requires that the tag DIRECTORY_GRAPH is set to YES. DIR_GRAPH_MAX_DEPTH = 1 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: # http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). # Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, # png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and # png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # # Note that this requires a modern browser other than Internet Explorer. Tested # and working are Firefox, Chrome, Safari, and Opera. # Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make # the SVG files visible. Older versions of IE do not have SVG support. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. INTERACTIVE_SVG = NO # The DOT_PATH tag can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. # This tag requires that the tag HAVE_DOT is set to YES. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the \dotfile # command). # This tag requires that the tag HAVE_DOT is set to YES. DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the \mscfile # command). MSCFILE_DIRS = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile # command). DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the # path where java can find the plantuml.jar file or to the filename of jar file # to be used. If left blank, it is assumed PlantUML is not used or called during # a preprocessing step. Doxygen will generate a warning when it encounters a # \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = # When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a # configuration file for plantuml. PLANTUML_CFG_FILE = # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized # by representing a node as a red box. Note that doxygen if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. # Minimum value: 0, maximum value: 10000, default value: 50. # This tag requires that the tag HAVE_DOT is set to YES. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs # generated by dot. A depth value of 3 means that only nodes reachable from the # root by following a path via at most 3 edges will be shown. Nodes that lay # further from the root node will be omitted. Note that setting this option to 1 # or 2 may greatly reduce the computation time needed for large code bases. Also # note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. # Minimum value: 0, maximum value: 1000, default value: 0. # This tag requires that the tag HAVE_DOT is set to YES. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. # Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal # graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. # # Note: This setting is not only used for dot files but also for msc temporary # files. # The default value is: YES. DOT_CLEANUP = YES ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/README.NFS��������������������������������������������������������������������������0000644�0001750�0001750�00000004154�15156613252�013601� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������NFS exporting is supported in Linux kernels 2.6.27 or later. You need to add an fsid=NNN option to /etc/exports to make exporting a FUSE directory work. Filesystem support ------------------ NFS exporting works to some extent on all fuse filesystems, but not perfectly. This is due to the stateless nature of the protocol, the server has no way of knowing whether the client is keeping a reference to a file or not, and hence that file may be removed from the server's cache. In that case there has to be a way to look up that object using the inode number, otherwise an ESTALE error will be returned. 1) low-level interface Filesystems need to set FUSE_CAP_EXPORT_SUPPORT in conn->wants and implement special lookups for the names "." and "..". The former may be requested on any inode, including non-directories, while the latter is only requested for directories. Otherwise these special lookups should behave identically to ordinary lookups. Furthermore, setting FUSE_CAP_EXPORT_SUPPORT requires the file system to handle node-ids (fuse_ino_t) that the file system may does not know about - e.g. a fuse FORGET request might have been received or the node-id was used in a previous instance of the file system daemon. The node-id might not be valid at all when an invalid handle is passed to open_by_handle_at(). This implies that the filesystem *must not* reuse node-ids even if generation numbers are set correctly. This is because generation numbers are not provided by the kernel to e.g. the getattr() handler, so the handler would be unable to tell if the provided node-id refers to the "known" current one, or a previous one that has been forgotten and re-used. 2) high-level interface Because the high-level interface is path based, it is not possible to delegate looking up by inode to the filesystem. To work around this, currently a "noforget" option is provided, which makes the library remember nodes forever. This will make the NFS server happy, but also results in an ever growing memory footprint for the filesystem. For this reason if the filesystem is large (or the memory is small), then this option is not recommended. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/README.fuse-io-uring����������������������������������������������������������������0000644�0001750�0001750�00000002531�15156613252�015641� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-over-io-uring uses io-uring for transport of kernel/userspace messages. See also https://docs.kernel.org/filesystems/fuse-io-uring.html In order to enable it, the kernel module needs to have it enabled: echo 1 > /sys/module/fuse/parameters/enable_uring Additionally, FUSE_CAP_OVER_IO_URING needs to be set and se->uring.enable has to be true. The latter can be achieved with the libfuse option '-o io_uring'. Default queue-depth is 8 and can be changed with the parameter '-oio_uring_q_depth'. As of now there is always one queue per core. A reduced number of cores in development. Benefits: - Improved performance by using io_uring for kernel/userspace communication - Reduced system call overhead compared to traditional FUSE - Asynchronous I/O operations Usage: To enable io_uring support when mounting a FUSE filesystem: 1. Enable kernel support: echo 1 > /sys/module/fuse/parameters/enable_uring 2. Mount with io_uring option: -o io_uring 3. Optionally adjust queue depth: -o io_uring_q_depth=<depth> Example: ./my_fuse_fs /source /mountpoint -o io_uring -o io_uring_q_depth=16 Requirements: - Linux kernel with io_uring and FUSE io_uring support enabled - libfuse compiled with io_uring support Build Dependencies: - liburing (for io_uring support) - libnuma (required alongside liburing) - meson build system with option: -Denable-io-uring=true �����������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/README.fuse_reply_errors������������������������������������������������������������0000644�0001750�0001750�00000000261�15156613252�016717� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Under normal operation, a call to any fuse_reply_* function should not result in an error. Should the kernel abort the fuse connection, a fuse_reply_* call can return -ENOENT. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/README.notifications����������������������������������������������������������������0000644�0001750�0001750�00000003552�15156613252�016025� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������During the life-cycle of a user-space filesystem the usual flow is: 1. User-space application does a filesystem-related syscall 2. Kernel VFS calls into the FUSE kernel driver 3. FUSE kernel redirects request to the user-space filesystem 4. User-space server replies to request 5. FUSE returns reply to VFS 6. User-space application gets reply from the kernel However, there are occasions where the filesystem needs to send notifications to the kernel that are not in reply to any particular request. If, for example, when a READ request of 4096 bytes results in the filesystem having more data available for the specific inode, it may be useful to provide this extra data to the kernel cache so that future read operations will be faster. FUSE provides mechanisms for a user-space server to send the kernel certain types of asynchronous notifications. Currently, these are the available notifications: |-------------+----------------------------------| | Operation | libfuse function | |-------------+----------------------------------| | POLL | fuse_lowlevel_notify_poll | | INVAL_INODE | fuse_lowlevel_notify_inval_inode | | ENTRY | fuse_lowlevel_notify_inval_entry | | STORE | fuse_lowlevel_notify_store | | RETRIEVE | fuse_lowlevel_notify_retrieve | | DELETE | fuse_lowlevel_notify_delete | | RESEND | - | |-------------+----------------------------------| One important restriction is that these asynchronous operations SHALL NOT be performed while executing other FUSE requests. Doing so will likely result in deadlocking the user-space filesystem server. In the example above, if the server is replying to a READ request and has extra data to add to the kernel cache, it needs to reply to the READ request first, and, e.g., signal a different thread to do the STORE. ������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/fast17-vangoor.pdf������������������������������������������������������������������0000644�0001750�0001750�00002222165�15156613252�015553� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%PDF-1.5 % 2 0 obj <</Length 34769/Length1 1629/Length2 33845/Length3 532/Filter/FlateDecode>>stream xctem&vvlv*m۬۶m;Nvӧ}~1=qM\{/2"e:!{#Sq{;:&zFn,+ௐ LNŔnj5503`�"N.�JU%u*c0_OgKs;�7S{[S;׎ʦ� S)@D^ASJN@)! 03u2(Xd-MM�fN��v&LK`pv05fal`dk` 0w2s{? +!'u],\�*;O Cb;[UZSҿtaj] -..22X:;z_i:[ڙg�'SsC'Sg0 ߪ7tpW.Φ6f0LcmniϠHٙ-7quӿDPM`bj g7$e>(o!7r+G%ZF�{�ng,cc17?8WCu'#bBv agYD`fhSڙ:Xڙe_11?g7M/IJ.7Y{uYXM cd{�\l,@bC'Kj:S|_`MeC;tKGWS)ѿ33r12Kj&,zŠpu2xP[Ҫ-qVeS/yy筙LeKvn+WFy%7 Ͷvܖ<ш0q*&  q7r~1'y_�Wb6EhӮ{2'#UtLʋw֦.f%*xm$ 1m"l:[,MFUZ3608Iyc t('QPXKsZ>Q+8D󲣹QP$suv8xrJ$P$f-{9\ b#ia c@`=-p\ʋA4/s k|ꅾ :DXPp}cCЍ.} 4 WnL.ѭR{uN0aT^Qf:]JJm_Kra&4WXT;f1Q5y&RFQ'TwG#wj%b(10/A"L48/<ґx;1 ᎋXfmX;fZZU u=$J6Xx!pbwwi|J5N!-�gQVd٘ubiS,`zxBb9F d ^쫯|, 2pmn[i,x( )C[6onD.v`w•ǯM{n3:.`>aV+A5{oya!bJsqGv}78'doh(sc>F[ZdH�|==r/g}[0%tܸ,@$#e-ay e=]yCi&<)İ>*Z1m<x#5~W G6qUk߭S%^@@MR؞l3vajl/l8]S : qf\2VنS c]C3@J`&_D l"ѭ zM̳zNU,RM@֣;-rfxF\E~Xw@#" tA_&,1:d zXZ=oq6 t+G+5U!3^T�82Ƈ1U(X50ϭyh͆T%hygr!M݈?o, m\֯{3>Zm=%_[L]Bx`\ G1+qì$!`Vkd"@ٕ>Զ<O#2xq)uB]UqrnH)ރq"uGy4syv)>,DJ^C +E=̩`9p@( rvJ Fg3+wS0)Mg2K Ιt=By`)MuyU:t gԖDy 63g4-?mCb'1h3Hv !ut *ǛN,9Utlqp]: @z\\M'g@L$]UGu<}O;!ni7.yYTUla'2XD *1a\wUhM0Mt"Ұ@OOmLBł<Zh lq A�4X |m c,5a*)^.gNc24#| .ɆO}SxkLʟvȹx, ЄK3JA]/,}A"+*/b.b x0{uq۶$1QK?1a XC=ADċǘ$OᶤJ`VjgqM!n+flLتUV%O\EtN@Ŝ.~cEBFvb-WH=H1w'/78ejao >a=LVHɢ So'E; F ]:^K SJx|"伽b �A$MeJ y&>ɶQ9.;fsj @P띆é#AybUZ4hr\atI,}i|I% Djuha+߲,w:hwx wEGBHt$,'|S"\>EAnޔF nD{�".ۮ}�#cfg<H'TXӳ_iˍb箻WD5<WyLJLSqG,A;Tv1Fd@r#埐cykcޭaB#l s(g^oLt#\ AdX|Kya ǑU/)-k;0+{A]6#ۓ\WɀG*{6v$; s!u3#.Y9._VGvegSYYr%g`!TUTt(5nR$_Gs\nz}j@.1(sOKLOcAss*{hm\)O%/~5f߯9;bwQVZ +F( @s*VL$ٖ(C<B$YOL1b`5|v*znH"֍:E,5&2ϹE^R9;F+47idP'xNƶM>^W\I} (S~1Ld2R#К  aL:uڴ  =yT3*{ݸjp뵶l~?oz&yZqvQ0gB5 v@pJDCPqNu\uW(̬%}Zo(:T b6hb(M).c<.Ohh裗bp) A!`E]\-Km;z#OU#rg|9ĩ*gc)BWֳs|O72 U[-PvVާsm[].{&ߗLEe]1p e) 3sQ;wϣPWBCIIpE+ !tIE%!0sXDP[{LRGWл) %M}픂yyޤ͵9)UOˏaT><rl2$"Dsz8݋ȷG%CBj^agh=ϓvɟ9;MQY<H=VTePze"7vB /H~r=q`0)h. SS| 32X[% ;OP'o'(W!e A3N0~ ݒ_pAJ5ɠ3兰 y%(eaF@K2<IV!{M~ds!6ẢgPC7,AlzeMUCgVΚU.QOzu0FS{IX|ؑ})|!f/$p8_%͊,: C4(b#1K 0!nKRY6i+) D&"*hV 9}C%zT64�z+Q$qNUuZnY|b .eh“[JiOP(~5~P`CX&nrKfQ2�60v\W AQc*ãy1wSMj.c>T E?\ǖ.οIcD1>$\x|2U.z:7-pp?JTKcLbEˠX_T4%َQ2[Tg V㒾,q ɳi0 )<ւֿ'"rL \ 7^m9?]&zT/@.fp#dV 9v:( ˔x`2UY:j]'ԿA1e&uX>zWKPUnZ])5ךiV[#kM>?[4g">M0qF~Ytb:wU|-9ZpAQ~' i Q EȲz9o[B}�[R-OC+N05�zs֝E$0 7AgܞI1xB`Κя i:F_,\ta ޱK@ Z$c9]Rh 2w-ll06xr y9 ɉ,E}e}NafoðI=EҞo8?:鞀~8OOK\G }ڟy8#޶ ~yz{J猖[FBFuɏI loW+֮Mܚu'ׁJ 1R>`*Fv۩䛨?$Kk[˵L{ r.S81w 9T.z@Ĭz[}68ek-i8oD}w1-ւ-1 B8sY|q_ "=H/3fyșaKNĺ[TK6)ڧ,5pDW{K=�nU3!.|8K?z,j-ah:̡ wQ Ʉ[I9GZadᾸ̳cX~/@q.IWF#aǤXa.#!rn9_9,İI6_SCHV8s"ů Ճ *wԄ[zI]p&؅y|xD$CV_5͜ C%#˔d\샒pnMKJ(SOu.&zŃ|g)`waFt<?~  6RM׭'Tz~X?iBk۹Lc,ܪ>e!UhZ K԰[; ?w%{b`a@'7Dgv洴FP'iD}LLSiERAƎ+ptZHMJ(Gs*ă!8𹤘([qTܱ;9Xa<tcn7;%І=<c0jYM;>ˁ u(w ѲR|L;Z/v'g#q,V] ̃PzK%/ɹ^װYx  U;_!T&f*SvZz0_޿}-zbL_� w]₌ 0fikT;lqU65qK>a>fuPxcJѡĂbnN 9Vގ yXpdAvפ'52Qby^1KʟNh3BvL3דWf(E%9ۍ蜆t s.t}GV�p&b4;_A dA^ؔAmd?]ثC vDyH;TRArL%ቀנZPUY[:UW@ʠ%MÑgHSqq MJ4kkU~IBY4 kR'@hQk>eK- ۅx33죕>Нa6?m^N fH;JTE~?7e_ޏiK*~ X4gv]#~Q691tG4I;ٍshz=A旅$'G$`23uNt!3ιkM8<&M֤JzRp^O ~EEI jZ2O*zq1̳sudy;e1дO O[G<[344}O-! |͚"Zk`B7vJH={k9h gt;oK0wFIY ~(p2 Vp Ϡ$jS?bAYGy_#|qM $݌7]*7?yÂ./321ym>87"i=[ =whi)?iiWjt+VjܝY,AWę^whcM9իa2td/۞!V|PMV]-g{NPڛr@I).+шC$;!RpTD6'LKwY6X@Z-6d? p ~T`d .٦⍔~,uXMӯjUlnq�5vk>~[bX֋ <%8! k&%1g…ysH5mQy++,lS (<0+d@I8U2 ؒ)02.՛@Tя aKa$y P91gٚ0!g뵒bD;Zl9+;O.9+,i_:Wnb0PFM^x`twLGXyiS!h% އJL7u)-ҼkhJF'5dL9,Mlꬨy}@t_4d(^~ܬ7d?х8;(H6fgZ@G_ $IjD!rIվov8}k`>�ݾgՉ]3)<:7lugELAh -C3 )qhtǂ;na]1c}o#1:;Fs!>݉->4CBB9<t{ǫZnߊ,(K]e ׎?|1A,1UwBg2;WDx˨x.eɝHf/;0_e;4:w>C"l0pSK9{lPuiOY2.uZH]{o6 ]}.!Շ\X?>DR@dGרΝj0` (B#ѕ�hMYj"$"<: ]‚] yN81{LmyaIv ݡYy=Ҡc@>߈xk/Φ. gK8+D"PQH~}{ǎ&l FT1 [T2.W^ /A4W$ ڨD~V\7V!ϙ'y!ě7&gKe-Bn4FY _;'Ŝ{^TCF׊BZ\af0Ȝǁd'J5ǵd2S6[@n 51i}* BTC\%OR½ZL/)`6SIt;v"U'M$R^/x8J#�*(5aqeT/Q 8ooi@]q Z_KC1docTVfͰ@O OQ1vc`BMPȉL hlR1)yɳyF~4z^!ghD˶Lb#]}]g: ށ5o39!jAE7) ;}& XjY9+|lެihtz(rF38IM _ -}c;^>X.ܑ$?T\ZYyYya{?jA_pbx Cj*=P"ʄC1+WsJv6[e}G�j1z?ęjD7M'G+;5ʴI3s&FJ_րR*¹mG:0A7jgZP0T0Nӯ*R,d}B/X1v"}4w 9En"lz?i:5 ]x@6=cPY2jKmtџqx "U#�i ,<%ϒF\/|_+#>7QynDžh ŸXf"єO +4:ZgŪפr?!usa, 4/Oud%r} ń^܁I5M`Q+esOg4܈|Q'>$Qwe8|,k慌k>`B>x r0{s&"@:-M%A~'~t c9lF4[K' '#1oxlHa,1|(GiHHU~Z'Y/ |C}0` ƫZ w_uSoVߩ BUB~armJ-9ƴ-;VZlS0 U:.aL ֪ZDJ\Z<A7(L7 4uGh sY魡eTD{fIEUZA/ųV=|5%#wB˳Noh׈Ȅ5 vG&쎳AIH[(92[4>M|1HCDDMJGo{W|LMHܼ4 pl䂆v[wB.(IeِA}#d4Q脻�N$`j1Nǀ Kp;yL`W+c^yV4 w m"yć/Jx׳%C,dg dm+yVA^]iZ[YSc.zb?{)y\<LtzZ6^ "cPqN9"8H@dP88ts٥TWʭB%Tuc!$۸TUBqʚ�3#Z^=cf#}ь~/BryPrG[zbz}Š I$ڂ1Reȑ$xHc|"U.BQk۾YWqj`FC fzzŴ &AjuR_[פYts`3)9x48XM{Vw08 YpfFG&f~1Ĥ[Q=F5!�8V$:]aR#^vlO0dU�P %d{NPN2b٩K||i6)̺oIhG=p4G1a{D Ul Yq1@5(mQ1(恾n˃8uj:t"jiaO@\5d#ZUzl8 U߮\@m/4 @ mkLnk {? uLUPU㊹%0>LoO[ux; Mg[Ҹ/QJ hDJc>4 �MʭKcf;N/hA` TcОJX LwO~h bIa)N3viE ^\7%E6}兴=nSRR4Pדt?tE²4�J FX*&)k*qþ?H:q3x}/cTHCZR$|cc(U{pvJw,{v)ONTqʿa?Cj~F}gYR E+/R7ɣtjE]Bnedo_1?n8".v 3ؒY45M3i~wiνf'PL z'bSTWD/M_70_@kJE0Pl(0f$[Apƿ8 an4wceJJT@&!i0ZR[S @2)vZ\b/5>=<Zwc/2˨脍?9^J} y4}ܖjlK1$>q6ކbqz$ Kƶu CE? k#O=MVU2�̬Q_ -<e0Tj|zm<C>^'2˲Pf*oV!hYc.N4 S#>Dמ+ ZrA-?o@5bT|XW#j/.K#Hd&P{Ɲgy�a3WRpqŢ9"I/ u̼f$C4G#CZY-&u£AՑ. gM%&vj҇_$rU-Q&Ěs%Պ\!#u9fΑ&9Ձ`F`J*u9ߚ8T&H8h.\ )y-|l!4[ Kiw0ى@)kҷhHE\֚USD0 9uvd$E< %1m8 eUx1iݻd%(]Lr4a'l&WJ!#Wf"x#{HKDiZ(U)I :,4A\Kr c*°w+TVK093s`0g*0PBed tH.y|k9Rw.r3&AT+s)%"n\#FMݸލWTܐP=@"~Z/0?9 *0\«/PQ=!I*I AbxY>''j'je4{<moU3#"Gzwnjc;˴�Kt{3I S<`>(Q5Y&� (y2ūh mk /=nZ(+<F.î��Q?mɲW::z.vbzi(_tE=:Ez LsNW`.h?㇬W!M.-[Vf(BS_[YEL GȼWyv[g$c*Ò'@c~Kohw(!\�s(60j~j&>d7(})L1 l#|e0b(uah>PN^s~[>SkQ6;R!v!XbQF+4&ɭ2`= {m*-&PV7Z~ ;|Z zm`zbJHKsdRK57%'s?p 7-o,h&Ơb8q>Id4۟ y=:3d!0% <EO:*m=ycKfE}i849\)\i㜺Tq{ER l3~L = .z(]֝De#17rYRz^8||1Xʬ$սZ3gi< 0$ Nۗ*T^9zԵm]ڜ҃L`dH<X &n|<2 9&7h۝^ّ]{qNQV3ԧb;͟k@b{g; `0s~}| Yy[9s[DzF:?d\ oJZ[6ROP;iҀrg|Jv 9UfV揚Ofucw@cq/|Ӵ7H00ԯ. <3Ov*VZ([Γ_Xle4fUZ]]s7߀Bt.MQhG ZRhnsQ18-`j1) D <张o8$e]8.}ܯA+#w5lyM+CIenCڒo%פ?}=uiɴ{b`;S!#"!n:} DEr0VpzcB0߼̮{g<">瓶EN7Z;,=\"c$Gpx*{Em"f97fZ Pq*};g|, 口O+NHBPmK)&|i%?.nxNQ 3B."^кsZ{3m3<^zlb Y TX?g]Z0oVeOp ˺rn7–t˱p)%nm`nso%.Ea #DYoou滒cƐ2dsZa3v~"?rZ ;9i~4-u Éaa-*nksEE#пLwO2/D.Vss'53qqɖY&UID1n`z(y曨%bEEЦ3[ViB,͎"Sh{\=CDu kyid Q?8R9Fte˭X]Lg 獟*wG64A \mU`ad>D\]ElS#@aQ|*n#(hgƘ톾K/Gp ]bz25K8Ht5r Չ|z"2zC%Uyӱl.Ui}ʡcNF@R)ΙjƧ1/8(4g̑nڐepsYvf�[;C�Znؾ)| 3gJXc? ̃NM eU#T0w bL�۬9kS&DJvnr:!<‡ &-Ъ"4HxElJ@? ~Ɏ?E|4X|jfsfִxKhryj Afm}m3Ռߖbc7 06õ߮.1aD,GE[`T dҰt�md\EzHw=> A] ;C%x5_mjD 6Ĉ 2K^N.Tw_:* $n x# σ?=/s0OMi)E5!%Wۛ*.pXXd1F?Y5C}w&RvwLv~f!1p\h8:n[S@3Nv"Mvxhky;U~?ʰ|uꭶu})d_rRivr=e"*AݬDF! sE;;<&r2xrlRmEOȃE)bȓ[MA)b@F}"avCy&/09uL7H bҍo .49 ud Ψ27M/-]G7+e^6p<+٬Գb00clTUuFZBNw=%DS$Ag-c:l׊]xl~͂Oo&Ee0yk"8O}"LG+ >pcʿEIW&BԶU:3?W'L$ D?;Q$¢CN}hU `p 52$}# ޛQ6]ago$ ?"@/Y(e>.wPx+!` ' Ҧ�mY c&b|G(S׶̘#auⴑVaD%6  Ejqah#/dւLb|f r k7n0G.R/+$EF?7[ WJT<ŋ$>LSԛ@ZC PgZ>A0V2%,mf #!#l`kf ,6ٸiD= jo:{�<F9"E[}4LcrmdN ۖKWL>zz{!T�z= !aH|t7,T#pb LVKmHًDtu^Y?+1q#Q =?#' u]V}YHPy`@+Ɵ<o?F{v97wJ1j'-Fȵx0e|T5#Z-coִ2ˡJVMBi$&}G3{ZP#!:wp<3-u^I">nщ{pכ0i] Lq)_qVj?QxMQdm}D1W輲dJhl,C[Cb,yI`iVRAM$V[q #iU wxJE Aq<TۦG_yzt zC^zMJYV(VݝpX ,e{z7 /S"it^$"wH%`Ⱥ`l�<iс|c{wڨ*u%WXl=<1mQY [ݎo&6h!`'oLS[wU.oXsJ YJVL솁9&.b=sS"*qGz6c8(12TX9!2N.9{cه+&<8 kwST9erN!FL< R='[ԫϵZy_LNMV^&\V# mzusw,%yKc,4l<H&nfj[(Y#y@l@uC:a0PL)m\`Ylv4qU$Yb%Lfxon_ G00N, L}޽%ua'CYoͫ/-361Dƫ/j>hꝘ9`?P~(܏d7)QYˏč2#2 .?Y!]Ӈ Xf)`Nu:{& KlFRTlmJ&>`ɋ֙A6WF7џ_G쌦:Il;!y^Ui 41(-PPj~rQ?^a|cx�T!g[MgP 0VWrWqˠJM,q?mY?/T[ǽJU2A] S讨,< 2K@ѭ =6U1pFLgUkaYFl;nݞ W+k+dAfu T0f*0jؤ4HusV1>pŻ0GZ e|2\1sLG(B1WEyl * +J τYv3EtMbeU*O691N\P�+V1YO1ieKHM?4`ǘ�Y /SX^#go wzzHyp�7Wڼ$R75?"3nX�QBވ-E yYh`=H D>BؼKL_qhKvu-f-59ezf _ղ &vыuPCփ#)'NzlSTEYgrqbgw݅Rv mj�gQ +Ἆ) mmi/2% a}$Pa1fa[<zA]?GVlCnY$O#g`>#75!C�hsׂ5Sn?f)PmTMF4V[� @) sc!{XxBeCw=T;I+p I  YX5p`v/L@0Wm"zB.;Wxu)TT{G}c~ 8%)ɓ,KY]k̴q86}i ǁSS)%\?wIFnuzcX.S׮#9>I1p$wWe6[_e̼.$23M}L$SӮ"6b >ɦYw7H᥎|QdNS.r`Jw\Eix !۲ZfA).L3DaPc:)Q km+.ʮ阉&gZb.~3=^Di Zʄ"0tro++S}wc cWlvܻϤV/e‰ A=Kn7+@!!RB'Fm:!CB=�lZZƒt0:f @c,90-];~<*& H? U�I'OSv Aff`2U*jߨ]Fx(F#'F8 A; 0S3ȱ@'7?s|&TN\RfU mȯ<AعQr @[ 1#˗ }Vb}ϹƅBvtu֜QY Ϡ˟[&BR T;-<p1†`UszfrcOugE4 <hH*p9 B : b+d̦ vcZV;va2ơ~PMx;>+,,UT []a,Dv棺<ƈ3M(UfC߸Jl IkѨX]Rap|Պ-۠3y;rC\:@W͔lpWNM#M#[_K#+F%i{/&كEUAB>T^yv4eyBO~&Mr:EI[Q-3Y,ؾo /pN nh ?늺T?oj9}!j,Q29k^'vsQ^[Ӌ.dJzoUı>Z l�:΀eT<a@2 zk*"Z6IJX>=~ny.xL\6 (.y??='A욾. â43S]Cxۙ.{{)ow<t8)<�'ݠV8+SØBW|B݊\}c]8~o^f=1Y")#FX:5աz*`]r3*әHjBviЂAH{O0snj gMwVPrdv* ?Y|ryX#SfA;i#c~>*}eSG=vkd/Ʈ{ӔD}z�Կ(šW{[\q*?Ň4a6+B>SLJizpQ< T9)v<g*X6T Nn/CGJ)<5OuitH] M{6bOҘa/!& lQ t]ˬ ց|4?Ax֓p^ cmgalǚ`n Bт ,}Ŋ'B<Ya3${BܶS"XSE*AS$ژE},4V/O>r-# $MZH%;zaa%yj?+]4(.&<岯 (~D�q>Hduij|%T+QGvLew[mQ:uy][]@aŰ �n i(% .3( �-1ZGm.+XP3mKg?zR-z"V|_f\v*Bf69 ;{k覊1v+p',m4ߺ`M f)o0^<M,^$;2or$g K3@^>0A' zU-岙{IӮb(ԅ\mR9�Tz({\eޯ>W3XsV~g*[EʈzP}ũNH>*x4̚\2tuRX:\-b5 %׼"Оze}q6g3nEDqIM {yPB=lF2Ƒ_~GICVn8Y[;"zDdoaAq˭$g<nч"-.)dЭɺO3B,B$^#g^h @4$Dq]땹^>~`=fZ"HH;4k4cL t8LfYc@G/t;w&ib).gUXJ1!~2Q`^y EZd=뭵cQmj(,W 3w}QpTmLY۟2e-05t,bm+{S�(RA9t^1&Ng.`Gcη7Mӽ,j"�g46}?ɸx~_CPtNK݌H뱄K]mXn7{2NniУHUc%ry0(?=XR5nN9 _$LxqS횒A-I84�K6N[r^X!9Q|赚ʃR3@ ȇT2Va'(+?Lu.r7/(CPԏ=o_[]dOS3>o mA KR#9TPf7]-^TMߙ_*v;u.Z03E MƧ4}ڧ)~>v<ؗF3]AZ*m\6L1]]⮾tT7a"x4劌7�3b.d7ΏU/͵tؘl 8W'OVDgdнV9@B^C!yt):Vc-®&~d˺IzP\v14(D8`=u2Yi0ѫ2jX#=̄q Aq%yE9@\6餋犅̠Tȫ{_dЌPm bft2-Ma!SKH76 Ĭ-4,TK& �2Z_΋ CDL??M!7;o\o0Ldd Ny f )M8_2[Ȼ짐 34L1o! Pg{GD4L2>Ol1Oр?dTiGk2%SLX7P-hchB=F:bƓCeh8|cPwQD7 WC^޻x@`TQt}ЄKY!;jrKod0R.`4|JK5Oeikm{,;BU, Yl j|i h:xln8O3_Q Uʋv "^>O :7]=]S\5繍נp`,;vy[TZbU'Ӑ,+#1&6;- VTv^4HaQB1jzF˩rD { bM+TO|?AݗpZLJE* G�&6t4UnbfjtYOuQ6ٹp';\2ik�m(C?7d`T&  Nf9e{>X4xpxt-VHH`Il8  QJf'vc9YdpUE@YGa/,m:W% (5O|rkxb6E1OdЬa(Lǣ4Rʛ<͞s1c;Z$3i[Aޢ 7*΁Mς?k'0:_m"0@ڧM]N(̣Jџ[\7T|:C|n+uFgC"6swW0.ϹrR  q wBd;b­*~+D7*Z/C,'^' +[xaQn;|*S(3 dWVZB+ T2M=g"uت1NXVkƩN/ź\1HYҦB0mAcTzM D|n KH!E_<_"!RTe3xbtݸ V=x.)A6Ow(Û?/<&$ d/Ȯ[Y;oqy`K 1&=`NS {MC~1KF*^!\P^AkA ၴ,7{vRN*WݾVVUœ+ }npj1ts>3|v_6Ft-9V\{R#z7Rs5?@sTUI;7&WPs*E$7syvs0o}<gHmI3G%ʼB t5|;#)*!{ﮛSnSXz < K\~_h*2xdd�`2.ل*WGw :" :ϏJBb`?{šuvZqS&�\`ӻkfXbK,Xrׁ-RY~Z}-hGd*S4^;7ap|Cۓf.d;dkjkE}`a:ၮ I3PCmeU*Ovj_ ~;u0)cnKr9wxWOx(<~}a րRE贆.{T b1s%OR#J7~0{5m?">O# U|0R#8krw+z qfPR!i6-B!n|)׻zyG)km3 {뼞qra^8h⹿@nKWopU�EU'Pj2:vB {U{XfYqs&Uĩf{v(/cMݒt w*MQۻbܶYC4ϔ6@L윰)DئPÔU�Ycͅmg~ȧxsygBȔ�. Q̵<n0j{Y0a?:ƕ'J9 .KTxuOq[tؐYգ'G2aHKl"${yzF_۸]~DdsWOJn+g EA7{yU#, >+'&Յ�p¹}o�UlMhwU}C*svR8>MY6ζpo8g6qx 6feһLmz'\_z1$XB,<`N?⸣1:VO:M;ʼnǼ~=ɗ[<6əoF%Sc>€V砄g/0"lwx> {BwCkԣ>S$Vh0)[r 6)Io;'wz6%` P}V`mֹZuMj򚺺/U%ôFWF=>?e}P3In9ZG s.]1)M\я%>+TEw/gF%cDTL Dj>_SqW!%-thsN`|FĿr{ Tg|U!`5=mQΚko;^D3V'^70<W^GfFVG�ڑp5Gs$R:1*t%ʐVu.qC|%+E|qLU :d,O[Kof̣=CP,!#FXqxz\<3B/͉3vqRٶ-D@w0TR miBRW0x6/#YX! %yrR/-ִh`T-DB",G6V~{­0ݺŵ^%(,}W;~W*GjY [4Q&MSHB d)m4_>Vgs` ):q|x詮D[L~ ê}A==|kelХߎ/fؼ;hm'D?R.hP/ZJ-+ C1.jad/S͡V)pQ\V&@@c2$ᚠVc\7^غMjd]ڱV(,Ukug# X1f?w}F3N<k EЦ~ї$yA4dB31T0c )dQ*eKt%5BLcv*ﴔdB67;=K7]V>)7J2Q0$ VV7͝C1V'P@KzEE˅8rUBʁ %w _QHw/Y!<_ܡڑ//.)ZxX]}ئ7c 9?'hD/] lV[R\EQ3� ǁ>υ<¾ݤ.a%,nf K1䮏Bu=u*�:>$w8.,gؓ2.D^a?ˎx^8&GÝ[~871hHv@"\6A=r|UIp/^k)y8S- �Ȝo{$C>]l }i3N�YJށ|01b:Ɲ=?{捴8_W>$.leGqlE-]_(+#ĐFC mpa[Y~I2F;8)tKތ9c 4:_ ZfY9:?g\" ^8<7YJf*l|ZyGaS/dZU_4^G3? GҦI-֐:E~<,]ZUnM}I*e2E0fg[MuosS40b (Ѝ,iÈ0-�C&ԝ(@ q` F�U\jtNMĦrcQ]�2 z0S:/߆0UXiB<7(Z=vĎYmP R( V=` GQDv.Kfq=A0?ׅVVO1Τ5E9u5.2tu\3LpM1c+˩:'o4l=.@d5`]ZZc7dmԷ7nbJi,+{ٵLq+~ϫkZYߴ6KN-ʞW~ɆC~ j2Ɉi)E! wԍ CiVȺ?sr_uw$n3L;@VE `r~jIn|w: ` iK{%J�ø�0E}1:5^{b�TUv:C}VRYJlÿ́sJQ OEjz{Zx"lj58PE93vZhi:l=B2ceO2P0%M^nRc!&np]h\Evg uw/F##C[eE$dPVRJ4UD &KTt3˲IRj!u踯 = d?i@bDB'8?*UͰNPvYX�8jMb.P܆&wJL), LKϙ݅Py5yQEr׆ijر) CT)nױzY۵754f?lDB7z4)龻B�pF<쵻[r Fr j# C75&U |=z4 )NKCM/c<~Q8eZS`[8 BWj`hfK^ďz¡a DLou JPA-UJ`+�aEȵKln@3ָ,kH` [Ԡf"AoeytExxFz_<F;3:Wb(zON{+x`rQ9X  }Me|Ծ7݋8sJ*|a<M,iS_z37 kS$LN va'fRƴā߹2pXz>G{xQr?/b1lloJwf ihkYiCOG̾˭pn#6Q�! Z/~j,ĶB`WwzM>k̰@EdpV崙Xk*Zw6DH0Cw A^ zd$/_9SL]9!ifP(/Ɖ\(ƺbf@qO6TzVXX:ÊPNPm }(ʢQ,aIX^P9 ٢63n$Pwz>#B$nd3*]|rB64ъ(A=i>`DzvWc<,A̩cɰײwOigKp70P}6% ,k_c`ڽwֈxc4mmiH(VPL ^8gmy]J5tr qi$}2ućzZ csϭZ^XԦ;Tat|Ddx/5cA#zw5 #ɮsty�tsvmLf5}|xűfǹ.\$ 7א: )"uugxpfb(:kN/IXhL5Gf1-z9Y Vs mpCUOlzg{Q9SN8;u|Y/*@όgJ!3v-#c Eʖi:e%X9dm?%�#(18xtWm&)ڿSq{Ue{ZF冰Lz`߉mw½"[T>{! /MZ[.$r�oS5I\dH>'î/c{NUt?%8&!ݼޓrMcܼY(h@:|ߐ's韉ZT-xCޟ)eqozBi.cynu:c1Wn_  ; hûmD,T[:S$;VK2J<qͱ8[7ϷAP$iST�o޾f@f.8/c}AK]2e|3h("" i&I4qQ%V̫Ė`.QBlmXDڰ&*⼓,c|*8FvdP5EmWKH=r˷MF}8{@ jrǝ'6 @QJ#9(f e',l} >01Aewt6|MASHHn[eLh~;6LpDO60%'@ <`G/eIg^%NdmF=.Hw,yQ8+q/;4P)ABt„]ڪhhADh][`b-Vϳ^ݏLdԹHP )P k^r{_FQ"nyStĞ_` FhnWAD31S XGQwn#]R5hqTmB(ڑv|Q@9͋D9\:yV[-,K .2ӁaȱUPOU$8|aæ񮔉*/P:!nI0CxY3J!^m(7Q׀ N&= #LX"%qUi MctiR{'NuQ&ŽyV)§xge/nnwMI``r+"l 7nKiJS#Q:Ej,.}ѧ*5HL 2{jMgsirHvaq[y!nnkslLtV6494ZZىFKR+rĥf.e:R|>K^h^>ѫ=9pv#5ԟq=Kg}<k ]|%1JԮ,3zVա?F<;&0Yɪ(!vxb{ +9U >%HWbb)'*!r W1+Ja]Sa"ȼ!C ~ 7+a p;x�ݴ�#/#]^Ax݇We*J!OG;B @scħs6><| uig%/d܊{1kݡ ( a#K^ҜF%jx(lG+CH#ɑ%7.νw&~w+ )w"Ww:'5cc c'_G&5$E # $q;5=ψ?g;r{eLɹv_r� vD,g Kxyq[+T`~EF?8=jq.ofh@wKx 7UI-12-} !CiSъW3Q"_"Z:D#~zܔ:FfO(ՏHI>ǽ0ĭmd42T)` Uv,hSZF``]8M�Qҟ V,reopl`}ėYs,5L*c6! huW\ ?'N'?LVXRW tQ4n8enϡaf8AcP AY5<0ON6c >]Y8 4Ԡ8B Um,0fP1 aYi;:A_2 ٮFӕXmZ6iR؅p?AO_2ŜLflڀëbI>AE,S̏0)юs'ZDaz肹ygͽ!dr*Z<ݥ !Ջ<kZ$YPcZ2l@g;D69!@pnu.\vx4BJO3E:�ߑ5jdŤ9͘?g8K)tv(RŐ LH%IwU 3oƫ`\JM.n|{5;;al:hH4ٺka%a3! J9@w͂3i9Qb99QlvOj+sɳC٬IfI v6"?(nED{\i趢}/aƊj!s.N4bdckp9HfJft;A ?>`qWnZ B5\|l5$9)2\ 6d9J?$i^(:UnPđ;l-L92  =&=a�OeDa\6R} N"but4VPI\w'"DV6ԽOaWY*sb"jVP}$dQ3$Qa-EÆf#J9<}T4|e ?gxkN. rqʩKa2K?JЯv*1hS.:��^wAܑ"[T¦Xː#dUyȏn9^sA#lk,Q\iRb7^/h36ƴX}6Cci{(0"^ɄfɊ`f"4/!F԰D1#fO;-^yF5k)ūfW;s a%+P#@$yݤ=Qs:jU `B8vݭ U;(U0%1�ɀA2D74Rx ZA"#'HA kByeYV 41-TIG -s<q'狼ygWjA9�+xZ fW2kHJ砙{\Bb\qHOδS%aݗ[!f�D-"93=s(Zy % "Nn$DI}FO�i'opGf;~MUDte5&(p_+aY]%v$Z0yވ%>BR2TpeS0j'g2 o\4-sX^MR[D̬yÓs\8gFҔo_u=S}c FfI6wZiXLMz)>Y9K&{NO"sݹۮ уJ0+`p/P<"@u֝7Ǜb[ dۦ-F1חMh/85HWbPc#c&MuM)6 LũC\iH)Hq+֧YcUT1M�5>; vh/5i1 b{KE<\c{>zWcQ<}  dp&9Pig=ܭ-EN=ìJS:|!X:pǨ^suڏ^bu}Ѹ1̫6Bv-!2I:Fz?){@)Ƌ<bb|Ebx>DZ[C7Ues`̣b[z `s6nfr/y9R,&%3=phdyA%<w_:nn6Nj2bx\-KxMYȵfGVX}|EdְX/p 'i&-6W)ΝH9+{}!6͙֮AUM~VCZ<F_}ss,yGZpBIκEG %n}¡Z#)'j{(L̂ʓ{(g'32­7W]Ibگx�~|d]<P+\ۖ׭km-̑vOtؑO 9a+E٣~Mvr]~;An WE;YX~]lXCэה)|a$ I?IԘyWPT&W2o NnG8>uufOG<"[\?5/U ?$ =]?zĉQHh:#+Un!+VcMO| L)[*&i){C CCy$oYqGQ0L2\j$#"m1ј9A bw"`pe!h=par;95O3E<ʟOrQSo  sH�}f6Og'JDL>%}DkG}fM?SfD. /"K=bP,%.V;+.8Zoz!']=KDԣR(' }\|^~Z9ut*p\'=<,*{x{ I(#!QfZ -=qxs͡ѡG,? gk<B^w/L<(I .Kz?C`; ;e:oR9ݢ+g<!Ϡ}(Ȃ7gFϟw4ESS]ocȋ< [/{lK?¶83PbqDR �Ie_Ejߍ--CPddDV"a12bSWE8B#EwoU=]`gE.#fb <Ggݨ3fF9P>ĆŷKRGAXok.5|QlV.1ݿAd lΩ#;Ja E&:@شu G0䧖ek]챲;f&t#6B TIAiu)`Ŗ\]*hVYOB\W8<<FEDL7FCs$bBuQ߿nJ1A>· EĄ8&:^b0u8ָJƀO80MtyuMg}& 8#Ǿ+X+ XIѽHBȀkx x!YJu Ѝލ˚!sFmp>L88%nW Dғ,@XULQbͪ4->)OY1gm�?4Ԏd]\[A{ +bd0(m_~EgibK%9Ag@i[RS1hTJYHnx4O]ߚ,mpI_Dc=M˟8Q/&#N5H߿QsT6UWYV<;Sb]UEOL,g(ȋ8 ~)(Hmնp&)e5 jH޹dV%$sciDm@R2Vl˾>ܠ˙"GaƞeZBmIiPc7aΥ7b�?Vi ARb$cQr6њi䥑q Θ;0u%x&`[01> p r[YyK?Xj178ߑ泦BߖzLwAL5jf5"mC:!)> �6!FJ'q@M¦Ϙo$Y$) v4#%eq"u08[H\OgeɐRe_}Oxf5M~Llu./)]Ek\h)Vo uOLOɔBTVvJ$'HOÕ:3Qm "m=7+.Q=S08ȓ{w˒幨q}.؞,o;F̾4eN&TՖ!ifv; _pN6@"dre 7->_+hnZz22WJB0_=l5)6j&3]+ m soEHb-R9}2lLt~ssR~RYy"<P^nRp[d:3zbTm {=ea]%$EDe>h^io^QXLkȂv|nuNv|s%t"ijU=`3:< Jhn<1Z=8Adt!gbA#_Jpݰ8Eb[ADW ؀Y7MϪY:.1dRnm|YF ILK|׎V_JenJحIԏ/YPg &+&G܏ ><W0>śl?2Dkl1: ? q1&4!c{fDY)G\斅r\GI̚[!9RDX6DFλy~/Qmm"55ڻmU&7Ȋ&a$.OwXvUAOS!A\KR@LgG(xiʽmϋ8{!mݶX_/%´NY {БSB55MКgMO2 UhˀN ?i{Dw^47M"࠱qR: ~+"Z6 |"0&ҙ;L 鳋g Z1 ?y7ʋƅpuO1Q~ЎcZU_Lnw}w�NBYVqCgW6GfŢHYԉ9;2Cgq.`%{H9,[fTM@eSt@ڲ<T/~]err.]=9p/kϞlNCӢ %G>ht9ZMi?'V/7;Ӳ_L^R|P[EỏP*3('w';8mq"aU|D3U~뙂Jl^W#L,'|^n5_,k_'AP@[YOV>c3;s jC֫L ̂'|3]h>wb[Re/RT|T]A;�*>-涷s-Q9@W"OXBfH'3qEvGcf!Xtc,s yE:QJ}Z 4ɍ;Qx֗&YnB,W >c~jɊ3hh�o.ERk$r\n `";bX7 2fSnuu ԂU+jQ_ )Z1뎨yGJ jG~z=ed֞ˡId]ŵm!547^�qy+8v]8K]L�J'^Bܢ|Dăr.Ā Shf5kctH2 +,2wCoduab1*}Z zTMhF4Q>kګQn ya] ١{`Q ƅ{]]ѭ1:a6߁ݫksH{9]KB iN[r;_^L%r%ZgU>5omGPqQ/OLC<Y2\@3GY1\b94KQ�q5$fM{Y|#(NuJa']>!!MN~:|'2_ljV Jïsgg. �agoi�+^ԯm#$x!?F/E,I%϶ac/$P*F@5sWZN ")N:>%Ƴ՝02SS9GUIVyWHeeC@7d{h2̆<Ȟ{Ij$7N4Y~hN(;qxY©0KK4׷ @|zJ+Qb#x `Ɇܼ`c_<ҿ}\ƧCyбtԟk;L̘?�7}"B|"HBBJC endstream endobj 3 0 obj <</Type/FontDescriptor/Ascent 729/CapHeight 729/Descent -218/FontBBox[-174 -285 1001 953]/FontName/NimbusSanL-Regu/ItalicAngle 0/StemV 80/FontFile 2 0 R/Flags 32>> endobj 1 0 obj <</Type/Font/Subtype/Type1/BaseFont/NimbusSanL-Regu/Encoding/WinAnsiEncoding/FirstChar 32/LastChar 116/Widths[278 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 556 556 556 556 556 556 556 556 556 556 0 0 0 0 0 0 0 667 0 722 0 667 611 0 0 278 0 0 0 0 722 0 0 0 0 667 611 722 0 0 667 0 0 0 0 0 0 0 0 556 0 500 556 556 278 556 556 222 0 0 222 0 556 556 0 0 333 500 278]/FontDescriptor 3 0 R>> endobj 4 0 obj <</Producer(pdfTeX-1.40.17; modified using iText 5.5.9 2000-2015 iText Group NV \(AGPL-version\))/Creator(TeX)/CreationDate(D:20170131183532-05'00')/ModDate(D:20170201191245Z)/Trapped/False/PTEX.Fullbanner(This is pdfTeX, Version 3.14159265-2.6-1.40.17 \(TeX Live 2016\) kpathsea version 6.2.2)>> endobj 5 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 6 0 obj <</Length 152/Filter/FlateDecode>>stream xe @DSjyl*l҆xĽE,ga jz4ňc A(Z]rn^z-FZߩTv2ڤ?Ζ ؁۞!'33:{CF~2h#(OPћF '�uLCHkz4: endstream endobj 7 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 8 0 obj <</Length 153/Filter/FlateDecode>>stream xm @D)\SDBNH^roC,fyQC IB"`nڠf<uy978w 8YB3 wQzܩ4TrtOt=ŏT`1i4 endstream endobj 9 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 10 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.ʚy0x{V?-ѡh8|3C IB"`m]jGʖצ[[{x "$ymTgto_γ[/>yaxRkK!0٘4U endstream endobj 11 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 12 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.- ^OKt(93ߠgAS!ڶV66ܢgmXqk}JSۍse(]F|F {v=CNvbtf d򌠴B7N& }Ʉ%47 endstream endobj 13 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 14 0 obj <</Length 151/Filter/FlateDecode>>stream xe @})ܙR%rB7IKP)oP3`H! V4tmc5 V׺Y:.Vܩ4Tvumv_%ˈQ\Ϟ]^ķp7:d<#(U_q83!844 endstream endobj 15 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 16 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.y0x{V?-ѡh8|3C):C<h:fUQ *͗צ[ 8EBg$6w տ3*כRO},'OpO*UbS`bZ 4O endstream endobj 17 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 18 0 obj <</Length 151/Filter/FlateDecode>>stream xe @})$T`#6$P)o3(FHdPvD5֗s`zoĮ՝jEo7)ͮǙ9ٱbq237^\,BEoN)Ҽ P44F endstream endobj 19 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 20 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.j- ^OKt(9̠gAS(8F ImU (Nצ[z8 "$yTigTo_γ[/>z~pR)l< iM/Q4C endstream endobj 21 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 22 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.ʪy0x{V?-ѡh8|3C IB"`mB ci'R2Tsxˁ}Y$tp?zdp<͝*CΊMYO},'Op(:ŦdcZ %4= endstream endobj 23 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 24 0 obj <</Length 152/Filter/FlateDecode>>stream xm @})ѻR%rB7IKPpfAE! Em V׺Y:.Vܩ4T\mvٟ,#>+Gq={vCNvbz d򌠴b7n14Tԇk4 endstream endobj 25 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 26 0 obj <</Length 152/Filter/FlateDecode>>stream xeO @S̱.Xa%7*\7l~ZCq jz4ňc mk3Mu9ϳ[S&EX-#>#Gq{v=CNvbtf~t2<#(S_4EꙆP4@ endstream endobj 27 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 28 0 obj <</Length 152/Filter/FlateDecode>>stream xm1 0+nE:TpJ#t-<L1wq*zF )$ `nXZ\rn&\;;KsP[ET6[;xh ,( Qb%oP)LS"RiE/4 endstream endobj 29 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 30 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.y0x{V?-ѡh8|3C)EL<h:fUeĵ.G=C<NnbtfY~2u<͝*CŦ,>yu'o8X'w)0 1}4I endstream endobj 31 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 32 0 obj <</Length 152/Filter/FlateDecode>>stream xmA @W̱. K nUV?-ѡ0 *zF,)$ &E>h82 T(cMP/ځaL28Nꗣ3P̳t]śZCq*01fe4 endstream endobj 33 0 obj <</Font<</F38 34 0 R/F39 35 0 R/F22 36 0 R/F40 37 0 R/F14 38 0 R>>/ProcSet[/PDF/Text]>> endobj 39 0 obj <</Type/Page/Contents[11 0 R 40 0 R 12 0 R]/Resources<</Font<</F38 34 0 R/F39 35 0 R/F22 36 0 R/F40 37 0 R/F14 38 0 R/Xi0 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 40 0 obj <</Length 4383/Filter/FlateDecode>>stream xڍ:vFw/݆1[שU^=Kyu�j'1U5{f6}}deCcyhME<7>ps?qaZ(Z:?nLlNkit<Nqx߰|T1-0WfLیP tQqp>z?nRA{ & 4R8UvGSʻ05f]ݮV&ɢ�6?<sIuR6MYt> \gQ<Se*LjwTHe6zev7lpcanXELr}N7qkӛ?XDP%_ZocQ*0\ت$ N6[mCxqro&p?C/ -Ў黢jHb0H#@ʡO xjcMuŀBPN0$h OeTrOxlc8{ƧS-Mн!/xaQ8I^JK4Ǔ۶�8$Nکr,Nv s3fF۟�| :D#J8c5/^SQ#TbvɔDiF@ǩ~mn\[bTsXؤy ;=4_ qOй(R癞W k4;}derQ}�hF}76f U3o=`[ /al<-VvyBcI{ZM%N;b<]K'�C1r[q!j@j)Nmұs[=͂?C=֭fԟZ^fʸge<TArls~`M hctXSKTDM#mg=3C[ DՌ%/\Xu.Qt쀓qS2vUo}r�jM] q.,,mhtB8!�ׂ`drҩ8a7,>-@<- PfFQqawz,s*qb\" 1KvX>E{|P%aT m 4ra)a|…s nk5/khBfUZcNbO2^} Ԩr )yIa įImDy<0Px {BJÍ1ij.? `QAĠ$$h\sn}<\OE4  `|pxztȽt(&.U&X@ĞY]Bj*Và Teq>4?`_}I B we7XFyXe}_!͂1ͦXRfX@ ܑd/ܼP97?\Y5yV4-Pߥo;[i? r�=bʡ.FqvbPڕ= *"v gV,gTn/"+=((UN<P8#. .xH@"O V {(;m;3ޔyplʡ.eƟ1WgeI`XECNAa+3UNvC,ϬS#3@>�=Q@bZS~D!6:똑} 7N t�cp{*ƑɡQ.G7s}[K/jXUG<i_n^REǠ$ 3( UG*j q9rClj(?b`w H"AܰI$A?-)n[h/h_'Yɂ*J\xJv{yz}όj;9*Z$8&X8}'TY$0QP502'fH@$~ăC /D^EU,_3d'*#1 $B%T\)|W,8zV1مssLiWRЊ Y`#Y <Yb r.Cvc%?9A2vJ|Kru;?~:vbbK}-? WtMwШ ;vɮ$Wn_߿a;ݯ*@}0G//]~Pl"MAsc?(hP�o`o##GͺnoOH)~ Xpc= @25w \eou}1]{f- 1:(ԣ"M\z*?~" ZKZ4W3D 1o$l`gBio3] ܲX[1a@M<Q\F/9@cffCTrOŪ^V.A ᭁ.A}=/dKji`3~aFp9-hB$T.@tm9|P z$lQ�M5WDtr�vD1tW]QH?-ªY6.JDXwx) O}{o b89,e]m-v9'paЇ8q&6X~\,cR<& ^c.b&fq,R/4gcԐ7<4{.(Uy7y)Peey>I V<aNC;呿dx>肪Aeʮ( S߱u܀7,�q#T2Hl4\ x7LWZ-$(*O%+.kvI>ClE$/i|t[@׮)dWNwdEjygG]39 WX'+,\ JI~1@>˜b>3l<ve!"FaƍbOCHFItL⬯9#S^{Sȉ�xjeRT%?5pqV`SX:wm3z#za;[d}{q%k+u1'`"q;a{@G~.&<^VΩKʦa1* di|1#JC/#-⊵MӊSjpE&DtKr&83SE'b~~\3d4?{e ҒٸpDPl%C#<4y%ŅđD-7IbW`x]&G hvE+'x¨:U>T›ɂ)X2vǭƎt fQՙKc8A0f| {MsboafukUFY?O8S$cjA֘aSnې+a7`#G s>#\bRсs:F-b ,r>)X89uaΉfο}%8o3)XAϜG 89`D&%sH/['->޴Fڒ-�Bw :LE*UxjUdP"oR䚐MN@WƘ8ˌa`K莅gJK*;!y3MWۥM,$;n%$Ғ* @L |�-,^}ͰVvF:*(4Pem𳤄o%S's:vLFkEoi'3tl1.jӌ]<17 YlOEƮ|'XH6bg nX=+P)sZD"\Bq@ ŹFְ0"(Rs)O:\[^:oBe@c.E=�tmB]ws@ M퇦/YIkޗ>/d^<x%˛Y'<}?˛Yj/DAfŅ{Dd#}e Oē+Sή_!l�ܗ\?ҫ(x[P L;7Xzb 1AN4`7K8Z.,(Ӑ3nǐ8 PcgjN*`HU# P\], Noj3~pǔٱM/!> %V6>~TJ3M_Íٽ˲8/ffוQ{?g?.v(7~vϒ.R#_WN endstream endobj 34 0 obj <</Type/Font/Subtype/Type1/BaseFont/DXHQIH+NimbusRomNo9L-Medi/FontDescriptor 42 0 R/FirstChar 2/LastChar 122/Widths 43 0 R/Encoding 44 0 R>> endobj 35 0 obj <</Type/Font/Subtype/Type1/BaseFont/NCLNVQ+NimbusRomNo9L-Regu/FontDescriptor 45 0 R/FirstChar 2/LastChar 151/Widths 46 0 R/Encoding 44 0 R>> endobj 36 0 obj <</Type/Font/Subtype/Type1/BaseFont/PXOHER+CMR8/FontDescriptor 47 0 R/FirstChar 49/LastChar 50/Widths 48 0 R>> endobj 37 0 obj <</Type/Font/Subtype/Type1/BaseFont/SUQKZJ+NimbusRomNo9L-ReguItal/FontDescriptor 49 0 R/FirstChar 2/LastChar 151/Widths 50 0 R/Encoding 44 0 R>> endobj 38 0 obj <</Type/Font/Subtype/Type1/BaseFont/DVCMRV+CMSY10/FontDescriptor 51 0 R/FirstChar 2/LastChar 2/Widths 52 0 R>> endobj 41 0 obj <</Type/Pages/Count 6/Parent 53 0 R/Kids[39 0 R 54 0 R 55 0 R 56 0 R 57 0 R 58 0 R]>> endobj 59 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/fuse.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 60 0 R/BBox[0 0 390 151]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 61 0 R>>/Font<</R10 62 0 R/R8 63 0 R>>>>/Length 1096/Filter/FlateDecode>>stream xWKo9 ϯm3qlv@=8$vkg;i$H]ɏDQ`r\}pFzT6U;~4d>95D�Q8Μ5AsV8kgX0&5hk gۨMmuc!HmgU0FF-YRz2_t#ɘѠq"oyr_W>\#" L͚kX]^:+J饦k�,+KM6%P $:[pPb룘yJ(уn>&D�\N)#] IqC魂*5 _}~T8$|8E #$'|<rx"1B u trk:5%_5J1*L,RRC(ۉΫ!BYf 2"4ܦDAeBʡy6cR*iNV~ 8sΑB:(пnP+&){:<tӉ+qp䔚͐#z/_ehb8Sz{FqY؛9lpy6&>H1h_߾ia`[CsYhg6OikhƂR BRIޏ(wVtJR(nFYSNCYSqWKL׿uع/>:tJC:u1\]slw <8<γur竤@Qp:!ƙ3@l%ǜ !o߶q= M8{n7۾=<<;Zف H s<?)ځ%wNe{8+�O'"`Y }5M|z7HJY\r\'d @!_78>qtًN{&[ID3W=Ͽ*ÓJXŖZ߯nq++*SsCqʪ~93ҪTY&eug_f~tpJ endstream endobj 64 0 obj <</Font<</F39 35 0 R/F38 34 0 R/F40 37 0 R/F57 65 0 R>>/XObject<</Im1 59 0 R>>/ProcSet[/PDF/Text]>> endobj 54 0 obj <</Type/Page/Contents[21 0 R 66 0 R 22 0 R]/Resources<</Font<</F39 35 0 R/F38 34 0 R/F40 37 0 R/F57 65 0 R/Xi1 1 0 R>>/XObject<</Im1 59 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 66 0 obj <</Length 5400/Filter/FlateDecode>>stream xڭ<rF VÁL3`=Om%>%7"! kmן�rHQKU0wv~zW~8YLgW73-gZDarGpR?ug8uQի{(<nJnfUrwoa6g" >W˲Y?\0O^qU=ND|9MɩWMm55?lU}MUoFWu_uZў(V_U}m.諦Fx,ؔMӮzQrRxԦl[gAy&Xmil7�XvS`ss{ <#N,IW Uw \q; |\)V]åYvp_T+3csO?Sh\o*J/%?" ltVצ9EW-pW`4b/YsǢlD@*KQEk-:r;BɡZG:�B @oew 쌠AW�%UEUob- I _8tmڲXr϶+aq2R}F 8X4K*;4E-e[!=tffZ}8N xd t)B>! G#=w)6[5ɝe|d6B`\SdX/d(AQ]E{͋f9,@l3/|a*s2<fhjOj;* `uS􋻲ʗjqg%? 3,Vۥof �%)"푲�o8X^/Ra-dz4 0GFN "ZD@Q۪'2f#Q9 ٰGj\F7enAF1, /e[bY3ɎJ3,uC,2@TtB:*, ׂ%Ff|`F @Pw CEQ3j%bM3hD6v g LgYtX?ϳ02NH]Ûjv;奢YWP尸V4G;?C-:0 c*p<=rA,m86 c %eVat@YB$Uzl?\QMf�8dE|4&&VQQOxU [cH \`:rK.rs4,5H3#q/Ԝ` !.<L�d�C.^Df:P,@Uk u2ײɌ7mMBeȥ+M6(8C&(?@"}7RQlG̓sz[5[%ݦ\T`qqeJ =w,IQ$`^[64,rۺekeM\.հJP;VDY>6p*ؤa@tG:h(H?,?D*vY|yE4jvh*<{ɐ#m2L?O(k3xC6}쿔Pp6:x, aDs38e,DLNNb۲F39{wўCsRhw\4_p϶$Ad0J8yfG>G/~v7Q(1HA\BÆ26V D= n|;W34~wH+Ԩ(�pGu:q"ø! 4G>n:2FbXLD:g:aY fk`K0AB; ZAsm({-01q!H'g5RkWM[`|7Dq- K{.1@tTTam4ٶq_i{w4 ^Pհ�ڲVRccsw0fkl'9n쵐C4t2mv* s_4ETgR˽W Je :٤b$psid0žQQi`iRavs�;1ÈͬI`)BEIE4 m9G+hĀ{& 5�'y9{D)X&Ca�X|2'' G"u<ڋmXCT;Dq'cEqsuo(jW0ȱow3J2 M^A"c|03d̯ 8!t�͹yI,1vn`5\ՠqhd>0JƷD 6OGֻYlHlcfBzlBпQ!8Q%sN}gxnx753u@Ǡ+|8(]WdhdG$W9IjL,>}$VDCCcy1o@*D{cP`nףB/^lj†( ܁h5ӱT7  R^퇮]&; pUt.ȢȆ0$ve1 0v^ N}yEݦ-M| Q)EHbiq_p_lm 偰]m.m(k  6)k&fa|e>*n]0q-xM"Y/Dzw-ZJسMN4L+L: :4WB\4x`Y4覥` w|�ӗO`$Cm4]C ǀY?vd<|BUe3&ҮFy>:YDinse"=,Pb쏿`0qgRh]>qϒHM#8Bnљ1ʤSh(~XPәgi.J&Ct+6ruUpay$D CLV߂'飡{<( Yhff4hIs=_?n qrro&zLߣ~(=�sZfY >P_wp R["(wt*8}2H ŗE_&ĭx7/a蝏(01�?bpL&i ?q~h!#Ջ8&K}`\B @(cH\ (IsgPRA@&vYgqFR_H> %a;0/r)Z`م#~|"O>y+zi�ly{,1 xv9 ǟB!mS5AK^,3͸I@37~Vǘ L(}@pțpiV8ɎFX5imaXw-YQ;ҷF(9Y3|p V' $�{yD>ZiTAAPH}7جs䫉6('£L] rQ6>~4&;Go1P-P.A%ƍmer:m?y?AWIyy(;.)>!G|`$;eQF 9Ȉ@y/ku�O^ڃyw)V/ O!'912,kj[K?U1HѠ`鈎P*<yNOݎv\i$@DB.o?c@c'`|S-Fw* Ka zUwO.ӝ6~w,]XI$d1@xq~x<vi"1B}q*Ld7z8A�7=&jE`~(Q[y\^9&Q}_<Μ21uDΦv]w;{ݾKM0JbW+ uˌ1v1t@<‘rq@<$̐$O|c;Cy }cx><RǾ)yq0to_*8S] wjM={$ p@"߄a MƬx[uɻPmo#;΅b bSޠd>0ƿ| Sq> 0g8ǽ$=u2ܘX@g G rX&PkA3a{g"F4Ԡp ۿ e&E':e`fJ)69^|~??yJ.oYqB$8qU5-"|+7!IޏarTNN_wtӱj9 Kgz?` LcSP05P4fRnG0>p!c}l#N3]0O$"&zRDs8d$%tcIǢユ?LMf)h|1c~x8X8w1"lmlnfԖ]ڬiҧ#w&�nXkƤF={Ljd,E0V+Gw?WKp*tdzrj:7Љ4e^U-r]}΃n5M.j_cI+f&w| D=$P3Nc4tC8|ƌ_jͣzu$⹋Ҳlu|q~("^"QC:T Rs>fϵf `.]X췀Q;FNjs`aBMu"t*T]+,$^ݕh&ݔ}Ia1_mf|?"L#6LRL1{ZK ;ٮJC'KyF7`/c?y"*cx IrzPmMJ1jHGq͹0 ÷TX҂2,]s^X2]F@_nL AK ?3'fjw&%ouWÛZRnО xvJa>~K);;fah{M_||ld0^## 8"@)䓻&[.{}L@Tu�g0JϘ&y|^ܱ<:ZxKjnۢޮ`؁:VKW]"]oxk%R {y#LHz_m(P"9GoXLxm;lx! $q@e5mA*Uᩤsa@Mleqi.<-Xښ)7ANq92dA-'Jq2 YQؘ)4CUMlKf.'O7B endstream endobj 65 0 obj <</Type/Font/Subtype/Type1/BaseFont/LEWTPW+NimbusMonL-Regu/FontDescriptor 67 0 R/FirstChar 40/LastChar 121/Widths 68 0 R/Encoding 44 0 R>> endobj 60 0 obj <</Producer(GPL Ghostscript 9.19)/CreationDate(D:20170131180939-05'00')/ModDate(D:20170131180939-05'00')/Title(figures/fuse.fig)/Creator(fig2dev Version 3.2 Patchlevel 5d)/Author(Bharath@Bharaths-MacBook-Pro.local \(Bharath Kumar Reddy\))>> endobj 61 0 obj <</Type/ExtGState/OPM 1>> endobj 62 0 obj <</BaseFont/ZRCVAK+Times-Roman/FontDescriptor 69 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 0 0 0 0 0 564 0 278 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 722 0 0 0 611 556 0 0 0 0 722 0 0 0 722 0 722 0 556 0 722 722 0 0 0 0 0 0 0 0 0 0 444 500 444 500 444 333 0 500 278 0 500 278 778 500 500 500 0 333 389 278 500 500 0 0 500]/Encoding 70 0 R/Subtype/Type1>> endobj 63 0 obj <</BaseFont/THIFVF+Times-Bold/FontDescriptor 71 0 R/Type/Font/FirstChar 75/LastChar 115/Widths[778 0 0 0 0 0 0 0 0 0 722 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 444 0 0 0 0 0 0 278 0 556 0 0 0 444 389]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 69 0 obj <</Type/FontDescriptor/FontName/ZRCVAK+Times-Roman/FontBBox[0 -218 775 683]/Flags 4/Ascent 683/CapHeight 683/Descent -218/ItalicAngle 0/StemV 116/MissingWidth 250/CharSet(/A/E/F/K/O/Q/S/U/V/a/b/c/d/e/f/h/i/k/l/m/minus/n/o/p/r/s/slash/space/t/u/v/y)/FontFile3 72 0 R>> endobj 70 0 obj <</Type/Encoding/BaseEncoding/WinAnsiEncoding/Differences[45/minus]>> endobj 71 0 obj <</Type/FontDescriptor/FontName/THIFVF+Times-Bold/FontBBox[0 -19 761 662]/Flags 131104/Ascent 662/CapHeight 662/Descent -19/ItalicAngle 0/StemV 114/MissingWidth 250/XHeight 463/CharSet(/K/U/e/l/n/r/s)/FontFile3 73 0 R>> endobj 72 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 3413>>stream x}WyTS׺?1pV(y؄j:lNjq" (�!s S28V^kK]۷am;뮻zk$$g~P (PPS>8K 00/p͵DA\p~y(> ή⨒Қ|qˉqɿ[vݿlٴykDv?W"vT"V_rJJsDٕO78PWYUW(%*)SB\Y]sxC'oA*:D5TH%Q;:*JvS{&jz^SۨT/,š^dBP9} NU fр6 ܀Qsᖅ8zoYܳx6h[A̒g`s\.Eg.a8gVMhZ%K%8W]V^ږR[RQ.�%+Ð3pŻgKqN5Z`򽮖3^.zWHr*YZ#nfU.B\~ȼH4z!7d|)h2;N4!1CZ 5}̖>Nw<lZ" BPY›@<}JX2% }:q뮀})/|#,|,JyXN4! B%.h8^={J|fEBК R ؍6dGMsX8PZ oobPB:``#> +_('+ ^i60o�VUk$r1z]ڦd.K8PQm=t#^8'Z(8 QJET0A0~Rbk]M2:ĕ;zɮ8mrnɾ3U,QeT?ΔTvc =vY>YQ|B2 ߔ*"YȫB6/ 4:Vsg:e_-5*jWpgL jZ[k!-ҘVqZqQcߤ U@E%){󇞈29,&6'jwn. qIG{' nArq [!ЛuCۊ DRuV"i4z!Lo8zPYY&D\yGB<3{ i,_4S}a)آA�*)"U1OdL] c:$V) gwQx7=Kr 5 IK*l׺]GN93t94/ r!̤5כwYgjͨ Zx&NmR7̈́u\9GT׻ƅ8)]w#bݧ<`fd {El/;SY(@,[17GNM&Y۫GϢS-%k*֩TҺ*KΎ -xc|/O/d2ލT* )dy5 g}Q %Ω2><޶ӧk;.A|P֋ :F*�Fc߈psAOy2?|:sot,yia%_T(MF !8\%oEp�˚SIÓ[ԹEAML<݉f,%~Yje mcO�nҺ\XҳtIuRj4X5}mHd"yyD[E`DR^8X>vg ^uEȶu^.yeVM=yqn\쑯U6U6ڥTG=R󲅩%n. ]}ןzo}Ǹ-4OiТBcmK56llBkd:!HHVgb!Ya445Sd Wi2 _gpř/.8IS,b0[v^h { I{}GN%Tt&pvn 8sS'|xwD%[Uqby\v'J޻rp:ǿe= /g/'˜Oֲ)@5#*V'זK$te L0=$y߬bgq0d}naV]V֢9E{{/Ҍ2aYF] y+ ߩQ~DNtOp0`#{_q"#;9qaC߳ J6d/ D^ gy?L^VD#lwsӼ14!9Wt9ݻ@hձ#2Oz'O"cG&.j߆9fVG27/Ѯ>8}926tUy]_/Mь5Ο`VWW[uN=-89फ़=Bkq:|֐m|ev-7< dcBUnhG+]}00W*:+:;ŝ"V*|xB>?/ ]ijꩢCjiI-fzƕ\2,wOɱ+UzT thLw C#u~?JM W\GW pŲ[7x8_rmKMWl/=1�GYgn7kZZ@""7i.Ia9&G`l@JT[T(/8L9*Q!Ft|0$56V?-qp:ΝK{G]mz .ȊKӣv(t[}=`p|ic]'q$(9t']R,Rv1fp2?p9ljNcX^,ˀ!Q-a~YJc Y}熑™N#5Yjd!vHEvJۂ an3g:Mz$lx?ܶMeNdjvl=]c'!8ޙ4x )lcIn<Ҳ7AUF| 7ގM-;(VЛ2Pr[4ϞR$y@ΞZi,]H>^HM|CӃUIfs XOdA_N .zh׋{, Y=j8P�I endstream endobj 73 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 937>>stream xeR]LSg>e|ߴ%M$f&H76 ]QdU TN[)?вwzZʿH7:݈L3f[̖xt;lYv}'y_RP4Mo4[L\6f}GJK m%j3!>&>.L)iOb[nwh߫:rlgA&ޫ=4q:v\X[du-9hՖ>M5f2UUN*]T SPvAeQv]B* 1u,05U1[qRuA ?1Ϫ}Xnh<$zy#$ `kAw>B3&1i#^uzd9Hځ8\VˢB[7ӽQ_{fițDp691wzykD�_u3˟c;9ihzsS_5 :]nGޠ+ƚC{J.zi̻c#XN^~ޢi$ٱHEЅ)J$ܺ=bBn10=!΍w&4>2Q1(3AQg\`=>zI= E2ÁǾp␫DzL[  3mWVvyt$k q%/:^ԙ<n@:2z{aF]s4"^jKݷ{f/s Mv}3O^|GbZFW$eCmB_ mB%e4& \D :U@ޭԑw ^GOŐT8aI,~=|^@ 7{!Srw#"8ҏ=N:/Eq7`F%c٘B AZGPXPmU Ec%L endstream endobj 74 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/queues.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 75 0 R/BBox[0 0 825 521]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 76 0 R>>/Font<</R10 77 0 R/R12 78 0 R/R8 79 0 R>>>>/Length 2744/Filter/FlateDecode>>stream xZKsWɕqrJ$]9|PQkJ H9)|=]`eGK|hs|P{^\~P#!w7 1GƏ2qJH[VZ[\1 )`Ka,3Rb9 A'$N> o*v`͙%[BzAES3z_a^lX\&$)V0p\nKgmNyrz᱒B1'L1"K QۘtD�I*hR*Tmڐւ(ʈ +<"/>("`PDR9Ʃʱ^`QkY’ c|:G/K*o<$Q6ZW"Q>h[!ST^82�k'4Ȁ*i%[$bAT,Y,`J3?%u^O_s9{ԇaG C*3=rpT=qE!я8YYrd(a*Z"SO'M&jRUA*APTTM"Su?!_9$nӒ]O5aMa1Q&i"< bm|"fmEHp U BL1ZۋY"T? 7.\r6u!󙘾Bl@x&rWmuαI א:BmܫpSet[`ѣƞ?g󁆹 EtD JTM/ )D=QA:"*P#b'V �phzLLJNo. UbSU^-=jT̫`ЎWAx5*B՜Tj mTE*5Vy|Ty]8y3{tŘΌsz2125$|S!Aٜlʓ졬(Z?w"dNؖH'w\E_K˪{\L]=1Ū"<W4š<8Ey:77!zLӅ DOB5->ԑ3zRX [1Hm^\z x b6e`^^ƥ.̊u+kv,xL/Cv-,c2Fx >tp ,h4=Ie{<<mHT"1?\C:2@aZ Έҙ8/Ar 1҂$ƕ;!}i|'`T.Lh1kHSy(UpiVU ˊftN_wCF*gXy&cd <cpHMʑzRw"%5-ʪA~SLlCt[pwt37.hʖtz^:]R {ؖ~|(a4ʔ9  C8 o2Bl+.eh~< yaZZff$"BzPƚL@W Kz]GAX!̒-c|W)A%OA0+rM}DK?tzʙkE0cR74zJ+}ϊAY_U<I 2yEՁ*WE~!ġ+B*V`*4qMXS +~ۇ<g;LFHzYNՊP(4D }&Oj¨}j r@gwMX}_O\=͵qi嘕X"٥+&,\V;œBZwkGsOr4s^\LaWO|8`J"8W8>`񽡐(a;ܡ/.-(q-٥2DqcNt_cz&݇oSI{Fj wK Ew}. 6|{VL3㻏{" lKμ5=;7~xy?>$8'%˗k+%#(//Y6Jǎh}=M8ڻ@6C#QˬϽqzSq>`G"Ga|LOA҈mh;YGu7!z93&fw,ϱ~}]17uR#uX٤<vm'76k^{uf˂I_}ڠ^ Wt>ono;%*w}{߾;t=f?X)x~0g>ݗ~|]´ U_de}u9̠\@wߦ/ۯQK|if%>FE6ȇ(>O-%D-<D-Q!!.C5-d- n,Hu F:ݹbزaEυVbeC$y!'SPI%MMfr'_6.];fRK4\ҬASo"SPZ5y"@TN@h${4̹m:@󎶸adGw lһ 5ˏkmZ7|&O F/$]CjVQYCS禷:/e-'*F:{yjŎ (2gb^~OS_}iW3UW*~y\-Pu~ S*& endstream endobj 80 0 obj <</Font<</F39 35 0 R/F57 65 0 R/F40 37 0 R/F38 34 0 R>>/XObject<</Im2 74 0 R>>/ProcSet[/PDF/Text]>> endobj 55 0 obj <</Type/Page/Contents[13 0 R 81 0 R 14 0 R]/Resources<</Font<</F39 35 0 R/F57 65 0 R/F40 37 0 R/F38 34 0 R/Xi2 1 0 R>>/XObject<</Im2 74 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 81 0 obj <</Length 5191/Filter/FlateDecode>>stream x[IsFW0bS`A KIfݶEl@B؜_?oKlLR#RHd&r}⫿L~GyT_*b^~ޕMU]]]JwK5=M=$=ĥXqo*U}[5׿,eT;sw^kKC5 uKr7R~Υ$laF2Tuu{w6ޠv1L6^ ?2np(S'=ǎյr#=EY_:qWb`ri4Xі(IDNvo#D5Sm`NEv~D`>YqrO^(,3Įh$`JǺ |PIMgjW buڇ:K*znBxfr+sw%]ӕvg+1Y+?q~CoȨCtUv00]uۢ<_Zy~/9#2�櫲RT<|<0C e;rR9%Nx`/:+Ǭv�+>Xi䙵vAɍtW2W` D''u(,IȃdUkHM @8e&=BPEYM] /#ZC-/ˮ>DQZEdm*}e~:@/оRN#IEI]ݎ$[I$r"=XLQW^tƣJAmxq!Ltebtb<n/saDZ/ڡ)FgPUT'"!kSZ}r#q7D߇?l$+j@ha+]WFifZ$ju+z9s=k�4ԒVx亮2/԰F:OtAE~Re[ SJ]b=A|O֏g/v'ŞB@st<׮HA&-NiICBMI$3f$j'/1%FOZ=c`;{HX 1&H%?,7'dw#3֡*냐Z+YܪƲj8w7$(ts*3оB@�d--~_!WJYP]LGWϿWhD&ϮJGy /.sp(̷%g߾rK" tg(:~"WbK#dlx#(3jX*D4J>SfIciC`8 VpJ�(NgX%<Q9ʣ)*GHB+'/]HgӉ8T>ܯ5^X�gPݥԝv6%ur^.a5*}ցEAv]ZMB{ӇXac<Є**#\,.hcWRA"/90f[O`C瀣259%<ă$*#tpD,Kx0kmMXR ^) sf-9MLXѬ1 U.-&DvU~X `v+sB%*;2*N+;ƮL+u> -w�= Ӎq/..:qVٟ u ~ʾ>E4X5a㙖玗PŠ0A E?rMxȎygRLwc1>Jiji$-BF(.J5,|l:x<x|+c�. =3? Y6 'W(\M-v^<Ry^T+溇oAHkא(g7=^t!o^@ ;< ] Ԗl.P۠`*Af7'!:|#T=X C`GPV_ $J_%N0< >@Wrg`dru! *Tz ȥrr_5]?H-687ЖqF)ޟl b'ҸQ@Lg87/Y Y*L}nW>1U*TSDXӖ[ O*p1\XMb.f`%ǞZ tH17z| XK}M*,;"\P�ýш y[M$pZPɷ|xUuՠGxl𢙠F=w/- ,Mm|!{sjIj5b,xx/P5Tzm3fo55z<itdϼY>9l(Ґ3Zb)˽i~܋waAP,P ٰ# OL;GR̸o܈t* NkGf�&ɃqK3/Ue`׃2oQ'45rG !Lb!"l4,LZC_"<Tqo- r8A%�tdCؼK4G%}sJ�.Yl 9aNB{?7, )ͳ9#HdkxܛC�Eu7k4}}W["| !Gf[#b'u<^fFX`df-^8pyta,eyf=G\TRY~CQg}!ˡlYV4-s/Dpn&VH 3U -l-,}:HYt=>>fκ p^Xu?:i3Z�N| _ra`פ Sv48g 6=\zS؁70 Bz\c}xas7bxhp� }-]5>VX*I QPǛŷQ&`qQLW=bgyQ&T~Wt <~ *<gD1hK[ᬯak7ցLPQC;9B5sPx7z*9dK=UO7Z^yY3Ɜ4T\>Sq˭c҆\�W/:p6[58uZaj EP@KswBXQ<CGDcQ ~0, B4^s]Y3'rf}p@Ԃzd)J)Ψ~#x�z9EN}psιj &ZOڬh*I:Z'S5~�_Aڍ6�07줳`S4C9 5IBTS`uWеSF+ZyQV(AˏP# ܏=3~#ࣕw*›h|""DD]=?SF\+e'4t*gJH vcfsnD9$ ʹS�P@NSP_a|+B+L kY|>*ن͝1x߂*-( {^a !-,8U#K^+~ajjM< dؿ=Je`hI أ>-e)/Q�n̈́�6Ю%53B֗GІSs$QMҤ{LM&`y[7o.l6o�ySw.$3{u5bxc)Tg4$B,AυxAmOAy2؍h}b<!©}Q9o^ut!/R#X2H=S" ;m( jAբ-XC#ѷ(ȏ Q`kLlXpbɇ3+Y㋎+~JEfsEQ :m+{rKw/hY0,Ũ>8. yU%N Cj$K/_ .Y{#s2B,|n|\[tq }ԳQǘ<)'7åv-f` ,f`S̓<09@:JfxIpg3ҽ0q�g|Vxx=|Ei1Wh}y\qdl3Ղq{WaJX.`eB GbJ+Fo{Rr`АU|ÎwJQZ~J6a:Ox7o.k dPl7ҁ�j,i TZCv`t<2SyCi" YdJ P<I^d& %$eO~r$dMb"I_OIR:<OZl6Lp2=$XlC6Pveqpmy0^}T]OD7j>l$Z^#U,o*Wv80OPc��>y| XD$,HA[,j6?F8ݪu"g*{ '(1� 6&2\lKl"ab]EYlmR2}-hpN]/l}Oy;B}JBI�.f ֨U d0i~q B_|c=%J܄)& Ùc["SQ?•-A{0a�lŵLR�ۤs)`Mt"<TẃOR K'ZO E IgHXS#3' ӥXۅR� &5Yt_(ޑMAO=Z±ەn;rQ9b^_E~ Kx??|_sNgTG-7LXF[vsqB/1Dzڳ=o1 4E'Pa߬os_ 9z*#A RbI,6;ח|_&)$V[*^냯\S,UrPo_ .37k M `Bj|b), c s C_TY/by28+!9^37hԓC'CtWTy endstream endobj 75 0 obj <</Producer(GPL Ghostscript 9.19)/CreationDate(D:20170131180940-05'00')/ModDate(D:20170131180940-05'00')/Title(figures/queues.fig)/Creator(fig2dev Version 3.2 Patchlevel 5d)/Author(Bharath@Bharaths-MacBook-Pro.local \(Bharath Kumar Reddy\))>> endobj 76 0 obj <</Type/ExtGState/OPM 1>> endobj 77 0 obj <</BaseFont/IJUPVV+Times-Bold/FontDescriptor 82 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 0 0 0 0 250 0 0 278 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 722 667 722 722 667 611 0 0 389 0 778 667 0 722 0 611 0 722 556 0 722 0 0 0 0 0 0 0 0 0 0 0 500 0 444 556 444 0 500 556 278 0 556 278 833 556 500 556 0 444 389 333 556 500 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 78 0 obj <</BaseFont/UMLCRX+Times-Italic/FontDescriptor 83 0 R/Type/Font/FirstChar 70/LastChar 117/Widths[611 0 0 333 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 444 0 500 0 0 0 0 0 0 500 500 500 0 389 389 278 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 79 0 obj <</BaseFont/YJRYKM+Times-Roman/FontDescriptor 84 0 R/Type/Font/FirstChar 32/LastChar 84/Widths[250 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 722 0 0 0 0 0 0 0 0 0 0 0 611]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 82 0 obj <</Type/FontDescriptor/FontName/IJUPVV+Times-Bold/FontBBox[-21 -202 806 678]/Flags 32/Ascent 678/CapHeight 678/Descent -202/ItalicAngle 0/StemV 120/MissingWidth 250/XHeight 463/CharSet(/A/B/C/D/E/F/I/K/L/N/P/R/S/U/a/c/comma/d/e/g/h/i/k/l/m/n/o/p/r/s/slash/space/t/u/v/y)/FontFile3 85 0 R>> endobj 83 0 obj <</Type/FontDescriptor/FontName/UMLCRX+Times-Italic/FontBBox[-72 -209 629 662]/Flags 32/Ascent 662/CapHeight 662/Descent -209/ItalicAngle 0/StemV 94/MissingWidth 250/XHeight 448/CharSet(/F/I/e/g/n/o/p/r/s/t/u)/FontFile3 86 0 R>> endobj 84 0 obj <</Type/FontDescriptor/FontName/YJRYKM+Times-Roman/FontBBox[0 0 702 662]/Flags 65568/Ascent 662/CapHeight 662/Descent 0/ItalicAngle 0/StemV 105/MissingWidth 250/CharSet(/H/T/space)/FontFile3 87 0 R>> endobj 85 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 3609>>stream xuWyTS׺?1sTL(Mx8:80 `&B� 0(" (uXmުKk}>]wVI} < cAa@a`(s=AϛBC7x/&^7ރA3 &h[f<G"K.'rxo EK@.Ȑ32f$ s lܖ=(gX;/.8DW.<vHl?ADO,$!0 ećQb;D v]nb5XCkPb/1I0ƒH&01Ll3�kC:ԅSvzW bOثk=p;c| 4u{(d􉞤jqssAG|`{sM2RVXZ ze% <}? '4+8΅M6 }Kf%}rPg(4@ :N$SU4IP^QUUhv4!iI}D{|=t? .k\΍> nǐ9$K䧸RvQ7гg~oV]Fx芘uQa&2ȲHz%$a G?9a;ǧj8ot-_JlTb.`r:f-] 0} ܫMRYd~E-y]p+ss$GQQ&df({8mx@4lPRh @ 幕Ǫ EEh Fp> )Hf^f< ooߧ <ժd~7 1„"U O(F)^.69\!v̖^~ 71-LE c<FR>n7) QԄ1҃z ܖ꾇v"북u`죴ԨQ՞㻵ࢿf`-f{slRTDdbvK3S2y ĢZy$&((#{k+G B4G&ntVF;%V8vrLph@WU5ek[~\jWTdxq3*!ר O(P`-ɦ!3Ο7O揑5M꟝j;u %GBbLԝo ]<GNuN]ՂI*] p2s. %E*PfQUnP[?8 sbvF{Bk[YUK%zDy㛇%YnwikH<Z^KKC t$wܐ (1& =! u=㉫*fi+%#voN/13j�FG$䅒!BdZTDE(@! --䚑?v_ &*(N֍ ܺ|I>treQ c##D߉%v1͉LMw  } a UKEMXaT*djAeRf)0T4ƂhAkw20C/8A9L_ |:+ɯ"/z$0E|l{KOWXUjn;㼾ښDSSɒsbӥ 7sʐ}S^Ցf0\YWY"K"]wZ'6i j, rՊ:-&ҙ0 'Jt""Gs1yGPtk'zV?BdD9h|ZU:zϓǧn&+uL$%$0y�'ڄ7]7|]R[nPNeҙJLS ҋ��/>%[?I^~DQH|V & ʁ؅Q}iWz:<GsәS"_U*pWQޥg'!I/L�C/ߏ7Gmu:o�5om黾Zx @e<w\>v5&\0}%I0"V1B;ɸh<38ߓ?uFPm:.VO:-kT\F58 z])Nuwz̎' qp' 'kkQ$Nv \UIt<BHŋYMQ1|H$IpA@67x<@FS]r Vџâ3iD3-AgB nQ5i }#oaLXw={?1<>s6WV=M O;?_RsmYk j"U2 -?uhryi.PnEwƴ. }E~1!9'mĹh)5"gߑӤ}(�G@BҲ™p݋ぁ�#/İΠ0v_Tck1pP?ݽMdVhD(RۉԨC9KD +j:eeᦤ+)ǏOzx<qw֮ ,LO7F$#yI!{ۙr8~ NV%1Fx+*]>ךZk[;lg9Lh=)Tj 2ϝ*mU#WHl7{(D ,u&ތgq B<~v~j=Y8 #\}.CU�ZMzKS',-PEr^ r|=7N(J<58(s(gdv_NY' davN]Ɣ+K[Ȃݰ\;  ,X̖Z/$2OSUVa+.u>n_Ùctт?u 7{˒O`jLm0I:YPb.-/5y } (XBv&*"wSV9(K SЃlqF> bj,ʝdؤA[nZ);\  2/41bGN1-<M]{ 3E]SrZFHqzsw >(x4|VgQIvG_&7$M%I'ǟEawr 2sLxRnH$/㦰%>jf3P\v Ӽxj'V6n[lz[EKo6J`R*ۻf[Smfhnץ*e{dCS]Wzpb?C۹𜺟0v4-(:)0[1̭jՂԄ>Ff:j:`oX80[Dy;M&SdY,Uf  [ endstream endobj 86 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 1605>>stream xeTiP[}BۢQ,ՎҴ$&񸩱.b1Hb�I.}z-# $a}!ıcq?$G_Nt~sΜs>q8rU;KU 2~/>Ã,.dY˲{H}i u:ɚ/W׭{#ySmkb6JӬ7ԵzTii^씔V 24ͭZ~äX1Varۅ`X!Ųұ0W$MppN==k@=h}Y SZ)(+Z@H :G^C`*hlǎ"fğUamsDCNh;xN߱Ay|.|rfO�X ]N|l.ڠ[?$00b5IOrF>Gsgtd݋3 !^GcWo:Jlw Ύ֕7TTZx[9u;䩫g4|&qsok~^>D%`yI[0Y,j/aBhFwG#0e~6.TOo^�%1(@>. 4ԱH8>OE K :7A/mM^Czݽacؕ�b,>81ё4fM YSSSd9졶Zޭ]"cF*H6Xv5-;ۣ59(>;®`ts2}W.49%vu$! 8Du.<$/:FթwQwGo:y+YO4# cl>lI/EdItpG"O[5=ñ`oà;"*'፹{'U]%Ӧq%#_B/̸6 pK'ձbgɓ{uDTTgиusnM{,~Qpwr`o9`%w30&] n>izwo-}; FlP?@1!KߧrVEa͖ $?%9Y0U9E{4>=TE:D}�}^O `wv˙ ` ;vL1 ?T ǧ =bZ|q_8X}(Ư|1!c¯hW!nֱ hs6@\VĆT1u9FE6x$:ˬf60.ݿpb &4pYءPIy~)<HR(w+_pQfO{繻RMWIiWhJ[ \Έ`K Dt_8Փ ,;3M8S-PEnTu`lH<0xh@(_V)Iva끐G60HE) ==, C endstream endobj 87 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 382>>stream xcd`ab`dd M- MU~H3a!3k7s7ˆrB{w``fdtu/,L(Q A($Ud\R3ԀԜԼܤb ~ > A9E2 ! ,,减}=gľ0~zeIg2kjm�ʰCe~\:$yWUh;adL{7[O#be݌+?&dѹjoFlX`kmǺmwsrehrv>jg=n&F{K'� 'M��|6( endstream endobj 88 0 obj <</Font<</F39 35 0 R/F57 65 0 R/F38 34 0 R/F40 37 0 R/F8 89 0 R/F11 90 0 R>>/ProcSet[/PDF/Text]>> endobj 56 0 obj <</Type/Page/Contents[19 0 R 91 0 R 20 0 R]/Resources<</Font<</F39 35 0 R/F57 65 0 R/F38 34 0 R/F40 37 0 R/F8 89 0 R/F11 90 0 R/Xi3 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 91 0 obj <</Length 5104/Filter/FlateDecode>>stream xڥ[IȑWgsB.@#y4=Vyx6Vk~ĖXXYu!\c""vϯy;S슸t{89ĉqc4}<|z껩={2USǻ=|}SNE ÿ JEP^gE4vw:_K$�Cy5Ϫ-MG pzC$i˱M1/hMCsѵ0e_!W粨<Ԕcu䂺'/k$_sQJsQ^KDUb% x<5p憛9* SIT~ߊK+?ǎ$˱/Lܥp YuSmo)),1e'7,{o:uE K{8]MA'Ǖ}6CDx7s8iA/LсOc+XA kI͏B5,ud0 ԗǩl@dçFi黺V% YGM}qHS/H*mKj8t;cV-�:쫡9R W| گJNhZ{hЂD!c / 7lë_^)(Mvj"ֶإEgy;\^}9M>SNE5UV2kSW6^ު L,ײ//H*VS*TzBW rDb (:dj4O՝Nte nj"#+Bƚ]+e0"1c]VοPX?.ݝJ_'<f5;IM[X5##<ƫR4ޡspz\da>c N<~'Y~.GNJc5_ 痦j/@D۵HF k<׳_+|C\$gL|j{HxUde[Œ^uхr㕾V{:`s `a!vr:ܾ48/h&cckFl\?eY'fgs'y}]+r6ùkB)()UԓKRgڮ6'N@7f*9IT%'Z( 4T22??_F<cQ•FLn=4BlgNn瑩?~ TG&/|KDTߍcS /ZPT `ZXM 'Rոfh~.iS`yFPIY ;~GS3zW ir\6TtZ=3*: r`3q⬟f U^{al*=WnFzQh6be<u<]j`D: #$#Ȭ@F/8F.v(FvH<D8 CwEq78j\1I=zM#vQj* Cߵ< H]G9 p5-v45\ =o (b8]@OF~U6gVt2^9gٍ.8oh: qIqg ,l@Vx$Œ6_J~4Ykh,ǒe@ciH@&+a,0rƵ(>]^M6�OQ 콅y4/D[y.]\O>"ÒGߑp(4- 1nHH!Kg_ÉX: fF勺 "{nV8;Ts=; \X.RdT?T�]4O $_)&ʆ=0x*2Xp,u2e@]o7YF2ͽd,T7)*+:R),g^4RtЯKa-N>0ڳ42�mۊ HṰ ±J&J'45eu{ٟ`L@/Ý#Ҋe Ckv\WW,w@"bzi>Af?\\AY pZ NW$d*S=rsoP9�&C__G&NΣ'$3p~d 6.WT)1 0�plG >ʁ;8ukqU=\N_8FvF`#4,S/H0qV HIHrƍ񡼛jX%Ki,dz3QΐW1^ˬgMJgk\-o3$~e)r[Hl51u+p̣AY^WJ@|a {JtB`TROGq1gcxg@0U!F-h,R{BG*@ɜ*Ar`ͮ$g^y)ԁfA\V(1*4?x\_%h#p ;3Ch1i�L:2ob `�lO[MH +Y,\Ip=^㔗6#8{oq |Jı�IQ b{k$`5IYc-Hj8ġ:2;PNlBP_s, ؕёaۯwuX ȺETNcw) AOs;pf$pW^dJaӍK*ۗ r\) dMā4=!)b"6LAB߀\>I(SA/]L(>:)DLze+>vpW*qжX0؊#424nҒʙVzd!`>G`Gk}AV9g4e.ǰH!3ސ`QS?%IF 2zr B%xR6kx'/{mZº`]y|"ܵ, &o"/eYTqƱQSc8.sЁ8f+$�0&1or.yH<V.` PqjӐ@B;I !I_@*` APBԄ[ pRAmJ1Z,|X), W QMj ^`Gx/Ryk`pKna /35ŸդZdΗ�jS�3eߛ@Gw ';JBL.6(:b GyBc䋿^aҐк+x4)0%)_v1͔/W42o@f�h4i;~OQpL~jgyPMuC:&`̣~38^r6), U`Pe>�c-h3I!F/a}nyҷي4VjPMԼD?3 ~?C]D@T'ZZ*7ET=@X/yq%8&#y | )wDPV(CC_ fhR<wLc<[_֤r`p73nVfmoew3;BchAvޓ`%g5I7"K$WKR�ȋC it˖CJF΄Zb!S V+lKd}@''iaUM["Ŧ+PX)Ezֳ6KD1�nru$(K8c\&[[EE b)]?+~2k9 W/xl^ߺ@fIn-92ٰ|N8:|\̓ym|NDR0"Ƹ9\P I"a?/]3*fT<T\/sb;}�i@y %$Ru3E ?ɂ�&gD3a78H }t򅡹36)}4wY2hHP_i"x WS&;RMPo f|c?!?bͬpj7E8)QpPkEtp[-+1o+ER$ 1:A;݉K<ݫvߨܣK�C. \0-+4T*i<u?,#_WnUaS|Wa金_8@AӐq#Hu geUSNhAVnR_̘g,y},'5btՓCIҳH B nǬ ;LDyŒߎB];OO9 4H3{P>a9GmNioqI?dO�ރ t!Z�tvN].WMP"i#)8tJ' B'l0Ԋu~PEEkwٞ Ƞ5ݮ-Hjg`/8Dԡ:r)bG t>$MG8Ik|"NJ :tTUO·H(Ҽߨ*cN5ST,YhHA,R@| q2qv^]or)<xHYko|ܶ@/nb&NSG& t`%sus}’ 5B,8Vo<d"gOH;30{V6Q(ED$n T}V\Fl.+Rr^J^YDRzX!zӬI o4 VH{.]83$xopzI!I'x<)~o,} {ON?۰(|k [?27h|rux*  n,Ε^<f`ǎ/2ҿrc͎ ]%>ֿw{ds2;N'BJ]7NbbK,k;+AHX3KyŌcn- O PZB`pe/l/4}O_V]6եK[AS9U= 8Y}Y!<6gl`׀9k:Xz|s(x1 endstream endobj 89 0 obj <</Type/Font/Subtype/Type1/BaseFont/LRICUV+CMR10/FontDescriptor 92 0 R/FirstChar 40/LastChar 50/Widths 93 0 R>> endobj 90 0 obj <</Type/Font/Subtype/Type1/BaseFont/NIWRQN+CMMI10/FontDescriptor 94 0 R/FirstChar 78/LastChar 117/Widths 95 0 R>> endobj 96 0 obj <</Font<</F39 35 0 R/F38 34 0 R/F57 65 0 R/F8 89 0 R/F10 97 0 R/F7 98 0 R/F11 90 0 R/F40 37 0 R>>/ProcSet[/PDF/Text]>> endobj 57 0 obj <</Type/Page/Contents[5 0 R 99 0 R 6 0 R]/Resources<</Font<</F39 35 0 R/F38 34 0 R/F57 65 0 R/F8 89 0 R/F10 97 0 R/F7 98 0 R/F11 90 0 R/F40 37 0 R/Xi4 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 99 0 obj <</Length 4919/Filter/FlateDecode>>stream xڭ[K䶑WmS|{o)O+|t`Y]TWe>4j/3GZGlD� /31CW~oԦOLJ>d& ({x~=ǽ-ݵOjdW/6NzkJktڪoM"?=kߗ hY{c"Id,(`&i(>_Y6f 4{9Hs'(X^<̋D76hhCiQ4{6euEIf[O=&Nݙ!&a,=A>dO)ܷ 9 ĺthޚ$b{S}xÍ]sį)#EjpfQ$wo~fIñ )6OW|XGikyhڮԷ)S95}]$4rl:'ݍ>g}zՇftv;q*˺}<۵H\,ǥz;:P0kb`^K3q= }|ևrI tNA~_/۱ff73+]N"֐"O'H"'Χ;ȰDGvx= ̃Igzl>VldH!,V%A$QjFKlY,g1gn2bBb^H=JLX Y;~:m {\VLZt{*ܴ&SD}ŝGڇFlh$|fc*:&ma6Z; Sj$@'vj b/^wI?7mf�=J|b{{5FkE2&j96Yj%i{ @AHNM)k9 !�2@hΐ3=u99`h̴Z^9+#g*p±u-D'zP 9?忟;,Kt[ D1J]s_G᮪gVM@*(<2d(|G.mQ.: 0hV}wu/w[w>dnA7 1N`GjZ.:aP {df!߭63wJmUa/i!MV!]%5y/!hw<txz2n׮~AP`Ӣ{1Lyt[C7oS4-#V @[>sxoX_`Xxlw[AZD6'^ܨn h|Krr\̝d~8ud>ܼ̌4dWalaEb9H 3 ~GV1RXj逕ʪƗk/V\!Y8 F^:~phQB~h:BK Ð=l4) h{z]_\hC6!iQoIZM �wqI:bbׇfFtkj8"l!E>{lpCH9B AȢ9+vZv)Fz2[oY&La(d!+ngbWjkhZ8c!¥?JR bS9 MYL@57ɕ= ˗Zg'CŸ}sir72KʪyJ+F2]n<j:0鹔%Wćv 0Fߗo"-_�Ќ%}hCLjEj H+chmK^c&@7 2H5$ ;! 9ytiZ̟?q!4HE *u>%\1c^Ҹ"SefAd쐶R.O=(#Ypь'�mm4( L6i0CJ.129$i!"Vc&3^b s_L9U[}&K6i˖\f)&ςn nӘĉڇN{A'j"T!ƨg-X-R⮑T5bIԧ / hi)D2Rm7͊sV9HivEq!̀Zkfp✍~tv^c̞S4yd_9nbUĂ{368 "6-}X :>jhWcҗY&)t6]{Al/[ 0[A|D$WX}]olzx!ژP:Xj3Zģ4sMplӍ9;34P#" c7jj0f7aǥOH qN߸l~W]Da#/+jlnCo$)ڶchr~W%RE&(rhb$)[;f:EPb%:$dv7*H"R\ RR~t0̭+X$=4c# NF1Im  0/uDRޣVIV0m]L,t8ɥY۸K0ECjN oZĥWxb,dGxrLeu u$([F{NL7#=˃ӕJ,Q_Ak‹V1W/M`(_IrM&&,n*5ٲE5"ӥ?O1Jz3F>>'?q#mĄeq$y9!2< P''<(ꓥZ̲ݚ7ճl 1uӸwDR>zH:ot õgb9B$Z?~Psih9 tOϵ�jh\kUPyF?ǜ97f|88ԝ3 yVa|G)ծ4t>> VkGÌX9`P9yy f0cVa+ggm:DǾHTٯnR'dxfHޟWJp>[[##+b0[Q{7rz6/ѣ̅#>Lys8Z8"u& RNR]&23 D0)d jx<w/Ҙs�bNn>; U+{F� <  Z% ,.u9L=}U(u$*RW^L^H =et �& |/@`;Y㯯 ?wӸf %,2JF$C5q0 /,) B;WYI^l}(S\PxpJ|/lDpY~ReèQ~ @"2!Rdh*EΌñjvs zI ȵI>T~f3'#Ĩ: +*8% NEHB2yop~@_ch~? 3 Hyb4Zߤ7ib2F-ktpm3wM;p*|6T_FRӄ;f&IvKXkJzA< gx!q)aQ#]dp!jCw]3p3ĉׯB!8̎3h[z,)�6Uu#Q1TR1@T$uٷ�:R<Zl?p܅ݺGKjy]8ف}uC TzLn_NHS#ZrtoAdn(R˃E"a_Q]]h%^^~[rwS&魣\R@r;RY;Aѿ**n$[;kQ{m+<Vq!x^y~7wd{Y~w-0ctſ㩫:ҩwi` BAba*�Nb$xcE"4H#4t*P'mwWro|6-+PlTR0 Y, ~+19R$Z2f0M0?*U %Hc݌ <DY|DZY?֎~Se #HBR?R|Qtb?AQ5<_^CJ7J�0~~r%N](]>ђ֥t*B\$,ty\+**2, h\V N8%zF"KT^_ ~6H\DA.. QR$=0)83,eZ.MDjkkuRMos2 ]zÅA_qn1ǃ3܊cXWeF Fx{ R+pĎ%qʋwF~N-b܆GvLn Dnp*wn 0|SWoRT͔)_4v+e6ȭ Z~UhRq.omR1W,ksXn"%ܓ=ya`hx$*~į rCgNSisRT{Ds=u^\ &SrUwS\^f$tmqu Gz& *:M:N~/ψpN|,+[w%[ួ7>r=eIFmslCuvB&lMn+E$9;Mdi~A\I1@L֍s %ޜ^&�BDAbR_nKN$%o d)!UKSyԁ;ރ(FI'duBIWb$Y><2f3/ ejwZ({Kks.pWL[Y#ބ\uHzf]n,KJǮ1W٘`Ş endstream endobj 97 0 obj <</Type/Font/Subtype/Type1/BaseFont/GINCXO+CMMI7/FontDescriptor 100 0 R/FirstChar 78/LastChar 78/Widths 101 0 R>> endobj 98 0 obj <</Type/Font/Subtype/Type1/BaseFont/IPECWW+CMR7/FontDescriptor 102 0 R/FirstChar 43/LastChar 51/Widths 103 0 R>> endobj 104 0 obj <</Font<</F39 35 0 R/F57 65 0 R/F38 34 0 R/F70 105 0 R/F40 37 0 R/F72 106 0 R>>/ProcSet[/PDF/Text]>> endobj 58 0 obj <</Type/Page/Contents[29 0 R 107 0 R 30 0 R]/Resources<</Font<</F39 35 0 R/F57 65 0 R/F38 34 0 R/F70 105 0 R/F40 37 0 R/F72 106 0 R/Xi5 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 107 0 obj <</Length 5193/Filter/FlateDecode>>stream xڭ;ۖ۸ Yvbq 8n;dֳn${<~(t73T_WAv/"X�q)ԽJnm<g߾V& D&#7FDa~!2sj+~tt}eB)`:?gBiB&.>|6{& Unia#,͠UmnooPQhdc[78ٰc!~'#:w>ੳ-=45SmN7[!yGq[nNU_n{wfoaI Z2vK:UYW"PvI^0m^.34G}]]DI$&ތ$$i*\H4(ܛTa&Fz~h`^"ؽ%Ym, sle$-2/^Gq\*ͯX/̻֒̓<&LrFqxBJ�_4m-&Jaee~JP?@:'|\+=b `N}C,ɭ"ywrl"3@RooWFMKq#:# \JomadHy{xdsˠvC~0R"&jmNw y�2@Q=\AI}AuDp,[قd =7T|kP628EpSa wA>j ʺuĤЁiT^uoM?j K*x,hC`ok÷oyTW~Kj7偓QV۪W-]W@m H*UH|HhQ@@6{cgOvk&Y61vw%OFM,�t}T8jnFH4n{noHQ֘�:z?:((c 6 z,I[BߡF=]@cUF:}f;Zq)2R�"7>J%HQ E4eys*_FJE܂J l%u[diz;w +?(9 /M%e ƈV~$,=bQ6vD{q - KhV1VLN۷0 T<xРG:x0a<?zr$[20O;O;\8L%fx)jh=' s43elX5^�ڪK6G �iL8%bފR%%xIMqOWPhFGG'J M�ԓd(6 _x2>vdHjH؄<' xR/d$ׅ6aGɝk֘Tӕ2;%ٗ˭LOh&F\�5A0[Wm :>[.#Ws?u&$#K&e s@0lX`/YZƐD$)iuSjgUK$>ߕ6h rZRc$ R g%y5 { 2'ڈf@3sm*4k9BM-r$on^u[j=9FO{7]c?s^ud7y"zFt>uwX/W% nmx$J>~;^':o2t H5c^݁tN!BE2Xӡ*goc|wWM9NGw&1~rѢ}͑J`) G#>GKHqϢTqUO58o~<>[/9?0]YC x:~d`'V-aʂP[W,UK8nf <E:f%_tH)1H \ǝBol~wE2{(#CT W8y2NLњ�Nb� bߥ8ycdd!l \/ĐA<H�̈́d~,79+=p+K?1Yzg}+3o}_ִ$ "Ap^�]ZkagB鯼9 %aH-q�?24JYc$dy[>jXu27/(@"BxӑXh Y3l4&KrH3:D2 �c<4'C[G^tk] ڙ@>C @ohVU1L!_Qi8 أBѫ[C %>!>bK%\>8'n(<t>4QԕNB %9u/!9?+!`''ܕwd3u_a9_9=GVa9E ҮD0IJ7v4XrEl\@C޳}a%#FйYDx1PaLfA%֥fmL!,�Re) |s5IfiS g6;ztSo{ox;}ʺۡYbƅHǐc?-Q-TM󠋺~d,\2T[z4HU`3z{2qgbb-w7` "ضoEPE*DQ&QmMX5\(Y6_6Fb)w�l`}$ [ޮ@\R$<3+4DFubZ;_ o,kiex%v kOH[9?-Ɇq}T܁z;çe@"I`7ӑc,Q&yTݸF -9-`2dD$*RN_cZr�Ep`K$Ga$&z 955D9 0�x5)ѣ@ja?4ccXE_fDxgC8\ J eHATqYN`c8zc2a[^8i&bEV6WYWC3XY,S`Rb`eyx0v̝hk$ Y{>Ն vEUvߤ̻SQ! $J%$o/ݷ-��n9Դ3쀗F(6(-V%0ϋ_n;s>|瞳tT* Y1?(c9ڌidK.d,yp8/D.HKYT/B N C A୰e 5Ǿ<`8:(L^QƳ޷)L 6P[t{4pBvV S3wf$/L8 du[r-�B(RF^t^Ov_(Y9W*`Lk%ЦoGGEy�U9Q9{PY ]XcO-<e"m=jD3~x`w?a_@r5Չ|\8ϊ"3n+9b<">Cg=ΎI.`<7-+K+))~]ĉݾkZb?`c>h;נ@tMXY#`l6r|U>668J޴Ww_8uy^\搷caas$af@ͮV='uXv6j:DY9c[C :!ϣѭCWo\.HUbsh"w9!cRaȓ.91g ;'T W軁TSQ !f`qRS~[_% Se$ˠsQ [pFѝt]GNT䓣.=}�fm".y~42Lѓ|`FgFTMu(8e<kPk3rx^=N<#z|R~ِ&NJY�%WGϱV{L-QsFEFF\zB3Q;{zG-a]b8{P9q^v�d%`kA[,o!?AY`6E.DfV)TJ {SjUq<40}WE}\i wR~\Z)&c ;{@d:5o35֋Bsgkp^\,Y'6z?h@w3 sa~:Glpsυ3{ֹL#UQ ,Z~9A(׆C1(lN{XL.3ؽyslvN8^dSRS5Qf5>SKhaH G%N`nt#kރt.Kބ¿%E2 ')yՖ7,X O=6.B&[,͢l ǹ/j_`)g: 0"xun#nڧA=l:V ͩֈñE!_*Ε{sM7XY0m><x. 7VnVY<[Ld(|HizGy[7 _? ._fWoG_'btZdgte=~QiW<˄eF+U0 SdZ:%5տQk/Y6ȓg,K&zp0VR>SE֘03t({'(u%)C\3!T|Q~AzF_8Ul2q_3@g)|'/(4ZW.O.N2eak* &sX 5] .X5U'5j8zOhAvH-`X2OT`)[:mlGf�L_BnMxSk&BZc9\.UITF}MXrBu k:ix.46S*S[CCM}D w൲sM]=}!4UVv˕u5C~6r׾b<5΋};.M̆?�3 ݗW8WXigR$I(TqGC[Zf<y,yٿ�a endstream endobj 105 0 obj <</Type/Font/Subtype/Type1/BaseFont/ZELEMN+NimbusMonL-ReguObli/FontDescriptor 108 0 R/FirstChar 45/LastChar 119/Widths 109 0 R/Encoding 44 0 R>> endobj 106 0 obj <</Type/Font/Subtype/Type1/BaseFont/HQFFHA+NimbusRomNo9L-MediItal/FontDescriptor 110 0 R/FirstChar 48/LastChar 118/Widths 111 0 R/Encoding 44 0 R>> endobj 112 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-reads/diff-ops-reads-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 113 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 114 0 R>>/Font<</R8 115 0 R>>>>/Length 8953/Filter/FlateDecode>>stream x\Mdqݿ_Kдc),K F` L(}ɺ6oGeee<Y{?|o}To{q'~O[v_<}|_}^|qq~G[{|o^}<þ)o=Gc<8p2sܧ>Շܻc;0n]Kn_īj!}j|ޡIOB?}?az?h}nO z c{ů|Z_o`Tÿ>~ cBr>‡ Ǘ%RX/?ˏeKW[[/?[yů>S-9{yЉ_t3G31|˘^.v h 潷2 lv߾ӛo_}o+wZO!77}g^nt^ooxzabm2 3^_|sdoE3vijҽo/_{9RĔtI񗼪WѯRۗF!:k"s+֣ٽO볯Ooxols)d+fn_y3G<Z[g_j9ʾ}sbHR\0�L!u.λ{y _ jlJX*VDc\Ec ,iߛU,>4:-8zx 1p*Wǩ/8fм>5AS$BoŻ'{R~3uc2O8sM>lGJ0(yce+'ǽ=jZKuL|FDwjOXK BL9:^Iw=[l#M0Ƭo?ŘM1]vVpA\lW;C()0$5B0S1j."# K}P<A绶PL],i*su1@՚CH 1c@"7a�O &aYS{ar3k oKl$]h35k0*fա̹i 64k@dq[æCcI1 /I5l-8{9H7jV8{Aϸ>7 iRZ{VX'NRud%;=* mI47]P!{ri91F'ݦ⪁,سڬ@{ P}Cbǻ(WzGN T6 1cVܞ6Lt MƷ ,CiGC9W `?L0\*C7GM.qqQgښy�T2@ ft˴YI l9mjJaAbŚլ2+1:% \D1@ޘs13;or\GDIn;07X9 vo0ݙ][!c(| orIPn{enS379Pe<™>yenJk)mm ԿM_M&3cueHq6%_*;cW:ml* b|7öM.'{emb36PW 6<n7{c/kӠXWgm`4p.Yr7KM.4 .ݬ6;@06P|bmk:el`26ol w Xnl p5zw6Py0W9'gk�~gۚ +bw'rl[3a-gHjnc3mlalBQ.]f mllalBK�Ɋ9c&M36SECqK {%yR%.z= ]eP!_?b`@t{@[`*y\-OC4 Ȧah0Nf4)W- TPy$;,`.4i]N4]QJЁ١z}4∋36~hӦHȋVK :^D Y~p# 66&=9lMliԦhЁVS{Nd^G^32fM[(yCD^`Ւ/ LɗgoV�!_֓oj/O:-2%.Ý|!ܽ| op'_&CnQ˱v Ͷ -?9T:t""H:/o?셀sc#o9:.=8z~>a;k9;#>l5do[ Q kAHjC4#M|SWl>BOg4hVqSIԖl1B#`u?bAf5ԧJ'wmGLMOg?ANBoL@hko4�`"#:AJL"ZBt8@+}wPݥ" -UCr/ych(T<{J RU,^*Zsv_ R T/U*_X\LxR0O fRը-CwTfRF*{ _*O<ss1&ŽwÙ< cLl8M0, kϘL%8fbj,&n^Ϳ@hLeh ]�14$6#fQE6/D;5f0UX! d3T 樰5Ņ@1b3c$|R a#̑a[ iw ;2ՆTE&[DvW!/ L0Q3  L[Nl@**6$E7v,¥˄P^"s|0qᶱh1oaR6<QU b nbF<uQM2IDɫޑCi;U:TwXӻD`ڽFz)ͭw4wEH`zSHޑ7rzG׽!h䴝j\.2NNH`N >F[hRzNhꈱX Y!]Fw9ɳ:cJQz3MywH]\pzڌ vlS �ގM(^2L ϱͱ}9& toK MkW@׀DJ K MۉP ͏ CW܀7#'@�1ZVo T6k @K;, ^pv(V]h/n ` /Lj9B`T<T6 iNBGHR#JᘤKOuHɸE""0oF)FhHcKMbt5= tbQ Tem#yh3G> ;LT2G@DuQ57qņhĬEN ڗŷ9׾kZ8# F =o0|N*Mq77x>mk߀%}&}:.Wc1Li5־yG +洯C-{N:7ViJi_k_~*^:{J xA}>i_}0N2:vGOyy' 3 ipPZq&W5|Q6�ݒQ |= vӯ"aK ߲jĞ$-aC/!U$@o`Fأ5r>eEQbT8[2jGMؕZ27288<BL[yeb&gc?Wtq+qC=b!N2tgzC1CfG2!FM 0NacbWc�W͖mq l֔rͰ4b98fhsč<M8 z4G1 l C3poeA),ZC,Vc� qSҌȸ=*[eڤ31M& pva@BfTW6g҅C < IFYP6oD3*b5xy Wbc Q'C^YA8Q|RQZ3Xr7a"V(B~BʰM 0):BrFPPQ+ŀW lߢ9V ,'Q<c4m.Hm,s@,e8�<~fCfN$ߌ!f4[(ؘE\@^t,=`Dze6)dIMb|X9?.vN(+^vc ayZF-iZq =[JQ/LwU1y~&(| R{0> +kb7r\!#/[t4BɱcUtNAvJ-̤CWy^l's\ -UE:3iV86lxFH!ϩS6u'L]6pd[?3rasd 9_X+#%-[[Y;XoO_$nmsX/@s_=UB[Qi)T͜)(dFL,'PJ2 ABS^!䟆�3=k"Vi"WHjJi2A+ 59WAw .*9 4Y ut8j?%9Y= Ή_ Ϊ/ ,\aw4Xi~དྷj 6N{@ qU/PX-/Ќ/P`5 Dʊe/~I}H16~f lHY5_RӍ_ꝣ/ C6~A<Us 획_ L|ԣv/SuC'~vDN/ҚԆB_"5~I G9DJkq_Rc=p%RZ,: <AF=KdIn\%400-i%RZTn>K/ M_"B@,ˉ_3?Kj'~ @/?񋸄~I5v`y܉_h7~%!K`6~Id8D &n4~16;K*E `c{E+ O=4�/b%_�}Hi/)DJE'~I̫r'~?Kʥk'~ NȚ_&_RTF_:b/[DJ SOO)/}ٮ%Rb 1lU_"$/0-E7~ApH _cM/Cpm!MS?K _:)0$X4_"* u%R`'~A?5d#%2HD54_lSV|6~C_69)-=n(_"qc *O" AֿǗء}[<!~< l+x"=a(Dn8>5xK#4 Sl�M, oiĝ)H0~LVSa0Ů݄i"a23Y0M$L'Li":<]89MZ)8>69y^XXx010UASt=L&L!yor0USs),7aHwO't}bawt=ПՄ)& SOEOCg gagh_UPk+V໵Apt}*I!>Bխ}o+A־O, ^{=>h9zѾ\;#WQ<]}Gt}b]tz$N*}zL>՗\$Q⴯&A}NH,UOö\<])kst=h$}MXJ_ #Yjgz0 }tfRח8@gV(uW<s00Ь2mz]?)ůR!}R|6EWewBd-lH0a*VcX-Ҋj۹1zBMsr̜VՌQξSUful0ۛ#b uod2oQ ݑvзVF9fpaP3n@@sՍrH˾C") +D@;|ս9u|ss?zc0Cr=;Rx =ma%.5ѭ%e䬣=2.[2ِuj\NݛScqոzfef,"Sfv3/^&0н5>Tuo&98 G{LF{̙ .` mge^Q`;+:*cO|˕M]lHp*UT⿪םZh]v.^[)][ε5К5xmM澸&מZٹxmM&smMfҚ.̥!i?[l/,&MmYlȊYu[ ,2O Mr#ZisvU!jsZK eR7,%#7dIy2" /TYa6\=l a{td# Jl-ʲf2|t抻 i(F[x(lV/ci[?.P&n/giҶ+dS.P ϻ 2sO?hj $鵼lNۃ h2oQʶ0CA&s@;HY5rǫKvwRfW/{rk=9EA-9l]9IA:{e%R͌U.ث�yT53ڟEU탣֗v[~R#'etmI]-?�}[~4zеޛ-?mY=-?I K[>)vi/q-X kƴb--V^Y.|[~6̵|rҖ_.mzҖiڥ-�_i-U%zEO|ª|i˧ի|zi˧-Xq\Yc.m�ܤ-8+_ ˥--yi!|tiI#-NEs'1~ig8.DҘS.(o),|&o̷\.LZNߘBm-nmW3--?1|{-Un÷+ -_w+m C<//cwdq؈T7{7kV_Yq;QXf¥;Qc&_cKTmXv-EE;D[*\w,9cMtK?}wl;^?X wjeD=ޙ-xǏ/-D<))|<9f) L=J,ӁC@JC@Nn<9G@07nɭ9]{5G@9*Pe"$!;덀`1`\t$~D@w-`kW\߼߼g;އߜzYaՎuR[WacЎxO\o)WQ͵Zt[lG u[ ut8[Ywsi\+ԭ4ŲDe5.k,^'uՅ| ~{ls>X wx>AA0s ݊yk*^o%\`6IRW/o]ݳV%񸩬*ڵ1 7Dhݬ~N>0o&JD&#QnA)5LUiuxjB:F}S�ga[kߕx;3/;@#Ld!/H!_rB[\^Ax3P%gy(%%YsI �=<_s*(pB^OlCûpI]џpDy4uJ33|E8S>gs}9#]I f?Z爕uɏ+LUyQ9D<0J;VK;}%VL ]N;}%VM6΋zkO0۰ϋR뵠yQoQ }`[#~|a*Cx֘1}D*r>΋RϋzkOiuvpӋKsGoM?΢;{h>/?9qf4YW}^jE>�qD;,KF|!|G?ga rQ8v}^T#8DL>1J!\'r}b늂SW #9>1cr<ΝTlaM_'FJ+WNdQʮpK8*˲>/c~J<h2U۸=<ٸ?|cx\i q#3Y}|AQYY=/x<;K88_ra9]Ra4�q)H2\S]"xiL].ykޱ]!w&;G!>K-|D͏;N2wC]2.d/zB枺 .dKېeݚulck[]ԦYVW{}(@1L0p-/˶ұOH}& u +tօ}_grq endstream endobj 116 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 117 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�JU endstream endobj 118 0 obj <</Font<</F38 34 0 R/F57 65 0 R/F70 105 0 R/F39 35 0 R/F40 37 0 R/F72 106 0 R/F14 38 0 R>>/XObject<</Fm1 116 0 R>>/ProcSet[/PDF/Text]>> endobj 119 0 obj <</Type/Page/Contents[25 0 R 120 0 R 26 0 R]/Resources<</Font<</F38 34 0 R/F57 65 0 R/F70 105 0 R/F39 35 0 R/F40 37 0 R/F72 106 0 R/F14 38 0 R/Xi6 1 0 R>>/XObject<</Fm1 116 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 120 0 obj <</Length 4667/Filter/FlateDecode>>stream x\[sF~`U*Pfgm!$V_gΖ`.==__gh6ٻWl'Y*f)b_l X޴ZTb!WyRʼnY72eq$jvy=KX0$ǹ].gD>Kui>6l.4~,ٯң'I4? <0 xSM?JX''[e ylݟW+3)1q]=O4vl?cNyڿHZq3մZ%̔ka<7,a2ndGXS˖?Ʃ lؖuW`'*]75 ۸oV%jY]i'k72><͍;| >~i6gBG _dҜtcw%/~\d~�R�tx # ݀)`;xA^P E(4t@gsXXsPcoGr'sd r #r8 S < r4@4Q"TgFg7EܬL}=@3hO]KD`Ǡ~*!衷L_&'A=�69 ϛ~\[`kS(/ؕv�<5G]m KDC$ 9u 'SB3^EA@Q+@yѧx=G1TD96m�0 P �w2;&gpQ>d*CBDU,Vi|^%\s�~yy I2dvL½Ϡ=Y&~2!_0DRJX6_"_eSG8}O{T豮V+̹Q5p3)۩ՊϲjEi*4%`2g&IϫHQ2 MI�OAO= 05RGHD|D<$>dXuT`9P6^γ4< O6jL84D<Dž_iP>f6 ̳Isq(ĺ\d*CB/%)CY,uy[6a"ƹnvFgz ;_ֆʵޅ`16rz2C< sg<y kII<y%qCvj478fpOr'gǤ/5<V'eDY꾼zϛ{>�tW29ycM} 4'Bh(߷qt3f#˳\Dŕ@I0QC]8f"VTMgePqi3Fe{U}CնnhLhsO*�5!,b^]YgK[?5R8P}y&X]T&a못Gq1s`5ˈ/ӪryOäA$w%H'CEeu =ۢWC,-J׭Ò"zSχq R514U-[KL IC,6ڊ\lu[:whٮv|;9 @r  .iY_),.Sid^ aé\v-㩏]tL먥aMכbR&һ^گmuSW&6@&ZZu_x*&oTez0Ks\ZQiM|N{vw/ %}Zuh;{08MԸN!c4*y(4&VJD?SH(`zo9iheMLU!(ݶ5Zz6/d@Gh-k-Cr_(MXO8C渲54sr&AQ'crzRΤk]Gcʢ5%zŦFm %`, y[VJZHTi4# :1b^yG"v)KQ;1=1ߑN-V4f<w-no<S  oA8TF8rp0 # ^eyvjvx'~Pߙfp_"d--.gQە\vȤ`�'i,`1�{-nh+Y@kq z#WK a3f+=rq6%es}Xov zl{rѫ , 60!`+ 9XFe t&8`4w8s51]LIRҁ*` UZ'26fiJܺ$I݈" Ӗ}Wlomxė(I�وIx rIގuSeS-<<0M惷l8RU[YTt"Bv]'E!7d +g'=^6,z;vXӞSUTy"pv$:L>avd`𾩌l<|rϳ@\x7V#PNiGL k0 XTvժÛ"]ġYr=zdyMH3v\F``۴nnMjYء A|{XHA/x[\Y"ܭ[4·˯Wc56�R[UY90od~)?,Dꮴ z罾ijv+WUktN|&s [Uiuϒ8a ݣ1:%jh Ǹ6Ђ45BL%T|U�~U�'6 Yz6~YuM0Ҙey� '!ƃa!1˓#N2p.‚e߅&)u_"\T+0ߤ) 4lv̆Q\hWQmDa^\}28Cd{.0$9t̋lUЀ*Kxj)'dttW56I:@TYv<kEd$0'k602Vvs֠0cwuzhvD0pX[3�pMbzj.~KIh%jTC07uQy A+A*ϝY_Y3O fAc.h-hq{ Y&8{Ck;<p|)DqaPi{i`W2\/[Śl^ Os7r?#,BxcJEdMu}U2ȸK&y=Ac3Q]_^C^MP (E ֎Q^L*)?hbmcc[,\XУql(<} ĽvX {kU^wqV&;K \a=dӜX`EḲx[ Xk0]cIj>텢!nMKB=$kܬ~hB)d!݁ )Tm�w3o ;%gN!v4@rĶǀX$%8Dn2!^cUJ25ߜU-Ean,¸ }m#`J۷SptƍЧ`ތI iqyhlPPCnIdRD.uݱaLڏ5%(I76!9 ~0ss #hmQ8*3F"WÄEoyv04<i0-eL)J~`*\,5 5 6A~q9d{IMAce�&z+ߟMO#.5zoGG14OfXWZH63DQpݨmw VMI`~kW?r.wFh?|p^\Nczua7<N+ܩ ř=5^̝LpRq}FzEYJ~d9};@6ʗcbz$lzٛvaDj ҋEL&PSs)v.$ 澩ԨzO/F 7KsesIog¥$UsMX=*y< .zoƐd1ˋ~W5Y)mm9l:Je.kr7^bƚ#?IG~]Ʃw^x*XGǒ/ݕ(\p?-aO@aÇ!4ۚ4bi_f")48GTpjU82'3�NZ!Zc?>G'}_ �UfSo(Z"g8%a(+u WM ̮QixL}GO!Ý*0bGbT wb)aI"Ic4hs#D:'#ǚt4|& ի0jYWnmeՎŶ\Z]QYq`) E L wul_Yc^n| endstream endobj 121 0 obj <</Type/Pages/Count 6/Parent 53 0 R/Kids[119 0 R 122 0 R 123 0 R 124 0 R 125 0 R 126 0 R]>> endobj 117 0 obj <</XObject<</Im3 112 0 R>>/ProcSet[/PDF]>> endobj 113 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 20:51:22 2016)/ModDate(D:20160927205204-04'00')/Title(diff-ops-reads.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 114 0 obj <</Type/ExtGState/OPM 1>> endobj 115 0 obj <</BaseFont/XTUCJG+Times-Roman/FontDescriptor 127 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 0 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 0 722 611 556 722 722 333 0 722 611 0 722 722 556 0 667 556 611 722 0 0 0 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 127 0 obj <</Type/FontDescriptor/FontName/XTUCJG+Times-Roman/FontBBox[0 -218 775 688]/Flags 32/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 116/MissingWidth 500/XHeight 461/CharSet(/A/D/E/F/G/H/I/K/L/N/O/P/R/S/T/U/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 128 0 R>> endobj 128 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5365>>stream xmWPg^YHn$FHIpq .M3S :B"Q?(H4ӻq&qlI0r-7Hշoyy^f�cXK<#bD1A Jj Pl3g5R^ U6ep}cXy'q\ZBDXxܽZ~ÿٲk.?;#bRDQln{'saiqA!!o^AѢ(#qq7޲e#DDLpr|'Ļ]EaA a[Ɗ'I<t,.iBNNGxxFqزut[0l vsܰ70wl-恭<7S[6vۀ9aӘ3vی {ێ\w2a1 f0;b6&ž^c5-.гװ쟬8$G7k1sE/^X%K=v[[^y+_Zo/+Ymkww}$7R9ֳ}X^Fl<1*إrQȜ8ZLKi#ZiVW B4pt@ RW!H ǚju֋)<Ů֐%N Q>UrlB OL: x8Tohc{B$BbO+`icǏ ڂi*t>Dja5bB*DqXh7k P�m7 brzq=؛"_I< x B154no<Ӷ2j5YeS΅[r$xk0 睭+*܂wD6Z7ે|# Tp,8Ȗǣا8ql0 2J]ͯ3lyB Y/ZpH'�yy}DU,i'[+JG\<;շ E;=sʲ6|U~4N)[ Q{IsLY} l>B43רpOw* &Y <՜r|LC&c8˺1cK{I1a5@qOb.0Mgt$, Ζ XssLZJJ 9^V8́q,\^Y)9:r!:zg Յ[i{MSl*,|YzW=A5G px%sIYBP-ȬDҞbMr>-;UpH N%tN�WNC } }ߡƁ}E*𐀱 SP#ӿKXS|uDm2)m.O]6{. WqT m^^6guՔ&'ůvp<#&D>ѕA "9TruEtux.e*fBMŜ]rO.%"#q<0{[-7Z"60�R = 'Qiw G2J@ N9 `pt|QIOX: adn$M 0Mw_zZ2s=.��. `�n+Nj kتB-iY/a)E<0SЪE~JZrs s wgK+EEJT 2B؃^̝4]F3pl E +*&uJIm 8uZk?P c]\) g⸻zF^e` s/Y\UŰ~v)>W_)q ΒUq@D]jsV_̩{.<6GR{Yqϐ8kedAg&rg%#0 <QUT% 2Kg5媾>N5ڌ}Ԗ7DʕY2@$iJV13 O$RLEyy:anY^)Qs) m@ %#@$q =Ww嶀W]\ҷwtӗfzVEge(@/ hu¥|{uMm%y5 $'xh NK?+lÈeOMu\5grVT@Η;C{wK06jS0 68*i(N:^_t-U/#_@�_naS^V=#W]ڋ. 5{Qq')ɓd4G@Rt<z^L3WTuPEx(vmbQb^hVK2+AtUrQ]c2>%Hc9bNZ$ٖ|n6TU7=8ͬlj t % rraIr6 cGvI =M=[)07-7 W@52O)6AMuW8xZ^.D&~pq)?//*S .j8G,|5hpXi8"+*ժu, =_W gӫg4,U 65HmdCHʻUUE ٔ;*ېa>GR/*Mb^K3i)Q6 jΫN̍p*އmdQ#?08pͱ7Һ xnNhL(Lp3E M 3xv6 ŕ;d]a:=6knq/C/\?b_zٹ.0N댺vu#@HwF4!))<lgk[# f,?=kA/t'BkJ[+׫k�`m h:W0>@TJ'ƵV聧�OxZ}/ĸf`:Y8H65gZ@㭶c/k>7ݲFE WCKSS4?TOh*ʬәʉ3Ty~AҼySlaY>f Ȟ}x*K頓YqP&̊U-EKu׳<k"ni`LBDy$j`j0 12t^Cb�@sV@ڹdE( nWAmn̓ Kb y1$ԆA2 /L-gf:#)*L-a}F+YԳO4?Zy| bZSi勏 twpy>s~4i(7Is@f<yߠlh *�qBpVa6kʙAG2.)I,O477[biG~UޙlP_4l[EyМHf'TRsWF  vz@+ׇ+EeH?:$q[P8s1 +iWm'ɺ{5+!_>׌ k~IX5*ڕRAς:Zu9wT*GFh/W2_z \PրL3wq`Vcst4>,_rp.^rr0 hw 삜~0dJDc ܀hcez5|<1\7UD.5@9'"ȁ&C.W{gt~ Aju 3*c:.ՊcG�tcHZ{GLڣ"tYO:ˆb865(Vih+Kg<N.YtTܥHYԷtRo=kk kz 0/^+ K,OJ3dy ܫ@Vz؁r|@M  *;$/,[kq6Q^q7 x|ALa0vJv#| �хxB>zw1i֦=# n" V;Z8p9q\5׻.%`+ēSCx(~ǎa iZĂl81WjhՕɢs8$g29AL rp~`<xJ5<fٔ&š4\:MSӻyz' DEȢuYv?v {7*DGGI`N5[6sC[}~N =qPALT<%| M}#‘qp\NmMr|׵ O ;C.aaLXv4q}&#zCM򾡢 -ιq h7ycH˅Z=c]ԍ,8 G 꺿RY)tD\Bj\~VQNi[V'xJu1rڃʕ 'S͗/ ndR{ZTD-PVì ؔqnC>犸��|%Nо�.?)R"V[zKNhZrS͗K-&1 5&cujmruEj4j+>sppaXf-6cO?OiNjG?G|tkb{}co/+9芷d_I01~EN>n gϾgoF'ɭa`@wYw +DP=@YYX-gL&Uu_ކ6ÅDE+w!v Dl{!7, R3TPE =oTߪk֘W4oC;!f_7.LH3_L|uLu}~MWv?w=Iz ~U~fLt>'ufw-M] \ömt.pˁc:1%&ɍiU)Wc >t". OL$%/@@ҒLM8T:Dy۲%K^.Ya<. endstream endobj 129 0 obj <</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R>>/ProcSet[/PDF/Text]>> endobj 122 0 obj <</Type/Page/Contents[15 0 R 130 0 R 16 0 R]/Resources<</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/Xi7 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 130 0 obj <</Length 13311/Filter/FlateDecode>>stream xŝoݸϧSS<1}7N6r3UIj/dʣFwjslhM؝FnyCo?l`.hͿ[<4v~<|z} Fͥt#T2B_L¸ҘQ:Dg!]b ۴Q6ŖM&5뽡"nؾLEKњ2N"5ݥE_ 1*RGQ4%5PES OHb0)q'a2[o\&o}2^KOe%oÇ�/*/_XMJ(QR5̚Qj Xu.?~ꝛ.S7Ck~|~uͫ?>>|?@׻䠪Ȣls*k 37#>oCK -_tT@ym* @F趂)n˗1w~|@SXXÔ4[4jejpa?2wo͕tϝy{̅ї!_՗~BKD(^kv x:nOko7/ e}\/ Oe=??vQlۗ=F7f1tuRT_jn_߾߾}~܊WC:k 1fH0葉0W6Dj;tu^_Ezbp۪뵹:Sb Ztvqx(F #=�#_jF"_F"_nFӢ@\F"_+F@F"_F@FJWw~Wx/Heۿ6ǽdzH\fa6C%#OYy JzMQ<ڊ&f( ̊ U=İgY*&P#VhEkak9/ՠGÇvC{ g@dS|wz>ΐ] )_I)PH(#O-<RŎrJ&RIb2~)Y/1mcX)g$piFK8ƘLJl| F b&*p' qR5ZJ |OQ2(@;%**pN `@*T)d|93 \L,cw&Yy 8dyH0PhOdkR!z-c;Ѝ&KdrF,36B^٨ ,GXY(#eTFۥ<JrBWbk 9r�<ư\SNHʤ-F6\?ޱw9o\/;:&%_npRNQɗT'ܪ@տnVUBI+2P|+;pA\|*Wv2v&Wy8\yH0PhO6X*OelXFK`rF ,3�c߲i<Gh+2 yi<,!am@ذ5<BrBXbg<4XmcX)#eF&6`?޸IK !H@mRA%_R ,*PNI�K \hS`) wJ�,2XZ_ښ \L,lA9!#_tn??7$Oȯ)C~ǩ0\?zyZNU+c8# 0daQ S&�=,aQ:)'_HzRGTmcX)#`jjwc8G9:(J@b*e )$-efIɠhY Z;%@^rh/#в.>TyOi{w|T<Gߊ=*Q<z'3CG & _oqڱk2aqck[&BώMTۄQK dvyg>wt[x$]\ו.n6&\L޽&p?:#ك叀W$UUO AR@ꯔ *2pdPT)PJ?wJUU*˝J TSTɗqÁX|*W6屽~+>e}}<, (qDzl'4w0%v3t/Wި%/ n=Fh+3abWF2 e51,S(9=ư|SNĪڋ.w1g7MrFX$gU,L h 32FEˠ8S?JgU,?%PSɗ1sPǙ 59M󰅙?c*y\@֯"RL<95c׵~\;`^yu]ā6nba1llv̳/N�87lNIr_^?w/#51ŗCP|)Qz,K ܤ(_JTϝ/*pNIK TSR #xg://TK_a=oâGvZ9nL ġ ;,=h]zȊ=>ZWQCV 036r`e&LwnQGVlX;|{)nM\N ^�gփ9 .oc v.G ΖGKNJ׀V&L|MZPﱢ/5 oe_@hOiE<OkuӖlqzHsi" U60I&r:I{HOLC+Ighr7PM13ۼYmFzt >| J|8'VeP3J-V:5Cc'+)Pj@VHCLkG2lX-Z*d0`hZaWX(^.v^?\vRGXZ:9Z|K% aK-2(Ο\|KO @@T'�ver2V/]޴66K-�^quKM4TXu]39Y+JHWt vS.Z^pajㆷe h:)h+*W§v{E>Rh lc3U-Xw)$pW⨱v]aeI] LEvc[ vG . l!H@mRAb_) *PNI. \DS%wJ�2vu"v-_P: 55B XDCUŮhfrg+IpWj]¡C.Z^p vb:)h+*bW';_ TB�vEuq&]Ղe`Bv%.u]aeI]Ivc; F o, o-@mJo.= o/@ o0EJo1@Jo2&tص|Auj})˱&v+]\]\7kmt Pcr\4t$@..X~.[E^p 5u zʩɎܷ; PศN9+r0.qmro;ïTR$T.mc-kq]1ǵ>{^tF:F@RX$NU,I 辴 '2FˠpR JNU,Ŝ?%P#S8ɗp\Ք|52V)/Y#(EJ뿢 Hf(q+GnpرrL e0P5Ph qx);1jsjO=h4ZbAϫ*ڡu�#bvȱ 짚0в/ :Yr(A�V ؗ,ڍWݕlTX(�_)&8nb3WE;wp& QwUb*PTŗ; %B- TSPP�j dĝ@-ZMi{Z˗TGQ7sBf@8:**v.jʝYSBi]‘d3kU VAWe- ǗC,"j% rEȑj@VH (]6GZ] DPH`g~,Zuy+u7lPcv*f,qӋGd, M!H@RA2_n! *PNI`, \@S%wJ2f&m.c/X+Sx b,0gZYtR^P 5UU!+5\ZB:蚐] Y~Qd rdYZFȊ]A#_c. Xy4_4&U#,*EQDWt%a] T,m+ k 2A JJ5JfXJ'J]Id@uOI+@W7jt5m͂&]*AWgЌ9oa>}~uw@lD]X&<w M7Vh3qui :۰V)bX[\ppo]#w1ڮզ̸0PFdSX( [TR4�5P@Dobg ,-+tآnmTm]'zZYȳfx@ A#_$> TNK /w0Jmp$#_.)|@Ց;%|{&tw9tL Ip/{°j!&tzvM 0 U �8Э@pnؾ|4wVAoU=Uo3WϣG]X9t5!Ms-݊SMh \=4;([`I-@vlm,͐]Eox`8k}[YSzlG17VGOC4W(1ISQ*HAє@*) 4WK'wJ2P]N _F)TE.M-_P~MR 1TbR U:V]dՊ8Һ8#uMS&VAW)ty/<ézʣ89d cZ!U�'5 M,$PHT8ƞjcL!qI}>nbZp& 7Rb*PgTHŗ%B* TSO�R dĝ@*T>ݰj,Xm LU7^L߇;xn8MȢMr�P6kvE3;UV5d7a!sb� [KL ʰ g]&W1]+c_[K~̈́^$}Zj2vBZC-ie'#k_. SK1`;C,G'mi1U@~pKΏ@b_ꇄ"p!DP׀J9s25*ʜ 7TdmЅw/$8zxZwpKqh.=Is3l~3leb؝$)J^WF`B6$W�]$*W.\9Z  9_Ǧ`MK-u%L9xﰼr_F<ẁrd&ra:뇁g`??N#Ї*D_ WrJ|-R2(�@-;%*pN @@) ˸,ouEA,rV,rfU,5Y|PhtbXj-Y|PS5bY)Yj_,,5-cYҚE QPjSY%5,bUjf1eE(Y\fq, \SkWY)5)#7b(+f[prּWL9�q_5`'8h;pm?j W]PW S|0yrcgh^<<2 V�j?Gf'7e~دbe?%P2ϓ42?-`ͤa=\t}[znbk6`YP#Lְgj5HcX: )9(,ְRd�E@QbOp[DZ u ?6avB XEQj.Ǫ>wL) $,Sq]zeYj3ZUU;7} ԋE Uz`@ʯO嗆}[9Rh e19:ҀՂB*r%.[:3u]a]IYخ?RWYΈ^wtb*p% aKu?J[|P2(@%;%*pN @@N)˘˯/ }Amj3_)u]z!ܵ wUz*wE3{B0 *xšnW)-u5+:WUz^kW-XtR͞е5'W=MlFX2hR�aNk^hdR�Ȯz[트q$\pG'K7[b$*PTP%{ %. TS2s@.) %wJw d}-&EZ:wpb]j!CθK7t旧׶yz}Pc#jI\k]bE^oJ QntUA/{h] [U" N5m` 6�qQ,?r.C6"} w]aBp !�q!諰tE6r(:<V"g||[˞ǖ||ሞV || ʞ'I7[K>( *>|chw\XɠJp>fRF <, qD.]GE3 ήs"ڮmT1|ne ZSԋoNģ�QOFvXszXBB9!kST6ɗ,z37y15.cO#>rJ '}ܤmcL]'䏀$LUO A@j *2pdP`)P?wJ\UJ˝�L TSɗ0ztf._}>3 ?`f~}<, qDzg4s$8q&vS*00sfbg�3<f:3OF%&.dA0|er>fFhRGw2O`@k}'@6rBdֵ s1d^$9(/;!HL f)$de6Eɠ SU;%�Pq /#@ĉx9Y]\P2;ߖ o݄_2 }d͞<h5zY A+g(;ً)d}lT̄<[ 'd6ݓ/%X(g$`fbp>rJgbK73 8s1g$LxA Ar&_n$g TFK 9/7*Jřs$p&_)@;%|3Hntr򅪜z̙&g~8|!gnlt^gܩ\ۤ2ɟ<h`zyfNXו1jxwl# V6*kbƢw|Ջr>Feד$XQ)'$kZ=-lÊP9!]mcKΤ#R">WP$U% 1h@ /[Jq$08_)@;%ࢺucp_lkg%-_~17Rvi =8{>FLfX!xc`!*0�y cg`=.dalT䄉<V|c}O`@@I|Jg+y-Ԕs8ۦh{ v0:}]xA A2%_n$S TK /(JŔs$0%_)@;%|)_n$L|wk0-SϟSơ'+bC G|nf2s0U!z*c؁RSI=R Sbc=Mדm1 L0dʂ=VL Lc3HyL\K6[oU1K4$LaKdq0?'`?^jC0W=1 R*HA@*) 0W+wJ�&2PN $_ vrgڠD_Aibl10 1 Fm2T4@m2ݴI:ɵ1Z;14IPmckuv(S&Vء<.B;M2O9:J  4cNq^i'd t]cyrX^?~-K|X TO ADj ,2pd`)Q?wJf;%,%2PNI�K�c{1pX._ c\5!CW;ujx^&cǪȞ7Ո=X VUq+_X<Ű`daQ V.bX=,a�QI7=B%rBJta �I[11z;LPMiтӢdfeBA2@{Z|KeP,PM?%*ϟ`f )f<{bBUn@&5|(qd j}.‡&GKmUqA+_(wM|ڮ{6D%}a�Lr&Li3{e\M W}gE}O>ͽ'>NGk b'*p' yR5VJɓ|5Q2(@5;%'*p}N @S)<  <|iQz4] n( ?e~>Hh<ۧFs�:Mکȁ~ό \uj42F?ʓm潲Qhdb)lPNG KlXgZ|%G9!-yj`TSIX>5S,#1 |C`)Pz+K ܛ(X Tϝ�|rR U*�Xe<4nTvӥYM<,qw* }N%F;Y]#q+_6+/n\G'{n%+*EXa("@%FM'_GQF`ʗWRÿ�)v1,ڔ2UӚdQs9n6tٹԈ-#, r.*OAҥ@XZI|KePt)P?%.*:˟K (T)tٶ.ߧʖpi&߄ˣlu|خGY#HV?Azи"`;;??UcWPRZpg&2 6QT9;kCX!jTR{I7S8q38dO7R-|GncHvΈCPGF#|xC A2_^rC,PZ<de $)+ TÝ|sXY UK�+e58+Y5f1UE QR*p"(Y\fQ*JjSYUjwJPb5;%fqeŝ\2n5"Yd"G(YlK"k[R!W5VA,f2ȚW5?%P*şfe@OR2ž~SE˗l}Ō f(qcR{ 8?5}|)8T8q=u(481&At)7֘}J4Va`;\ Ct̷Mr(ubCO FSxo -_!a(a\n<;zEB+OT*"< + SYӦo-~Qה&CPH@c"_^4CW;:1R*HL]Aa@) W+8wJ�2PN `"_FOnW3l�Ư~цM@ 0(h\ j > .3TX'D 02at\nl"atU"A"lmO31A3H):E"Lhx0 rws-b~� ~9<ȡΩ- </PӍlÐAr�"Axwp%P"p]k@Bxpk%T"f3)E􅚏Z]suEE?WU'-n2#> Nq^ΰ2C!؍m<.ЕV=wOhq`/ZOhľ,Υpz\hktʈ} ha%\ac2^? k" @|?K0uEpwt|su^?ޏ"-`݂Hkb w*ʂ^ ) dH W+Pk<wJJdzɝ}젷뉾1fpB e7g"^P㐫n{Ӻͪ$4gDM&lsu͝(eIw>]>hV 4L?B7m+ $ĺ{TB!q-1MК N#e�#vmG1^o˵[b C t 鐯ub*PTtȗ%C 3S萯Wp� dȝC@A|꾨 af >(xmQ| ѣ.-x͚G) _oWoLu*0Ec_vEHm&)m&l3M᡽ܮQK~ YKºH7�rPk 7ֿ@EЛpO'r^b|ٌCPK@/_Ų! ɗbYZC+PVA/_i T@OI _W %J|2:K0H/Sԙz&x(qj̻D= zX] ލ%|/)q+_Ԁ_[GSwU2wW) ՗/&a]]y-ҕwxS9Uw3xsyŸ?m#иW4$U1S*H]A@j7) W+<wJ�w2PN ._\5f#, \!JjSY%5,JEIb5QP*PN YLfq, TSkSƭf ~&Y5bYtbXj9]*f*Ŗ,ZY*fj[RS, Y)Yj[FV蜟=clnW#g'bO8 P㈓CEP7ϏOڸWO,f{|1A@&dZOsͪa�lmrU.zʆԋT6]h}2uק q!wUZv�r&w 􂽀ՙ»'s 6smݞx?~<S "! !H<@-RA)_nw O*PcNIS \ֹSx*J$wJ�O2mzŧ,_Pi )<6QCٍC4o?~*O!}9Û$H3@pu6dV]x3AI/}K#jW)W~U|&HVeN&{njMȊSLdL$@>L~Mޱ%A )9WL.LG?dG1#^ȶ0!(FG/ !HFݝdD g)$#eEɠQ F;%<r/#0J})v׆'MJ 2(Ȫe2(溠΀fI0b" Mu"-~fdsU<~IPZc*!>}dF}Fj& h%ԵC IXWO�D D�j<0r ^<$ M̃Y&by)M|hUC@%_^hCDW$Q TL /<JEp$%_)@;%@{eJr<$(.?쫷?~N'_>\پ_?}yo?՗o_>/ǧ_~~<"vehUO-K1w>ϒԸO}mW_>ޟ;q^3 \* VtmZW}{=_z;9Zۏ{xck _-?Wo?_oW~yOqӏ?u^}:)a?#}u~I/pگx:׉Q_"N_??_{ȷmyK|0׏_>?Ywl<GO浟("^f)KC?-Ó)' x}'}B}a䐼dm%ۭ30:<ȟrMڛQ?"?_5v]:?tu c3} \\]ߦ[7mY|<:߯Ʀ/_~9-6w+-/r_sn^[~aS� endstream endobj 131 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-writes/diff-ops-writes-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 132 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 133 0 R>>/Font<</R8 134 0 R>>>>/Length 9243/Filter/FlateDecode>>stream x\M$q_1G)7dْe#,$jEGJ {gfgfy;n�Uʪ_n=7|n}Tn}J~哾}v[R}~gOy y߾;͟V 5ۛݎ<ZzOq/a>?x?ιDz?<u?+r?{ E/4r_%=<_})E?(E-^BQq.%ZkD'ޏF_9c *9~z{2'0 amկ7QZXӰ?~i4~ћym1&w7]ӈ9 }#lEIH~It/$~6rB~ ~| Ra9?~Yuׇ?G?M).'yFŻMC* |u ?n?M;a|o~__G#98i06^ϗc~ǧ FVqZoo>gu;gX=$@>7SOeܳ d>\bӠ{+❉{s_c<~7!}tm֏O󳯾?ްo.3t)=z z<n_}$n}+p" y@y]a[L(!J}ɑ!k> 1̉{+hx ;q{lJ8!6N&y;AC1%U ؈FxbU~Z+'=e.mhڑޅ=fs __H=u o5(p  nQ5rSw d}mk] GST!-Q{5X.P!~߽aXZL!펍A#k>�ۨXcPNcq?+ QtpXxCË @1ީ$3SGCRӘ8zp$~ ~G; |5;4n5*V'eK+?uPڝ]9lVށ=CstH>PmL ' ЦSi1JӪk)sl8BB$S9�:Tq+t,`2!y:1*ġu ˓UPf43ђhU.4IECzEOE-$bIX)gcxx8h(Þ`&ɔ'vEI̐L !?R^,+A2 uqr&c ѥ-Ǜ*Th He'lQ 5=$&h5g4@R5GESI0D1ZRFKYBT�b3m `#1 ?76`q"#7ZY1Ue,ԅ~rLx&}4 k `15 ])rp9j63h\Y(&qӘ^d{D7#Zo 89BZ؉pW> jaCgY98 <Պi0:4ZўiB pV+a;fΚт :9߳3ZS�vF+$ $g2@-20 Y9P ohj!Bfu,§*ajvf lQ47fBI2EB\]r٢�iB ZS[-x7 )n|048}YV 젙V-Gg]yjQGV ,jQIZXY]Vb/fق6{fclAi(we Gufby8 \fX2f f(xSle!Hg(h�/bq8eV sĂ1#",ڋ8K_{e"glk C5s,f_G8Y<Х\0{Ì,R;)@a肳D3c] ,Ǎf{ ,'=!D E"IdEN`#G7L'`"jrZsDh0P@sʡ缸3Iyw2013wXI^ܙssgzkg2^v L:wv/v=0w( 0e9whtLG>w0.;żrIwi H803{_߿8qwx<b=/88πqFpR-e{�0@*(xn~#a biU!8"T<[#ΆA0Ld3F24m38b-t18Z &%CPZ ۅD8` '[JӀhẀL{sXσs5 lg.0 5!aN?{z]� *FνyQ:pcKT0&KTѶ҉(ED)Eu VUh/*E&\:b|U/Q=!xQ f/QAkw29Q jKTa Ë hsxQ\ Mfmx_ g}}ѐYƁ+6Jm&<� N2ȓ'!3Wb~H&~< p"D{Q#f:Ar at6TxCŹR9֐AXܔf`%*a`8P|s_}OC$f24SÀ`*$ϰ`+4*qO1PB1PaJfTJ`/j n#8E` _>l L] .0|Ҁ@2T93o3$*Gs鍱eb?)>`�/}{a>pK8ؼ>0R>pB,}H8xx^N; >$f>@>�#P"N3- v=fڊ';}8h*="qE0Cd^qӰy}a?®K_꾧 y-F>;e^5õ~8ӻ8mb|n_B\ƒLWe JQq g݇& n44nL@lKHp 0m x[a@޲:ؔR}!4FS~a*A@_m+&0b(x7‰ }fӕa|'(I�[!*Q\y9Ai&+1Z1B9X*[qNAak 'q*^ã.E/(<JKPg\(]bGXLJntT̵{AQIvAQGY8f t!KP sfʰv)킊riKP )''(upY:G'/a_?`yΏ!g/vE[)u(*^-@ZR.3a*4`rHQ8{q~+ye!#L9Ntt+f� 9+oɻNZAGwO*߄'sHPUÒi%L&sL 9&3=%c̞c'|Pc ʟfuRB,Phc,8#r#stK=da9œ3f>Dhb9}tCCiK0diKzSzcC$^1L3>LE4)>jNI^({}~]CٲP>NxР8}TKUe \Np2 ʥޫ;} aiʭsMx rN6}y.o|&W~*xJNC*C+\Ehҡ[S5P} /Y2%%Aу8 > <VE 9c􍧏`6[cJHcRgHF0; Es}fh G s p8+*<kI`E m(,jQiDk&9+Fl.(s`Ul THx֣85+XP4 wIzgQM5+aY|C ~(]?+V Ϣ302ġAȰ `ICY<lVBNGUY3ʐq,C}*$DTI~ ơb,a 9!8 yuԀrh+Y@{M_E6a!A1˵ʣƹEQ9lJ6+[!cXm2d-f*ìUm/VWWC̈` Te$g=c^%ȬK,wvL)]߸B.LA3Y4'6$DUSвgQPY"4?X ɲ:gQPD*,*:xQwi[61}'Y⢪,5�UYTjhV|T@*ڞ a"7_Gjٳ@1�0l eWL–]J=V~b1m VԪ�#1*(bN*j#̰)&CL&Z .ur⡫1mp`=3tIa.Nr U3}GB`pʓa $lW~LYZ,r K bV<j+KϪ*(*+GS̊I `o+C,�N>%"\yd<1}Xuϕg`VިzUT^`EPC"U}XE^Aq?1),/b1AKz0ZVb {\`,/LabO͆%giՊE3PT9CI0rƃ-_}'F*XH[fa$`D<1ST}aR뤒N[YR#$˲0rҾ0R4P # BnH%;F'FF26{a0�Hn_ GC a$iR"1cT[ 0h #H^pJ2Iua$` Ma4FPa]FJ�ZH ֓H0tֽ0Rc #%ТH{)Ƴsa<FW"a`F“V0 H4|a5za H օkhiH Kh #ŬH54H1n0H/.,2bF,a} #%20RdaFɮj %AsNPBIM] PR YJ$xlԩ%<,7yP^UJ-:Q* %.-BI1J*-`*pҀp'Ner8i@KD:qR&94%\ș8UNN"hI' pп,I#D.<JB,`JƩ0@R@$u( y!l%!IID�w UmV/ŒI;O[ Ã$!Rc88$TArڂHE Љ"5(U'D"CVUOO=9eDEwnU 01eDjlĨ "a}ʢ;ԎcV'D&a"UjtX`W Rmq'D̔Ewc8+ +C$"D"f"a\ jŸ"a-JuWjclER[`Yp&ђpÎWxrKWQڤE 쫬S>^lLre_eD<Ү2 NmJ v5~3U˸Үb t=XZ|͕w?xJsiW+ #FJ .kT4Mv7tam9i+*(\HC;z]ͻZQ.6 ]yWyiE@+ g/dyn3 .McYԄDmKb5 7MF 1>ȥ r TKLx r HgVAN!xMH"]7_Spe =&$ =&$I0MH̕¸ 2 " ,M "CǦ M^Ud® UUWW&c29b |2zT(z\�kEf3V.U.J)|<!>!T9C!!"V>{+\/a"r+"\HE)ˇy^>_|+"I{mˇ�m/Uˇ^>D(~֞|r[0o|?^Ї<Ӈ^>L ^>L ڶZabSy/&|n|nClĀv+BaӇ4>�({+B^>L ӷa [01ʇjĜV>LnV>;[017'@2 (D,ְ|ouwe^=vx$VcZ1�E={eO7L̴wo({6a޽Zy&ݺmv{6)+{6ֽ]B}ƎX'> v紑s_ḟj#l4M' Sd] ,#)VaAy2Qf%7# I֚؂}"xZ#8́dQ<#CAr[{ ijCڔ0 $)L +ͥp >,hF+x1lRXe<`<44l./NX_wxMfpV1ىtq6a4$L4E2\,-zJڲIycbux(ڱɬe6tSM6Qy6i87Q%,gi3 _EMTO Eʱ_w{l"{DqE?&w.XStv9m_jMʅKM[]�[Wd%WWt5+/]qEKZۿ1f؂^5~53.k>\zzدnlW75_H ܮn0 bmW7tHدn [oدn oapu]� \vuCW7 zsut>\0uW7c`_�_݀8?\w|!]݀0?<\puC`@n`j!3`W7`Gn3ѷn?=\gFq]~uCuuH[W70دnHuW70zدnHuW704دnHg^W7W7dgغ_ݐ-un b߮nȖwW7Vnzbwu+ t ,JW7jW7Tܬ%}|H Z_5~`3gVC-٣j=B4�y:B`tj&IQ5B#dކr0Fhvu(]#4�Ab <]#t Mu#`zt�/\sr^u]?fʆ]+v )Ɏm:H2VUF]2#WT]-iHDIEB+뼩8FIZ&ϩxePfe^zpa e67d ,_mI^3$17_߿}R{bpj�O>l/.գgCh /׬M궳؂tkoĮ[A:7YM3Hj5mє"g5]z(Jge#5lzGA|A_}]?|+ ?=-tO9';\KeƫuN7L{7Rv9kBfrO59V2ԑLs uKF6ߡH֒.U9!>,֑B9s#?Z 4|^ "]Ej珬?;/8] ښz&֧K7X9lSfҀF˚! bntjo]Y9٦&bk ?QSw=_0d|>-x =ʿwsB.l<Br!r!KwEH٥ "޵\Efh=Q{Go{{/zxGV{F)f:]=u! |ѭ=[ŕ?`~tGcwlp mNe/&hNU%J-g0[2r/~) +9W!ȒX5#Ʀnńd*H<)?2Xˑy _16{Jy#UPHę3:)u3=DX+L3"BB`y]]%Π?Te=p̫9?V7FոkSTłEӺɕ$Y#G1C"kw2f`z)V [-r,6 ѷ 9Ĝ3hd˾: TcƊ3rw~orCCXw Sz?/![rV�ƔҟYRs:zl.DY fѕ؋h[B7^ōceyGFW &+/]dmRnYzXK,cvaNwZo%qb!6O>f@~Y`u1Efx`Y]3jк׬.qʙ/K\a.񄢻K7yj]]"uեDyW祳]]"$ `~//Ο=qXbg>b_obKdb]l麄]la'=bwqb|[lK ^l ?b8 endstream endobj 135 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 136 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�J\ endstream endobj 137 0 obj <</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/F57 65 0 R/F14 38 0 R/F70 105 0 R>>/XObject<</Fm2 135 0 R>>/ProcSet[/PDF/Text]>> endobj 123 0 obj <</Type/Page/Contents[17 0 R 138 0 R 18 0 R]/Resources<</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/F57 65 0 R/F14 38 0 R/F70 105 0 R/Xi8 1 0 R>>/XObject<</Fm2 135 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 138 0 obj <</Length 4556/Filter/FlateDecode>>stream xڥْ}U)*�ΓeI$Ju`K$J$6_wVJU=}(YV껛Doo^3LgSIݭ~>T.U3i1F?V!]90Rg8vkG2Fm#$ѧ*^vGX-,숖=V2_;0|"My\s9Uzc8 (ֺ8͕&[o~؊vSo:<}?p,ta(벯P5UWՎXzݥci3U <ۓZ@pێ]_4W ?~HK/TT6x5`\M%k?g8QUٗJF5*@+nP?c\ٔǾUx٫D ܏<檦_wնU@Vﻟ>jQ<U/(zv[{p9*C=ȕ4;ϩg!'n{Az4�}h|1ʁ OTwP)#ЃkE@c|칟I/YbjZr :Dy `BN<z$z ῇ-1o?!>ng^ 5VF+>DͨI܁0*T^弹TSV}ҿ´U 3 Q<6qLcW$x-bkΏp|]lhŪ!`"i8 VboP! #731n5ڄoPǎ~g0Ό"ڕCW m l\KOJ#;ȉhNqW"yfDz<xlY ѡ)e36pȩ$.>X٨CWnBZx'G;6e`۞j;nKPÙIz=;Odէ δ(u_S]Yoj ֱI20&]JM;0pnft6^GqN7@v�e /&9XUK׸<oa^ @B^VgHy=_⟱xvM,N�OSZՂwmIŽ3({noOB6.rGs*2@pGv3F6A|PxE38)&|۞M)� 91$�{B$ 26LN.N�kZA]O2ģ)Ĺ-&c| -y~4 j6h| !COk_,8HB�7D)"3zyoi,TOG;;^Gz&Wƌgt dVy"]<_~KV;Mhy"/�:>újUni&ù+x@Dн#W>XC(O0[,j ikV_?As#Iu~zCFJԔ]DNժ(K+EUxߗ6x %X魛-va?sM ɈRI6 id)m-#kZ?Ta<:О)/ySO7wHtt�yK"?m!౾]{Bn�N߲]v57jتsK Aʀ;gh)Ms˨\AlLs:j<a@=')fQĄFXňC ]wcy/?b6Cж1ft¾m{w\[ 棔S#f*tT'+08KFsn;2:v*$5Q݊,#H`CAr/i]Ǹuq9.W/Rq8;MWG=L{'];CQ!7v}P:7G?)s1%A6PJU pP_ޭU]-G6۪J:4ۺQXb:g7Gu9�WAnfO{W{7o ñ&_77,k7!W1ssGu=wwcSX^XB43y96a4W=.M$ "V{?1 QOC+En.;8g݈sI/D鑄tBlo80 ={L*dl_]5ʥy"ːYky7ts.Bg3Lglsn:9VCo6~2O!`?"uZ=]J2_t~^+D-,4ł4gly0} ~]({p4G(΁K(  I]8�}f"S*ZTf# 0E/usdI%0AAw[0&@U$fj ]ߞ@9W=y!Ʒ7UTw`- לQSI5 MJ Jn@1'0A7K6H4<-Br^x/,+K JL>>3Mq{RJ#.n %3uR85q1äpgIa_'q^Gڝ8hSʘFx!K gb;JA%` vIqJ' A D*]@<2qLs/9U ˰/OdxHyyR)blH5Hw( 1\B&Gq1$"VUX.xc:]u09KLP}O[>B3p1Ǵ3 B͆2Pnr/\-,`R2Ɉ({m6ݢ/d}�ڄSF/}bq] k%-va? X)F #1f|ͣ&]oRx'ќ~ҸR͸DаRt%9ŹUKʂ ŽBX!0~ DP&?_p  #|01C{�Bu nkvpbѓ K6rӮscugn{l*U�T~_7M<g3*b !6 J&cC ?3. ˧:fy#? %e@՚d((1It�7$v t 7w_(Tn~ i 2maKL7K $�Xk̪K$0<*նêDY+%bt@l=ZDat_s%bf/w=2f.NKŔՊtFZd15&?$_JSsY `O=s; XcU)�hz�'Ca`\C3>AlZE*…^/[މO XYz 9 3I E^֥A n.2`|06d[rɉ-Nz+4!j>Os+;f+y6casyN-FWXYY!u fU%XnAVyRa۱etX>i4`JޝM{76~lJ_A2G0 @GdV.8Jopwax_I@5Gߴa�QMsQB dC0F|/&5OE5f|gɏh?ČG7?JLl1+Ր6~UN?M̗>w$}xw381G`3ċ@Ț,^Q u6_K_?U@,TcB^z)C]HP­`bK\QR*�,-Ίa=NuO\Bz*69;͵e7ŏo :JK)CAxD2 YGr ܿz`J`ӿď=u>Ĭ›k7Dr[ڑ7l;KP:&ݩ@IҀvr_%38Op *u"�K*mV̗}}eVbu?\UiByǣf{4EQ F~z&ʷ, [߆wX}܎7 X^'lqoiW =Ɂ!aEc6ocsycU*v:[M/L7)(// ;+kz~7AFϊSt)#[|Ȼm< Yqxvms+\<Lė rfo<b뮲cm:u`f:ɧcTo1kWm* pJH< JO^0S\>sw| %Fp0p[~Y}C~[4%S6 ޡ鿍p<g&ǵPzU endstream endobj 136 0 obj <</XObject<</Im4 131 0 R>>/ProcSet[/PDF]>> endobj 132 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 21:55:29 2016)/ModDate(D:20160927215541-04'00')/Title(diff-ops-writes.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 133 0 obj <</Type/ExtGState/OPM 1>> endobj 134 0 obj <</BaseFont/KPWIKZ+Times-Roman/FontDescriptor 139 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 0 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 0 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 139 0 obj <</Type/FontDescriptor/FontName/KPWIKZ+Times-Roman/FontBBox[0 -218 932 688]/Flags 32/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 139/MissingWidth 500/XHeight 461/CharSet(/A/C/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 140 0 R>> endobj 140 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5716>>stream xmX Tڞ23nTSڄZ+nj] "E$$$k $,Bd_qj܊q'x{=99|.f5cX3<£+EQAі,z6@8;s\3 6aUvȆ^<~ jfalK=$ą%}pe˖?Jb,[̼IFb 휘##ÏمFJb킂yE #vGĈ>pn̟ã&MFn_hb. M W0 [-[#ng݉="9/x@k{ACQ^׬]# |b0G[ab >abRl;sV`3ہvb.l51#lsfc6Q\b<l6>bYaB1+54e͔rvV|N3>Wd 94uԢyN{8yz ٌfް>d[5f5jFeszNs7~y{ҷ,`=&ָ:҅=DyMdOESSybk$B`�N@޳,F <'U{bXLL(4C9t0.BQv*\"\+ޙ Yh'IV@E~*@B(> oaj"xpit.SB+ Wz&S2BibBv$�R8ote>P&Cqk9ֈ.YU�:[ZnFw&HHA-0Nt ]oU8$E 9 1E2]0 p1sF)L1_a>H!>bhm'ùCs 5zezqRם$K ^Ƿ_%f1Ufc'8IxzRJj2H|YoOa"y'\ iY{ 4p9)iเNȯ�doTsIǺ-qd*Uj<r3Jw\-(I`"�^Jk:],H5WQ >#jxp7 grC"=PRCʃKRMmA$bnu:ğ\^ ݯ6fW0et.W'MCTI0 e -B6y=|t�DArPYNxA/I$/(PS#A$%"nϙ?|ynt6?&Я&J ,0j٥�$^�B@BY9)SR0Jp>FV<g8f+&`9DC.D,;ka&([KϣKr�y$%@ Me1pDKߙ U)B^dܚd Np|Cdy&*\ J#?y0b R7Jd߷^h }_>wAؒТHl.$AcQ<UM^iR؜.-*.(4M-^:P) IneqbbAOܽ{'>ޖ\"y"H}t"VqLAz\+cu)ٵ + UGn =z] n6\n _S+DMj8m{Nb ޗUU6%@}DDqB@m?t6]MZ\ C9xX"O*/1a*y^rN�F�\)ְYubh<bH⺜t&-=MzdܗJM&c&wΣ +CuZҤ8*Bd `} nQM#Fc !'hI#j#R%pU&K_"&x^B^uvS'aawq(VIǟ2pe�~V)q=L|HJ�Q\ILdp^>Er'Q!i] >u)>cj/=[γ8nedAɐ, lUnzێ)dLgˌ2%@Uu}\)zT!d_�e4\%\[Wm)) Krlhj>O@ab<'.{]s^UI=O겶NZhUdFZRj\0>Av8drTY25H QhZo KK1 v'e '[?Hl$<@WsA<QNPY7l$r:BwzfEB&BOug& Wo2d1 ^B�- K҂DϠ߃omyd[o2Ӫkܓ35bu:R{?zEFbp;%dҰ` wEm:�˟2r TUW1F-^EL,Z ͗-i)9YRԈX^<PetTNgV*G9jC٧" d .(ylXAZPH$'ou\tA�W-|aiN#^) ^] EUj1^1;9'@ |BS:cCcDߥ&|n6 m~{zC 2;R</2`;9�bt{Mւ_Hz  &9帥@=jxGTlP5l3raR^ Q[Q:*@%JƮ-DsS.uO 2MJ)ISE| r>>?tSp@iJdkcvmk pUaԗ :-NY噆4$#WE9h>W. zz?ew`t $Yr@m A`ceI5 ꛪƸvj/Kᇏ8G﯇w@T铓5P'ү0ZNuC!ΆwA18+Z/ ] ;|h~qUrgC2V`@qy@ӁbgWZ4.!!,#Ua^a<3;Hv \Y2mn[66wVeJ@jCs6/-lW:Y|<"-hMq5L2~)5 ~>2<t}uW[گA3X: 8aZsU[4o`I̢H:ɪAm^ܼI}5'B3|4qږL$39YXl)mN. Ϗ 4=x"ӤԞdg |ݣ'͕-\?ܻ-QEyq%ZKr9)j631VB"<U,}_*Bݶ#ZZ~K]1 4K*Uae < u�_- K-GƗ)a~.$: loZZꍽ`):b=z6SbZsRZ��e!.4t\ ?e.-W$a˲-VAtC y23NUgG0,O[xu~UF�'r!*iZgy$7LbDscmM96ZFޞhQO+(e.w}DTJ{ňl1m- )ǣ"%A l?\ =!,Q&xr5.?oBab-+7g,|QÜ$VwχėO}q,ȋOCK,ev&R`S(-qP#ES:<nL\fN^YFh7ql[:`"ٰ,N_a s1oAڡyYyYYbU~: Sb6Ơhmlb<!rL 4<Y@mi"3OvKE9QǍiHQ`ⅎQUNT$%$bwF7r+pu1Me |0iY ax$Imfzv_[2�F@(Ԡ/-efEN왱8$Wd0UPTHүxBN)zsWb酔>D}I�YΐaΈ3yRE6}m !@8T[` se: ֪@^qPn&͋ST.<JiW7yǺK.7@$wp!<oqCƳYiib`g3 $ǪDh6مi59t#(u L)Mbr.VG[;Ԇ34URS=ܒ-&f$+ࡉẉ̐JK,׏Գ꾇-hv<@4$�MՊ8? ΂ዝp& h.?+=q!ح_; a ϗ3`+6̶9QVXZPW,`^]"XquBJ,MdU$7EN ӵYWlZߡ!#8:mc#yx% G"?e WnX#@;'g9__ ށ+P]v=$7 GUI{''%yZ3(ٔqY 0K|YlCmσS�PYh\ *J$?)&SO6~KG2K[�nMq“h#enPZy}) eOg+@A^K4,ITI#R1-d2 [5>$Boݔ,D#jt-O?p`Te wEӪ'cX'_iUnpu k|:._g,̬WA>9"4BA\ Wד(^qb(0%x^Ug&W'M,C wEK*}Fw[M/ylsPt133aMP?oNW ǻ[~0]>9p#`$|,xՅ#GlۍAf8t�\"O|v/`舻@fDPG[YM߰$RJx{Y�'*8Qhh>1.VE-%8N4e(>D -~W۩cXf/*6y]ؒI&s;>=~"*l7~&jyK ~ӿhn~!n ֎=[Ւb̥!̼nwp/r ur*ݔh5zzJ :VGԊۋE|n`EQȡu4A43^V͘aA~` endstream endobj 141 0 obj <</Font<</F38 34 0 R/F77 142 0 R/F48 143 0 R/F39 35 0 R/F40 37 0 R/F57 65 0 R/F7 98 0 R/F14 38 0 R/F70 105 0 R>>/ProcSet[/PDF/Text]>> endobj 124 0 obj <</Type/Page/Contents[9 0 R 144 0 R 10 0 R]/Resources<</Font<</F38 34 0 R/F77 142 0 R/F48 143 0 R/F39 35 0 R/F40 37 0 R/F57 65 0 R/F7 98 0 R/F14 38 0 R/F70 105 0 R/Xi9 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 144 0 obj <</Length 5605/Filter/FlateDecode>>stream xڽ\YsƖ~`Քk* h ybfd<@$$bL �Fsn,d,y8Ys`1ٷ/_/$Y523!g͋%9γ̔ұzMގeif fJƹ\_ݼZg,nf6Ma2Lnߣ]q_Wsh{w??vNcd75hC$In=Aq.o(kz\,.ʶ{|"a.Je8d78|)qda.E'9rx)fMg_B&q(ߦ{rV_feVOZʓXI=}Z?6uj/1_Y4=͍HG_un۔\}KW2r5u_߾һ+)K<XelN%u8TQ4{&>/1 Q{}v�o(N)%b]+Osxg\`L1Xa:TǙ|fMT&"$q.oW:5pD${TH#bCZ'$Ybߖ ]8'J.aFWiĠcƃByS?2"J˘8J XV]eQnyH`zv ;2g> v.kS&NO%F|4:N ^>m~٣T yHvG.>>d{YXfKd<*OrA<Au,Ey.c`33Yduw({(ɟK d<hggɣN^7J}B=K`Y>yBLz4WѶ+֧S1d$=c\dT%QLݧ;9i|==iF zd|tDajJo�`2xsuѕ"(*;Usq+! t@ݵ_maKI-?4dnj.\qmn-T,+Dvj29Ge_wՎϨ6CՓUy̹v曘C)9Jqq466q.ͩ3NyC\)r h+[|N{,(QcŶvo8sT3 +<I<~� RF?_~O%a!XX0`NPwSSPƉJ>ӯ_k+(I8m *zKu h+Z=H8bt<&XF˴#V[dI3)[7 *Vt/onICghg6ci7[@H,n{*7^UʽU^7]U+^u( 宬ҍP3խmi 7;[7L{hX6 I�"vK0ZtrwxK%\$Nm620êݞi,}p"lY}dXY,V\%Tʮ$h&ܩ_}&Q[aᇪ޿Ǣdi^-ɡǞ ތ97n׎UIp^wC@Mxi|C$ƝdeVվF=7tA I_'U`#R-oe*ꭣn۸v;H)nQq_V/KG؉ `ݪ^,zWlY{ >C_NOhoMRvܙ9|}d1~ 8K\&\~8-A]ؒLE1LG{v*ҭ(V%C2&K~d[#LNTGnVn X�fz4/X:aQ䱵'z b VEǃUD 矃 -,=,3b/jSH 4?rUd}\hm<8iYrQi H4 Xb K5uk[Wc`rl +<WFO|54p>XQ4m;[,YfW�$FA&4-US`?3i"}S,ˠ|©I.AnRs/%N*Ip7 6(pNEQflmc[^EQwxG�[&7ϙo,kKKl )ՎF E?Ğ|F"aV+byhIЮϏ (]_(QfE2Z=AO1#DShrH\V0{sN rUWXԬC #,lP0y!ł$Ɂpv_N3Sh p7# 4QZsчcA"[W5gLX9 = p܈Z4(e[Z Mɥ]eJ3;ݡE \}:Ď7sn? qk< |[߶v"Ać0C(uC�@weCKR c9vF*,*> 6lYuJ#&|")b{cC9l?�7*vEeX(LRG 9vnAX[r]$Z.=q=&]phNݣJB(mEXUS*jw̬7ON42Wб}с&rs)*οi1BbǧSv̹)Ȳ 󩍪 .:qpA1'κ;<Gl1V:X!/Bgc4NU<UTx JC[AHX<ް↋5h=N 訫աp(A:5Gg>] 'o)}ħPm%L�@WcEQ5o؀lGqfpni8%͠s`j7raU}F @_2ʷ>c0aoιM9TTu gY[- gա$y"n2l6ctK<K.tZH k^1TєQ.r[lVb=.lhsE#yl 8ݶy~=5 tT"Z&u){iHM#?;ө,,@0H<H$Pji" (Ak 'hC@)Žxؾr# 8kXk 5Vj]4L{xG-d8=c 6;X,}-A &=ݱX/a]Nl' KpdAɃ4!kjD޾{(Xo#\[OؙtO�VdBz B00=};0('>p�@H}|"G넗E'?5o?HHSpޜνH$ذ`ߠuSO)5yS1jx)X:N|A% M$~ 3s}dЂc4fOv9aZd2gP0�.#X a#r\Dv4knNzW vÈ=ae{ lkB)dp+Cm4zR31 _ kx][Wdk?kE@PA 1(Ub 2\rK2EL9K/(t|ʦm׭_C9]T<n9vP0\챽0$/W*}k4 @x]խi] $:$pұ5\TT_xK8?vY3M0%#$} sݔ'ޙwmrtsBJHa-<9H]'V;^g lVb߀’'cPZ3ۢrPN [#&&U+52m)!IL"cX*$LOBll"XGjF8jHdOxK@bJǰ(k�4@{%n R9RMx,2r`֧j%id*KP>pO`V.jQ'O0H{졬d*qeC>BV'n Ae4c=hU\dKBGK+C/=̠v 󆦲їb/7]%b5lK2vv]/3i:PLi:'b[WG7za%+gxE "-2RX˓ӻ $]$? (_q嗑34\Pρ:4>}!X֪ TI񂩚SSzQpC 繘12"< B.t X9.}: Ԙ$:i';m,� 2i Dops@ʮ'bd#HH0,1$`aTi^uWR8?dLS/"NtnYI0"X?;%<m=BV>1L%" 'z߮lf,@#Gߗ-ԃ{eF]z[ ]r1 a{7tQ31NmEOY~uպ썁DZ xQ`es|b;U 1"xm5 w"[G ؎0o;]P w$5c܎Ex~޹/k']Sx?U>ʼnБ=LKKSX B[Ȳ!Y4!GO%L6ҳ9s6I?2ŐoI>LЩysx+>לtf9>-ckz?\ } 8فi_+ǎUfJe$; F>)}xPp<8\E = V,8) @a9kՂX,L  4|ޅ5GKJUح|ր˽?81g#͹-݀`xe?+bLQA8=3A0Q'-V%Meݭ8,Tuᛩ0F4RP]XolVIlm0 I.@o;J{X�- w: %s\Mҧw*W\ |iWH{Vko)g) 'u;c v Yf/F I"BT3qeQg+f˚0?QdRHa�.q/a>`)IW1A],3p x SzIJʫ%a.W~P )q,~mx>행-~؜VG !2TK!f:}ߴzꓻsjm*SR:;BaָI�+=hϒuxCG@tۣ7/k2 endstream endobj 142 0 obj <</Type/Font/Subtype/Type1/BaseFont/PIADRO+NimbusMonL-Bold/FontDescriptor 145 0 R/FirstChar 40/LastChar 119/Widths 146 0 R/Encoding 44 0 R>> endobj 143 0 obj <</Type/Font/Subtype/Type1/BaseFont/HHRWSH+CMMI9/FontDescriptor 147 0 R/FirstChar 22/LastChar 22/Widths 148 0 R>> endobj 149 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/file-reads/file-reads-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 150 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 151 0 R>>/Font<</R8 152 0 R>>>>/Length 12549/Filter/FlateDecode>>stream x}]lmTJnRDzdT![$<%FBZX;sgGW/� |g~$ۧw_>|'~_RuۧOo|Wdȏ&ϩ||=ҿ}Ocp=~?s~%/$Qasovn[Q(kJ<uyUլt\Ly[_>У9LSW>jz}ضvO"SI_9u|cB])99u|c\rW?_ "͏|"^_?h$$:N~/9Uu|cR^oϾ_PdN\o~()NjE{OSܛRs˲ON'+?=_/Ko{w;|MfǏ~wo>2SM§HwcØןH/W\(Ls֭İ~:I}[Cv# tL+Ozɿ?OmG,O_-OQj-/⿡*la~=G_=T%zJ>`%ꘜ4<d,zK Ǭ,8H_3T8_Zj35c>RkС:AC3 !PrE(LO59ѵƙu]m(CuyI/{/{&f_Fw.{$,wْx/{]K{ VNeK&lL&J姑 UZUjEglijϷ퍤\OY+:1S0.3:? CRZф?ou:_ʹZRkww=ԩSY;Hì9H:}|iM ojk6gN}Fco`jjY=/) r"57Zlc*~33쬛L[W m!FRӬit3Q!C:#0DWn:䂮<z1+E'^g5%datgROp!c)GKKY8DLܵsu15\wkepңc*?bjnSuw YVpLj[ņKXctrO[UL;3-TQ:cmVéG!f'qZՔ?Tɍ/! ; #Ea(C 2>2sV7(he4Y"e4UQfi i696SW q6e8+Vg2M! 0V36l^kNg2mg2 L+2dd:؟3:c2]DޙLN1 Ut&p;=63֏Qͨ7ҙP;K܌.QύWe3Y|ی.FM\=EQPR< 7Agqc^^؇7 ϙ6q,Ra7)-zK C=.6\< @sjj4jMx{# {%`&5QQ.r`qiw5{%]pJZ!gwtR2ިWڌ3P/P2ިW`QcMߙ8+JK`Dߙ2QۤMo+:a78ȕ=?2Aoq.ިWz57B]6y`yvW`mMbm A6@c0.!y{9w 14g/.{!ɱyȉ/{9w o@e/lwˁ^ym8{9w !Lg07m{!Iqr6 ^Z`]k^wCQ~@őuOso'o{=wyl}߮@<ʱƗ9v0%9zzv@69;.PONc㇚W1bx@p!S$9_$R6jxAjl@mLǛ%B¢<F`S7zٗ}IPn=' 1aX/o#'6ƘB3Ʒ^iq8ӤdA1MRH#LSUzia3)~"rӴa5iRTiZp4�Ӵ2M6i%#7Mj/4'5M{4isnJ d>ܖk;t<[=q^|z?>n}kݯQ{nO^q_ <|t9tukN<l÷\(GzQ0_Ҟ x:p^Ofg='?'3w8'3NPrNF:=t'h@W4`t"ʟҙ(go!\<Go^rٸ+bwpd#W:Bztԃp49`z5#YWeqFu=�=KYƫ BPwO'H҆iī^2OF=,Dok0WQ. n#WPn, S WP̻-*D/61l_Ĕja \U}< ;R+qV|6jg6K0 7Ѡ1F7]ж r(]&Zz3 yp[jJ!3_aWW._-5ڵU'Em�`JOtLE,a' \ƹfWDC2)t1sݸ7D 6)45Wc%c*2:Fݝ1\XMD[6fETƖ a? bZ n m ydK ѻ$TwQ-w UV]/i[üf:LY/[7}MGi#rm`{z] ´.Ńz=#=z[ 5�7Tm`A䆊tk.**tRaILP;Hm"UFFT P*IE [pR$4RW\Cۜ:)t$\A 7 Ta<H.)t= g7R,�#R(.0wb%nP|)Mx wͼ (@t@Q7PAJB=)g rH1=R~FBw(<H!p=Rg�T PLk= q7NLmPq81O@ D s,>�o(F %É 8x7N:S+l|{ϑk0}㈓h!NzV5P' Z!V!"oE2ܪ[m$=#P 05 [ Fh-e.F>BU*H Lee j@Yћ�7@McQfMkAVSqaT,>1z.`Ō*@-^Xr+`iѽc< Um)dG;!+zf.EYqာaK($�my"&㋰T&!z#�݇ƈˇ3t*fpKnw q>,3[fZYvźpLDفYjs#.z; a 1tpyA3\x}_8\r#cFqM6FOnƤ"D, l d-WN!N#D(^:AY"tj#"",сHDT@r "jn"}:" 3:(`Y!b,aePq  Qt_C"Je<APz �SpQ@!7DeYsxU<DtD<D¿CDټ"D[ADÜ",CR7m.:ˁ^zLՊ/ssr B.I讇<s1tYϵhN !̚"'D%…5Ii9eD @ )/U|N'co.cŁ<aϛ[`@rG 9",HNL4P `pV$FG08+#~$gQ0 ~Wu4 Os(Ztd<WgcX"t<x*%`V~4GTOs9O<,hNp&X+?Үư쬧9gE< HV*%,i'5i[`/Os(O 0 aD 4gh4GD_aPOsDx ͡ʥ%L4G~q6M29@s Y„:dTL~p+ب B]X^C9ކ aDGo' txB`ѧ`B90ay.K&ꨥ5z]&h:j$ ]Q6$zC-OutCu97] )&`9Ty9`ӡ:A`BCu::TQ'Ou$ A<QȤ;Nr0!IaTjR.pcTg)(@u.tij%PNӫI@3ՁL#k&zߧ3Y\ԩ΁TYLŤwT#WFY83}SڰߠƋ=~U)c$K8KL˜[}(Ba*fth};V؁|j u+uG/,<SS[?p<,mN+}3 5UNr^&.5Y[<ҾnXh0ȟ]8caN0eә1g4ԡ c&s8{ DcVA�uHɧk|_KRpH-N4e.8"犁uϩc EL1I:zeЏ:2!Џ+oc~Bʾr !uL҉}; <*PW/Ad|aBs/iXTZaDU3uź-ƔA(B?sKt8х?fʑQ8y͐lP&� xxaZ<ɘ_8EL][2Bp%Z4[WuU{dj0!97`_¸2Sg ^E1w}P@ M_!9dq!ZfZȯ8l~- lմPEDžhQFi…hQƽXv Qd\_1i诇.ǘ_B\2.;n1.T>B@FlAEgw2ژeA,nai~4莾3h>bQκwu }VFњD}Ht>%pZƨ@3,@C2ځP1KY%sl^vV9!06DÐ.^MݤhwT"6j&[-fWvDz3ٱwvALeGbKPQDMW6q, gP8@0*bTЮk*겫 !]aSlYػN]5p6c ?BbBTAVoZ HCJM* M JTᨂޫT5Љ*(9*9U`fm9 `iyQ䩂,D<USԚxSI뻩,Ⱥ (.P:TA5ەTIC1+\ (d@MnCRM\T5SA8T!AUW~3pTAѧ>T! M*?TAP!M -uSATU'QA-ZvT!/U~Sַ S;TA݋F 0vki8 #3Lwn6y+т>0E26UQPM, U݊npUUW}S+<sS}SGo`sn\&!~S9y/pU7 ~S+vKEQg c7UtTA$P UGe+Ž5opEUP<<TAPM,9UPP O*$Q+Bb9psUPdT ;PC er?T P;Ps*\qCJcP+Z~B̔;bχ*pT;P2Ӷ*\CtET"8 C,!*"G2b|T"p7UP8uBePCG '*䊂BfP];q !RwCqw\LxIW/58k+m(XҌ`,ʡJT(` CV,ܣS(2`4 ,`6 91[5*X5%A7EPV,j,`TAPpQ ,\A"H{"D AYIP(Ҍ`iuJ.f[S AaIPXY KEʂE' A+X `!@ ,VR;S((XtoQbex>cz>A" eAbz>fj`t} ^b|>c!A4 GԎ+DA,zP Aw`ӗ`8:3A7z"(Xp+XBwPG `QKlQ~4Q"h.,M)f+?u.,u1`ނ`QHQNP(``Q`-2EeE! [^Q`Q|Qh[P>A`]`Q"^[V  YQ"`Ah 4E,n#*X^ ݩ%*X}`945<?R|N'co.cA\"Z, l ;, ^PA"@ `{9a`Y`a+X`a*G D ,)۠`ۃ ,.J05,JFTVT:QR2*=;E: [ A >(X A"{KA?(XHİ4g,E@,<M,`)",ר`)RzTAR`h.@,j#(X A_ "C R,Ѯ`)ErT.QRJiQ"ЉK)@),`B4ˠ``a2(X AeP?BȭK )XY (X dAWJT0paBF AR -qK`)(xpTOuؘ!(X*WTˠ`)H3 ^R - ,[KAW0l,lm>kT%KTW$ˠ`)) CAR . ,] ;x CARІ.(Xm =`,W A1(XB , ] N )rwhC* |gȎ~7wCTv)*xC.ϰRJ4T| B( icQ%3+h QN1|0KSXf$3, VCeнrMXIHH;aLṉ+)զi) U`$v2,|0kdWWe_=FvHv"J;eN-n4֧&NRRI7ܥbr۩h]JY2*5 컔dVU>5SuBݖw/a!FwZxd Nf}4xc!ÆLyT@0!X0ٱ/)RE}9]6dm?AjE- 8k9xM*,=QJe^+VUeu9U>Xm "'gKwJO];ܲNYV>gYA%se/YVlϲx,+`\$/[V>(eљ/+P|ULle ϲRD&98 5fe gY!FWò,+P}`gY!d$-+?aY!o 峬J"_V`*)[.yP>*)[=8 Ujme,YVPe.YV~Y!J BsUm ,+nPnYVZ[7½er-+,ᗕn<LneGFs-/lg7Z###s5~w}JU|/{?E=q:&ۻC5tLf(hݭzbf\dAh<1;&ȮcmK;& Z,H.v1Yc25wL&ީb3r}C!Ղ^ϯCB_ߩf{#+Oly0ry0hZg3~C|ywYA5f[x WP 4z:BkiL3RnVod=-EVo:M-!P,;DƄT-QdLj\WiNs7y Rv/߱vWZ-I M-i!fS˶ *_PɚBW˱f" T)Zb1 "pk[ nhb�JLj)&bT 4ctIL^>L*QmT-9#BQЫˠ-)9eZ/5|9;>Y(5\560.pӊ8\֌ ]=ZY-2|֌\==Z.h͌V{f|V+=Z3bG!{Z ѪE?=ZYT]V%Tӣ|VjXGI,|V=ZM{ZjףjB}VӧVzhe1g=Z)IGUjggU+nǢ?s} 6d4˞Wr(W\iSNi !jz/ÐONii:<gQh և@:C~:Bu<aNЪ&i]�_:wZWRծ:wZ#$tZG9;C} 0CuTi0NӺ]uH6N{>s\}5Z2|yJ0Zeq)aWz*K @r` uoٚΈ1Lظ:5i`;T6Eik7=dX-;Ǥ9̑vs5i>]D&!0v#o!:l1cP!ऐwz/,@o:lRxp9c" 5l5 )(+8РԽ'`aw9ŮlQtaJ.Z+޶y�$LVrv ؏x�eʹc&UhM.!)Jdj^$4R*LeFm^9$+t%P4F俵 };w<%YAT.QI0D8� xۺ$օ•Rxk]mQ{X_E ׿䭺R~/~N/5Ygjļi3!,)ԁ;aR"l^bPcL: )p(Y^XJ >=ڍuo%1qGbM0- DdN z '6(yEi �,\Xx@=L˻�T#_x@- &` zii:W$ hyDi�j6<4]Gx�`d#<b� 2C�"L$4!+N4c>ͼs}sۢ[4c=.ϗ-QAo�`ь?F&Hhl-,-~,[Rh,Ef<X47ZX ΢_~h-,E2-y,4[%EDf[49h$ԂE0h̖hgѲ{UE3|f ](Gŀ;u 7K�U[v+xw t)%[=-k͈Q]}9 5b%K{DɃXlFjmk3mU5?#l)�n c,[BlmJY՞Tݬ83(haB6w@HYa,hBجn)I=XY(i}1 `8S& |?ǯs?{?uFGU~CW>Wp= ò׳0v`?¸! w`\pp=pe';3p?63C Ђda1Wc 2Z}~$1@}z7W�Ɲc֓=\{\k{\/m%GH6gؒ̓zgiC4V\Ϛ*O<ա|NM/w/?_Zgk"ZNh̎I1;[ٺbTl6E 2ZNnONPQNldO)LY,W ;!5GaCxt{�ϧq/5`YK  <cbPߡp/[yԗ,]$/YOKmR*VU^ʥd̷X *ب)Шӱ oK/EL|Ml˄dhg#Ԝ޸mT=T .UC *&ն2߅ϻ|dC,wq: s*hɠ=1mKih< dL <X2}d6 ޵8z`c?TDKF%n(cJd!ł=K'X\Rjʟ`Kҋ%(5+Zr6X2ԐђY9-Y v|oɷe%;YK~5<d?NsYK׹ Iй Ж 9ؖ UdбdSJC;1(b0c0b0c0 = <=q о 6.6d86j94ނ86͵BV[72 &bˬn-\;,t0VJ/Vs$6D?t“hCi \$c)Q Qpо%t"t}9_>AR;:!+;:-k Z0ִ7kІNLVq,!#,Y9UhoJ!7!UPGy: u%_[DWAkH}|?y܁ა�м}3G;ZXr? 7B["&Rd2d4| L%V]8o֨ac" YjAc Un^Bd߻5< y{Ac%\U$< ASiTt? c, i̮d+ҺjàI֕.MlffpZ([; H޳|ASrm ӗ"js΂GJ}: z| W:]B6u<s/@&΂Jh['YQ+@&΂vYށu<.c<,rD vuM sՁuT: zKy6sujO8=,x[`%ك |Kk>_d>\uG}/ DF#cS�id�7ҕ*m, =.Kw;GƲ1頫>2Mk,):?ʱTfoLNӏ@g`Ј5ָո*\K[d5!B{G U,R7y c{l endstream endobj 153 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 154 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�Jc endstream endobj 155 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-file-server/diff-ops-file-server-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 156 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 157 0 R>>/Font<</R8 158 0 R>>>>/Length 23133/Filter/FlateDecode>>stream x]mq~y$#^k=R6(Ӣ@ aA3o$`ޕVϝAۻOϪ^7O7ݟ>ŷ[O*? ݧ_{joHx\s}^_?>O=c|?S_;WoetKK?o[]/i^v/ % ^~O_<%?o?CocZ~Q҇SE%GBo#O(ߩ!o}(NEk(ۿPkoqX/r*ҧ<߰+<oN^_b[_}]T?y{R?-đ[?[Hx W7漇zgWͯ[hYo_o? "h8G7`X&Fo~7CNCL=7o~7o?܁ ]蛟]Tow8B?<~o_ߟ9m3;m "F4h}r[}ޟC^, +$_u!Fz 7d?~W/Sf>hcyv {Z_ˣ_|VF~w#$;&3:È!u O @3n/y_5_F2ƨoO|^ʼo9!W[oBj۷|_: o$on=�}t-#.`ݢ_,` aoO];% ^??{=@ tqo?3ϯORW5=5J}oú`fi@X1<�+ؿtk6HoXƍ$^lnX ߢ}.13ɁXBKMIxwvW<V\V y8xŪ>_쮌Y`c~3aG}WZUzοeaef{|]fahu|wK <>~mg"xX`'6,HO(1oGm ?9 {zcmC2:\i{$Ro]{ BǞD6x0Om"0~m_djwsz^aowpSi|wt4_ͬXmc[ ?2 (axOTm%3;!3ߎyD)O`>`Q G k22{vɹ;wUjā6~*Ͼ=zGt"=yU엙0Op/>}F ̦C@Td7h0̞!=hf)Ucq& :ДOٰܪ> jU`=|%C|]L�AJk ;?Ij<&w029>7;S >l}(v|Eb-{%{Zf#l د,[0 lBꍜ°4]l! 6e.c(c 9ۯNc0Sqz1f;BH{CA#P1+O8<JDl1<1b+1! Ҽ1B[@Ǝ`?zɚP`6 L[Wi !61T;74g f.-� d !CC1XOchf =$g HN�H{yNkFJԆa謡 5k>ag CcK9c0H=c0jyg v A�Et3/k| W2f=0qo6�dƟk <NdGccPކ&+2}]0;]SP㪼gZHo*>"x͠^3"l<3xq_NxƇƻJH\OA'|vqM'4{뙇5= <p@dj#ʙgmЖ)bG�yyM%RV8j Z# "nM=) xTn`]{RxIڅ]^Rh)4ac]:b =9Ck5 %50)`yO5|o&4<"jؘqMe`^g\ pE 9X{]@Pa6;CEdyI8uy!gz%3x .iVcJ~02v pʘ:;/Va�C?fRɮ(z'$oRPo=EgSygS 3 3){<Ǥ24),9pI1ՙTȤ,jI;f':*vzΤ%oR>a2)c3靏Ef--gQS,YlLv6mlB[F3+:92*3iT>3 Q16]ڟw6ŀxr6ewǦ}dIΨАL ReT hEQ {DiSYeS>RM!eePewA;wΟP hp2笺ҿ|+)~<g@Qڙy\)NʥrOO.Y<ϊAqĺ* fq̌[ŮE(Yi@a.*X 4w6dU;PcqdNd6oVZ KedD-I{= \Fr eQ+[Q{B?mFB]@a!*@Z94w$^ĘjۙYWλF9/94 1%WSO2wYd!['�7>Cy硱nN $IA@l�<<OuhG >4;4x;4<CcCc֡sx(i;4<=C3A<ZO?44B%֡vwhh:4a?Б}MUl{)QhCC#.`*e[}UWx~s\×n|~f~=+x50F�J H1ەXdH{`H>Б~05xٮ`<\{GgAG:d0fT}gwݼ!e �JCEn;'P ^cWg('f J^�"M7k!}kH@' )A'`A#z533 0r%*I@Y`P9Aҟ恎^d욤�6N/fK!3QJ"W.Z̫Vvϼd_ud 2y`Nȫ{HqՁH�p=ldU"\ R/3kf! YE@~QI%zA!:xz �8-+.ֳ}Upw7 2 2÷D ~,djQCPT#x@o o+qyqHɤɽ;%1g2Iv, _gw*`/5%t-vYeW><yc =zeL i(uCd)؋N]Iv{v=%PF ƑDM8mfp�&ffdfX,U e;caWw1]�@qG oqV>aGȍ2m#0|õ((>OdY,m#Q œpeYIUt7Tkcͳ%9*2�C>v5, "+dz)-+age](BL8yfOo*X0#N# DnTeup* }4<L{=0L=ׁ){VM SO`u`ʞׁ)"Ɂ)TJ?8e(pj 8e\`NH8ra)x@5nrY=-}uN22 r-BC@eyTݞZFV~C@E:m#JA*QT>D*R͘[H A6Rc Uۙ[Hea(G [TeHQ(!* Eb;5&P5K�سTT(Gw@eN3[4NXGX8B{TC qʀ*p Evd%NdS{ S띩S(:0eс9LT@Ɛ킩Zi ZU2 jTR}FRm_/)KPb)6Sv |TJUm 1an*>5:23*(,B�ITI�U1RأlS, vpm)te`)<r܍S+p*$l.PypN>|*@RT!B� hB XiNUFH<s3ޖVUjU~J.}ߠ)Y=NU]28">Hb1 UM i͓:'D'oA4`'ZuR�=S<Zl,6Օ>"l,1w^ľWfJC4 ]2POBxOs5E fKX!0iW%BT`uV vKٚus0&!  6^7p< KńR쓵i :JPgŜ}<|,UU%5Htl2rɡ7XYxeZn~O4X17[b_6HC)n/xaa *B, 0÷cg e95cd zavH'ĭ]&8Lšn1,tzX r?ن}@&3 J.@6/tzX1kLKз" 0~ C2KXmvLd¡ YS6M|,d ,:a[ ;A*=na'D5B!S`Y M[KȄ3cLK*sz&0Y17xm!^d "Dзp-y4z?K8PO kX%ЉK8PO J_e',<9v6V2JxRd ]WH0,E4/P,kRd `e?1(ty|eGI+|yXy+\GJ0c"?._]~4۷t\ɻ|%sp+|旛whjѹ|/Ϧ\|( Y+Ügû|cb|o.>ù|_sWzG=x| sRԝ7'h-㣢`w~<E]Vw<>* y|㳼U7ƷGI</-j{|;_ly|bYr 1a{8_ 3qߣP> /;ϢR~A y;>}F_uv1A/=qlsn a{{IgDa%ϡU/Ok7}Fm4}2 h,O{h FZtnTp= r~u&çr > ~2 v~B Np ;|ޜ'q%4Kc ?* 9׿+IH:uFO3V?9؋f_+F}{$FPu#HkvWifH F]] D--e"z 4eΠAl3HT'e i/O"r;O3k cl5=3EAW^qfb;h qfQCbĉyEKbm2K\H@2EЃRiw(C,1a):CV$ Z S@®-*bSBL_0EBV;%,݆-C<J*'a(,eN&aF]9{vL-*tj8,E(\h~d*W)zg*݀k*2^/gp'vRgzq`ز+ }Ha6xenc51 Ϥ`50ޣ^n]bܭθ<b +Gٔ\ԍ`  2v;4i]2Refcp֠[mug+ZdpWvQ1f#!,<0n}U_]A+\lꍱV=q12lI &" bVk,sewg ' v+gr1v9NS CGS vܓAx-ޟ4#Yv<"zy8L0"9<'sd]۟gT^?G 8۟^Lhls-tϙVޟObsg؂yQbsyI ޟ#[><,v9 vq9yIlޟVGϣΙMsylVu1pds.\qϙޟSbs&@熈۟3?LyO~y@s&u!s憼?A$ǟ3T?{?g*z.FW?GvϟF4ϙ5Kޟ#/n'-sf.$9l`pnޝ?gV:"LNFQ')ڝ?-O:S88qȸƷC(c̭we6rБΡ?86@plN! Pu7ラ +4y=LM+nڞX0U;,E6Cyf*D;,EN (j|/<U#EYn-nTC J#+{)3TH,Ddz?+"EhlROEōh|F[2Hzw(spFu=ʼi 7|WDSLDc,VHq"ͶlԊIQx-%a+&-,װ:a+&qB>ԙ:IQyBQʼnWTgHji2Rg6`>?.~4!:OS,k(0So6 HX35͆7^)OYCš9xԛ >Қxߑl^lg D{g!\a" mU(=%n;5e鿦\+o;5%VyM~DL5}jV5kVvrHIzQ8'wwgw(s@" xt5%,A$q<X(\5[^摅}LWy$0GDrWsha_ςb6%DV03( Dp)f5 Z-"q@,y oxML`g8$ǘF]lZA~1{PXv0q@4±@!±% K8r±@!r}<(Gq &nNP@7  8\=(,:Pa`@`;PT4T(,;P@<( d'=vA!=(,8P!q89js`8HuΕAdR8A;;Pl g6(;P|�Ձ9@AɄx@!byPPN9P\>< H 9V! JxP@q9 xP҂ؠO1Hj,PP(zP@<(0 EA10< J4%(0)r@#;HmPPrTXY$] 2AkjZTQݠa�ҪA@(t XPP 3{P�­oPP&ԃBSYܠl(@.P(=4q@!_w}\կAA8p[LFZdYL ?*9׿ϜV7AR6L% 2:e$|hRahUg�+sL^tnjDif"_2}_vQNQBE� (/R.@9Ccc\i)}U\R6p,@&K9/O:BrieP#rTb)$,S,PMQOVY( (bglv,> IEx&ijCf"<@_6)/Or?a'ԝy-D<lgL*oWIL)$SV )"E23v&9Ǽ S6ENS%S6M`!6Ba s"eљ2= 2H/Й1u 6ISL_A  yIhaS�hԅmHM#!WUBm+H51VN ӜaVL L8QRBZ9ʹm'4k!BHVQ12cO5|B236uEc̤mQpPHfUs(E(R:If6(!X.:K< 3K V~U, mOP<uÂ*,$63aH<,ʄM0,D8aŸ 6`!‰v 4 U1,XDf&,X ') v@;A^:Ta,,XD?',ء3 "nxXp#Y=8X0#쪨,XkBhC<,CT,DB`!@ tfm, 3 Hq�8ҙ HUlX^v!vׂs% 晻jљ ,a,Xh"Y@ւ*>`7zX#Y@|ڂʂ*4e FsrMX0}Hh&,D1a N3 ,,X ekB̄Ra$:` y,,XG"d]㚰@(ҙ \=*a,X0Dg,D(Ȱ`W3 r`,) |3 rYY`o!Y3ځRX1Eљ =0!aU3*{,юtu,=lB"7;@6 %>ؤ%k\]>SKl~#yYSM6N{g*\VT68֙  3:�Ʋuq'̿);eNeH-1on+7RR`D߈2i5ro$Hgz#_uo2;M=# }g_ίPo}h$A-Of)x)Pf"z@C E70j UC EO1'{ۘc{.euB n?ӛ {hxy’R4NR1@*[HzXP8RpVyh['dc(lLӊ]svN!aLP kʏ)ń3ARRCy)hR4ʦ"؝Z3AyR4rZyd^5DWH'* p׺h-ugkp⭸&j&OHkLPN]cm®ETZ]IU `q&pMHR;/ I%o [tK?Fq9Cqthuy BD3gjA`&~gKm<AclGO[)FS7x#R <DqiIm^R5u܋nࡈ`)mvI/q 9O}:ӈI -l IBRʂ!ʡ)5^G:'I`0yiQb8n;&E(KH1( Az7]gEs#iFgTuQV��g)s xs.C<�<'iih-SFDA E6Jhi4wԘJrSw_ڹ9C4E ZC 'd^(f&L=6xMf "Ca(FAk(,{Α}!/Khj ,F2ɼmuC${} 4Z SS m!oHٟ= Pm!�]pSj:KE?o|17iLmoB7IoBRyo rnE`p<":8T=F M}1zEY|#gߘ57LE#F� Q|#p^7_Jp? S@Ճ/@7Fno(9= u= =$�FzE_|#B<lo(PPՁoDC P9 4<BloDj\ ћ7"r|!"Ճ/zQ$Z|r; ?7@Xt/�}"|! fr|7R8_poc =b |bTpKiÃo@^|_L*)z[|$Hs%1Rl/f(p_|'oY |1GG/do@J yY|X�ĝo H=ك/�q|W"_Ezq(*tHTGJ5mJl|Ǣl񚓢?/)/OIy]E9K]%߾ +}#KV*(b3#k4Ɗ1iлwi!kvfM05qfjs(u̱j6wIk[6yҲE|{jM#8$blQsOl[^ܤ9N oR1 =ϲE(Vf5/%7Mo&*߸kf.eZr%]%o?B - /.[Dɗ }]#KTBf4qMМ6ih\]cu@9B)&NŁ >\"v.I]k~j[g/,`be:!%7j8aR!ŀ1!U%Ik SHܠEȅwi0Mr\1` )-@HǴyYQ6Ws𕠁"w|^ۆf/szMTH'Ҽ\` W8/$qDmi\iIKYk\n[gq4뽀diGi]qqMԼ\arFr;;V)؎K f0Xj|N@Uxu%]sqώ8sIw?;S. u,(-t&2?>q k$Ƈ^ӨIF5 ld)R8Ȓ D,EjYV_` KA{NYͪY$R$'p%A"_ʌG!źY3BȒ%'p%qFȒ%'pPd%KN Kp${U7$GV,YjY#٫%KM Kx${Ud dI #KAHvnda19dITEY(8PYB:G*,zd[ zq# ˷! eGA<P.{da19yd GYw! P/8da# GdPh>B䑅e dQ{d`avȢW(Cpf7P>:da),_Bx%+"xdcsf;BU%K#zdapٍ,5=ݙ),'Y(YD{dftP;dhA u@Cȶ-!K@wf7P# %Gj,F! QCR+ǒ5#?¬2ﳺWuìtgV=>XY}3+<B>fV;xf|ͬwbbVQvP.f5 3,7R3+̊IYwsIYQbVL:\ Y)JL9\̊BΎY%*ަIYQ3ĬŬ0w1ĬfV⹘Uͬ(왕 fVYRxͬUŬ0s1YQ3Ya6bV 7$ŬofEgǬ/f9Y̊*ΞY% ^fV_=Ŭп|3+8̋YaxfVtͬ:ͬ7JYL.fYnfY.fY#/fYjofY/fYof@\*Y!ofpbV_ì01+DV7%!Wz1+/fTͬ_Mnf<Ŭ?0+/ftŬP57bbVKU>g v3+B.fYr145g.s1Y.fͬT9̊Y)ȳKVS0+<]J%4ϬT:̊Ŭ̊<Ϭ#=YJŃ,)LìT<bn`VtzfeX?0+L=bmfV?7bfV*:dabV;dIT:̊dϬ31*=s?aV,o{ft3+V=b7ŬPh[2nf!˘fY1+^*iC1f.YAfV)<՘oYK`Vcf{pnfks3XͬT;*SY0Y6fYh7S)̉nfէͬKp9u/kj_>s“So#ʎ |F}KGܐW!x,g^sFLP Liq;[l>VY īh\Z`\`zvpխGgS|5(кףxխPgr6ʁɪYR>z`܃V3^-~:˴u=೩hqY۳>ֳŻù3rr`)Ruob~r`;\eΒճ:j`)RUj`h3ZuK= #PݦRV}n-<IB,T4W 5(кhխ5U�'֭a-舁qQ[/?CֳE$kvr` -nM- Pl6'3:r` _nK=3o5M4(9iC:j` hgۺ]v f a(t '042;dXBc vŹ]y%4؝S LICQKGj`L $=ڔ3NYj` 󼐩 %Yz/1q^qgkK-]b`M|YR[ N=a5isNΩ'7-r8N=aӋ㐶q cl86τtz5wN=A-x # z8 :ܽSCǩ)Tr\m7~GIo5ߝSOԹB<%>E{csNʟ:xs!p.=AWcs<\zT?.]:~\:=DKMǥ3OssL7\zl\[\:ǡΡ']rr|kΡ3ˡ&YЙ0r"wЙ^7“cv qr/Cg)xt5Cg Zˡ*Y.Qd:ۡ3m֢s&Й~+^e,hYCgy"ۡ3Y1Ρ3C8v$n|;tܱk;K^u{_ǡ3SڒW9t&\r"wsfِnP;zp1x^Ysr3OzfR"2 c^e|=A,]KR.h!b׹60o ){f!0;/- {0zJ{ӛ=`^fp 07{@qf 7{&=tJ8E {|`/=k.0$M0aHY=`  {= 8= 8A= sL]}`XrX,=0Qu,g^{n=0ov_=@:b|{0\?lg؃Ys,gIؠbXr8o,{`f[/8c[/8c؃Aqf=`A {܉&J,h7{SOPqyn�ə=p  ]s8{HAoݱRp{`[PJh]i7{ؙvh]쁝i7{Rau졶|uU7{.mi{4ؖvLE\A ӳPQ=\ֳ݋=LJ\:7]b|;PjpaxH/0=0Yp&.vɋ= UБك$Ρ3rf[.ΌgL\쁉=t}Ρ3At9t&.\sZ{P˱fƼCgbM9t&.d7Ρ3w&.0=0qx\t[/0S=4Бta&CCo{9UЕvu쁩ً=p؀gFzwͧ7)/?w?wO߽ۿ|7>y}G!^K?P^?{Zd(ftkT8žLXV@syG9,I V01ZpPй@PaGBs*auSNv+'>7A'lCIS53KMTs9.pm~H<jDhchST uzX 伕8_TK3p$Js}g +˽)Z�\#bJߣmYRkjA(�"h!�{O!Fq% 2ClFaiR/ʩc$ss0T1L^ jHMD 3upa4YsӼiJ͍u͹nf܊΁9hB34sqcVh )@°@h *thUCv G ;_7Gmim;bmQCNOC\glUI!:gWȋv HX*F#5o{4ﱄQA5953H 4efCqAjfu2m$-Ts ]rws] 冡uDlfi,p"=އ Tb:"Y&s;i pb>PqED/,HTm{5:=bE{-:2A硗nR@&bK*,�\]ImYMZ9in)E? y ̈#OI&M$)ڥH\lj҃F/mSLjZbv\)>'Q*u/!s}-4O+(#L%]TrQ(*k7mvQkpԜMs .=TT\%4xp)ʆʾyE]<EURp\ȲP_;y>e8:HVܦhCiG!>RJ?(#*k1@u$$dи`I!{ ޸뒒K 0BX'H1Hv)^w%>9*M9$6NC|i{{CZ;s\}Ҏpa*(FE8^Gm]Ym� z\QJ\eEMkڒ@Y5WcDk{rX]ň5tn0t<hgy-ÈÄְS\̭c(֠/^_ZsYU36>sКԲuQMY5%'jsR.qV{»T'?etS 깔޻L-.%Ny.؋Y.bV6[ȯcU~�; v Lꎭ{?H:hĕV~A䆑[k v{ wDNHK3SϹ.Ղcn?XU/_5u^YeW\Alxp*:ǝ%Z gP "gZ+=VG )~:ƙugh@`eg[16ntڞٌQS t!h+RvKeuGʞoY 8kZǸ"[Agy*bK-%q"[Y%LXpX 931[ zq9쉃rAHʕ'yYQr-TC2Z&!P4NbgU[L8m /ͩshٕ G)Z1:ƾSԘc-S/i'3=@@`0<W sdD\tLOD<UT&Li'2l>U]W߭i ge 4gv}.:E-Xvz3Fe"B' \P \1 \jBݨ* ,FM ‹UbN\PQ/07{'ԷoTDLp*/orìLi/q9+q{`vOl .E}5?[`sf FavgqBEȤ|q3p@һL>2ߍ(3&<ېDDklv;?Qga+i'&E]g/1`o^nMPP- \)IMI[!|%V'4X&xۣ`cxx E+H<2  씁/kf~Aq2"̺0kEr,b ,' 8q3sWZ-r`xYw~1d?kgl C=n*'kLzK⻶Pkj,u9.| Vū֨b ȳ!%--axn[fWׂ$>&) ŵ-{]6v\1%^٣۹O2ָVb!ÜZ7ֶ񰷶c4UvurOm\p8aV`Aќ`ܝ\x v6.nTҌ\-ЙBBSme 9�y Cu R!rh&;O(չO;OQ\ԓ8]S+]?sy$Z2ZNk5^#9ZDΜfWBYZcC;C,ORJ5:\)̉52Gҳy)k|Lu#a4D8vF)ReȫyDhK6a)Rhpq?r9e^#1P_"ȍjy!#.ŋE~_uCwZۊU^b�›&?ߥx /T*}V*i2Գ.A\Ĉ m5 %M<J y)C69mG ^J-RcZ[:E P?ݢDm GbsFK_Aʢ9r `$FT`q&#VB C}H1ЌX ez>!̓QTgc_"ŖbPOQL,kJ?q&3ĸĴ>@"#$K:RB"~I"TB3E=9:;-$ɇy tZW^ԱC$yt.E4=A}]Wt_8(_kg@E/hjתL;} .3"ijw4̲EX"9vawG}:ӇަyL't:+^L6}z[M?K>oLjeMu>En]OS6}7} ]O(wO\mhL ܦ msnG2}J#o|>C1|>CQ@M!m(DݦmȪߦ6}.Gd{>k?4}o~˺�~B#ckvBN6 _WP< {E:AWB q*t^FvBS v%:'2Ҝ.J`*8Af֠R&h,lX {hɳJ1u!xJ1]p۩RhP'*Nt0ѩRp 虲9U]`;NGܨ6RЪR\0!]Hp2:HSVe 5E 2,SHJhk.ShMU ]Y:E ν_cIk."g8}jħEU2Y:UHR=[�~W)r[ /MO&?Wm\?57{Ԅ;M=K3ˉDE_N+'Kre|91ArcGM(?_NdeD rb\_N/'&_N~/'O5r}9=ŗ9|91!3}9Q꓾>ˉtD6g_N/'&uDwrb<Dr"۱/'Jԗ]|9Qڧ՗%ˉgȾˉϮ4_NLؠZ}9cL/'ޥ={}((]17:o~Ζ~O'?g#: :-Ζ|cgNc~wr:QggQEDճE"Vj UBS>6PKzYCiz?]{>6NLI'>&QbfӘllRy?lҬllRy?lZ"_,Fx.Ox.x6zXLJ7lsE FhC0jc/Xk@F cGK/?^5umxd!T}ФEPР u: vIG$TA>p`Z`6~a ICM $s pt(SD%Q@A6ZR3muцUF7l`:%8Ā4=,4.5%9k[Jdc]I*^�!2p9739Ka Vp2I*OV*a q7 **bP%RmtQc J=1BE8\d$; OpÆ*"b~n'w}m *FU3% ڬ-_h rVIи>D:ٱs7{~G?/ӵz:~A߸OOFQQˬ8A?sci(nCރBG<P= cTE2F<P IeICΙ͘GᦱfyjEgpr) #\&t<NX=[[SĿTT*$W'7+ff]UwO/tV)l¬# gюzխ*:s'j [Uƴm') nS QC ڄнa8^|P:^F+i EBZ*`#nlI2\4U¨*lYQٯ ~)@59hVM+d 활q7խf(ve( Uܐ>GFKllk; N|?`Sov'h퇸AJ=N%mE@KmEt;?Ͼ"?QPG_c+FK^#+%q�kV>@gSS| G_C)FG_l*Fq5W 81 dfƹ裾#2~;_T5-6v :zlWvrpt5%0U&-n.5lw?Xmc7//OPg3cEG{_̩tI33F棲q~իPii9\nj԰tq*F͇:ҵj9Eub"kBi *&t^K}6^ U>Ԉt\:Bu=TkSc*ѧ<Vƅ;�:$j~z**yjLS0sEvdTI8>,9>eHG''V+;y}STA lꈮ\:P5s]+�~:%QmBGUQBmP;Sٙi ~-{_ܽ f6$Sr LFB(zLp\T."7$Ӡ@aeRmdtTwNAuviJ WO2gl%KR~z<0hMlgNgiY=4>'Öl ~`9WVxbմ%]YM4<Ӌsݰ<ԜP 4j<Di|VS270ՙWФ|D>vixuU WjX-#;ufմY iu9Ă<S {3@R+TUVtd =(8d?zVYTyViX;:eF;h@yIe&<zC[3~_zAIMVl%0)1N94+6Мwd9 ?Iּ~9_H5GF x:0iI@Kzh$ %sFlpgtQcYv6UH5n5UMgEsZV4W6H_g2Ym9Z0Mn齳9 b^?_t?Mwu6UŏU]Mn 1:5ıxV ǡ&>8h^l\n5pО8u6Q;߈;}Qg|M:.ޤzgo⚧k.C?>EW\^u/Gw-jlڻo݋vŶ5}FD}fVfL|gd9~YǦ=nRw_$_J["zi4M{ڶg 5XIh[µ[K+_qvQqe>rk8t-r;8tmw one>or~_4G +#Mg5X}ir[},ɡ'3W<rx~ѩZS$+�CktL9p"'-)(ev7h`Pfj+-0"rkkJLSsZPnLM"lCUW,~(Ce~cK\ !yMk#?G#qy<'.GmIYe6x@yOj}<Gg6x@{9u-ú9u-Cm-04^?G60Ѷzr{<zvom om >ZC<6xqdnk g2xt<<r<N̽o  q?b&9VwU`:{ e|9栭} 9y96ap1J)ЯuQAG 0vv]fEHr :0 &kQGD)'$A!f^: afLi bP6)9utu[e:R0g.v HALڜtbwSuDeV~EzvZ2&uUWg` &Quۊ00E?KpmsNa^"XꘆZE^gɂǝ*3C :tպ%R+$2y.b}";ѴЧu}W݇*tg}N?NotA+SR?)PST.;EmBvNQ㗝u)/;EStv"mE*?NQS4^vN9? r~iBSΏ;;N1=~)vNPb~Sn䗝bWm vk`vG㷝9{㷝SvUʹNu ^7 endstream endobj 159 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 160 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�Jj endstream endobj 161 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-mail-server/diff-ops-mail-server-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 162 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 163 0 R>>/Font<</R8 164 0 R>>>>/Length 26324/Filter/FlateDecode>>stream x}]mqhGHͮ| [u�<<l OYUY}{.`pL}>2W~\O/kzy??/C}:^RxO ~݇kϯ?G㥿|עVӏ/>~|"/c6^ө]zgi|5֟I N^y%ϞErE?E>igU޼JbR>T/~߼NK%}GL}(״^湧>k|!y5 6~VS0l+U׮/?_}Ery)چ~W˧&뛟M֑7yyO6roƿ?W}Zo~Q|>W8oվ߂$[_70r'cʛ7o ?-?ȏ:|04> OF;_Ðλo~wQx.˿8AϛwF˟ /_Ͼ9B5߃<z˿_r?ۿF_x_9ţ>}җ#t_9E͟>kאt= /_=:z_HYq}OO>;\?Okq{vm?/gO_^4k$|?׻zY�^q见 8;g+7 +dloopARo5kF??kTָLkfƸ>4>3u>suC^Q^×I :7}Bֿ\ÎR}_?}gџj=|~XũGbo_߿3÷^wq|iN8?Ոj}H+͉ Of_}gϽ׻^r/h@ԁZFSF~h"Z-·47DIUdoU?Tϯ$^"GdM/0C'5i&ր1[Mx�.pL_5=dom^{n;mw=^O=o+I=x۽ߜ昩\j۞Jn[smim7&QO7�F|pB֡|_;rIIe*N9ԃ ǔX Y#/OR~L/)EaeWJxB!նY51SU3Izoy CRmO(#®<5zpn S(͟z__`8eFzҪòQڙ\yFs%NyUo  \Az敓4c_G iG�OSg4Mf q8z$qqi/M=U3BTV )Dƛ¯OZ^xF7;5z3X{͏=B  1[FE><YDD)!M6QwQ?ӄFGhiDa*%}ϖK*OiE=JV5k -fV'E;zlqd,Imzg&nN,<oFϤ^!|Ngԡ>OlJ#)Tz&lJp F$8nnUiPQ}ޏMG?1M}O6*z"Ѧ-ʱ/{l*ĩ>6U%qMKUXDZ'tl*Qnm*e 62"l*!#TSg86aJ F%Q%QQQu}fFcU]_q*ufvnU] 7* zۦR`x׻LJTX9PSL*UIlRcX$>fm*U==�4NѠ845sc!*ECN}Uy+~oƿٸWF#J>Èx>%h#EC@ь'X/y"f 󑀢O0](S#f<r1"榮_(G.mgDD\(ԗ䈢P/ESqhW_2"f]}I(> E Z"f|BWP4kP EQh(# (FUP"N}W#bz�4E U( HJ,wЩ̓Y@-#4JP)P5'_ʣ^=u{:) AeQAݢ6.:jttS@P˞=]) AeOAݞ.{ ttS@P˞=] 2fPAAu:,* [FePAݞҞ~=m\2fN?99E\tseOۘ6~1tgDAG?_zKܭjWwM5dN?:=.i :KZ}^YKOx~_vHBU_?c2 L\ƬȤ?=jgeigGbؽN(T0(ַq:3xai.vqQT(;ɰ8-=8pA_*T-ﮘަ; W-~L6_ 1Tsz^Ds\_*F*y_U+7z"$Z8.Բ R3AJ4eW.2JmVb5}m^*GkD48ZEE]4aT$3,nCMSMհYe?]K'\(w:jIGR+*qTQ9P.Rzl@ZԲ:p.W6-�gpTm3,CEn ZKfsn:Ժ4uvpMM,MubCf'�2k?x)NcΎ!kӐC1М3]BuXf$`/S۱; mnvlӫ fvYєLff0eev*LEl7;0+9+0sFS җ0G;Ov@ NÉ ñ4vӄ} >ׁ4o~nx]Ӟhx9�NCln�8G0;Eevifٺv f7zN,X\[f\prmM8MnuIIvQbա$X]zү#Y MMJO CIY|M{ "ؿS?aGwzIh?:E;:y ~ y{:LEЭI)JV Z=V O{=+fbv@Ֆ*GZVN@w>&di~۫W1űeW6Rlk[{]g tXRȯd{u܁JA_<,yXhc:cRG <=Z3>&.>F<<v$c%>&$D3>!{PcñZ!Ǥ^}BwcPJ4oԗȪgz2w5?�YPߨw`WLc�kA<O41E0P}\ r}}V+HaAJr `d-P נhz> ц>l?yp S-6FӋ17'-eN_ gQ\(`6F�EϳdG;!-/�SV< Wi=80%in8 iik#Tyt22H5yMjv nX[0% +N3=vènt~VP`\'3[B TAE ˢHCC8 8x LlcW.N[G?t 2HJ]$mQa /33;uv,< MP K*YtTI\ нSMAXo*gQ\0`gѡR.֥`̥Co#F(x.-ͺ.Ӡa"B76f&\ 9ؽTMT}z([j )~DǗ'3a,xg΋f0xzp%1d9aǓa5P5aY4N֙N\K1}HAHa2`)>)E9B OҪ1>JD\ OBNa E+G?6TchG?τ.`skiEㄢD-""5$q'.aait �+c(k=TޱWᡐ:d SB1t)R%TdN<8<)VE /'I}v!jd<a0VsJzfλ<L}4%/֍cwҏXr�s 8v 8S8Mr 8^,n4cSY&[j#( cd=q` HL@2td0,cG(@be fC:U_@Y7 }xzT 3 3,,S#>f>f 3$`m/0zqv%�t X@,qˆx²>#m,j0f2|YG�f 5j%R0렀9>`l0,K)X+C/ |�(kЕ,`cYgD8X>AP7rF2}oHA nmo$k@L?6Rl$k(iL?}!bCYo@"h ʪ~$ezlThCYہ2=T$l$TtLf}o(%n(n<e ei 0sڛq^6Uqd07xfYA f cG#,YH`f <ԫ }Xp�p34cG"T'E0 `:ezjg2( R2f! dGo,n8%�_=Yj l #G#"d^?<] �9C3N7ȕu�?!'K3f9V|oԴ%pCm^|ZsyZ=hxT >Hei:|;sfD>lXW@*Y2$^EVEɍ w&${lIm85(r!NCyX.0%+JQ*֨%]m}ڐ9{Y5XG717t$Y[4+zd83\BѱxL1/Aļk`CP ۮU!x7pgp $Ugeޞ9p zq3hVRCP$xҗjrm@x݇ќ$;wf4D7t \*vk`0G hpϰ aIBu~qj,LPďBt9'# wތ (5ć-޲;d<S29PfkB3):s@XR+k(?j{J7,N~0; .*RT(Z}~ jhjQV_'^SX D1W{-*ww' 95G-Ujq`c%ab %bMq}F�ɲ}zQJD oX%+c0$cqL^PNj5\~E-A`8Ӫ D!{$JL$^ �`SkLgG`A!p|F-s Yh1LL<cD:a]Q r a\  2jd- Q@z *MB }+ }mh̤B/>B%ңq#gUX5BsXr!~Bq%]CH@(15d̃P#KAJ݁iF8$B:YJSQ2| c3R"F=0*r#D=@VO 70#D%28 IvD_QBt *5kc@ZÂƆ<�Q :,Ho T2:aC & hJ䈌P9�zTnQyH>%HfBLؔOoӃOi0':9@i\�: ЁG6�TA6@Y� n}T�ӆ^ +j 4TDB[*?QwBmuQRB�Q�Q $w/j#EBSS &R(qj0<8B,":҃S(袷rۃS w`( ŔM667< 8j)n]s�z[�,҄Qz';=�r6 Kb5Xf,&b@� 9jIWa#Q3AFv$I;.7AB,jcDˁBZi2,;jr21|txMeIH"&@)-Lh%ߋ~�=(zW9q橫(z5f Nz=* 2*)M*=K\j@.@4<%PxxznHef68ށ]<~XY' @SVaLPQ3=FDE%c|hL#UCT)Kۼ.@3Q`F lo-PJF'#2;ig Pp0rgn ",5n[Ю0-`WqҒaB VUlo搊l^*D_0b_h4Mfl6r_Q*24(7N7U9-)6oU@oopg2z!q=~Pބ{G}VAD:uiy[R% HcJD*0l]<THS>�xjn<?ƿ08<Yal�DP,P߰ 0dF·ŀ  )Ҙ2`ԥd(ֈ fdlL u8#2 iuJ#283bCL@Įm!A�C&r2D`^t2}F``KG`@,.``c\"0d}5E`z r ` {X2BB� Fa:a �1!"0J4 @FEG`0bF$ �D"0@&Y-Q_"0<C�Rh"0<QH�cD`xH#D`xgzI $I�\빀J&r60)�D%0'h` `E];@W"€ ;0|(el} y2'dd E. ꂈ/dבpd`s3*,i'jy|e1i4˞`AόGGIРn i&jusR A*dBbdF⟌%<Hrm&%/<Hf 0n0 gOCz|>6_- F@Svhk('y#/;DfܯH|F*#(\xޏ AD̰_;k҃mhHg̭ć )~vIQ xfp҅(yL~ArPH`tKFZԕ1De<J* h O;H55^(ArZ`pK:A2 a zQh ƀklv[A$u8KzCZdFYXvOz$:+]t 8Ϛd%<F n;3$i66XC#e\2عj|( {z6k.9G+Z"WPR kWB&p(V(Z ،(C߇ |yp1BRCpD'!cľ*8yTq[K+Dk@Q[bqrXc *$ٰV8sMQx$J^p2A<bN=kY>Qd18ƉqĎ(G 7j}gD] 3>} Bn@(#Jg@(_x@(g;J$@`o!;giPFF!|-PF>!>B9y;r B9YO [<"w Br@(#aAFiPF>!W zϴ@6edplm#s F*I'A~!Q > n) B-ӌ Bt2Rd6 #�B6Ls!r;rBӻ�ȝDc0 4kDc6Bq@^4!wdO6G# o;lG&AOaӊ`<u  �B 윥B$|,qXа B<Џ; IMB!ڎw�><Z D%aDBf*P-l*t-2ϒ#U0[ D$f]_ D/W%P_Bt 2!:|W s$A(A-�B= dsh+Ẵ&pH1G|-A[.O40-/mL0]:<Q,8O�c̼"+ hI=N2%^ked;�RLaɊs 0+%V"FF֟ M<Zd$ߺ `k,Ri@z2Ye1 .w5# 0`y, ƌc;�cRL32`)bU`0p0E@� �(.nX<~�ЇPYhM +�aYL3JNTȠb.�S v>@OfD`Ll}Lb!1ͯR0˜Y`Y47i3/*d=�8=MjpB#N2x3o)[za8>b$K@@o[8iE(닓2RS.椈Vչ rYJ)Y]-@ca�Y5]ә y0;�w9Q!wtŐ3\/}`4G4).͘PŌ!*L"3qގ)4W4d1 -rB}1 WSCH^JD4üԜ~zG īY S(uLyyՉ #` :,jTȈ2k+y 鑾ОyZb&hg#cE wu(f*1yVXeQ->ʕQll݇̍"ිQ8.a|Ă%dZQ69?<<y)p筯/,7aK) YaH&l$ԠA(,#hH)F$C񽖀d| Er,ӔHVdl ݄b o YFwdz;ALRH#Q=HK,RK@L Hб)2J#"YB'dd,}Ѵd\L)J2|%$KFLHFzk5"Y&Yi$Khafy#YYiD$Khay#Y % SHxSHYd9F2Ŷəd<%B{@%tTk@2Eqp,'yc[81P1} c=˚|̓c=%t{^*eNoKb4+΃_pt򡥶Q,?(b d$4A*=h2{s_by@14RDM{>{O (P+9KA (=KB~Pl3ah0 y0,. 39؃aS@,`0)`XJN^P-.^F"N#G(_05%F=D4K.SqX\?_ `sL 3g4asflYҭ6sOi.x?ZFG@13&@z1*& TJ�PV(B`qB4�ǔMCˆX'W'>:Cyx8MZfmΛ@Hel`W:ĺ 64&/F}ؐ}1.KaO&ܨ׬)*Y!І3DQM#$i'v_A>ŇjA|L( a2ka |6J!;%2Eb>1~|~DOcT:{(CK%K,"$4*㸄+G8 C퐜ka̺,$L]EM󗤣p*Oh@]]ii8qt : J%~b7Z2&a.[U'K,:kFvK309f@x䝆B&Ɣ QGՙAV#CQ:,& *=aL(yD #+ 0Cah1Q F?i0n@#eS'O tk#AӐҦ4Αkq,>fzɠ�^[c�]2qk$dK-5~&r15l-AT1w"03 >~^S/y s⺜H@eog C@0wo#a(QZݳI31?}kt0mg2Ȭ3k&h #FahUdڝ<,31/�/#N)޾ N8|.Ok::i톀wT:x-u]F嗚\ l:ΈwɍwyX+6bqm]礈wPb;=ՃrNs1m1H;cm٤:xLxjp;xJ{0ީR|;cm㝞1,x�̰mSG6 7ACl!9x<;x3]|sNsN=8q]n-h.S$ S0_.ٽw97).wӄD٪yN.S[?xtZxX[ܲN;j+Nmͅwd]>xG&r5;�]n; g8kys~N+w( wH(.{;0-KxG&Aذ;nU lK==]8x5\;n4f)qUL&2lXx'6qF" C٠Z'6erRD<U9ǹGijx6~'A<NQJ Q~7|+D}VʏKer+nA||Vʔya3CIJOW+΄<O"+6vN哾onVT b{i Mi;MbQy輺sy<ƶd@dY n6OF'[ldd/dyŖ#"qQC G|\<flM.6'c(6CC 1�qiΓL'}!.lB0P@!VNXlB9OrCHYl? !< -fYactaB,<sz%\7GCOrD.@a؅<+ϧoB)71*efN'ꌟQ'E$,cHZRT>iN[Cd>|RHB ;vIUHSMJfSd."+ TnB)u1*L`IS/B+"u@ ʩ-2J@g$n"o|i>B) A;մ#6GP�X\I$ HB<`Ѻ2 ]yCrym$�|Gj X \R .mASGI㝹bay~jtgneVT^DAӀ"^Hֻ+t; ?m}F#օ@HZ@  KƹHP@ b) X? 7#B"G0!< ?z7 = BV'T.A(Ea?܉tĠl'cB(ldԌFJQdq$eB)dSJğlIBѹB8H 6I? ")<'E6 \g}StefO$!/ƒ?}&_hD_Jl8Sh y:gh9O/3n3:< hn; 4a&  }E3P l U.IF5А}}7GAK_uy禒{z`a�@vqIQ7CNxi%) @}5%EO.z-uIY L*L""?H&EI=Y(dRT8ArIg֪%eB 7{Rڍ@ jSIYMKz\aoYi];ҳu9N+9×g[YU Ie -J} ,NtFKsO�5޴He((EՕgKoP_ZǙ;Hcoig a"?9guBM +,cc 8]l/ b -bé7nuÌJម rFU~:\md3:{^�u+ŐL+6'@sBsꭲ,Ϧ,@ٙ2`s̐ݗuFE~V*~ũ\%]&,H#o\BBF=Uebn=P8TK^ekfQg1pQe3iE-Mxh<@ VT ۦ+q^V7OukE�:F#lX߲ %�\,w6JSLVLVlc0|UzI?�nH ]qkQ�<뜶6oLapybZ ;:fj1ܩEq}.,X$;D6 {Ʉ~ٵ3W(̽ $v!fJ K(%. ਟܢ&0\RX޴2($=q&ǎ((Խ- b~B}\\Hk6K KUDbzj9|)W(pbXCI`J 1\#yn<V] %ET9(%H7rPJM.a d6nQJn̈R9 PJp cp 3RBIPr A)AQrD)$0(%H7(J 8Rhap0ݘqp J[~PJoQ ib(%7A mr p mB֊R„cDB 9fڦ\(DBiR|ēJ!9PB)AA(l (%6fD)49 Q fٷ8(5mTƾy [~l(6aDb>ؾ!RR MnrA)2o1 xPʙHA)2"JzQR(|R@-B))A](圌R+70 xPʹ4K6 Q9"4Zd@)P["c"6ErA&<(RQ8 Py82V`<3TFi (Qa(F)c"7GrF)o9 QʘN2`0Tw2pA)c^bS6_2VSWkiϾ|6YL A=u󤃢s\k,bUM=s&˃XyNU E:gm6X3m7sO{.. 1(j_dyPjr2EE@챿+mdmo%r {|^W\1T<ϳ*ˍ9Z sg 0c*̜!Mk sgQ0CbcFJ,f^ D ڗ4EY854 rg`Բ=?8*ȝ,%SG38jU!yngq1K^FX> A$F5N9ϳoOHF+9^Ãu 'C\φd/O=$Pcz5 mN>O[@%={ӲWnSQ7,tYȥlΈÃ[k;]d{Ƞ^Lp3@OQG;ad{HgK?[@*pG>J={pG,~l1݃;p^H3= =g4q?O_Cp*OD/J jU ˉdl*Avr%ɌÁ  ˛ pJ̫,9 .'9\݁*Av"WUrP%M<=eZm>pAeI9{*a~2b‡5ft']b5f\P%OfLƬ�UD TT XO P%NRUP%LPFhz{۞|H3@0C1߃RP%PJ &.d(bGŧJ0 vR|ܞT#@0E1僐Ir{>b�U %|ՊP%OH<I8w~_k>J|�U%&3 $ՇVGɱaP嵍' )U h=@ DQF";g}b|tH $'TAN(17T SS>HՍUs28! |cȞ2ā*^JLf 9! (7"TM/["@մbH(yj>ue|ٸW)$9 xJ!RbJoR埽<!x_I<u{ B=OPE>~uN 丏f.[ӂysh`}*]T=# :-GQuԃ괞4ɿ4RF@%7KsZr(m$9-șK\F'֜֜$5.#Wq iqO\F HGԜ&2e¤9.# R2AҜ2/KԜfq'j\F țK\Fh<[sZ7׸ Ceyei/@5渌K ӂ9e^9-L2Ը@7@#l oQs:Cayei/@5渌^I iNq[Is$k\F HK\F@W2A\tIsԜ2A<2G¬9.# 希@7xV2Aޜ2bys D5e:Y � 8-HG\E!EiA<*9"$)"`R*vRmH|ĶZ]�@h{q@tE%[$@Ee]qA6 )B� S c؈kKE`=!B YkVHVl2V帆@-!Rᢸ #{s2zF\C .@BOkHȐq � 2D%! !cH[w !#2Oq b� @�  M#!)ͧnH[w !wrݐ 11@B6+{ AѸr|5n\C~Uz$f3}hp\C@@k,JP"$ERnH ̸Ԇ$FR$ Aj5! cCc B踆ď!c9l! { ԭ[B!! ~Aqm)V10-0iU"$4y];bBf K&1kI̹~f%1vIb:ޓĀrlj$F̡$D;;QNbNbF$wȅX)$$,;`Āx%1t'1 )I I wS,$I Hwi;INb0M~'1`)I 5I XwS^I Xw yw`CޝĀ|'1ؐ$w`Nb@TLI wSg$ bC`?ޕiIb/&1d*^I fɯ$L;0Đx%1 $Y D;r+!QNbJb?$$L;4Đx'1ֈXPƻ_I wӑ |`EޕĠu%1Xw'1I VILf]o'1%$wJߝ`NbP1 ?';;;.8} $fߺa$1Q;ӕ×@$1[w[W#Xu'1X;4͕oI pJb8Xw'1̺I $uw +ߺߺy, I p]I $yT{ uw9+|]Lb v%1I̴6$1mI oI o]I voI F$swÝ$`Nb0[w%1PnI W9+a/Jb{'1I_I u$;İA~AF-$+jİa%16$"wC@ NbHD*I 0{%1Ί Ib] ARch\I Ԧ$D+h8_$BB$1!!yh2w淯??_|~%Zhjo{?|oP~|=e-1n qO]䊿Ȼ>PG#qSmJxQAsj0L! y@̴I`0Inx[X1Uo=/b4wɮUo-9ޒ3A(TqDeɢykTve`OҦ}*e/Q| {8٩p培jr-lH  ya^k{Y S"<’nŘz*>9:|O>EWa � )SX/C|Ͱ}1Am\? :sb0U=M]u yl>!D6}݂mA A`hA*ĎZ͇̉qgmpm)|nMPT5"1SLd 9cVB?I*{1(0].1L $~!Pl I@ƅJ8­bo IP DۉG3ĥyP fIsäE!ԖCzTksރwd[,ʃ:F@fYK4ć݇gL 0BQnΏ %R m&A-m[ ؓe{/k~`]]#{Rm dFu-,yXjѵ`:tz &jXPuײqsN+5Za*=a${r=ڏ9ap i 8A8Pn2QAw/A~ﶭts}.O\o59<Uq|eoFv�cMu;67D p0CY Txҏlkq_CODL6,lͤpnuYɁP+x "p5}q@nԽvEaΰo,} RP7�|obT F52f;0~1K3U( Zv韁m'0� y+׈Ym6a!{n%ƌ a}l^kVlmH >k1> L9;RȦȣ,nm`ofU{!bl;(4jY+vqs'XR6-t(M+rE܂G0 䮀[\vQXĤdҗ&"OA+p9KQu"az.ĴAsWoͤCMM}c.ҽWD-𐭱O2yZi *Um ^ڗ4@ p`7ZwB[\nXM%.&H^(B1im``DXo}vntm֒&){['/Fܗ׬vؾ yeܙ󟶜^?Ŵ'} DXĹYGc".C1}|!u24m6QKYuZ]|9#7C Wjvֵ1SvipEUuQ6\?Y'j(>Y>ZI w/9J"RDRM]5rƤRR!yVǽ.Rnt"ʡLm̶, �JY̯KEWx\ldBEa(Nky |>gmˊE(KKTL$م�V(klcpE=,y;;9ivFbs鰢ν㦫WYy=y ]{m4VѨv_Y;/<ml:.m b\fk17{w/~8o틽ᄋlV.4(FW:`]?aq"'tTT!N|h V/F?TnGK'DFx$4u`a"2pM\g _ŔVZ >4hm1č K&̄2nDXL~"ARXg pÐBwΘ*b~zGs3J/WXh6=LEF t98lGnuq SE4xk%{Od-̭2m.\ +AԎ) l<"5 |ל/T87U_:C MNl%,r4jZXo*L pϚL Bcq[.쌣w篕\|[<e]g[)m SgBg(&כ.зb,!bJEYc|Pgݼ^7PvuBusmʮO-ǰ#9Bquƪ&JQVT-=u+utTsm n;kdE=4]2幎Ot`4aNX܉vfY΃ <6'P΢c9Ľ,qٗsU1^TW`㹬IǪsix3odrrc6aL;G7>غ n%'礷KX6[NS'Pf7o}c*'\L$Pílj9%=&{&͘pͧk&IYN#j\rDGbC"J9 }A CtDr҅LRf;c$%|E3R/d2pI2D+ [W' v2ؙ&h,d5+N),(+ds <So'U$OP؝T!OLOt ޼ OL e.ŝ*<U3I3y.sz<˜PɗR$g\aZaoŗ'W*O͐-`f 6ض?ق@3Ya ق@_KN!]szL1]gVe fuug `Ord %` #[;5G{ޏړgB4훠9J{lQMrLE肱cͥM`#-/1bpPMṲ-e\{ONT3S^%aC�p%Qm6X(ζfC٘%{Usi>jiװ]VB-c k8]emmB#ΥLOě>ulA>7CnީKyyxЖ>wn1~c%1u F5J"뛙]fj-9Lo"I#Y�p 7H8-i(!$ct r!3ZaZ4gXfe?+0iwڙXْ-6BrXA*ZB"+N|V0E~5 m^y&h25]ä8v XrviR59]2k5 \]`,ݮAk5Tn pokv N1A {2v 55jmo\DnPyk{]Vݮr |q pnkhpkw5`nmLp mk�r :]C15];n�v~n�va[k@v |bp nkq%/]C05z{?k@V\/rd2 o Qd" 5jr5d�*E5єɚ`F@n 6Dd6�[# d2csPJl2xbsPzl2\<9tIlf 36n-6Jh ZA=6^ '4N AZl |`F9csPbs@AO9dd2csPհvf5 <9(j=6 HlJMk{|mn^5fbzE{hM8Bn e~mN.͗#Lc|:ҢvIW7^M Ƌ'|{`d%Z/<j^?T ֋�GzPzy[Fc<ݬZNziO1rUz'j0녑6_չ1j=X ֋Wn~tlo}!qt NUT3 O׷zEAUk+u6_JtG:wn`<gl's6^N6Rxy콵cmJ1C˙{]vjip=FgU$o.v_D]!R]Zm}j7VX9ӡj9e5M% & ;3a.$T)'Y +VMaQћUsc�9d vڦo##o/-e2w&sSC@h TWBTr4<m"0; kfMIJ;E}M10U&G:/ XrQV\F aU0Ä7ۚTqwsA1 m6AC(qj%(FoWd>\a._(oð"d})Dg紡BPn5m(ENZSa _zH}MŬ 5dǺk6ˇ\_Y1zH;S@k1sSsy35EkptӡهEvjv[c ܶ\ƣme7Cl[dnޡsί}6 ͢w{tXB&Uq7M|/SK@Ou095eБOӦx@/淹*Pk!Dp$BwVAHhoiK2 Em K35waPritBFuhhJ᫄|P�E>\� �X@GrAl"In&Z2&IfU$IE $ s 3G +֭, 1 ɚ\/$+1MuF+"RBcX#94P_L0o0YQCP=II'J&"0U} H2?4?AMN+ip5--nr3 ܠh؞n[g=8>PxN -ǛC更8k>s8~N 80ɿdJ˙IO`>\$?21_̒'y%H"2 rpPRI?\[#pzGS7NwEåQK# ddZH4Rvc (QK&#%0hK#p[$#EϜre)'p.Y%KA~$'JD~$r KN"cKmH(F2%j83P$jϗCNń؛o}_A|&hUUˁܞ\^I( VzA P6 y &#)FpmOcO.pf~㝡]7iq{nvRܻN3?f:ީ{a:^7i>] ]穼cNK~(!\;!]>{x7Sofqxj盥 <Kzy\@\Mc9˼7vעn4@91ƾ~䗋+{f[@3*~ptaxtְݦcWf5`ѧ)m\+Jh2jUAݒGW8Wd_j2mwK.}co E`9eN2-gK2X6GK>;y+i]XCt8sb;XL%6%|&W7GCq[67ɕpX`fh*<ѯBjC l7v;M Mn*KtC-sHg˨߈&i QpvIb$�`�!rN�_ NN*wA7v_{~XU|h$>}N6o~K[~Lbo x,,)e"ybXñnK_0QNlʐz;ɻ?Hxڛ;i TLȚlgIg1H7i]xaLˉE|L #&t wVg,b`fNM p2)Lc& NS1Lc$cdU ?$۔;BflX*&deq*d}4!/+.0k;!2 /~(3QMhT g$b2ƞ{yDkkmRE!ɸ>7vVj%W86 j"ВkelHABd0!k=ށiȱ rM211.馵 /!4aH ޒ?7~ 99oܮ Ie-�~2 [Vb]jH!2ڔv? 6ydz(Mp>9UD`~~Q!h‡l}iTW =l}1T=)7>t8E͵?+^3K}P\da]'a9p#\_J15LOSmN" e}'B0#fhvK"Bũ+@'ö6,besI$(v γn# YUYx~uyQ BP*@$KWh}CxF0\ 7nXnvg/$|$Jy!( ntъQnY_?ϯP&s6j?~mVΈΠ\7sަIk�#nʤOX?mڂ 1ymZD9nԓLvWX_ Mz~m6҈))s\')WOJRZ+#D[]+#va\ i^ NzWXlV='-D!hk#]kK#-P| ֺ60&Zq"z+bOXљmD$ZDh{k-b*kZyѻΎ(4fΐq򵪐gתB n'teS9?86++ׂbqNtestzbG r8ke/<nD�&ww4OdAm]mH)gTOW:K3$W|3+cƴ](+PMR&,HsiͰR ֺjYNM}c?ic5mClQ3"o@-6 ֹ\-,Ro\ʺ].qK:F܀?W2<H]ͱ=Jg'Vq؛'Z\k慭%jqG�56q,�K:65N'pZGp]³kR�g3hVpq)B9T'/"an\R\q 'XP ރ@�BM%lգXQnR& Ok�9xyG\NtA� Z@R�¸V?y2^Ȇŏns1qP'oR/NdɛdEaGcR+1.Â/$ #jWayr^eq1K=\\z8ҫo&GqEg5eY(/EnژqdN|T)l9`K!!mۍ&W|{?Ɩׯ Myձgd7hꖱh }Xl7d<-Ikh^ ?f 0hh p{p[3 gAkdK6)S)fH<] R ..$Қ}Sc$ƚ@�ط5;̚mU�5 S wX,n=}3`ž}Am6 l=L',@yv D[@=<=CAs1md4g肂={^{w'Ly۞!R ;bIj󰁉jۜqFcffcy2nA ߿% endstream endobj 165 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 166 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�Jq endstream endobj 167 0 obj <</Font<</F40 37 0 R/F70 105 0 R/F39 35 0 R/F38 34 0 R/F57 65 0 R/F14 38 0 R>>/XObject<</Fm3 153 0 R/Fm4 159 0 R/Fm5 165 0 R>>/ProcSet[/PDF/Text]>> endobj 125 0 obj <</Type/Page/Contents[23 0 R 168 0 R 24 0 R]/Resources<</Font<</F40 37 0 R/F70 105 0 R/F39 35 0 R/F38 34 0 R/F57 65 0 R/F14 38 0 R/Xi10 1 0 R>>/XObject<</Fm3 153 0 R/Fm4 159 0 R/Fm5 165 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 168 0 obj <</Length 3781/Filter/FlateDecode>>stream xZ[~_a*cEIbtw4MN$+[$f{.n΢PC;<NVd)BUX _"6ybh|h@x>=ſ/ mъ_Tئ^W\*VwՏZ|4kEnQFGO6zY"O{<g{~_.Z?Pt< ka-X hDEv<q;6"|7]]$zo,ZXAfyTKn*f/ Mkʵf݁gOfqbDnVu rV}n̵VO\=|4Ilʇ''?tpe͚sMe}q^>Uve2S#߲X/WvYF?%&QOR:eJw/mqqo8By0U0m ;cQU<4j7'˂8SC8qW4*r{\f]wa~⛘[wK{{J;ۊcG~P7ajڶyKoNTn?ӲsU͍k@Mi ;A |&zTP\xW?c500]렼hÏP[J/[jGI&{L=ϣ:4Py}Cwwi{3u.N43io(00Ս7LkcьArZT'j/ RlFCԹS;Tm;eM5_䞷*a5h"+g`gJYU%;诿gs>-qgM1 |͞�۪Si ox|:6bPTɮ7I>A0D�% P^oR"/: jIl}p_l>4ോ¯gVvՎmm%`p[GD; S%h\TR�/\;FkHaDE*^8TXndWBS)TqRؚ nyp(O6/>>AYЃ$zg*C'OXSn|ɛAO4aA'4Ou7&ӃNxBDKy\LPK:EU[s+0'auV3m}{> _YqX ')k:iM fQgƇ@NgEaY+L KnmVCy_?#1gnB,ٸ&E*yJǁ`I"3`x�n*"P^8G1a =,N!zv e"UG臨$<v|0[}[W΅><l ٧bߡC* !Y+i}=8zSK&wTMtoW;̴ѱnS}*d}O ħ8I$-mK~]K)6=H!bÚs홓ԛ&+-E+iL )oj" twS^kb!{ mtMP r-''rB'e,-gzl|BZ]RT̉,Z"e[,94Nr1}_v,A&16oŌ TtD\aFѲ (|WC·3HwkG'a+kH'ո OT O܎3[6Hב6&Ӟ|Ϋs{Ɍ!6QOD㌚lYaI)> ZJƩ>'A/F<3JE/]:i [07 %DH(a2 AiR!LIRD>ROu' CęK> x0 $HBk= 7 e1daܷ7jDzi5G7.Mq· 0G)8a"4?VC{� %Wީ=.%)_%JJyU׽X A?ZLqL)8&FG=T.1 }dRyGGeSZlP/NeQ7݉r,pXH:!w9v!@`Ե L>4F Wsm�e#],y/),x}T͆=n]S:;t&KOLM)t p 8I4kS[QLݚ_! e&iy" sUojvuX<RJ Jgtʤx}r 9Nо#Хeus""*'QR3<B&Xc(&]Fd!墼ʈ'qL,'H;T9dk"s'UP ,/bKLž�ؑFH0wlBΜjsf=].p0ֲgh;$,s_n /R 61Vll1~g_Xr{tbOow2O٨ŋ0P{W wfܥJ7:;@7*~8ݷKK vb-˞Fil'Td"(0P Gg9O� VL o'p׌+@7ct417Ŷ_"Xbkb/)xe@>;^07 k<JҍKL\/*T2@<bkp?' u A+]B\c« 2m@ɧWU>-y]W ̐<Z? \r8ͳqζ45Kzk9/_'wD�F vn`5bzV ʰ;ˊb >'}784<"\1$ γj&.Or{~γDl6gjL_܃, 7P^"T[%'qL<JyZOj61|:tXlNKմd@ KO-<*(}Z�@ql7Bj~aH'$A/G!o#Ӗ; ,\3:V_0:!}wd,iHDb3 ;92FxdɜhI0}:@5)wv:i6jTCJ}mq/̹[au`-*sւ1*CiV/BKDi:'-DH{%V<amMr %_'ڋZ.="膸2+A W*uS<O7q)Kuxpj5@yYJ##)Dar0Ĭ&CKACˌ)WϤt{jB�� dx 7�3ؤO04.8jҷ0;Tԑ"#-6rF酊T-.U-,p'`|UQc.mT0jμ7EGPG<-GK>-m [fX-ùM ,\l8f-q b-:8KGQ*HU'6;czqy\q g?W^qq%l PmꙠ65DuR-�0 endstream endobj 154 0 obj <</XObject<</Im5 149 0 R>>/ProcSet[/PDF]>> endobj 150 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 20:38:06 2016)/ModDate(D:20160927203858-04'00')/Title(file-reads.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 151 0 obj <</Type/ExtGState/OPM 1>> endobj 152 0 obj <</BaseFont/YAVGBP+Times-Roman/FontDescriptor 169 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 0 0 0 564 0 0 250 0 500 500 500 0 0 500 500 500 500 0 0 0 0 0 0 0 0 722 0 0 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 0 0 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 0 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 169 0 obj <</Type/FontDescriptor/FontName/YAVGBP+Times-Roman/FontBBox[0 -218 863 688]/Flags 34/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 111/MissingWidth 500/XHeight 461/CharSet(/A/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/a/b/c/d/e/eight/f/five/g/i/k/m/n/o/one/p/period/plus/q/r/s/seven/six/space/t/two/u/y/zero)/FontFile3 170 0 R>> endobj 170 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 4949>>stream xmW TSgھ1[T;hb."D$!d%aHI "+bպ+j;mg朿$7}y`0372V["apE\$LxaUY`&t89s0r8RYj|dxD˻|fƍ.]q'DK\01RYX;swLL TYDKHX8_H8eWdLL&Mvy=kV2oGƆ&%LE_[`jD*+aw➤Cz_|WwoUk֮[m9-`^A[`K1_]-v`+0wl%y`jl`{Ou'ۏbN6`Θ�fj9`b -N443w1W!G`Jl">'Jr 7N6m<k+B˳gήß38Os͝)iWklljwL٨c&]i#do9(lR-Ѥa8bMQ?|xr( ^%尘TR Id9JϿy> =FbTWH{2PUgJa%fF³ąs5@|,BbxMaV"@c$L% ػ[0S#i:;i Ņ&8 т@'?[-zSR7Ewlu]"{>B,[JfSǣ0il?^D@-MFVA(`&?VElSHh�N+t9;_- ?,!2#Ծ \:3h`+"7pQ3BA6y;]nse!g?ȡF5a;I[7_WO}8mh Q-_('Jv{VZC��ã�yBS,ʽ\U oNv16:f*!$9yrFN~3tBy'U5r׬/=$؁D,N$Ā +MnLn3pSvÒ&ofJ xz 2<2M9e�"^�FBe"nRjUjT s텅 B�<P\CƳ[I;ϓ2,*dߩR`#cd[DϧM%yf@.I ! +ۤ$L"{/6dʄ 묕IGD/)\%gz3Ni�(U;ll '�nZrb#J;ėRitM5Z]tin7ށ_PYp!Ors _Y`OGDCQ) F-ȅɧk4jJLu%<aBe!jC.&h!si4ht5\[\Ei8=w"�JT7ۜm[ q'цo@70p ygi~p^!OU%H3c*y5l};1�J`0[ HGؤLlvLKXLQK 2tN{HZ5d2Sɿ{]79~crP]%Ieg"I_OQvq88΅b(Gee#E 5ɉCOuNkV0C :{Wh;,yWVʝ]LntE*Җwh-oQf �LJ+K2,l_ %*Bvta{gECF=s.V]*M88`2$Xڦlf½h#6ép[Fp/ʉf-Q\4')JdO/g6*ڎۼ�r{gbuJmݩ܂"QNIn1("k$ jB\Q$߳/f_7|Ⴊg)sks f*&3=Y'2BzWϠ #z8ܜq.fbe-LYR@Rb?ډ9&/  xMlZmfv=$??v$Jef-аuFS~T ם[w 6djn3=3Qg.\94(ejDK# A JCDD˨yNo[Y`x`ϡ|7#G=wcǝO%JF߃;(XTDrC]*c pYjk*7UӭH2J߬R^hoHO6FDŒ, (*kQPQU-V@:X{@E8P 2SK@H7 E|/. ·*z$`5rNpЕ:3<{CklhO UV;@PQ@t7D?<[*ڄ u37Ho\pqz5t[ی6(`5S[,Xd!PܤF /2-\FUr V'^JrePAOs`(~˼ "57d2\09G AQW^ !WB,+;# bgE{:�AӦ$t}gC'WhllciJk)�,,5r#<˚^* H1Ir*{A(9^ ˶.4CCnԑ%{78(ahɹI›19-a�#<F ķo@GTӳ7MP'|2ZF_,3@ 9F!18A+] y 3B~TywCr?-j`Jf�/Ĉc~ h:\0n<;Ps }fb̴qlf ٚ:N$UŠ8ڶdAq`}xG jYl2-It$^.x?4Bcϩ̭O޺On۠G!k<}%J_0Y_mu[ aɤdC';45G^U1q;K0α߃i# v.E;S[4%;Jeq\ճA|;J$OMUddFI)&5)eQ$4O{. ;*A>ڳ݄Bg?c| �- %E&`˱vemjh[[EИYj&ar>bx1�ȿn C6Yz. {kS"BXaa$"@ȭZC5[ˑ6JlJ)E sKCsFq[=[FNN<֗j:6g0lĠ<O({Yq;t|e@rH\<* jfX[q뙉wή;}yJf%�:.!:+q}$?LdRiMўX+aU[R5#AO ixVV`#$A"J|uA2H Xy :3(W=myrg Iʎ.}Hz [hPHJ^>#o6EUx1xj."_M`d6knΞc,FZAMj-Fp o&:ȩASkq\[^) [JGDn:}H;ad*tALYlc)[P3eL2==C#+Ċbl!Jppc_nd\lEUxgnYP}JaPGgڍ254ANJR=a2ǥ!mE)JNT$̆GqZޘmΧ6!|6f ` "/7-N0"hcʰ2glz/Y-bva/I;"B8R2ň~Fe$a+ťEEk{+[WS?zKyJ0LLoKU? WD(�zAsJc*)閃q SG6@r$34i9pTl|\t|cSƦXv}UO8s꾃XQ Dq2⠏�qާ=Rz+Zu04EWG4OGcF]֭ s;&z +�"Ǹ{\y*\Po*g tD-b@>1-]akh%6奕 $=keKK"J]p[~ ǫ;?yp҉HeTsښrA6] OCgOUןq=.~NAMEӋɺd bq1 G]ePw !q>.|)Xatmb)c%*M| .ϝ}޵h)M*;95K{Ol;(Ᏸ-^h>ހ D3-7w|dS�C5$ EEw4M ۿC՗VѩVv|'量8e${;Uk@�cq85#&󦢛p; >J>a$Õp|Rp'6W 1)fevC9rg!DY_oSׅc>?? Pk 9t;Ǽl�m4B#줐c(EP>Sij[rƙ3_V͜ab endstream endobj 160 0 obj <</XObject<</Im6 155 0 R>>/ProcSet[/PDF]>> endobj 156 0 obj <</Producer(GPL Ghostscript 9.19)/CreationDate(Wed Jan 25 22:28:05 2017)/ModDate(D:20170125222810-05'00')/Title(diff-ops-file-server.eps)/Creator(gnuplot 5.0 patchlevel 5)/Subject(gnuplot plot)/Author(Bharath)>> endobj 157 0 obj <</Type/ExtGState/OPM 1>> endobj 158 0 obj <</BaseFont/LETFGR+Times-Roman/FontDescriptor 171 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 171 0 obj <</Type/FontDescriptor/FontName/LETFGR+Times-Roman/FontBBox[0 -218 932 688]/Flags 34/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 111/MissingWidth 250/XHeight 460/CharSet(/A/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/period/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 172 0 R>> endobj 172 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5666>>stream x}XiTS1dP`M:kBCyT&e琁$@K @eTĹZ8Vюz9Y`a`V01#264y[|lpeBhO8,&eձ.2J߅{gcLcyyreúdNɑq?'Ćƥ IM^<Ň;/v O NO0 ۼ#'~WS={S~| 9BE{D{zotf{>^f lİ%as>\ sǖcG'1慭vb]js`l[>cM3)v;c8l:6{{`\bVX(v8=m6{L4 k,7#`[dy}:{~3g<88k,W Zjyw3gg>7'6lgcgs%s掱ECD0A}ctHMݴKIL"WpH6j xp: 𞕥 ʼn@Il/%ɇZb2ؐfH"3˚&}d :1v$OՖ�䙺/x(PI<n 3$ ^8#8QeDJ=Qѩ4xHE(]TEr'VQ)@$ˈ G,ĵ |Er/�A{BeVE>q3Jwă Gɣ+ ׂ -C}� ?Z|ҷ2 8 C(TRb଑*FlL7 8423h*_hڼWh./3=vy%zN ɿ \˵X+~f 3'0ʛSYi<M(IY@Uj�`Z(r"Y |sQX{qN31?�$K/ FI(Z)+6`e( s*Ny [~T{w!#K߯pL=$jϴLGHA Oˎ&#%ISp nn=㈦sm}47s-}w0~p`Ṽ8$' ߼=x~{~qWю$s٪ )l%uݢ~{Cyȳg@@3B6v'U$%<KRšP8Ƅ쀆ؒc6-Esjb~w#PZNx+H!Uvgl)J͈ĀK7F7�H8ZZS+=8z?g$dF]$p 1(9PK>K6+H&3 BI\I3[eAB+S+S5|A`Y8}ja2 S޳梒\[!Pi>f KU{gvkJT/PxR2WIPȦLH N]Y1HnBLK #+0.%*+ĥ/j$&jS͹k|H-v|%ٹ@djia60[ݼw{jKL1x=)\!ZW tE>zPPSؐgZȝ)'v#BT9=-Z8*fBZ9 d#m(Wv$ѧDᄝhrC?x 渊Cd- Y<=#3 Ȁ (pT@s�-GA7nv}M(pv[w  U}ȆYQ{$qBu'4$*FAGAt fSY&j΃]o=t?l71L<bķIJwI7ݣ,P.N4>Pv CdSXo1Y\ce~rq-T|I$!@|X,MHx^|AEXOBbi{]ܾ>=Q8kjן+Lb8Y#+F-7W/+}wO<Gt3m2eWg3 ~ 8ЗlX+dJNy?z`(ITdt~)jSMPYS(ll>=D5AHEϹ?jOUmWaUz“~Ҳ+M$�W@w=A8Wy zh &Tu%EvMK;6AmFb[D%aK8^ 9@R0ey#F[%֭{Pcݠۅ#2VR331ٹ`QBLN\6KH @1 {Y "f,h7lǗFsۇ2~55f/E")~1YdZl*^qd%G%"vߦ(I*P++{T0sqcU-Y<<?5!ʁ }jҀBDC^T+8Rڕ&2oEa!A()/-V! ҋ<VuuZw)V!Zױw'rH~7e0(UW%jc~>!<` ؏@vnc|;OU3'~䏣I5RjOR5MLjo$rPwXSD.*,WLͼniٽ©/`:=ÒvR�Q7hDXVU(:"5AoqZb ؖ!s4xw(ȇˇXh6ɑȤDY =w! xhKMYm,?ߖ?m)Yǟ 4bMYȑ#["2tT-q|Z!Π6@{cP.et**}S֔~#GBw7Aer#|* 6ZXNg;$% ΄1aFh�[^p8;}":92+i@F{�.{ҽI))^( o|`w;SsosVlf@QxΚ2Y\"TVI Āp!)+ˤN0_f 4R=O[Кx /Ph s>fdҟ,ǜJ E Jex ꮴ@} zŝ M7W\-ՉX\^; _DUL_aTWaL*cwm#ҀX^K%&r xii~'-U.Na5q;[*[A'vw<%N5j 5�݆fC8 rkŭ$ʙ\>X~? wu<XܚkK ݧy%mA[5F1=>-xc|fM,7C5麸(A gO/#)ǾOYr)/*_bX U܄OS/f ZMTXq㟎ͷv/ sJ,@"OzDDT%S7 EbGlo>iZMc'դkjRj}^ܞjPOP|U;4vQJT*X(=QZ Qq1lwV.w-,LIGw?xZrSfmrB=3':{Bǿ=\ ɋN۸-.t0ԛ^,=v(,:V jpk*.GXux-dTWBBM^Ǖd-rx111L ^\jr5L#dV\`ϡˮ΁V7 ]J}YYLzy<u$.Uȉ-޼J2yi+?I:%6e+ OP\CCz"ޜPal % ft$7X\;xWȻI8mqvd�<$a9C_M[1>ƬY#)-+S[*CBWhWhՉ51Pzi/Ơ۲ &pP&aZ]qq}m_e k"vP,pI-rb ܁BA/Ds_k /U%P8p7Btq`g7Փq)Dڇ)qDFk@DCE=SA?wL[P]\n$ty,A?!;JQUpFu *Qern1jCRr*jʋYO5dt' 8%[X9,י!%uKwP3 u ^ 4*{bQzN(G $S*(8+' C`9Xnt]jI ŒЁ6;;ux�բSo6SV/aLmb;2WNmsA8@ O|;/$_=mA.dz:Jh1D!]4xӎ+l2+ZJ,ߘm2(IwA+{nˆU#ɗ8Xš݄fQǃaE> nϜ./Coc-{İO۳GMn/>";?yׅwr@~G7{.y0>�ɑS=o97bOPGY?1/UE3? gt4ܰ -Dw?ފ1U$ ' =/K&}8Rŕ^^oOmB[yO|hb~imvicr4cwK{RK+\oOM%~=mf~wىvG4w |m/�Npn؛OCN'xJvO#鷃e`I}]MmຮA['N]Wȳ])\~GqJUĵ RjHFWB&d2_رIIqIM- u- Op 6 װ5z`aX{("vG9W6r�Cu}4;EPxӦ?qy N{443& $V}X$K�Y]"ёY,;*-�P &cgJs(9([/ǜ?{w0nt(ZTk;|>yѵ7ʒP ,BgޥؗA6zJ;C~~`G|zq]^*Ey4o~C~i6ۃ<u7P΍M%6uj/"ySՙt>h/(d/\qtkҕu�D[_-8wrp_唗3ͬ/((Xf,4fa endstream endobj 166 0 obj <</XObject<</Im7 161 0 R>>/ProcSet[/PDF]>> endobj 162 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 22:11:45 2016)/ModDate(D:20160927221224-04'00')/Title(diff-ops-mail-server.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 163 0 obj <</Type/ExtGState/OPM 1>> endobj 164 0 obj <</BaseFont/RFCECI+Times-Roman/FontDescriptor 173 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 722 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 173 0 obj <</Type/FontDescriptor/FontName/RFCECI+Times-Roman/FontBBox[0 -218 932 688]/Flags 34/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 111/MissingWidth 500/XHeight 461/CharSet(/A/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/Y/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/period/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 174 0 R>> endobj 174 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5962>>stream xmX TS־1D+-DmBU['ZhT@ a&$y !L28"UV<}=wx뽵J7'{6S7;<JK}^s½?6ڱg/@ËǓwŤą%8.ŋ?+\\\uM�IFb k]ݑC#Sb)p#Kr\J'Kջã%;NF[% M Wp+sq5~[wrO9n!^{#FF[b[VeKb>Bx Ou‹X@!"b/CIl&RŸp#[V‰F o;UjxE&Y!$^"ežML%3 \†x1)NSu6m68r*i$K}NB{j`+g͜9m_p}/Z0|;V0Gx/ Ό2_^}y~~Ͳ˾j{(L#h&5 [$f* hȯ,4JT4+bcJzZK 5,c FV-L싼O~;)/-T/AҪ|3i% 3Q A铵m|OpW+(BMa ]ď(CJ;U4 i| >&*H�JmJt( COAXL%m\3(g(BC wLy:`2"cM8P@J.<'hs4\Θ)"eYpqNʮ򚾂_a>Ġ#1h~Cߣ$jb ܶHH}ODv.cpԵbyoc=P<S@ISPZUՠ\j*K[ @(ϥͨMNήT^m*)lV*PpLECYr�[W?N>LL$Id"*8G޲K )`Y^Wv|#@y'tJ&uyE~ } /K:#n0;h5UIQV'00})->Q/>#.P Jxژ.p~^h8h_/n=q{%Tzޤ0B4w ڀh߫yN1<{a�WzC-N k/w~ ~_E=uW�H\9P6ژgSO] дCkYÚV$JU5Y'B%`1Jg-F6h>hw#NH(p\C +%fRg TH bʒ-MIG87=rtؖb K0 wp##^t  堚�.>&љp(SU2~2GZpVY>NfVR+U` =ߢRnѻN>{_f*Ks+�}45P)g0`]0m DDLcQi\\*O7TmmKG'K2|Cr 062�Y\C }߄>зPY5JpuPxHW,Zנ%&d¹-fJ"J<m!-.IL,󮗯O$#GۓA(0Biթ/ѩHKAevfBu1f#o{CeUQbkpUgq?'JR )(m*bw+Zpw֯Coa0$mUMבz*H$)$)MJLǢѸqCrcR8<pV{O; )p8RH9vFf],6[ M䧋 ii;( 5> AVnv~6-u] 4]?&V')t@l@)_IJái9P )/-"F Z:Y5::-fjcڈeh]a=Cq`¾Snت3Ly@[>(?MA]˪?i( P1(@8'VGh:UhTH{ngwUc_L X:V8m<HaX՚5YϧK~j%;J94B5ʖ]a?>dw(T % џhR'v=�t{% 0X]SڞZK&&QZ>xuعbq_aMRXE{cHKīdiI.3#]1nT䔁М] 1!mA`0X^I)W_l}]ZǙ4|j8y6^p"Rd9@ &'ڗ*s`7W,ix/p:&#/X߯ETQn(tOiZd|z1tSk -,>quv;Xwz\b؏fF!U%(aGSܬ.jQ]YCmoMr: 7s E?aZx QUi9Yɛ Q(28Ly RQ $QCe <2]M@:Hʋ )G[n/H2v>_uV;>gA'&`Khv>ooaԆIKd5)ǀ\7C@6.^~ OTt͍͗iw_f?f&p5ϫys!Lm4:#By( 9iQ6oR`J4]Q~م,8%$U4+L~‡?𜠊JYcfTR"PR! r1凱:R˰9^ N:!ى)k Nc {p~hs@kIqhhjW:ʲʂ.9ՕUiJJC.}EM0\6~POO좄v.%41^d`t+:xl @PS.C[U^%uMqQ~`4|7B#ՐeHC)x* eUUtN9|qgAs9邧d#_`BGxW$t,Ʃ)[4x&KS%$L}GnY;Ew^s[y2^(8#斮cqUlu暺wCs.$جոr[фU[xTBFƒpiȮi�+WGOtx`u)jRX/5 mnSpa$dX\\?M?D%rē߲@hwg6ZQց^O%cמ\XSS e\SRGXL*4nba6wT.pmߦ|9ŕ*`iko,))lcN=6MuV&f3mCM4PWl;#)2- *qMXpv?`߸\A5fF\}E7m >p ;c \>HI9u__k4o _8l~pLCWp5)`18k$h_Rp\| 6,}q6gUpL7[1cot%fzE1':VCt&B}k0IHjMuMքhlϫ:-, ~U {0'VZVR1'*FE}`hqGzqi0 l^~f&%܊Jl3|G3R_>SdH�@_vO.nxr.?`13EZgNgObQ2rIPRd:\/Jzϟ8xb?gpr�VL$.+˕sj{8w~S%*姃 :--]#+a ̏4EKCS폮 6Ly%&"s3 T* ^mXg*3O{Nq31> UT}ZUUىDoxfF܀}̩VC'~ޝ49C؁tl7:6}CA0x?=)"s+rh-S 7{Uj r/Д4wB۪+sgO;y1{P AE=98哧,4l@^IYqv@Ԇm l&h^\&(\e5FBfЗjb%(^z$3QyQy-(8ȕ{)@ S9)C<\Mkiә{ٌ-I:q@´ڜ|Qڱt3(ueG9u),6GԎ3 5:j+[ SJRw℃c-\5V`"2+Ssp }<xSKlCx?@\;3 XG(kCSO <?΂]p& ^<9GIWa'!y럯Ƈ$PQy5 J')TY\LԻe4Pǃ_?2 . R"'o_}{_qD2Bv,á秎[~rkͺ֬Sags;x\ ?c.k^`+kǦqTs~JURXǣzOIN6lZyE/nN0elL޻'>�и8ITL-~R#,p:<p[6믳$1) OQvD?dwUG<81>f/Y 򋻾tH+V&2p$!6o9 [QUcP!r~XD/޽q]6JQPגh&Nz*;gHO;A3^uh1.j|g72y7"b" V5x!v7/>K?AT`A.i $B̳3S%VIr2=j$=j}E Q$Vpa&b ?rny?>pB{gs/mA1ob( B3S3wՇ=wlھSQfH GN~p?p젗T@f"gmohch9E�e94}4yzϋ52g@rf6Mp+$ՖMj{i̴ӘibC [8da_Аx.a7/㧍׬ך;><w-̽ ?o�[cNkդ:<vo n^,cvܣB]=R[RN Ы̍WLXpXD87H, + wNIQ>B7Dbla},B6O{:dƌ53fI endstream endobj 175 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-web-server/diff-ops-web-server-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 176 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 177 0 R>>/Font<</R8 178 0 R>>>>/Length 19438/Filter/FlateDecode>>stream x}۲lm~ȝy<*QMRTy%JLrdQZ YfO^910Ϗ ?}9]ۻ|_x}7Wz쀿ݟeZp=߿ӟȏ_|:w?y~}̴e>zWzt5!_^cq}usYz{~ϵ;}ӏ^#w?GFק_i_ї?WZ[~I#g}>S<K~g|6<=<w?/u\М/~YVū!^4}ǖEJ{2Sȹ.;c@NߝS{޷^! ,x?/y>?9Oů <΍?Inė_'Qv >RK<Er {H/N×?sc?3n7_ssŏ~+_s'j΂Pw_ʺVyWCLs-޿m۷|rK9L~!FxQy|ǟ~/kv@~)}?# _-^K`~~?dq"LE|w:|Y. 7|}זRGoOuoY[^_~5 tK|V^ 0V޸_'\@xspb>I43!7?pw_R<;)K>?WC/4b OEo_?!ûR&V}^|Y6/߼,MBi|y3tbpP>^+R˖ćPӫ#+mq{czclT8:U!w˭KEV_<%eҚ<x/g761>5)Ze1[e�*xNL_wEoۗ=d.{p˞�/{m1__-f˞x_z"v_6/{qi%\y.) {Pe0ջn-ͷĺ? noW{Z\<~x[^MNZ8q|"$BǞ�>N*/I>2cFB;A*~lYiu1} ˊ)48,4jy01φcīh`hp<a'?̏U us$&µYwWW{Mɺ)eO6ػ*wZ }LٙyLJ$jJl,w.Rץ;![3ťSIt|PTHWm\$xY' kk4eU'e I$\K..~^_6C{ ,fn ˜q-_S.<�k6|Tz☉/Ćѯ;@^xk&zb YЅ>ԌɦG+/jB{VUՔJpyQ+(%x`7'HSwNE]>u D 19/`[L\M9u17[oZl۶8-ۖr t%0M-&ȃ8ʂqv yve5t2恉<ڥ]AڮlJY ە]W`R<ve5î*J[vPԵ+,;veYa]9d}ݮM(0ܶ|vdzAgWqȳ>mۖWۗSX[n[f@^mK�mK1Pή Km]x%^]x׺+ߛgWf nWۖbŐT-Lږۖjdql-L9\~ݳ7+anWRnW M+ەyڭJdr3`Y]<Z0r.ɉl0:e8K)ݠ-ς.+ĵTgp He* PbTc,RQ|)ʶp2j{&yDqE01沿dQ<~,.kENsnYDq!&A12VOCf(}K=e"\2#'fACw<F E_g#˘(7l%zf(.7= >Y*_ .P^壩YȊ]W #nXiE[Qk<FrGewYy@=: jG(.w2*Xu>u[` r+ ޟ݆CW.R& 4;+Ƨ%SxCF+oE`Y{6~OM#_av%r /نri dOv씚AFeTٛLmgk̗m*q{ҭ)H~/ۛUp62{Snl1ڛr?+.q{S[l{7ʱ&˯`fEuޔ.?lޔU+7lcۛCzSf{S7X9nwIe޶9M9{q@9|9{nsyiv9ު~sr6'8&_SZD젩5).۝3/6NAs\{)`>^r첨)` >t\)R lsn6<d,ns7&SnmNAs,nsFkK|tnʿW9'7HOٿ~?QTǢRxkݏQi6CMK٫QCTztyS9l*7"N:@Rԙ1§.Ouᤪrux4'ޅ t7I{Q 6ŰE%Ϙdǯ'4- UC0#R룁]<ZeEvٵ/e]UaY]`Z) bN ƅ՛1ul|앜cN{L`*ucj?&GLo(`/L$:{L `?4c*HɆ$F3l cjb1ɳ$pilR v<\yLȅ ;G>_ ؜b~o=4p_Wƌ{=~-/p? y9æCv `^P(kUP48F |n$ $isF̊�( �Ӫv),-O:KSD{]4w$gu1ɸrT%bDەH>͢{eR eFREtAI\&R2,|ٍ3\UnYbW^&{`eό& ef| ])!l_)p@&BQ= P( �ScaGT\v3L{Xnnr'KٲfejˣĤĻZӾ<daLƉ}mj qta朤rW]5\%&ƀ0rWEx'en'fq!n'dcMK@a;1TX/#R MYᛅ^yop17,U2HNrr9?az8' c;5M]| &$;~jM;‰)k ԢƉ I݀~>ĥ/}&9�T/Χ<E=Qp9;t.ҁp]ĭ)vY:~ i% o9` w}4D̄`EE<\&cCx\ϽN1N! 4 '"2$KX2 f#Kq yg[V<޲)2iqE̫)zls^PZ#srϺ)F֐Ƅnww@=OLnɿ "p#^Ff!xd 9$:_f7( lU*ވ'|7]oģ@A<> _7%e䠆T<'MDqޓ@w�O0L#z7!!VO߀�x=\!<Y\6"roe$p7mdɊ<po! } o!ֲ<|!%v`^Fn ~c+<Ao5@/#6<2<{Ā3Hyc^F}cܤr[\rCXs&z@d 8țj\y>y [&@eG<ꈕHo"$ ^M}!@Q ^K �xY�oI=k�^;2p[xtz)ef4@GTLMHB"ntO?xY;!^GS.!^Ɍ.ɮ?WOB!)nkHbXƫo1:exZU߀z*𘤺bb⟿zO1pnKza)u(>Wn3,Ԛ^Ǧō͉\cprӭOD@aOJnr#(p_DU30WWzhy*.J840<=ƙ< ӒEɇ_iI,TO׿,qK4ocPz`|#$׀j.L4I]1}8_YM遅u@]@BLcW*}d 3W% М|V�n}2t.g!= B/]9뿕Xv`<4 tAKVW:?~`|q*ġp1Wëb.CcuoO!P" UZrObЫ/.A㲑rP js|R!'gz)xvh\Jm.ϸ$[Y5Ԏ9t gTp’5]Om<f.Ry՘K L;3YaՔS]0|UCV.3cd}ý|Pe2>pgq"^72)&+:Ř eogxpy!fEeNv�4y՘WgȊBE1+$g\j*:}TXj°kE-)1l[ .єU A:%jp܀I`J&Bj2Spg.P.�\b� @&;(ހW9�Nz^fmOxULv'+3f~��xy/*C$<YW~�.:GtxWdvxW�57C>5wy3 d^;YsxɂwbމZLq#f>ť~.7Ķ^;y;1kݼO 4x!Jz�oJ x eD#�^BRl9$p~oKB <v/ 3H&r #B7M'55I fm$@!>'4}n�Q<�O6CQ/F<@ 5l:jM<Al:#P1=Q,;-I  Fz#!^udʅxK�x,Az#$A!^q\!^<!rCX�pP"⵩r Z'=7 ruJ7͆rAU5Лr/QWzc^ey5YJ܈0RF<aּH<z<?o)y>4/?/hJq`gŃaXU@>|+3,ox)?Axf6k{)fkbĽԷFo,F,NbnAɧ´ňuO>㙡'\{~>X ڞ{ 4 X@gX,G7#ZZ<HQ깧İ ?1}pbṧ8 ~'<oeK*ټx+tXLX:,FjX:,°,�Mx+<X $2@؁˓O;`qmd7`1'%)sX %cuX\?؁͓ςѓς5|L砘zHO>KV砸⒕9(f2{(.k�( %|b 6O>KV砘O>KV8P\ҿ�Ť vn5R ( %O*8,�c @> )Cb<,Jc@?R@Ĕ=T:@> Ti|>]uרrjl|Vz0#E|,J}!wPAs'+3?s��xɞ|"u=H$J!L{�Ob~[|hAde>hXTDxY2_xHdO>Y{ܞ|wY8$Z"='A��^͓:I|L%aXZV<F{�OPۑϒ:<q{D5bgYC>$=:YyM뢭:C`S*yE[K&uhkA@}=J�UsO`y:[>* W�J乧j_x4۞{"�o:)fy7xsO<9d>ښ&<hqjA�⹧2_x%=Q'!wgwwཞx'�:l@='hds>SOO$K )OQxnqBUo'ߴ@Q&g1t%la(kDU 4OkRּg T1o)YLW6*[3bR5v"k51D_Y7H 4_6`#$\9Ȕ]&NJۢɊjR?fϕ@&F^5IK"ez'D._ZR o>= )0.#F8T^yE&VO^!dBR8!qPA NF *R gA}9Ō<`tSFf cA~X=eЀ=xExBHbd !JiյD)W\1C I(l"K-܏+5Šl_5uA54~\\fS ^xUGqH€pթWG ..HK6YOHʧ uEӮ~8D[.aQX:pʫr?곌 31 g{Qdkhʴˆ)O<@@\Ȩ,<S-UGZ`=)mj;@GG(Scgr{P7T9467@ʐ1,ȕV NCl(pbRR sF|P*R Ki(c(vUvhZ"Y9K']:Uw-ס V#he5]QzZq.]WpzjJjkk[nJ#1O|TMGX =в<h!׃O%7Zʝ- lK#- 0=�Qo,hECЍH>�o璔^hIs;,VCx%-e('Pb- ВkdLo6FJpeF- xH; cz%p7Zfd0Вv 7Z-e+!ђvKo,M-gRVw%eHhYX8teA-ec.dIZ В5HhYП BK2 ]zeU@F&>-YZ|鍖rb"IZv-@Znb\hɚ; -Yw„酖nd 27Z6 -Y3CKqR -YM- h)qRZfT,}vuhQ(eA-Yl$easВKr7ZǤKJHFuxYؿh8-o5b/h/ {̓ȇny7^R^ꍗK!}{xY):9xYnĤd1ŊT%bRzfRRL*= m S7fR %0=vsIAҥ3V;d1!7fnTMz̤knr0:fe:̤ެD)Ju}in.${ot]h[2>4 ^-VrVjx `u_N<0׶F5 *ʜ'Rq;2>[}aj1vR:FLʣ큠F#0frhѵvFEJi9Dcu,T`DԀ 3X𼚑B*%StnYubm� WYtkiFm`� .wo.CJ0tXk?W'pAH-xӼٚ�Aj.hw6Vh ;x0NB@J} 2&C@J}CMR:54z/kw<Xsl|Tz GbZa{ƞ)*2^wߝA:å}w.:/>cp-Cl"?p nzȨ2ڑ\OTj&D\̈́J(WDXG, _mW6ӫx[%c]j3 ;h$i^6m b h쎴xX(vF9im-@OЭ/%^-@Uͮqy`PR*ie#v<Rl0G[b!v,Caio Bwd%iVq1Sh"\UA[Yl�’SYb-Rd\: p*1ɺ7.O-%?4(j&8 M" %�]jb.d.5i.IÞԤ}Hbȅ.5Y?.XAD`G>RW9,>"ODtХlqX.e[.ot)ۺtxK lX.źIz/t L͡KY rۡXAe?k<責A^]8 ˲]]VRAY 2`<@xe"8<@xeQA.(�o<T肺 ƃ.hGw8tA\A,e]S@6%] q<2 : V.AotA ^WF6l]2\zƃ.h1dхdX!]n&5tN F6H](g<9%55tAc`A=j]2H\ F]2t% {1.ѥ-qBhC*]22xCl%o/7ENK弆/sQ+Mgͷ>EMf>}?-}zw8\I.AqAZBT>e*%&-EY+YkO8|XQZ2_bj8dlGU>�"/k-K)S�났�,e^kë|ƌֲqx*Qw9|ֲq8(Z/Z (ZVT>@ ke k-@ k-[S�lG僖^Z2Yk88e5)p*P֤|CAZ2 YkM$GC.ZkRqT>KZkRq|ؑ,ZkRqT>̩|jVqT>d5+8*vY ֚p{Yk$GÐL֬|u֬|؂.ZkVqT>Ω|jQqT>ld(8* ZrOyYk-J:ʇA kDYkAG kvYkAG kЇYkYkeY/k-hd|iQZZ1U%Zkm=Z ^NZXq|*2hAZ Z+zYkAK k�jxaxldeh�^}t74lx�Veժ<(< B�yY+cY/k-^ZLx^Zi=tt%uط7Z ?e5+tVV*rB kY< kY<>%xl}eZ+r˲</kI< B&%+֚g:'xd^MJ3OC-$Z :ԪY+aY+@AZLW!Q,qtd:Y+{|YkA=P6u+ ֲ3}QZM_1sDqe} k-( ւ k-K@XDq,ZTee*MZܱR+dAJaYfy]kd� x;Z u<?е*up&o)y(c;Y=}q]=J,3v>ܔ7G񟱝ԤQZoOϿv>68H\韣(4z~nQO·z{Y?G=1/;$IٟS<YٟhF⊈`SxwaFwxY?G=Qg<$n>푸fezbPԳ"P,fH\ |J/ 1DoS?G=Qg6<·|;ZݱΧz=c;Z�iDOz8ꉨhSjJ,UٟCbv>tC;ߡOE5){TUB;1&)!4ġMٟCiC=?ӄzBpBoS񣡝VOmOӣٔy$6]M=5"@=1)·B; 5BhS~/):$^?:ij+sHMtPxO|;bzBmڬC=!F|6סπWǥOzKq�OzKr�Oz[YrQOJ>妞ׇv>ك·oS4)]yԮ7ZglS;iTC;B]�'v>)uޣoS^G= |jS'Om<SO(C;zBԪQO*+J|kv>bWhS |*Zv>jΧ5Мv>*uԓUO=^G=!|jQ'OEFh#&ov>(=^Χ%zBB'4SϊΧPg-J{zY^G=!.jV'&O=+JC7Գv8t)OO=k&ug{8SϚ: {WQV䋧Y8ԛjR'SϚ:In>eSOJ_B7rk&4)(i|R!בϲSS8.POwb7VD=SkI=~}v͈ǟF7혛|b}{bv{֢ϑO yϊN{vExY |v|gQ'Q{fż縊@oYH�WM>6j&qO>Gk1Y?G>Nc>*ss\E7WM>+|Nӆ:ٔ99ˎyڔ99Ϧϑi|6 =6F'*gSxjA/ٔ�:ZЛ|ve|.=+tc޳+<sY)!])!g'ts]co ?4=1ٕ�05&Ck!C[!%11[fb9|xէPW!M>?*'J{֤] yJ` SdS9T<=8=+|ⶄg6OܖjA\�g=s:,V zR 8[1衟K)bFbsiЍłT^]Vqtcq-W-M>U.X\MK4nتWG>%й/kב,ϧC7VgE =>yϊA{Nemvs* $>ʺ?O>?yOHgM:ڑϩ!9:K,s(uX\'1﹵DӑO4 yOcs(BVz9z O|Ї嫞|}T{n-uBsrG>^G> rJ|\u+u!ޣ1t#]y!ʎ|v|.g'us19u#M# yڔ:99ޑϦבO�gE먘Dѐh3בOABޓbDO{֪בO '+! mK{RȄ'u41 M{R센'e=1i*TxE#:+ཁzJ1IY<ż'幎zR9& &7@%CyQ8.6/7}|^\׃J?"{B`P\ EȆ<z\=I1k)ѷn1yP68v1cGz  uAB$kb+fՃx$(m63-4A: ^²ԭo;pTޕc+�A0D>x3S>M ;TkǔkܫNTlz|Akg>∸s, 0Tlf+1i[l Ѱ37v2Z!ţamBLƂi:.aOm\;J8 O ~쁍FukP0՘%6{51:SJ'-mI{ ߇ũ3FoEY!X~McڄxV,mFhN[6me&{Z ?^,6K*8=Ѿ5 &\i+ì)\/؀0oڰIx715_ȀLݦEaIǫXt5uM,�-<l B]%8ha܀{[hxZvh?jvHd M{_/9[ +ܦifZs+ˈ!~pGү g ,d?Ҋͷ>e:0=/OUt12#8)Q#wIC"-|5?ݴ=\%mM.V4z240F#A4َ F" ]U4li4Z歊cAsu�u0L2R1 vŽ4.U)C-@AN6'6|TR,+Bcj1m�J_b mXvUaihV$ru/jAf_-ȻS2؎mr>TJ>dUח1O4Ŧ`zୖf+mU+P`jnI,f{j! .yϔ憡v`N3+K5%gqsSmAtd½Mu"ٖhV1g"\T.Ú7t4M,V[8`双0OYp.t}q0XX ulș0#�j&o68Wu 7;7mg6L;o68P3X͆,9%{sfCV.73>Lo6c6)`6òPp fǿafE˛ t,l.֑ݛ ʵ`6[l"wɛ ָ`6αzwÛ 4wo6ۛ O`6cx(`6ȶ`6ylla f¢5 FBh@>0Ei>{2ژn?+^g~pdc˗Dom0D@cRS1<E`pobC#65r*QСAնWg5\5k# Q#du-¯ZkӋUVgcxb :=e+\t6zBAP"ssYEm8Abxas0nI9Ɂ=`Kk8.sn`rCu}@AS[j4ȵKN6ޞ ,6թ[ZZo +fЊC;=f=LՍ0m1YZ#704NA=afΠq^ዪXEMu9FՀ+[v6!5,+Ǎk>kUh0J_9@t+[8kZ'a·m 5&̓iv⩵Wނ<5zO>}nţ\{jNX:/8d 3:]Z`3rwbvYg6tfaH4tLBͭc@-\EeaFb[/Tkt`Cw3_l GaP0L!1ib vѶZln5dެJy<6lԒqEfâ elaGWy0H;~Y+C0w"= 0yWn:RU6LdN@FΓ2Q6rHԬwrYH`!ZzWM �y]me, R�`~`FBa~m093via&B*sk+" Z:�ЌhZ J/}1H|h�\3rV_Nkso|j_SNُ~&g_e?@C4D|4LݓU@C4Dp6EBٻ!W!T"+~FRCCdHC(F4DPihH)DRCCJ)i,i_9Y#ҐtS4D|4D4R# *̞Ԭ[АV4Q# 5" ՟ ;<" EMCh E!bk!$HCHCj=yhiH3+ҐZ)hiHr5B-Ґ l!4Tr?!J^>V+[24lϻVp:-V}AOjBг`jf,ɰ٠)= eo6*gAH!Y0(azAQztCKlPD7PŠg@!Y0 =+zgAlgeo600=+:gAS}p]гIztAϊ%!IAϊ$>YЇ+=+xV!YK$ A 3= cgE<= Zg[Cг ApjzVx!YU5=+< 7]?�_?/w˙\TL0h @ݰ]ZFgY CAsa%58s_*B5f"ⓞmѽ\T5Vg`g.PKMͶiTh Ȃ(g.P?KPԴ/@q,URӲ9ʙ TR,7]=/l(W-f(X R5]2buo)⼁a9iYl,jH)k^[kj*);SOJb!jв^Py'MC_sߝJ䶜4b5o( Lg.xg)2,)]ὶ-WEd,:K3@H-ʰ.d9JN~d,}? E>=gz+⯣Oe>ʜɄԥ̹^}{88ՙU['$Uviu*_UV:3eypm`U-=W\VIZ�­AOzMNToB\fShWVuW|8Bn>x^[1|TQ:8)t؀5Jw\uv,L/; rTm슊Jw_Z\B @H!3Lc!lPVDV)<)s̘Vè僢k,&2A%رCRGJ8& J9qo cɵ{ZQ-XbXvک q>HB[FPRw Q@5'Ŷ`A1z$ sVxvp0Hk[TҒL!-8<$&:HZ$4SPdbx0L)2U"vWPntx̦noqtZ5amU''cve9&S4`ǧUOSAG9@^CUNr: tZ! jG*Cjv({`^3>V[[~BCytF1;_rR,1B RpdxT4Nqe Ůf栗>bU`;n1<Pᷕ0jsB݆axV. UtUxax`gag`UaUȠD+TeVq|, G#xkq<<c@w}nı:̰`َaَ`ǎaǎ`^SqVA\0Qg0QG\0 e0Hg`0?g0?g`07G07g`ٜàq ,m|\:T22Ks).D_ 2^}Ee}B_+j,C_4Y%ǞUٰАJƆTT/t2tͅNR:IeB(N m2(_hkT 8?-wb/м_C&85tfbjvCB%=8Tb0V}$f,B3$ ͐7: C#@ Mث9t("ЄElq>&ˡvBlqh| ` `!^>lZ�DZQG;o},+SW-q wx-))ύ.]i ]3es›r=G©f9f57r¬X61cx3eT0eU%P6Y\ |dY~>2"+>91:ƨa1b5-C3a"1x01 Ʋqh�X(]N%ac1Zc1.?E)�sx_0m{6<{ߎ0w.͋swQP\]%]9Su03 p5ynSu H n8vk3v2�@}Yq&#jwڝ=L(ϣѣ^XG!(v?~a0ZF qcM݃؜Ej"ݕd5Sg JҚ3lY%4aލ V3uIF�e:#Vu}L>=jT0Xԙ˳ ShLٙk:q9GpM]p*p,)jj97HVÄf5S $a>MӚ{>ME&5:̀d%N-3,3ft+d@1DUFխ^wb|_,/ڋ[ǣՋa䘽bޥ{uGB"]\{'eqӹFyHO2-9GzRn:׏i@OMz !艬Ŷ6sMjn:�}6s}Oen:׻86;yċ]asO-_kmGu]RWksp}X:ccs T jm皣"sRfўV-T4vyjjd%?5,mZۂԌ]w9SѦ>'l|ho}$XrEx zNs:%-Z<F~z?uW?vWKZGi5~p x芖fN-ϲ,f{?3+ZWYњEg?폍Ȋ,:~Ytch]UkZG՚Eg?OV-{>5ji=uZxGӪEg?&hZFg?XZGעE?-A ZF!̧X]}sel<8gkaCZ`(Yu΂ -Zt 6RZOUxHu>Zx,HMZ-HQ2Ʒ^IQV)gǗ3֪Ջ~PO'ch}UZGeShZGg?/:1ُŋס8:Ѵxُ~t-^tci]XZGEg?8:1xُ]֓1|؏~ ^tc勮QbS]~LV/DI ZiKC?,-_jxY+XZqoC Ջ=XOǾO=!|F(lMY+_V|bS endstream endobj 179 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 180 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�Jx endstream endobj 181 0 obj <</Font<</F40 37 0 R/F70 105 0 R/F39 35 0 R/F57 65 0 R/F38 34 0 R/F80 182 0 R>>/XObject<</Fm6 179 0 R>>/ProcSet[/PDF/Text]>> endobj 126 0 obj <</Type/Page/Contents[31 0 R 183 0 R 32 0 R]/Resources<</Font<</F40 37 0 R/F70 105 0 R/F39 35 0 R/F57 65 0 R/F38 34 0 R/F80 182 0 R/Xi11 1 0 R>>/XObject<</Fm6 179 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 183 0 obj <</Length 4209/Filter/FlateDecode>>stream xڍZKWJ~$'[&rr]Y@�@W>u�j3z7/O&]o/tC?ٞ^3/ %e=%7o?۵ דwyD74s ~wѹ]ES*LZqd]c?wޔ9{#[f/OC }2wVGSϏb0;y\}=7sYPlW|?=Ğ{#ľM;s7R|0uo/�:N^4lUS\ OmFnDл$\~uG bӍA(�Q@ /Nr83qIljy;zYaQ4n_1e}RF IN +U;.}$~*-4JvñGStFoSY46fx0{)Z8O r=!J" ` 43bGNY˛ǪN3[Ho]ޟ/)Diځv5Y/I'nܕ:nyIc[5I04KRxweE˹\I={Le5S 'ywͩϭ U^ǾM4~.!K" vHv<wnˮhXZtUgEw&v@kJܞtsX2GQ~9o|ndbW  7-l[ |8ۣ"`WJrubFеjޙmS(r޽:}HOEUAQa=Þtq]M_B$3\LW=gQeP⪉hn3ߤrT}=f*tU)RbMĺRlfC$cHq:t8z}id r3GOpXC11,N? Q^b;w,rtpf!ppW&Vw9c(4n]փ:7ٚ-o}לl- uѩmfZ"@OeEk n[ bLg0P8ddV3: ^(s[{B0@di/$FD ̕x"4ӣ3$U �<rB! u1P`|ڜ4[+2RQGq x=S`@OŘLZP�[*OLv*T{wEY `2tIuv"|w7C@Ao9s;2cjC]"8mz` f@!Cz)p =gOy{(l+wtNrQ8fA̧{`"\l(GN݂#(Y[G�y[ yך,rjЇ�AH ݘ4c- #L09O~Ym.]I2 GhL,BN;P߂0h*Fy]DM%0dt;$~0^Fb%[U$'lw'`Fb[$I91'w2Œr>'AY`L# p0<HC%{c3J/lh*CY(9@"@yu/pEXW~IV!.`*�xU-؅ǙOdpL CJs&�RuF hҙE&Gҙa?VF}!DX>-jT%"@@Ai\" rE1b8lԦ_)Fc)Ee -MbO% XӜ;a|Ũ*;@vZ0iVΌ:[~AoNFG6Oi[W[p^|(~?JB'uοr7:RROod0%]?80yY6%S/Ӣ__H $ⴙc�<C,V�ff6Ql UtE yq],6zY/ȃ(:/޺/߼Lt'x^FVCc {6'B=AqID0?m jfmZl0ۊt@<44I`6?t#i<$v#) =x1rzX\q)0Ŗ3œek ngLrS9˚~=&Mn@%QYhbjQH36!\J.b$ʀ yM_ʇ7c%4=Zd+푆P6qe\iz]WQ9aـp~J(zjrA'Kp0N㹃JxD1t<8Fc:RIC=;,d˞<7Ó.l|IVS#T@ ŒtSͭz0A76O7?Wv?0Ui$zgEϝZsb9ۮX4 > l&gj\!-" d;#Ȳ=F7 m)"0 ZcY Aެ9BhQXʡ~IaH)QTUI --& f,U/G=yGyh3ȓ|YV(2y*QƔj:ᅏ .c7z8P%$p 4 ` @g- 3qKf5/cTeOϕ%O. Ttb7KQ2"QlEDC#td?觼(Fs^/YB#%qMY8hw"$ /R?bVo:\U)kl^ri'J5/{4'd,vH 5Fmb\/:Fx6лd 9�b~3^> C{Q/ A;o*ЋS>K><{?8878*uҲϋꓶŁ|_!?K.,`Ī\K|ƏBHB̂"_Z'i-5/im=Ik#Mkmjz^6J Fr=s8ILUR_rBި`CO˒\d<#,|aN@5e-[A9kB~b-.d7251Xv/}W=ib �?FM o0~FpВg0E"+]+Or;rb.~t+51;ZwWs-/b2{7g֑ƛ<?Cb+zoL 9;rO ȥv=Aޓ1xZ9?E�Z r%.s-cvyjX4`]ɰRn䒊]]ũK~0z5r=T?Q; '"}RrkI-#'#Dٚ+_,}<˫ܡ9?rnFcy>{nD\#bPЉ%15W\8'ݦBRY)ϙm6mRf_�j&ΫW 0aI t۫P算&_i<2'B|9s) ǷpUGFjxԊU!Mb,Y .]ӜCC.^ȵ|b&aAtcg-ii2L*qD̻cj_\8[&? _mX@MٟT %\F$uܐQ xR=Jԧ{2{[I^h/RO8k +EoxG% y<P/;-9rjνnW){iu4f$Al iwBN@=~?yqן,/J*ԿrXPE,4P.+c`Q~Ĵ3BT*5$V\ 3Q卩۟^\ }_˱ʏ|? F7n]@Ďc^O|?L3zA%ZUN&rz.w%Ap1?wQZ.IX~qbE@ endstream endobj 182 0 obj <</Type/Font/Subtype/Type1/BaseFont/MZCEPE+NimbusRomNo9L-Regu-Slant_167/FontDescriptor 184 0 R/FirstChar 2/LastChar 122/Widths 185 0 R/Encoding 44 0 R>> endobj 180 0 obj <</XObject<</Im8 175 0 R>>/ProcSet[/PDF]>> endobj 176 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 22:13:37 2016)/ModDate(D:20160927221437-04'00')/Title(diff-ops-web-server.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 177 0 obj <</Type/ExtGState/OPM 1>> endobj 178 0 obj <</BaseFont/YUTIPD+Times-Roman/FontDescriptor 186 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 0 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 186 0 obj <</Type/FontDescriptor/FontName/YUTIPD+Times-Roman/FontBBox[0 -218 932 688]/Flags 34/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 111/MissingWidth 500/XHeight 461/CharSet(/A/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/period/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 187 0 R>> endobj 187 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5740>>stream xmXTSg߿1$P V['u VT@d CF؄ a>@ Haֺj].ھ}.}݋op<,a,kkh(v8?yZĢOf݄m̱=bYU_XQ̃sf.f$:{qTRLhpH\?Xrn˖-6I=qņG,H"Dq[m'mÓBbmEEa6BC 6`vjצá6SoqqLJ'};C7f_ pL:y(HSKQװckl׭߰qӇ6* [`N3-Îb1W}憭`0{l59`kZlfa 'Fl}cvlf-X5bӱll3aXQi; mg,Zdr6<n7@trn3f<y`yֻTٷ,=-x̩mnWʬZzϙqm9oUc*r"G5f$Ou!P.u:'2ew_w\WVh,F9HSݲ ,'PGB!K*XOCrj Y)X%D98pQ=2 1RNV@ ׆,D"8*̡@U,L_^.=_z%\S)MLqXA bV*)2q}Ls]72+e?"!dh: :uwO*5`3#VP ヤduhvPe+ 5n2r pcSg|j3j}͆y޲]hwˠz/( ~ bՕm9W`PmwP|>K|?4zW@߅"*I0BBY^zƿ\K/&sTNioE S^@B G}c c ! W)E6]n|0K{)QA~d^ؙN%u_]/Xm@.o)NdsIQɡglhF揎{D:+7⤲E8{\V/a<> ŁV" Y}ȥ3B䄃I2:a)TᮥEW{З$PS% DMa 7tm.<x1|�]sd z`q'O xq+s=D hyY%�BoG@YW!SRqq:50k.,lE\ղuD7˙4sy9A^t{=t/iVm7=gkdY^qN) ꊓ}(A%ߟ'5+[6K4= j Sd[<=->X$z3$�ܚ$Jz }?Ł2p= P!+jZcRT[[,ୂM2.樸YY)l_4UE\<쏶|-9ՖX abp}DPRB$#[=Wϥs,eY54* >s6Haeul O}8GQhrȅ_6+I\Uw`N<7M$O�j=a):AW8z-G~4$N/F@_\-ՏY}b r#yAKhRR4jwr{Ҥi̜,wwu|@Q#U~S!y/~NQ#qxy E JJƴ5K1M OuZkUewO(,*eVw}tnTY2*h=k⣙jI %r**>\ӽB^|3e2C|] >s:kj/9Wgc0 Ng3KܜF%S/?ϧ=+_hW\F(Z_XNJt 4eBHg>QVgt^qĚ*Su~vAVU]DCKupC1a9$M'C){uO^Pг4*mV$(c@O iT;ΦM, Ku 4eDMПNB: 8Tb6wmF|;3j^_%v]t�| k@݈wϑl@rg ]ӭE Fdjn 5S0lzb;/J=Grœ|Dwqpq)pW;{% ,?>Y"-'j]/o$*R<N& tMEaݲ'tmTIBgƯuBdܖLpjD8,L+Ѓ>4hr  ")lTCٳ<q8Gi<:^"i %W] J4RcO<s.(kU]mA=ȪcSK-78BKwh׫t"qeRWk. ֓5=].h564]%,o\qćrғMimS@k0Qd.Jr9K}W= ,K)Mր_j9'8e\A95jx j.?)Ǔ@& SifS#[>('E9�!E-Q%'iuЖm p#v4tZxhjURb:vO~q7!)֊vC?CSJER580te 3,ÐbC$) p@Yjqbl<=uΗimv ڑ;"y%SӚu׹7ԍ1->UOr�[$ ? ^z W%Ow\AXaviz\6 ;Ei㳺p>.6ׄ7Km1.z5h&pRKtOŅHr>DeH_�dqCDWp%@[+Qs3bl*SGV ʣK1Vִ9-M Jl {nrҞz7%eTwóy&32d}VEv=x .j_^ygT4** ɩ:_(USd©.=t'9qS9 ` ੮3|&5)ʚځ'Y^6:JU 5mDJi"&*{ܣN t6WN0zww" rcA905gk1K]:@lhgAcz@Ʌdi0 iU; D ΍5Z}YajnlF *CJ}@ q42״_Y ԯL-ǡr#)KEF @#QcWԳSuZ| zӖ SuyLzS/IsIQ773?X"I[X53 m&8{D3ZWfzi>k j>!jqA4(@FʼnqƚFs\M$m/M 5Y} ,-ҜHfĩTR^"[DZ˂_Ȑ$NDDŊKٳqPs4攖>èouvv/WO~}zY.K6AsXYt큧c/ jiL|R1:;@[|EqI5hQM;8׹ih[ Y5FhA3pj3NMuUN&л]f=<1d.6>tġ :/R祁t"%%M)`n?VC7Ɔ6 e4 O.�r"+%Қ3rETd~D*~jmO?T) K˟hU1v<,7<׈#6j(E S[`mvmŃ` X?>grKrJsYѢn4tT1_T'[N}9td~Fj +b/NBl++]lAnNhDu^!QʅkwɑUQM}Re'D8pIMuЇ<:7"WQ 0�3pv>&Hp Αxkr=$*WOib =wϥW@HHV<5 R)UiFPjktsR$l]魭[mPZJTfT0~L{.>%ϥ:Vc \~l'r Lg,8d<_R|AG=Oeby?1u1u.gվ HW/ q>*B8@bCG;|ŎsmR'$�M@[Z Ȍ&#^D66a�dt�/ٰ{l9r$??R[OSX&d̩ԸY<ӟicߖgƆGT%÷IEA"T.xw]O BeaRt[n}l^/D3GI-dn'7׬'{ xj%ydզw}fOvG%jkR-d򄐾;cO{C=#&8<ٜX-)5ҋ 8xy4,X%?bȰ?) n :˂cbb)0QMVppz_r墛Μ4ڊ~[ǹg$pf'SzpA~n+,A@WIE,[6gtk=]8'V޵=Q$R >|~Y@ܓ zwA3fbu;5rE"Ya;`cÆ>Nߋ{(|pQ�WB u @]˳4;޻+\dV71V&Tu:FjuB+RnU/>"870dbT]fˏ>ۮq (B[/E<>:`1W'!Wo _IK(ɉgս#C1rH L |zwH @F&S",̬oWIhd5ۣp.e-H4s2Ybz{.fĶA xN 1#E" RbC8Jza؄ i7:C3 ʹA>CO1??VZnSq[؛GgYèyKKv'K>8T1xm?8A?l8]ޢL~bU{d'XUE;F;3J3%fmD7[kِ!q^UXPS(jYTT&:$L4Qn&j6x1h֬fc endstream endobj 188 0 obj <</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/F80 182 0 R>>/ProcSet[/PDF/Text]>> endobj 189 0 obj <</Type/Page/Contents[27 0 R 190 0 R 28 0 R]/Resources<</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/F80 182 0 R/Xi12 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 191 0 R>> endobj 190 0 obj <</Length 4349/Filter/FlateDecode>>stream xZvȒ}[,$k6kzN2@vHwm͋;"q&gr_>}9D;}w4 I(ۑa9agOY=iL{Gch<8pq$&3>ɏ8ֵ=k4kOQlگI]T;/صTVEռLoM=QgU}<MD`q]m'>iVc=?a:V6u}wI<)U%}1±m(u~RuOgRV&~ n|Dˤͫ{થ^fmzEI_N=gLk"BW实zUY <zi+G'j!"Ki!25 +Idu\* ;WwDuM BIZHJx6?n2|gq\7CyTi$o!:؎gd|gsy ʓeRn[\ƲI::Wmi: ˗)Z\kK D-˕gca v=`ҸK*tb+ Eg骳QJrWYKs,nN*/NTud7)d[ټۥ[7Ɵ.b F]UGm% U($wSCBI*N eSeKEּ5m׆l:) 26E*4Ӗ))^٦"˿ϾsLop\!Bt\Qс9)�Nlğl? ;^^6mn[8R4/By ӏ` R>;Ħg&Ar &+eaK;ZbB?*;1a&G(: t1W㲺Qvݍ|K]]UZdiNXI{K E$&k7Я_ئ2VUՖlT%6Oo$�=h hu Оudǁty^,ǚ:w9M ~{A9-Ny1rMi-MjiT8j2մU ~iT82quk ]i]1qXQ?^r)+/2~bB`k%(; DCa߭PwA9ew0bx0viGrϵӸyo.5\\'ue Kڊ[`X|}`=1H7}@ Y!\kѵNg [% 1? ^׍~\W#*,rqf2[r}D7B¼.X W1WJm[w([ #R@2M5/r h ]dIQg\?}lHWCq*!1i2O[E- �!@t=Qm+bNc,(#Iʒ5$�AA]ʖo&I0:`:!Zs 亟fvs{*lĀ~`0]Ԡ; otF!P(!%dlZ'a1SC[JY롲Q#Yŷ ~"Ќ=+c/3(2~\K<z)acYd qU?fBW.MDOrg˜+^ el|o†CkZ]=<aӏZRb"cu u x\z JgQ+B4Yq\'EtzL%o$n'T�|oj dT 8ek\\ ,AQЖ7vh 3? %b+W {yHwYpXwuzJ[MHIF/&lp"A4"DЫDLºD:xZ,rYJ!6;Q=|+?Al p@*nbno8}қXtTf慠ځ(< '%ĘNLžOг1N6 5 k΍ Juxg*\P/yք )ЏGS%<œ<Hѽ́7ybgT5>c <CR ˜V#^QE"JQ=lyj=اzK'!>LS :ܰ{1гcqusNBy*[sz7!R)Aoλ1IyR-QGe?CP*|uЃrcZ}VU1@y\Æؑck;a<b#^H Gza"U|*a0!4+43Ay煶G^Ի *wI݂e& Ct?TLO;XmVIwNV xn18Μ/;ψc,yREK!jVmMb=4Ưlٲ}"2#酮U.,r.@)6΀tGz6^~p"ޏi4sl]/ݪźz-3AAxUo7nw_3qM{V;$8W`HT.^'E,&YXe�V(: sڱO2RyD #:.uU7}9nhWב'=rŴe6ü3pmiUHދLb".c#3IWY'yAAjdT,r�[�.Ju'[/ͪ%W-W(^m\[gј$DY7nTEHhg`̘3eo/DOiHj7i{{h3g7ym4Ʈ e5ePww<jծ ];eoηOzsl brѬm]i.s_o%Y^߰*+rhTKu 5nd"_D`obA?CQq$bui4f~2:B˼i*EhLQyw7]ct|qlX8pb:zwBJe/yoMҿcR#Aw8ZJRRm;#~G}a{uPx̿ŁI( n$p;<LS'. =.q0O/5N;}.D%cQP<4L9htM#ٞh?{f?)3}Tel5خ&|GT 6�<Hގp|NvV}ěP RBbrHq|=;qp0(Zy7|Mqs P. )l}hmblhUPWIלۢg#6UwŲ^VYS~&GmTV*r%RMBVUD"<x D-WI"W*p4B]f0Xh'XTjdvOnUIYXڑ~<'8kΑ[|_{]'Y-7PUUSppT.'wK?HKuwΝ45ū|0S#O64~<H}s%+I,g{Qo=_'.\appJKxWhGx..wgZٶUA&u{kѵ艮2]R?x,4]XiDiWO� C7q<tf^K<iGI,].MvTM8YH#GJDs pT' Pͷۦ&чEzˏ#y횔⛤Nqdժkf3�DtNkUrݻsTdx6Ϫ}CjH.L#vvC3*m]s$oR'0J __ K~x{2 r/id2HhQ_2md$I-뾵 "ڿҽ 숶cN6"LC_Epgo5~<#UM:֙cZwsuw1etg|_HgxB(}H']3Y 2&z df=2RbnѪ /`/\[`=V[{rY>S71W{ `dN <.f̄2遑P!#ފC <? endstream endobj 191 0 obj <</Type/Pages/Count 2/Parent 53 0 R/Kids[189 0 R 192 0 R]>> endobj 193 0 obj <</Font<</F39 35 0 R/F40 37 0 R/F80 182 0 R>>/ProcSet[/PDF/Text]>> endobj 192 0 obj <</Type/Page/Contents[7 0 R 194 0 R 8 0 R]/Resources<</Font<</F39 35 0 R/F40 37 0 R/F80 182 0 R/Xi13 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 191 0 R>> endobj 194 0 obj <</Length 3569/Filter/FlateDecode>>stream xڭZK6Wj,�/IxGl6N #q"=FH\DA@mfl]_>; Y꧑fXb|ijQD}ӿ2.\0S/D(Z59uYdy{TC_Pxx7Z{YO8-80̛9gb]Vndͮ*׶OkG޲m67w7`ozLxE^jUc{[¶Ǧ;H? ]IvJHއo^Q5Xtz&?ixU)w$<dcb;SF<µ**3T>1XrȮW[rZ�.Qi .bf$>p+CVr x7꿛9Vb#x-a7- _*s rb}_;UfzF�QA�jCkj5{cFLJF"@){ ~ێ8~ q-"kWˍc7>-9 v꬛}|0^ܽwϪPP,`-LE{7- Qx-S6潒QtHD&o'u6k S$QOa'iЩi۶|Gv{CyeNy9H ]Vg"=np"G,@܁W>R7@<NO>\ϟQha'@I͙cI"?ϙ \<1!{Z`Z5y 7! /lU׋5o $z#9nEQ(Zy,D4X4i@O@hbU#$fwOВiO4dI$ӢB�L M EV~;{ظ tW �]D;N}]՜CruKp''#�6lG]j>=d+)뀡^W%lGD].zxʃ ְl\{]^8re<IDaqV݀t9})e A-S>R"@]{c?YػSKU+5vIr]eKs w?냳TzL$b�P~D-ɱ͡$#TVkk9"a򩈏q_Dbd' KCkXva80Tb6z(^T[3c#R`2o&4e>51>Z}6% (׀Z='U_v]7UQ'e ?鲬!`2e 1"95__qŎ 8m$ ʄ a o 6_ӿe8VB@FKο]6敒c{s O�|t8FNsN-1�CIST(�)ˁ//Lo]TL67z}s+��ki! C ɮj+pu>^g`./ ѡ+m4~ZtGƐ(B ,XI:A JJƲTPRE40]v'=D.I0`#J` +{_fW7՘x1p@Xѓ,ջ -�֘kF01^�Ž126G:G(hC{5n#?8~ەOu~*E_pT-F@M;CNVO:[2z?Y DVA՘)Dr ~S .b! C [DU2o(]F%ܝZe-*v8̲KJArR)HN+RL=|{c}]G8$;;ĔS si3/@hmL1X3KZҗ2,U<4LY1Fc -x#H 3^G D++TV>q�Hw@1[M@UsqxM;C,C <&Qx#/T Iwnv\L$C9$:}Mۨ3b@4,$p=)�MI[�ҪjTźq#N ٩7"ϰ&XI|ZnK0 4i]$-y45)H }.'EwT ǏQ<im Hal2?p ح)7^D� ^Yv&iknՅznBw<r<h,H+c R aK^X0h'îp)gviq1wʷ^e$\!֝3~'(HNlm&އrs4iVl$,$. }xe;^U[UDQ1 Qt >krpĭ(_նVӛ׏ZQ Zu.nuul!!lLݫ-Mtjk\,e(0mu.k1FݩV)ٗ3/d43|rS7=޾3 RA<]g�gR :h@k]+zJ$t^b[jOGe Lƙo5@1!0_ci%2~8|6pTw&>Yd?Ҏ Ъ@Fu4TT م*s]4 "%.jk/L!42*6KFLbGA`NKE锹EVܔ%Z˰Efj? {g) .Cq# bօO~9*˕6twIJLbzJC4�+z8FU|Nزhy<3dxg)AZ 94|qLGˋ'͌.o 5YK? Y|t} {&>Gw3#C0XJ�n(iDt|+ /&/`ES7]~Q= 骟�iH<`�&gi\􍇈R_#|t& %QԤ {k Bw}AK,1qhZ=L&Fn& crB2 i8]]fC'3B?@Sٞc¾7dֆ)vWʶ} 9]` =gwfeN:ʏ}o(,cNjx,ehErK`�ͭ^zm{}:XcU;Am ]j<B RSWLک,9.;ZجE˩BBL"?V=fnۊtjHn6џc{'soTm"c*` eޕ>A K!�y endstream endobj 44 0 obj <</Type/Encoding/Differences[2/fi/fl 33/exclam 35/numbersign 37/percent/ampersand/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon 65/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft 93/bracketright 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z 150/endash/emdash]>> endobj 185 0 obj [556 556 167 333 611 278 333 333 0 333 564 0 611 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 180 250 333 408 500 500 833 778 333 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444] endobj 148 0 obj [619.2] endobj 146 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 111 0 obj [500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 832 667 667 667 722 667 667 722 778 389 500 667 611 889 722 722 611 722 667 556 611 722 667 889 667 611 611 333 278 333 570 500 333 500 500 444 500 444 333 500 556 278 278 500 278 778 556 500 500 500 389 389 278 556 444] endobj 109 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 103 0 obj [877 323.4 384.9 323.4 569.5 569.5 569.5 569.5 569.5] endobj 101 0 obj [904.9] endobj 95 0 obj [803.5 762.8 642 790.6 759.3 613.2 584.4 682.8 583.3 944.4 828.5 580.6 682.6 388.9 388.9 388.9 1000 1000 416.7 528.6 429.2 432.8 520.5 465.6 489.6 477 576.2 344.5 411.8 520.6 298.4 878 600.2 484.7 503.1 446.4 451.2 468.8 361.1 572.5] endobj 93 0 obj [388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500] endobj 68 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 52 0 obj [777.8] endobj 50 0 obj [500 500 167 333 556 278 333 333 0 333 675 0 556 389 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 214 250 333 420 500 500 833 778 333 333 333 500 675 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 675 675 675 500 920 611 611 667 722 611 611 722 722 333 444 667 556 833 667 722 611 722 611 500 556 722 611 833 611 556 556 389 278 389 422 500 333 500 500 444 500 444 278 500 500 278 278 444 278 722 500 500 500 500 389 389 278 500 444 667 444 444 389 400 275 400 541 0 0 0 333 500 556 889 500 500 333 1000 500 333 944 0 0 0 0 0 0 556 556 350 500 889] endobj 48 0 obj [531.3 531.3] endobj 46 0 obj [556 556 167 333 611 278 333 333 0 333 564 0 611 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 180 250 333 408 500 500 833 778 333 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 480 200 480 541 0 0 0 333 500 444 1000 500 500 333 1000 556 333 889 0 0 0 0 0 0 444 444 350 500 1000] endobj 43 0 obj [556 556 167 333 667 278 333 333 0 333 570 0 667 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 278 250 333 555 500 500 1000 833 333 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 930 722 667 722 722 667 611 778 778 389 500 778 667 944 722 778 611 778 722 556 667 722 722 1000 722 722 667 333 278 333 581 500 333 500 556 444 556 444 333 500 556 278 333 556 278 833 556 500 556 556 444 389 333 556 500 722 500 500 444] endobj 195 0 obj <</Length1 1632/Length2 9261/Length3 0/Length 10320/Filter/FlateDecode>>stream xڍT6tJ#Ƞ4Ctw7 0Ht#%-)"J7{_֬5s_ =& ,u@q�@�rakAvɱu08 $ B<Ȥ@Ce@'J�pe �@ 2;@�cKB`+kCL�ׯ_ 2a hhB!``F 88\\\Apv(JAX4p0l2@d5vlz5Bjp�py`4�`6`3�';;/g9qXB�U%v+�rmCA A�qu� 8;bGa,`! ; ؿ듂swrm.B mXuv8Kc �_qN�5Zn࿔=xy8Bm l8 @ނ<T7X@�3?`˿ �8߯fus_W̡"OVJH@]l\�6.^ xp8j ?u+` އAfHφ0; `�n?q?/?eV$/==bu"@ k {wGAl<@ lA[Mڿ�V!1^@1*a]o ~XCj{x�  p`׿ `w"\�={,0PEw}@N�r8�!5?zD?C"?C"? | 8W�[aa=`W94\0*B҅mkD0Bm$#=n6X$3)clӮ"t<sYMSsF#cEcad9i  `=HjXV=/fc]H e^)uT)Qh4=!na\Q;ƥ`osO `?Fb"3Yb}*#ͣ}#ZƸW֦q/ I$`Y9a 4(}"pd\r:Bpd[Dy�FVỒ^ΖAM/+7YҘ,{'u [D_4rs9Cʴs~?p>nk`w*w$p_^Lxu<2B~̳uHUb{:C>*�Ita;Y  B_^h 7soվ>r @۱HTPg݊/YdHS8pVnߣ%i5Z ֨rܦ{<?~ݘՓpJܖBa_Lןp[Q>=N~LcfBsov5!)Ar7Qwy.=st }#Wf ˥?:jtPi.9:ݣ堦MUcvN4"iud;. @UzH.)4y9ޱYN@&=v<:;ǫ&`jfdwτx]{!}ɩAWܣQQYf1)?1A8<&{hD׽#W,cVq 8BӤӊCQ\E,g䪯 h>ۤhys\lnR7cN2Shu}-h|-xsxX7I^+F1/N bEO􈀜z8#G E_ΣC,~(޼m;[793"H~ퟵ4e֨W_$Y2d=̴ZZ,уJ=BB::_o ^]%߬ʚpIX~ n<8 mڡLNv}+ +=ϑ"`ʫh{,"UScB/ P&^ոeͿ7J;)pCd2#=te 8B_q7k200 cb`({ X{(#.[WQ|}ey 6͗-p.3q×+\t#T|*t^u7Bc:(|&0@ " .#sh^zPnmYNqdV?ԅ~7+smw3{Q'@-5)^P>qy)T4G`, quxl%bBz~, GV:{lʰѥ>O5#euR0CV=޵mNT8)@L>Y^gN5V/b&8[M h3,&x% c}x2_t<vdrRA8ny0%p<:Җ#xe,o%ۊΛ"<zCC6d"ғU>Uz=J.>Ek'Vn�Rڽ7@qŴ0h 7͈ (ysҽwI!i6Xm;kd1G{2qtm>ڳ(v3|NYXnU.^~rrW f7nPׯu6e?]s4[P j6e)Q<Dg駴f/Y&U%X3/XR#+qO0ѷTFTTVʈn6YƾBLC-:Y;-r3%/^[.٪wxoB^P ϰfk)F <Q,T>N͋?&=<cB+]j'h(^&le`(R;l y.~Yr2{ oi׬ |OL8r&]�84YD9D1q'{V65"hP?AQl/6LvAF<]U+rArϞcvpO< AH =תQuUt?H9_ȵ .sКVn6j6uκstk*#gC$'VVqOC Cpu/6l!e6!Q0I>4H?Di~\ *I 'սQɧ P ?HubLrȪN[,F}u͵D_Ö1+PK|bC)~xǿ׽5a+ V:N'Q@)`zyG2c|-nsXIƓRSꖍж176oYY}زI홎7MDt`,I)ȎHGe?CqcoNZ>L&G)ls\i9K$m5} @Adf�7Q>wW|ukB{[jt(6>.]@H2,Wh2fЪ3ߔn蕍m؉8|́-h򀀻7pQJ>Szv{$W]�0ͿLb1L@5Α�T_C']##iĴd9IһJ& f2e4&oPV۸zvɶ<o6}!7~r*{Oz&M bW<V:СS+e\MN-'ef g`pV2.[=}$TXB]ߗk 09f fQdYy)zF]3Ǡc[l~,9#MB|,ƾcSvK_݄N y#|)aQ? `pS4;!YiIQR@@ji|p$R,H+H߽SڂAlcOcZfK7\qm"]V KSw?|ӝ oKG7űrª2DJ;jCKՓ}3uv~*Mf^2"n b$&Č �|Dֆf@sS˝Rj&O7%ў;7"E&e^-SOoڌ([DkI6Oh9 {lO9̈́com9<Vw⠩#YQý(e CGZ+r3٧-hƇϋ&yoJaM}JiW!l%3;wIrgC0(V.<qu'镬Ƥ/¯N1~ӥ2E^[Oih%;}u{ -eIk#XHbuy0"'衆RioV*\܌V \N?D3g=NOy:2; T' VlW-V]?[K �Z1^xM?R(^&`U$bOŘuk]Ev xط=(EOm2Kq}ylù} 9sW9TrA2[j5}AALa2AG(⪙eA/׎#R rϹВp+B6NiJ%b2K VTt_o]rIVkdcU4{&of`DAK$KJdT/>&92r-')wE 9G&=&!%4UּV\VLMef9́qaDpN;r!k,qGeF6͂>j@?0;q.l Y5#2bBF꼞5(*h((-21F&mNU%,ZgKrnW,1yMRr{s~dZύd yA yNdnK5w2B_o<W& O8`4f{-}95כv%v'7[ aѶ_l_,eĈif]y*0ݟRxƍ;kJ 1 &oS+s,R}ݎٚ[8R ?2gE}LaShh<~ہgt!G!,RQe޽s'x sd =O )t&<!y1PN:1+_P &xN쵩>cZqE JPDvkA Ǜ7%eI,W}$5DgU$s6ZsTG>U` ~@рo[Rh͑HTOܤ1uM^Ę<]MWK 'Ʈ_\aMl}=#q{|2DPnB]脜zv7B~ VՖƸP߳5W4Q ɓ'lL_:+%J) VF9*8e~p\)ФbPnpї̍2hȏdw!U>oPFaHmR‚y.Li${Lr.UwöY 3N'[jI*aw~BB{JA&JeYt fJDT 7`23 j*~Ejo]|'? iMUOku},lF{-f2aOD%x,hWXQ3 irA*;HL%CVصP}IߟX\mQB .zo R#(fmjc^TX+%@,QU<^U5 îJ A+~EYjZi{ϡA@n jbuKʹ˷Op`6k?>ޑR,~XwKr4`ê(VB$"4Y#O{�V+ڢLfcЈz0>>o$,O}z.<m t m8 N?gPO/Ǵv黏id11\a~ n9lg}T]-|ڏקDu]sX^j�?rh5&,谆r!jԐ=d;6^N|}"%l':.!|#Iz.X>Gލ}$iܾ10"cXX(ӵ$IK? VJ)) D{D)JӂJ xxeʕ{#N 3;3/u|L}zN ~^L6-ڹ(GMc7@p;fMBJMK Zs/x:d2p&|x)9\9ˣPG?05Fu㇊Y^o~4}}w+3vqĔJgx+{/Lj7,�wg..bM=Ks<6i(bp'uPB{w^F/Q˝?&xS�<1:}tq NYa2zب ;B/$ rb _&_2 uz^-伣/`AORl {0o'ϸB(K=a#/q^ LJ^@ qvU۸}_[A3VZ?$ qZW:gh;i.0,*8Z'| 62O0?ns͟2$ gM.i~Q8aj^_)_ Y%TRI׏HMN\~DAd+Mr1`kYe"[! 9C0zҊǗ3I$"Y>tǺ̊pf tpu p}J[~kv&wKruC拚) 4TK rEaضjJCni.&^h;Q45:a"$T ɛ&"4ݮI*7e_:P;9e tr1;Bfݴ K#?tQ{-%I:`}Tnn =+rUf:e")K@1,[>z@rM#%@Vy5M| oܱJfEcXNH<TqcJme=n%CcV&yt` ǣ*/[؞;vQrţ.5r.[1ʑL&5o6x/4b_n+:iGx.Qёb43(ƞrE S|;[$yAԌ䦹8BRu~m?\MP<ػxdOzTڢ5e樅)Gתe!=}y7HotB2[x 2#Ctք)}UБf1[[ڡPr>.M5 Cy'M_izGa)K;nMͫ.<Ŵ-43Fv7xel 1O}Ș5y\?\7Q{:?x\&{W)bߤzq^%![zۆ3^К-*Qt1C%̼`I] N}_u5Ff=HU%qv&I;byAv0T#&'Asa3C#|J2o?;=]e5v K,SOz+|*^e*nJ7l@R5a3ϣ|h*:%t u ຟ[%4UGF"ۆ $r[!i#ϻzi罺#eʦHmU; ?SeƲl~ EzSې1- ;牛)rqFMh]z}J1QUkf_@yz{̓Y٣c.#MS}!/bs×�,&@J2]ePfW݋]$SWP\rgv!b}e)QV+%`{'\b<u(y۬f&bN7B.o-4-"bEU}yWo<lEe.0P\.F4\t-A[ Wcz(=eaJ}6MQ^;~fôm@T rč�MZoc`\3; C__s|сy+z�1K[BƲ/ ed/l_h5aLOp[rX0i~ЁgJ̊˞f:.̧tUOp 5 ўCX.>@Ï v?^h"_uؔEm/'QT7ⷘؽ.SڪI*- ;VqD?U-4f0S ۪&#Yۅu ZW[Cs P/)M,OyߖE-ffbk pR,0%-6p?|`n#وeLFw˅jvWar?I5KDtZ|g0:DI0<"Vd xA~>"Erᓅc=@a6D-A$;e? 6e.H li$doAa)v<SS76ǟ(P[(R(}K TZE=v׌WŒ/o1g,N~.TVday K?_M AԹJfb|)HQ᎘8uUدv#EA6ʍ)-/Ԓ Q^3CU%<xLO DmuK،Yr^:an!Rc Ɓ-4 ,l1A:$,;?u/><M`Qxdz)x(m>8l!}<4 lx= ٳFU LsO(+(&hφ>]Ô55BsK?~Su6u/Y&T̴%Yuaᔊ5.PWӞ'¢+ʄckQjuP 9xӅL^pM’鼁:=ww\v/8h **OLmW=0 xw2gQY\aMBʢ\`~-7bي2¡JИ .OHBQ8Q|BS8xÑH*5|Io+J.2YQR:U.3S#V) &* fެFst;K\mش(]%Kr*u2݌@kCz5Kc4Z8.pFOq^%R>Y6j$k}=AZR1Hn\Y6v)ڮ(JYMǻ$NC9VNx5Ϲ-om1vvEM;8=$ǖB#Iedȣ2 r:ʝ/Di%D aΗmzŧ;ӐFA=C7g[-]s <Yr "pPrzRƖ%")}sa wz5�8n-oQIҎt{ eNywce@5:j V ib.].ZFʫ1Dj{kAn̗Gqқ9(\+|m'lW09 *DXƴ<7ni]_wi|#=tU;y=@k2y_Ro=mv@@Xi.SwUM{ (>0x)9273~e~4>h#:K>}o,OOݟh}LWIaZ=.?D endstream endobj 94 0 obj <</Type/FontDescriptor/FontName/NIWRQN+CMMI10/Flags 4/FontBBox[-32 -250 1048 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle -14/StemV 72/XHeight 431/CharSet(/N/b/d/e/f/g/i/l/m/n/o/p/q/r/s/t/u)/FontFile 195 0 R>> endobj 196 0 obj <</Length1 1384/Length2 6102/Length3 0/Length 7057/Filter/FlateDecode>>stream xڍtTT6)HcNE`b$[PB@N )iDBQ$D@Rq}k:s~b30PGQH�D, jJ�` ,L  ЮnR BJ/'Th,N]�HH0,/ SP ]A@ {TPG'40�"%%;Pr{"`P$ E;ݰ' 1 Q[ vy <y_ 0{=}=d or@B=�ኀ^ o=k�p�?n� f*@N`(7w(tp@_]GH_@ "\vXΡ!�w</'%p5Я2[VCګHTplE5H{_C{ "pMտ?>G8KJH p�sU;NrCpi=;O04`wD IS;DwXA�߿aeB{B5T,L2�b`��ؗV1"vO&@^ӿ:K�YLe-ɭb`URvu?P7_�h�tQX j#Z]=h(VJHG, `?~:no@ÜP\H ۂ+Opه;a1q� 'Ůk@~  "Qhl 9p@yZ$ E0oOO칿ـ=_om~p4 &\vT+2&Kyd!,0V`MSj3]> QvS8zqf?p_PU#]!TZOfif7=p X⯿&Qw3Qq`|9K%":^ה2Km.[LVܒ|I{®-qL[}Se/KW|Oέ(4�(QB>Y^Ɠv5xN򤕤doDW&zu(;Q"Nck8f{}~؂=~ؕ*ŧ77 4ծ "}d5T8bftepR|as˂ݲ=ij& 27\)4*+�ɇN "Gbuu1Wl[ sX:8NNRqXftk3AwkOdb۶r<RM$RƘR^S<?3  6rUXxjBC2C!((kJ.@GY5z,+e!j`T jFv;6U.\nshq^) 0K;(eBB<_-u" Q74DU4GWYe=h\ Tm �p_}\Ю]bXx<ξTD OC4x>GgB6ix"s[b(zlSQѣd:/xdw?߈ *S%]2;5kk^_ύ.;2}b8Nv9)4AՍ$GkrYMt9E6ws pt S\_1'a>S[Eٛ7ǡ{Ia[B17hR"koH}ðϽ1}~A—xN="$.eNР좮ܴ#6)4Kgap9azYolxz"F.Q;R>xk*wKErcϷK4υ@ߟ2/ʓ׷dslk͹;vS|`9:BG\3&_'l_}C5yrW4YmL_U/c {)1=5ˍwm^7;2K BH>j2D׿<.md;/2Fc_mt_˻ x~ݜ]XB%Rۈjkx?ɋKD#e[o2wSxXyOhg#Nn7WxtvA|K3;Dsxŭ112kߢs,B Ly;zIm%Т~42@Y?#5e;]s^mDNE3'dŌΈgP +ȓL{TvRB=w|lѻ2KG㜏 $P,wғ<9=*#s$ ~v|2 VT q$-=gdogeX ݢJfeqo6..6 bRZy)ctgDq৩ZwˣїWbw%{O9dklQ8l١rV\uV>Ҭ@9X ,MІp#*XD *hr|3#oJ;%D/g9V^][{9[UuC lR~CR ߖsgI74# rP)Q%GX;YT6o/qf;iN:jnp\|Jѽ<kUl5Ԕ?Y%5.h^ɺI+8]|B^%.AR 4 ה%-хt7mWI7LIQHNR!W$u3"_=g̅WN/6+%S ~&5ڨ42%KOZޏ8\ʇףVr ķFj)lJ4گZC-FdM]rz8wٓ9 =qOç3oq҅,qbVJG6㹐Q HoJ rkŨPM0ghOt=(2yne7\[pi_5YM/ǭ>6uXrxe^(|S/&WlLIRoӃEiu9R¬o XԙR_Cς7{EHgJ O]>"GYZXܩ)W:-{ZOʓ.ԺnF?ꨏ+gYꨦS}inF)qWc, YW7a<zT0+YvL>N HPo#wmr\.x� k]IZ7g+LcEOZ^Wf[= HUnMٖgr`njѢ|6NoI*mU^_zj8c4M(GC:_9̞Hzh}\9wKœi,v(c$_ܫAP-GWb~?^M ;ơ\Uo/rAе7Ž )g߽0e{]@ x<& //usS|Xiٟ%W>B'Z];,4XK"ːh�U[x. a"FM$rjfQ =UY{Y9 PT?}dd`bn*FuN%/ 7.5!K~W.Q[F/b"cIQʻ=KO9?*Þ0 S [qS5MosZ\)͙S&l^/:&eDw-<dy=X |"8]ۙגk|,cP.RCz*1aEh=5^:h k xixhN`5b}xmIDCrldB17N}ua'݄"}_AeoֲOҷ ijCml}qs7u%YUyAUFCl  G\V <L%5o?\*7n=V2S5pFG6*$SJNAwb)ӫMDg x1۔y?huH !լK:m=w/8ǠyUqw{mX׺*?(4!86!鵂+BZ<~q;g P=:XK>vқ}UHw$!(jצ?Q~|0m)MC~ۺpɘ@%'oUVt V/8Sҍ}Z/@L▸!D猢R'h-xdvʤdLMQ}=VH,Ͼ}e}>M:n"9Ln짓˷YJyS <?{&fѕi)EmׅBai7}_8&H0a->0 s?!7nIN ?̦ߺ,U6pTRhy+é Y)^ScuH(~GFGb>ګj'l'k- 2)RoRSAi[kM-="V֒wGeޯ!F -t䥪oHةr% Mq!U#SeWvvͦ R 9-nОcfd;Õ;j}6aJ@إ+qSG"L[d!V rFmctK!79O0!7]RJ.u3'%Ǻ@ˡcF{_T W3EEYջzDaiLc y/; Q oԘ b yxoL*κf(51RzC &F ]3yZ$=>RLȇ+gx֩ڴ󲨥x)РKB-D1䦇oa^ԥ ힷ�?hm!+!'5[EE89$5$ J|B}+x{GҒt&[d "_0V1vע^La+@;[aإ {#?;<p(!'΂.:*#$Q{4FĨb?\DVz~O`[~Ln-q: MJepiRQ%K!Qz;.0}pIbN͹o IݸJz4xܹ=6z8[0ֲ@F1{Y͋KeK 7D2}fʩ_ ė #a2]o7]e WB\K]Γz8g/1R},4wv80,h]gmxj*؋9k<0h}.UNs�vz69MkĎ-G6="MqW 8) ;%|x͆>s 7wB(Qi{+3 !87ݟ;vi6 a~{ Ԓ1;V{F019۽dVH?Gemc`M^Kя\NЬR\D:6~?| P&s0Q:u𪂊+k~ c8m$eKYUcK[Gw+oXB4; U>@pELǜuhAQPF ޾c_} bZs]vY<A =SLLZxLoo^kˍ/Û3uپg;e{+J~~qg4F6;>)"#ӛѓ-UyG:ĂOKc 9w_y 7^Ll"_O#e ݓE@p/)3Sq ms2l kgfTxc{v吭Y{R/N wCF N(QU(l:v3�Nd(u΃ƺKGǒ}}.Y*:Sɤ0%iϒoRs|'MSun{MdrfpbkUr%\[f:p%Aկ7ɮ FAaƙ% Ǩػv6SzJoDnnR76:gnl](UǏl)ZMzKXǔj%8SiAJq;[_7nMy,$DТq%FqCI=*MÓɐ$nsV;{HW||̅tuNcE,Z1'tOCUS21SIl)ᷘڄΩ @.]¶Ch󁏖Ptw[\WlrYH&쨴\Eɝ5\3v'tG|JӺ0.nRKL{$WAxIQ)݇ÏOo{vuLMXIYK^k7#EPaEU iN pXT@ ;%HGMg$UiȄt(K<Jekޭt{j1?m_iLj}N9d?F_u<_|Z8a=Nb-)BݗqHx|$ɘ2,6kM`7W|e5!nMi@@gKڱFrשY n+m'-6aa+`Gm𒹎5V@̒& Q7k&Ę\/ lnhH5pUaq';Q^,Ėؠ#uꁾv)# ԧLaxn+E3ݱVBUk1}{<$/{Z&0#kqAF턙y|~u%s}2e$;׮l endstream endobj 100 0 obj <</Type/FontDescriptor/FontName/GINCXO+CMMI7/Flags 4/FontBBox[-1 -250 1171 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle -14/StemV 81/XHeight 431/CharSet(/N)/FontFile 196 0 R>> endobj 197 0 obj <</Length1 1386/Length2 6040/Length3 0/Length 6993/Filter/FlateDecode>>stream xڍtT." )H %!% �CP҂tҝtHJ )*19߹w{׬ͷ~~~| p;*�Jtt4$@�(LiEBI8M $_�%A( NhzD�Bĥ@0(/ !PyA:�M8 $T"N(1zpy�B| n t@('D0`C!(vBܥ@nH8Q E9Bk`. g2Now@y� C`HL'�`jh!?`?�>߻ _ 0Ba�+-A@0_@+y ; w �xH0B FUs*0{%BO1+g.07�;]h(`\$9BP�1(�7u rc&s0C@�E"$Q�;#F7ᏍY>b'�C/{8?TWhjg>�?~aI� c^YF?08&O{W^W<Ӆch pV@1 URtu? 7_�(tj Z=(F 0G DP*bEp\0> qd 1!1jC^/?P',v�B @$c,1F (L �3s� faa/?=9_ouC >0)8AsMXImoi3aGD^O6+YZ%{T9*z,NCײn:O]aO$3MQ4a噦(2KAb߉ w5[=ES*ުQUM$il7`.n[\ZճT 6-SDJy;^!8T 0)V̿"}㵓AgvS~Ǿt'F }czϣ'h@[-IF^`pB"HK^O%ÐQTӂpE-V4𼊎cgsR, +WOo_hYE>GC+GW8 uxx^F|!6< [\&"`W& +s (wxNW0r/k1sOpoͥHeADKQ˼)A9, &G֚v2?.ȹȜPCSBYCPM&n*FhYOhP99uW2.E/Uz#(( r#!֑ܷT{H5=QHȔ>yN1-ԪazPv{K~{ܗi.<Esvz6!kVي #@[<:r wo䰁@5SY4^;aO%[D~1CNA�tI|nN)}|?'BJJ؏F~')2ۑ6^_RNjn6n2ޢrtȝtp8vH#/*vMVgѤS1CՇq"26|#k^,ﮩ�8AZV|RwK{ϗ* gYԎq)[>\tQE4(;~:Zj -z.˫hHO<E1I6nsf[KJXKೲf?"RX+34߳\iykjd-R>xGvŶ26@fy' Mӕ?pJл/}@5~:A_6߼0+۬ p":Iބhw=-#2.pNU6Qpt=,I4eCLU~}+bTlv=3)'J䤁/(-ئ~)ex>q;{>-Y�{φEhs+\@/0uSi<h%_q|*׋ngmY흜bWD}(Mjpq o9:u.PF{Y tb jS"Yiu 0b5[V~ Cć8zˆNl[SCG >gf=!Eٜ<OyOl5؟J Ôx{ӱf#/l6b W˜=ri`G|\㱡Tܢ2_o $H5aׁ-26dgZWorsYy:Ղ -:O}y.i69[,m "IMX>x?O+yfm`?rfy*4_uj;SCėR 3!o򄕬u?ẻ^I .*[(-`*E,eؔ|hWkB˥R؃N~'MϞؿ ^.oa~Ӧ1q*)P))qw8]I.\F9jX![얫R8ATS JL#)Ztqo1y-)gõ?Nd;\]nr,D(,2%z<g�b۾ *f ' TM^u%ؾ<;z:wOx:TBJ;AF!0~_9Lu֎?'~R{M)bujwV:չKY5E_[,:R6z, !kZ4itnbzre&\7)M#N!#\qHg$>{.qZmu=sڏ9*˿%,�צIePp}ȕ_(aO X8+Oxmxs VPZ2%Wklg蔷8`["J鼑 -\ T]PX\Cޖù(l~|;}G8hi@t 9(cl!; I쏱,BȈMVr͍ޕ+Hclg;ų\<͟iNV.yK|%Lb͓2Ö"1ƅp4r#y{2X\x>nCO{<-$,x~^-@䇗ugXKp]ݗ8&4Gl+ش9 \Yk״~hEE"ѐmژ g6>ǜ|h,Hv]qBD)!ESg4`}L p_s'.erllOLȯIyx1'%ϫgSASVu^{.imjG-¬ Ppi3!<"iS&Н;X0Qř@c(3f+Wwqxy4åXĥaҤ{Dq^*oza[2?17 w6i}ؘoxZңa_Ef7.eJ7v<Se2Hy$ߟSr1󪠺oԨsVul+SSь!c@|};~I%!s˞Hr_"-܂]j|6k6�.Y֖,)ݘojuԵ&$ t_NkR=eCqYI#vJi/Adq(LۍtrG's?Ox#Wsޅa=v@946(ܠlڂZdXJ:u"k)'~MQfdrtK\?UziN>֌ȸ;k>]QRuH'v=P!Md{pa]R ?\OIw`LH x#~FÆIRebj~ZLAZCӋ9*m(ȃIt@cHuPMp E㗠ʭ " 'Ņ{:%ɻnH4t5oZ3_hK{KrA&TYLw8=]O|| Q7zF0Kޜ b1G /; q~K/ ŏhmZOLҶ1U*h#h~%C<:sU.2xW,hܼ bGan*mf΋8+CKxQvJX8fqP{=qɏzESYǏ<"NŸ>u Ս'_M -p<$#S 3 7|ߜ}Y\;%%TFA3`|u'hԲ?%(:aP285rdvjetj4=ۥdWT itQh $ZhTr`c;Ey˛l C-VHX2Q۷fڋz"7^at*$[].} !er .yL�K$Zb:rjb4k85~&h>, T5M_7QT.<d 84T@LgZ? NxMObtj3\6H*-VSrN ȫO'/ Bhscng|J56Jx`DFE', 0z ,h9%:bcr4sj+icm|R㄂oR/z\U,L{XFwZ(:JJ5K;�zvCTU4O-@*J<DS3Ldl{=VV.j"28Lxx_Ýp{h2z_gsa\7h-HDQ2}؉L(?}t@uOXRs @x*\-wE.dv5j,n߸I4m7\DdBLr/©lù?'M0#*x3׃J睹}=nm_k2~RUcnAbzq>ݓ hcξQv-P,|]U@E[$.sZp|hO ,6R26T=_Q.a �E[%(|nխiKV4/<t=hRYLk ]M6+Όo �w /> ϙujǓN1Ac%JBtqp!{H]'�[p ܋9&6ngzEu(I 4`Ng7DfXM6=%,d Q*D6 y5 = Nb+NQqP)>n*ܐ@dzJh\5pniK˯L(oR}&XestDMŷ``=>P2W- G 5O]Ԭ%ؔ6%xl*-=Գ7ڶ"7vprLU\pgfM=)5r:;į[Bg$ʁGLbI3b9u" .c ةAi@Bj�FW;q;ki9v:<=\qkx뫂+iLfZBUmĻ؉0)9E K׭s,wB00d2 g,=CPj+$v7URS9U_=Wu_]Tvߏn-Sw6Ͻiܣ*߽/cD$w\%BwC$$�DtZ$:.  R}|'mCyfb~i]Q0/,܂ $c{ _XQ\n~IMؗAS-eo쮗-A4MXY_jMƔpbW²PG4R虇VtxI豿N_ I Π42jhi5aoHrbǛ~[D0<VqҏB9]"w%#+_{UX M6pBjwh'vPcUi< cZu[h@ 3"^5ƐDspw:ˎpk9Vm@פa(6P:+U3#[�g}\柵==z%O)/~qpok)3NHOKKGo LNKi}F!H.~NU!u]Saa>ZUUФ"{K2W:*t̘M=,L#ف8@,9G\ k7G(_Q㍩fSN)aE숡^�T&q.k (/wvJoz2ľ>+a,Je-IrG+5ɀ*?t/-'բ<hJqQ_PXf.OsוuYqi^9n˭+en[3: nOY6}/w4ZEGQ"Ö{k&�ujݼ;Jk @qEaJ*mFy et o])Gcz/^?Hخ.P˺R*ءĻ:mt0͗d;}p'T)?jvLevL�A5 endstream endobj 147 0 obj <</Type/FontDescriptor/FontName/HHRWSH+CMMI9/Flags 4/FontBBox[-29 -250 1075 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle -14/StemV 74/XHeight 431/CharSet(/mu)/FontFile 197 0 R>> endobj 198 0 obj <</Length1 1443/Length2 6987/Length3 0/Length 7968/Filter/FlateDecode>>stream xڍtT6" %"0tJw7"9� CtH#"!(H#1twt|yy}kz׎{{_{#l!*8W$ T6�A !>HnE �v K"F]cJ`Ե6pb P!) T{@|@ `WDx#Nkurq$$v;CP;0 F9AoÀ;(\N($?'ٍt􄢜7bU0P Shu"P`$x v۵;^_4T@6@>+lgpvýpGhP`/C0 q�Ca`kߙ*@ufܠ_% sp{E3rO ]?7>#< Pï"]PWw_&ߘ#@bB@+e+ R|] p]�= @J�=8BG!#^@s5_OGa6_~-uEc?SP@x}yA@^APĮ @?^?ӿ2�\ 7t׬�Mr #oEH `\ yepMZw�h#�ߦ?C ;Vy#g@ zPM !ܠ 5-'׫횒Up;H7גWz!^I #P.$WG%G]7q@ Ϳak7p%?[Gvkoo"]/Z@ vT㏡ux.>d_6};w?%4=xy(Ԣ2O [5Ig~։˵~궾MO Fr+~~&AOphg轥8Ĩz}jEOv,믔jXgf Ӳxy<$wŬߊ}4'w23]d$HF'y�ZͨoATۘWt/xo B -e KY/w( +)T2'ƵC&/&$}9mV5Nwv9Zl7͗mepd55)&sNy 0i  #- ;"Y~+tFa!W!:W_^9 )ۤse)7=˞ԗ AS)z3k=_Ζ'ڲ<ZeD"oǤظ6A$Gei+g"C!$ #qhp:d3R3nH,4YAF7TU[ln)x8WƓ=8]i|wyΙ7ݪ.7n9B!P@\XD鷧W&qZwV|WchtV跞Elb|aLPJ$&2`WLԥCTՌ|CYPdvWz!L03}F+j$)O)s|Ճ73??h4fU4 _9}曗j>QtKRL Myky,1aȢ9);>Ƨ|7Rf ,#$' -9ыBcq%7n I:/"QudUInWzЗ]}IH8`t�?Jvyd,}io&+tx=Ç4N:MjB/Ľհ&HDSBgnB_S? z2\>cbu Y`1O>RyaS9Uۊ]4=hƉ3PQw,mq kkV!G8F44i7:Өvi#)f'H49[uA&/KD`gEUI4s捴Cvz|:QAvdR#nEZYC^{GL~JKo,&G3ڎkѼŞT1lb\hӳ>i-^B*-wBpO.TЋyMܕE[%re?NY@ϛ̲>'l'G/T3t,BV+Z d� X=~aQp Q V .bQȻ94S@bA%:s'%]r +-agЊw[Jbb)[vNm^od'&;lɀճ) fK2s*hHwD(Њ9G0߯\wkބ=gŏz*k0xNք1Z6ݻa+&|iJ6{ɐXO콤v%>̪dƗRΜYțnhj룋is碫�<r=kL wD!TՄ;wʰcJw?{7h&׀:V%]V;uڼI$MRb#!Y'�H.!�Ӫ$w 16<0_z#HڬcθÛʶGcKym]å|ABk&&Rcu=$%|suGBZ<x0'Ǫ2wrlm{*$(s,iʘV;RNj1q^b$*;[748Ob\L:8=40*-:CG!Z[ULM] zɑ$?xgƎ Wѭؗ$XՍO {˱Zql<Z9ދe:ѿ z%s'ͤoVc|aͳQ ">>s1A[d; 0wGBOĠq5{)1\O/:rUմrIZ9Պ@.&y"X%͔;߁;8遌�v�t6 -lT@ūogh,91g@]/]<3SZ بUfŊ^Q8VhYR9CI S.<±wRsyEgϋoޫn{b"NBB5XQ&d\}'J?W%WY `C'Գ IJl=uY,f"BNH$mu lOds!Jb4$/ R|kҕYf}Ǵ 4;ot~Adg;*SP#kV:C{mpCvO:5b<#[ȼf11^lc[1{!äj1+dϡsR 0 YN 2u3ΡIpgCpk  ljB|9լ:*{VOlgOmJs,'խ ZS5+o~b @:_ln;؇?ŹZ"V7{EQ9ڗ4] p%M=aiP\*R_ӴrSNLgvy >%]oz 89"N7 Hug?[V!06|=͍Bad(9%ÃtVL4={S< 6 v8T ˢ۸ez^.54ByیUH{scm;c FA?NgG^TIP2% =InUAl% (͇Z+F0˦'Q ɏ"+;Q(jI+? kfIT0e@/?fS_%Ft 1|SGܶ j'n=_D"Di=Cme6Hi&u[T ӇM=Qssib@&y E%1ۀA'yR 8J.p5mPzRꭡwW؞Ĉz/Ttؒ{ ( pMŸ~ ]P4idhya(C(^#E{+`>S SEGDS 296RK){T=X*k�85 :r:M5<Ҫ- w wkȜHõfO `jpn,%n�utH.zqz aFKHe㈷ݨT^S)f'/K&`)һҫ?CT|l*+e.aqH_ݬ{ cSe3X,Z,uy(3Xrni_<wMI}U0xAmCS Ê*3LEa(vh]+Ξ8fRk{4<I>YNBNr*:J^0n].65<2U>.*|0.ڏCbUߝ퇛>`,Te]̕,gkM~\jA x͊0hiS MFNڢ)Qr?e;εG)Ŵp- ,~VnfΨbpڏG-S6U KS{do1ֈDhE;U!֛=PhڒsuΔϏyBjrS A}!O 6~^xp;s.6+vüϙiŒQT^aT]Z5qŸ3R\l`NY@1<LIg(xZЊm)}b3fZUPJ=^gf-:uW@A>x֌ ^<Բ_? y{Qd|=-<i`<hp{qZ0w7\|NJ1,?d[tLbp+k{H7:WU"ZhF ?'FD ZXGt/qHvlc~E[T#Sk۬&DgzـuGFݶЭIts t(g!aS,'jٿ5te[W =_RB]2U.Z9g862@f;_:в5_k۬,Ο{N5r5_c3Y0`%7&8pp/%d JP<<B8WwJXesp랷i] 鯌=/&ݖ{OȼQSP,/ƺn /nmq.쉄r p.x(=TO1zOcPqŒaIbFokD<W"TO?&ѐȌIW½;iX}[G-'*4&Z#yVݨT/s ][p;bZINN1*䈄 ެi<6uYҮ}yt2m1!@"2ͨCcЁƫzR7X4&eUZ_Y %0J4m�,0jCW83:lV1 0vKےmOW4ote^3"Mf,?Tp/i e nfHݾQI-(oRW(k$-7KsExa4Lit/^ءZE)~o n+ÛLapOt܄T4 }�|Cm_^͋Ө<Nj 1|="TUx䶨:sjԡ̹dٍNwAԃcTB>m;ݗTps~tq+'D祟zr9RD}r|7t*k;a^zZ9@GU,~O? th/,?5\nH'voڍ3$<K1d_9tn1ank6+@[*2q䒮[ã&6~yX t4ri㦇 v<se:S "!Mcљ-#OkFuKVx\c<׸bì(uX-f>yy4ȗRwl#آ>_aQz4yFM {JѪ!j)ͧoʻcjzbM4LfKU:CO&�5Kv*e YѤfhtW`')FF|! 4vA=IS.UV> ֹ,l%T�k(_Tiܖsݜp%'eE 8FDѝC&UD^J.ZsenFfzo{B}J}sec2]}T K#( 91?ŭ̐N/TЗEʙy{ 鯙ofyۍ%~+& PQK<yx;\38J0y"&urɃKIQ.b1%j~a'TC@34!'z$5̆jNFF~h~*ܙ'i߃TGIᢦ3缤T+k'S,\SU# _b*Ә_@~c\Ϸ:}E ۸˕W~i^$5M3| j 5햠X㼙r1x$]pm#Lr ?7ڇ_$"h %cՋ~Zwen@Qdr榚b:߆Ofr GZc1(鴢H魴8erÉbR.Fqa0LIh'+^"iHШ.4$VDңypNͫ8R4t?L1I}GCpKxcha@t7Zp%je߇ }kpNj+p+)v*~\FFe=pc܎r1PFQT[;:K` ͣ'#P+kD_ɆOb3}v>[Hg$u<bbx%ODAMC2mĚ>lJ㙤^t9B|zPC!P%hĚs/4!xxaJU~|Ek7~Y�vK1ֶ<:dEKtTRQ9kɉY`6:K`Š'bN7Jv|㈦vHOP8;Y5bT0}1<9G· Jb/g,ӡ>뻔Đ1Xc\xWjҷH!wSFn?5omsk0*]}W!ut$ {|8�7 Qě [E[<ˊ;<h} aƖ_y湕BtذۥI N`RQm?y*?zǣ!|AC֔,«2͋BȝtJAiD%mՏd* k@ȧg{g*7IG.Xp@$"3psL(3~B=d Qj#d͊4MdrLу…` endstream endobj 92 0 obj <</Type/FontDescriptor/FontName/LRICUV+CMR10/Flags 4/FontBBox[-40 -250 1009 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle 0/StemV 69/XHeight 431/CharSet(/one/parenleft/parenright/plus/two)/FontFile 198 0 R>> endobj 199 0 obj <</Length1 1408/Length2 6391/Length3 0/Length 7349/Filter/FlateDecode>>stream xڍvT6CHAN1 ctH(% H"!- "t* %;ٞ;;9㹡o$QX!0Xc( E`�114Jٕ0p(Sbqn:hP B$!`0P ## Tz#:@M4 QBaNX-9ya|@pA( GnA݀FhW ޻X4# Ez 1N|@hc_uHƄ<@cg8GyPp w7HCGq k4@0tEJ@`h;@9np0+~9B<Ѹx79. TU0�Bq՝' pz {"~u7d_)#0pn~kuE}PΎA&(\C/ `I)0œA!Ը@G\ #zÁX<(- Ý(?qjy xdÖ4U~7IQ  Pww}(*j@?>_;.X8[0CoGB78zaqAoW3^j`8("S wG`aGo`n\� `p O8J ]"@(Vā ྿  X\^ڧ{~)Ȣ@ @Xg \) Z`^ +?op`j JV!Q54>)L)%q2߫̑Br' 4πđMg ֚# oo) ˯4 u%hҦɓuR?ا[ͷl}Ě+ -BMbBx쟍3qaIi}Fis/94�AۏE ,EϽ0lgfdb'8}ff&㧀1+hv<keRɻ~Ur}7֝~l5 բ*S,_/q>O0^F[Wv\RueM,22819d9S*nD(8 (T)u^8Yx9o J;r�Z!t=6Y l,YI D:lzlL\ IΙ;*7JNLA2NPDص+e=&n.A-Y \r-}X jҚY5a78˻jmgqkAj^ wû}rݵvwkB]{fmJ dK>㣎p~IP[/)#&E*2[9yofv.5--|={ dg,8(z4z0ykf84ХS} !MS_!Nߞm7c3Yt !{/e+ISqţ'VTy;W:zWazXd 1_2ntA>; )cZ+,KTgeyylJZ@" | ,Ͼ|]]A51;61_e3|"f7F=~-_kRY1XXU>I*`a[ �2pQͶc^ᫀusqYiβ-I{7|Fm>'OvNz'|*w~}3\@"2^N{\HyF(/Weg}8!>_f"-bL� +|6Gv8*Lnh}[};*2W$ B z*r>R/v8fNP+'I]׹Re,w� eQIF"XUw&{ENc^T#%:hp xΩRxJ8ogU1ʥrMLQ] eƙ@u67KҴ\� elVVih$9m*'s.2P,Dd&A#<X(} ĥ3`wmi:R[|dۨ!6ۡ"B,{誡cr*$j=rB@>m>OgSlW�fL11+x ip6zWu�Q3E[x1Y9f݆qFKrx^'x?S-{8*뒣 +dZs{j=_l"BÒۼhlԜu8&϶yR=)| O%%NUڟDޢBRwGbMnum զ]!؜ T90E17.e6uNrf֗Tbk0M $?w;~s, * mz2=r] 4&і+d֡{N7LT'ϻHBm9vFw׋YBO7Rg}UxLZe9S]Zb<mw./Fh)'4bos_mF G<Ry݀! V +k^-Z9kI}I;;VĽwIs)HT pRFFYoEZ!SFÁG92n:u"\Eogt0gsaFݧV{{EؖWZ0u~k[Fys>TY4y?<솷zMck]s刜96oLzw%Jɂ)p<;7zsJ\Z%0sẓ6kϲ$a9$c�5:DlsϏ3;dڅKw*}" q0_FYH qˌ ;@s kKYZt뺃&|Eޣ-d_JXZ3|~RӐkP ?N =&&LVe7WHS :z$ngOk&;6*⯗NX\|ߣ2 3?],N<sX@t\i!_rW"~ZI]d_Սjpv݂7Q4'~ຳ*@py{ xve0$xf`ЁHL-'»6<#5 S{r5;$<1_yIO|W2b}HJw;G,Wze9}=:oVLrv-XYa=;<,Bd#$,}гI'w-3M?J;�ˣPlozL'T|DrRW ͩN =žGsVs/Shd"TCt_-|k3m<b;ad< )>h`M,Nϴp:R @>g=v ,j8`AoHV"(Ri S%ϛO*s:iGGTB*&%Sْ5 7쎦fB(f梸 D쯙veq{gl7Zx+IVo,^Y=9gNZ҃̊ݵk91E xw9_Y\2~%J/gR^P+$8}g"ZBͶH<Q8޸<//z^bA3\xvmQ`r0f[Sweʝ(!Shˍcv/?1K G A]Rk~؝: 31,jѬrQfHi٘Ij_^檾ysq[ KdKt-" N]Nv_y֝-:mU##h-BP.R3sy] 67S#zX.E?{u=qd] j6Jj-Xu.hէjJ1̰Edž4W^C䖓? [h*Y?)Ks8cwR]Q3R9#.7e_JZEƩ)QAq澋h{e' %Y :F£ӑZΤ (@)nZ"5 mA./?n˅PɏLs+zsb7]rUǮu\p_AJ>kQr1{FDdn?10wi7u$"Y(0iu'H1W%u(1p,كˣBgLzC)J눱^/g@-fڢH"huD^@,ѝ+KKiGט) pP_NWҜ>KLի`P.Lƣ5lrUd/uٸ 9=fQfNLՑj_ole)-p`9 xsh=XR`D7y"tSB>5?s'nꝽboDx ]XqѭϽ Pn"R$c&-AREѨܮ=y7#|&ӸAoXs8^i<L5]CSzYz+U_## Bf.SJdz:&|Gb={: Rأ #swļJ8:TbRMEǽۏcl2so!~L]ҾȀ)x7=mmlր9X1q3"oZ:!z&(W ܠYDp~ m[=J9rCc>7 ujݷ w;RXAh)l؏xiiuaAuإ|I K%P$cLRMta#~+|cgX\#K,))^#5X. 2ȻWS~bE}mGB5ߋaE")}>7 s6߻Y/O +4H#VRˉQRDy:=t=FUOߵ*}\fǹ~66RK܍ix*y8eؠmbV\GR@gF�x4Nt{ 4X্/,A Pf?=UQӠX1P87|p!-T2]f0q8ϙoP J,[dBoWL"-,Zg;;c%(!xR٤Ǔ\ [*S5�hdl˜en-7W*)jg\�~|UF-F#  8NQʽy;^HŸd uC&͍k<bwgFKF'lEvj]ʚt8Vb֌fRNήa|2w~r ku� v"p|"΅Of[a|_g-�ߦC;b+횄CC:f1U􌼟Q kc7vpG7-oPqe%jRe7֟ub{<~4nE 7qMb 7-8>& ΞWDQFY}N2^Qk˼/iNJ`wJ/, BaI&Wu"dRS!s U}V±f̺о;(oC8d-Em~xc�ya*z[ aM*(Ȱ%2OO9v$+SF(J,xH$6ŊCRߣ:TS3/Sٌ%,y¨{.eEsk@&B)C{oEAbSe; xZlj-(s8msL| Vdq/-A7Yx<@wM[:FF#`L@.TUv3`f}E<? Y5ת/2Oec9SQ]2ՇYR#Ws89k1ԭɱ Qmɸ g5o nWqXN'8s!w+.o= | zph=y!7uRw7+95f^Unp&/Z. U46}JȨ^:c(dXFj2F[< $$72nRԷnFHmoe f)y} A} kg}G;ٻ52n*3rWlŞZ*?eC݄fǃ\(yGE'<av;ΞpZ5IOL8sP }?5o{U.ʅ!&%&#*3Ev!U`JppU'IKBtncrS֏ǯ~3& qPCH'J/^Lj$C'հlr}8ct]IK0ȂWzb|At6k`-q cj/J}ut)WS[%46\P\Q kUv]W_Y 7v[O~cdd\H-wM\ynˍǖf  B&=n䶖D{Y}BvםgL,oL*y762MЂ/\> Y^mbI[+M9ύvW @H*+ 7ˍf $+:D9<csRv�k(F,-#/K?4fx|w):Tlb PkV+FYhST_y8N$L/fA u2<L;7QcK=_#AW#Q_BaEq-6sӂ7]S> GZ-MΖj? j(zo9Iurֆ!o'@Rdgv,V"ߜ,r>@F3m-Kb^UmQ1,W)CX DsxIsb}J7ѧ0>jeq38p̉H yl>0C_iKPeE%l7/cQF-mtʆ+k~QSطZ39./L%o~]$9kʑ XeQajڧ9G-~qr. endstream endobj 102 0 obj <</Type/FontDescriptor/FontName/IPECWW+CMR7/Flags 4/FontBBox[-27 -250 1122 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle 0/StemV 79/XHeight 431/CharSet(/one/plus/three/two)/FontFile 199 0 R>> endobj 200 0 obj <</Length1 1373/Length2 6093/Length3 0/Length 7034/Filter/FlateDecode>>stream xڍtT/]%Ȁ9H#) 00--J#tJK(ߨ9{Ͻkݻf<~mp/( P >"bc3?Z"6c0 $]:E ݡ�#�PT(&)(#! Py@�Z�u8 $bSz# (t\�p �Z #]m!`?Rp>vD\%<==A.H~8 /r胑`k\6{0~"6#Gm�Gy`�Z؂aHt;�k 4:`g?�;ѿA`ApWs�C`&? �~9H8:@A6hߍ�rz�zC" ($?54KV)]\0W} {|!0;_#ع  n`5ſ=*(�vl~%7v6Vu#!`/`mD ( #OvlGFo dƖ *&yy(OHD�̢ ݅b`pğfѷ=>36X0?�7E0C,w?Po+/a@xuGG3�߮OU Bs@�%B/.e*Fp$׃ *[gD &?K*lv%$" �! o"ђ708 @#~SX ~~):(Ool4~ſߜDp[Pֳj9OQ)ͧ\|6 R4+>+q.0_~kÏhNkJҟl!8N7\m/!#ߵq3vf:[8nՙgWmopVƝI8XiW63tx(>&n/)ʗcI<Dq ş,U !;қ1aSOoP;[f1>C6 nslj!v~ZIr `SĮ4&$ |R_R)dI@jHz&j3ڐR[iuӃr+Q^ujяza~(It)i/9K:*J(9镤+;xz$LiR8΀ہFmCRn|qnV.CǤ1K 2/tx;\<+1R]0sߕD55bM;EJp@*δ;3Ŧn(rD>IE7,(sA%V=0!J%a8.aS>h;Y&`=uʚK#H|!PSynf/1T4Shn^B!KIi!! 5J-#Q(ͼNqE3Ɠ#�GZHLwW$wC>4l(B~ב:S6!U/~5&, YOlj hy̥U1 N\Id:v@ SQ/]tCG2uk@uѝ,$ ?c}Q0@u=44mg z{ I.DmX6WD(LkEhni(9}d{az 1<Cʵk]F1/_ J[MZSgI{=4Q;Zi.fVY-=y vEW8 6DA{EE\3o' fCwF2I'셢~D=3nsio;vzc;\]Oz>,Ũe(ǻ3e,3&—$O^u'5oU;ЫM-([t` ?Rl}1Đ7N.ĩ2t7?ER=zYbf6]pD`@g31,ܹ�Ro>3kMonFJy_^t.~X] |N"K#вMd Cb.ך"&z B##]],P A1±V^aV36~jzwQu0<~՚ζoULby[p#i:m:w \!ܾ-onVIz6(JhqSnuߧpk#Eq",_U@i CF)(؁XkaD5lPB<Zn8loGk$X+>- ^K=&j2}EHLjq2٩Y 13̾< fGSiU[x"5O-ݎ7u>1^E.)a&'ѩ' J:^DN.E\&mدg#bCbv^~v& -ޔ*,lc@+nNG)d_LQ0:}_U-!8]0ˎqksm1m 6. Ǒ$2Z{ګvZG7Ym&Ќw#0<pqR,ꡔt[8!1F PFv߄)Џ;a'^O/HsE6ĉ%jqS]MXXa6#R3(L94es|/3r_ㄯY&d5);_5Wyǻ< Fwkʷ$/\RH=fbC>Gf}P${Ǖ])fDDzGbez"uO>sl"ɑÌxG^IĺO4Z >�A[0OT_q"2Wng]ŸխTw ΧRټos`bA=swǴ-Wer{*RP)N{^Ou/|fYڏzΜ~4N NA)lV#xbg&G=We\[i3SSM/:Xа�*s|^4OA#~kR2Vq`L׬=GY¨Eg dw%nMz.+1T SFv7rTr]LRSux·{pD+6:5YE#05.h߸=0п# lD)cZ͓_g)'IXg6}ܕM))=fL#C~}wiZ'I*屨{lּ.嵐]-u$#] pdi+t}%-ޮJ=ƭ? _(UwR&x@fTf֏;;Om-(a C䛨LQO'_y}#kjɔB̞UlU$uw:y�x4tJlRB7Z+&2Y'cdy䴧}+ݔfmycj'DUzkɟX ܝ=XE-*b7x2G>[<9ЬOgș}u^=?XecYʀߨS0z@\)"Jҙ/~nwY1z:|wZpaťM*)j/b-HΫIƹ A’C _?cG>o\}ѭ$JrxdU=_!;YH}U, - o'PWoܳ L|] :Ut&UZl¥RFQ'iSW%bgGO i,CG_ޱwȓRi[J)`\R!zB+l[4Ct?4wSK5uƾ>VkS#9c^z`J"BNu0Y,e,5v;4fc>ج]™k�Xp8Hx>:4"9 P6!K@Hf./+w52:' 8G'0c@|#byS�b?C(sv,l_}cu (g&1y6Qyt+z4TtHHVaGR#ikTʻ�e;m2 h v2\pI_c!@ڻ˛xԑm Pܽwyn@.=| joKLy[0c-lrF2[f1*1^5$WlyNvGZm A>Nh$!JRt6ܴѵ)cԄC]7ĔgWGScmVKZeWІI3/}FUTּXkꋪO%y~<!0|NrĞ褰D<P}Xͺz}<�[$k<??L,vׄ{1"<'GߑD*O cmUI'.N#͹pzG%͢�̌kb7Ffw_\Ț!g1O[d蔍 eK7g7RJc>�@5drjoSXz_yecvФ%^Fw ΂4:[Ay~Q5ewWHG)]3YgwIR!&y:gB;!]| +V\8t\GuX mz}mNv-N?(mۇS3o ;z?lt `VɊen" eԭ$ca~f6Us< /Gl#ڿhD;M2slFp^b*U �yµR69 }$ܓlF_7(u"R%k9y:t5׼I bKc`UGܾ̃#-EKqiDr&"Vi<Pn!KM=#OZQ˃J"vv1*NNL;{,I #W7O,~=>J|Yςc9(C"U)7ݣ6%{5!9i!E͘0o"ؒ]3{V�p_} v Jv|'n`#uAAUcmͰw!}> _!1+m%O=XX%cpW/QjpAeRQ}zsJrKCy3PE5,('v\W`68cZ >,.hAQ Pgt}h=,J\"a.hR;LRXk:2�#[\eCQiV[ٶ--dÛwQ+Bƒߕ^ȩԼUq)ey`ɖwڑ-^l7<W4EӼ/w:1ħ xf/綻n/իs;kWuo~8?UMl /D"fv2k/!"0RšzqRt./A,>f@7-�lHW0p+ YMyGQym!FF 2JcX>c3V<,oΦ jc-v/enHy.Qiʎ8UP*!ᅀfOnux\'x>|\vLgEO~ ͙T' CMk?n&_~5*^o5$ʽa]-M'}6qx,ez4rtxglޗt͛=!pk1!Z%xu@.;R Ϳ9sp Lo1;8!Z#xnÛxe<q9uܭe{c9ѫ:BT.<>ctk->g)6pzE ~F<obgIt~,tUA|~iQuŰwc&04:)~GJ}Wtp.ۀw/R1KK7C#o5٬xb%9!<K=shjt<ʡwweC:4R#iQn_nd.ܿ8TnK=1g_*ɬ'Պ*Sg_B'{aӣ K꘱V; Em|檍@Q -)ǵ+onGV)?152(bW}p`4&k{օ9ks-> u`2٬ojrVS8tl-\5\KF PÑ4AM7=G6}S[C]IT"2VմV.^ۡ9 xW_-]` =1AD3M&ī^?-~){?g>cAM]Q?a|&_5jzhg4D\%&J=^Dt[)þN>ET mM$m}'݅{M0}C4C$M'{@͖L BN5S7R*9?ziZr. 8$x7{HH=5=ۊs]và)~YN8?S7 -) ʩb ?I#C>u"Љ*m9[OQE >OwmX3z`Ќ%}]nk;1Eq*- IuF%Jz{rAdEګgJ. Җ`^]e|lw3`(=y'Ǎ!գg'8Ы|[qM` e#�&"VUp[&(D$_a1vy$�ê endstream endobj 47 0 obj <</Type/FontDescriptor/FontName/PXOHER+CMR8/Flags 4/FontBBox[-36 -250 1070 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle 0/StemV 76/XHeight 431/CharSet(/one/two)/FontFile 200 0 R>> endobj 201 0 obj <</Length1 1382/Length2 6009/Length3 0/Length 6959/Filter/FlateDecode>>stream xڍxTT6Rn[A$aaARJA:$QR@ZZAB) }}9oZ{s]s]{fmN6cA%=T@ d�]cK� @D&04 ꁂ!2P*"]O8 "HȈHʀ@($7!`p8U0'g4߷�(A=`0n00FB`P?RqFen(!<� C;FP �A&D 8P9ho0„x"�:`Cu�6W?ѿ!�G }� xc�[J�3P;%(+ f*H77("՟* 컯uE a_c8x "`<Z0ѿmNP4 �YW_woo3f@w;sbQ`/(+"AP'1f_k{|�+~"�_w9 pTVF�Ҁ�$%Ł1�ZG$ W}e?#^B>H\Dtc,+#uO8/z1*Ebo9/B`nB1jPB8a-(r[t/; :X0�z`@aDq<EPjvA1g]5Kl�K9kJ̀Ƅ�Gѯ<h;&AO~�SoYC>P,"<N[(jWte8k~e; �&1rC5 XjʇCߎ_9AYNI,dvVV)泗${6}*Bmޓ܀Srt&tR~.cK6U}xJLO-+ǰ3XoVY߿;*b˖?<@{ KYT"--kNrt@{ҠgJ o=r=G̶9&߄I[289kN3j�^COidYbs,01OvqI"irpO% #拗w�*FN*-=6Uϑ[B_ê,n NZ^w D"9YhB-:wv^h0S@k!my q ڻ4d)JO'P̙lbeZ<R &'rݯ`yX-\h\T05U 7 {]I8%.lW}};ּxl9r(9\4ȖǸ_7O ,Sma7Ϝrso'wڦf)! {x 9AM�M�X㤅c7b3!6[Mz+(dktq̂TIJQ'BEgBXΎDpEڢ]+-1 ꙅApJBե{ l/W ]pwjn+^핲"˄7Ej:W붢7jZ+iD"t>S C; H s,FjG{Y '߹R37ҡ6ryƪof<?Nrn ,3{9 suwn6!|Q56AF՟+"6µ+ٴn>~}[lV/<*|oʨo>X0,Qu,[̈́_<N4W*Ym`[4[A6LflXjӐzS65^n䝼@zYU}oitF{׽TKps UHmiGr:])iJZ6ؕ Y TZC~<301C!M *FQG-k; ?{J*l>ڢé_Bygـ;ӑ Fvg2]]wpI/9:%TYb^͡XZ)Ƕװ42U7$9iaqEScm  Uw'w6֔Fvf/^,DU}lM?SJ#%p1|uyU_nG)\.�x+,>RI8Vlx.^oMGqx|dM!OKxj %fÛf/LrZ0ѰJi^(vieM$~%,GTX2Y'J`4yVAe-7*590X09FzsG -7N$ѫ:pD}>ZViC7>V-n u+OfхLgrQ^=exFo=6C3WLggdKoulxͳJR6i&2ͭ). {"2Fs4T9CKٶG%FJ 8>hw3^Vwun&fעXL�ȅnwtn#j]2J<Y7#crt4K޶i;9n n b?^RsVM앝b> $w~m\>TLނ'2Qߙy=;[ʁ ۮ K+F{<36 l˫nXcd�0 ?ԄQ$,zݤ<X\ڗ|'Yw`wN攅v=R`Ҹɮ!H\d ߺIP.el*fF̗jd#9߲Gw v#@)O7}oZ}){ѪXn }[703h9V\&jx0ߢ Ӽ*2 A<2k|V$:vay.FҳkJ'&zB9@,?Bz`ݔ~ǛR%?]|MBz?e2<({2̐tt0-&Q*A}mIː,|{ұ3Z .{ڧT>.mBx"uϿjUu ህ5"'Gw&,;W𠂟EIc $Cboe8D~)FƧ[TsQ'sb{lÚVP{hh H*_}{. ilLTXn=YÓ?/H1kNBv _7_dz㣒},pF~\dRUz ]PZU&}PUGWe smNi[-zZBӷRnR{^WU~9Ca !QL1(WBkצ`G #hMt28EşI;[ͷb݃dp"/!btFÌIG*EoV ݃mUXU N޻/˦9X𾉛:N<0 < ?#`ЋʖR1])XN\K8Pdڦyf mar&PQ:(w-[JMj1~7٨XWpGi*Hl͐f[!ǡZQmckj+z-Ytw<嬭JU\y<y ?58qb_䙁#[cYO<ˬ޿@T=;LgSxθP`a؍7/ލwfB곓Bk^9]7Y)'m$ƣ8ۏҩثǥ喑k4a18-O)3ͭ<t3IySwJ;Jz%}Qp-JZ֑tdAE~Z/ CflY/!Xdk\/guN2QYO7)|u't;DڝͶ ̰cw^FLyRї.֍c1 ~ {rz珮u#HX[ȳ/Hm=u-;GŜc5{HTZϭ;iv% >Y`X͢1[tfDrrXޭ=8^Ԓ+;p 7}b)+~FZf]R?f(Toisޙ q;ZIm}E5L0BoȸIk^Zѹ-Z;EJ&2C\ajŧqSzʁzrI_9)s9js ;b^rkJtҝ,N>@M^Ƭ|Yׇ<;D] )ɻ P,= 5m5x?rmofS^4m#jj'y0\;|QLY?6^wn_0qFWsv).$'�Ĕ(/RK01n뾬 ' MgOV-5YER5[l,'HpMT]82cTp?h6XTkP;]8-О7pnxhՂ5lBphB<HIxg:=52m̯]vFxdA˃)D]HϻbyA;G^;'AKb s qGdd)n&>"ϼn&{=\f2atUBIq t3ΩƳdI7_{}j#CUpڐt< i 1`pIdا~N'Xe_xo7^3NK9AnXp:PH~t8}xjRn7 j{y/yzu{ݑ q"j5//Y$cLQf+|7?Ī_jx8Ḧ Kh!x^rc^*?cO ,l} 2^c=foƜ|cPCB#.L[~pnuP͓ IvnPdGfPp(b^mV(H܊➊M2*\T`|9`gjDnJ4i1WUlY=2?H_xH4 .}bj ?p:!RߔQA'AH]DUܬJ>TKʇ_UtbXr=g)%cZ|NoIJ಄##'κ%}m@#ͯ&YG+o}֚'pc y~\<McK<7T'YlYj}y H>2鵣WN)1yc '6 )bߺo5y"q8^h ,ǃ{Kt3]h4p_Ghpk䟝7pcJ wlwDLF3TYG1-QX̩M.m *l>{kN3 9Q\Z1=@>q|"lVRQ^d?q`pȊfZ'ƹ�;ޒw).J#gf},jT-gD36F=$&a ,O: ߣL KlX|㝷Ǽ0R&\_|`#ܻv"Z æ,=1nqӃҠzGYwvÎ9W ތnz /veə-+Zt*W*8uynUr<3:sRtBMzru.j)Ͳvd^9o.֚XAfB1.q+Ux1h/O$Z{MS*oҏ|>ZxBޭͪ*1Iڱ,kj'nmV2%1j5Zfk^D~MZ/F@ o\OT/Έg8)}(w\0jC"vWSV ߯z5e!w|%+l\> m+":.uznĞt?@&$GfY*=L : QrF2[7 N&:s*9~Z3殿ىv(w%~zD,-;6>ǻ%n4x𲒝^ɓ(S ,~߷P*wH@]Z5æ}sƓ:c8eK)ŀESJuaL:LtBNIK-r2hXftAtE~H$ju ;Yyۓ0I AZ] U\+ǩ>JRZg5>PG kǤ *\Z%65�҉?GLX?o�NמxTy9Am8 .Pؖly~aZʹ^0W@:W=cnz]kJQ~a+|; ©yA|nLy޷A8EWz:ʣi1ʏ=.8W{Fo|Rٳ*b"+6 'J2DDcD& ^|2^/9kΤ\ Hd:2PJx_]k^lv&\{2N< flέ/RssOS'd=/xHp#,UhN4.�jk걀h &hk+9Va|rG_Cy TP G8u Aq "--SY&n{CSOیVw5* h,1Ehnp endstream endobj 51 0 obj <</Type/FontDescriptor/FontName/DVCMRV+CMSY10/Flags 4/FontBBox[-29 -960 1116 775]/Ascent 750/CapHeight 683/Descent -194/ItalicAngle -14/StemV 40/XHeight 431/CharSet(/multiply)/FontFile 201 0 R>> endobj 202 0 obj <</Length1 1606/Length2 5276/Length3 0/Length 6086/Filter/FlateDecode>>stream xڭTgXT[%J|sF2Hnt7DP@ɒQT@rA$E@$HMko53?]U{ժZKJ\ v@pR*�tšQ&h t000Fq0ta@FVVV�t~+pp wEo큄p ` t A>Ї`00rF! Caa€  <�4 UV�|!0_.1A±X?�0 G QM@|0s4`8\Ɵ<qn`ܯX8 ]P4WI}�@X؏�恁ᅅ\b ``` b 0_NT@Opp!](j_brARڡ^y0$kf $P4 @a.Ԓ 4T_"ELܿk} �s� L_;łp߿{-O &A JBJBO#{ q7 ap6RRY!P. 9A߼% t-DMGTYyoh?0Ѿ@, .((IJA"oΦ` &,%:9 FACM럆_nC['|a?ptm-Ҥ]= +__nIU~tZPrPollHdq9'aZ^QtBu耭O&3d R7,,^Pn ~q ]0$]S}%7ykOWuvwu6.fQ Yxp~N|[ыHS$:mb{ѐtIu7QnaRe[/KjVA4?mk2XQ13Ȋ*.ͪ7K"U}a'R|dB*vPD׽/WOUzwQh1TNhF+S.KzBX]󬖩C4+GYZD%7e:D۷l(w!5Opeۺw^ uUv]M�h9>fi~@S zchܱ'aY} RU*!X$7ʯ-�6\N]fr Wr*iEŌ#=^!44}."^;\PD4Wi ~嚧W,)5˟hO\UVu&y}�$u<l,9SNd<+XN {VT%!᳕ ~rQ?.8:R<dB9AgtXtfHߣjgUj)o1O'#OKu>ͻZ9J$f\VG߷$-!=O duc$oM~,]@ՌtL {HRɭuTIYd_J!pudKǀ>n;6}Vi>iww^>֑|b#\+'ӝ5(W^si٣򹔄/34C3G#L4ť0x}vR Yf;y'Ľo_tU YVb88nhl68Yr蒎6^e6<>8/U"2RZ_lqR>T16XUvB̡<b=+kF!)X/dԩ:wD$xJTX̦ b/ !mr.Fa}ym<^�6Xz 8"Y4Xھ!S Q':w3kou+_yܻi KUN1Abٕ_&V bWY^raoTwt7wQ Cz<(|gN!ӳva J4>N%Ny)=}mY$îL5Ab`pʮn5 ߼H%Gx=Xnp=1v6Qqt}|g;e7k'+Q̻R-u;k) KsvૹO*"6CS-,;]җ؂n!Z ܌vZ1%Υ!r:]1:k֒| +'c8OƐ9c` l./<<SkN/˯vV|?|W~v/UQVK$5o)  &6B~&{WXF:w0tnp+oɎ,[h-N iO+Uk} i}r]?M)Nㆭ\G3ƣH`@We)A7%*y?ߝ8TTݓ`'arf+cdCS*:d!Υs.r+P%D`xI8 Sj\v,xޱ*CLU hvlϓe _$jTNfS ;M䊍Lе$|=?|R!mժSZ6\S칀Å+yf04 0YL_LR[Aqt85ƹ"lpҫ\yng+!F6?k,KPm y:p^LdRMs[V}åKbR"+.јiE^ap_y[Wܕ [H?mm#=;n-#o7gfeGg XQ:ã48߯[?iQ]ٗVu+2{s V pM&^nz&)|O X *UT,;*Oo92_g]cǥ8kofyQ`):XKh.}9&„!5m7Øіoq(m|JMrLCIR>Q*yј/ On'25cvl*3CeV-,|o45yr\Say0ye%yW#@j|zי 8&JQw<ODSR2I.}{ϒW3/Nƒ:d!ÃNO‹=ӹ*J@ 4D[]uWB3iBN䁙?/oݠT@.4w x?ԕ濹Q}%U(7AΥ _8,YQ^hj A2U@4?:*gw쭧vhKo |:Z&/J+3/aM diYD3j"յjj)Av~Xˑ=%PFУ�E=,9X#c\M2Xnux˓zNl1͡+׽xZtjlsKW[-3\otϞT=z^z6!{͞U))JNŖn)9n&瑐wAGH{.4G P,"1ΡΣG70hԠtx@^"zͲ;WKNW)ZM+ކ8@6ff,e5\btY6j`Ó# )ӗbPʡ5om&F"I1\>|P4zUe~oHcg__/!ѽ;UH;|J׋Ⱦlrb|N9Sc4Uizn $9'fD\MH\6w*i/Y! Wl^i]Ќ{S[*,Baq͕G\9 I`3D*GDvɣ^u.F1Rzd`m:ykm}*#@ Õsc44~ƺ̛s ]>Z9E +M "!u̿2Oj#2Q??%<tayxLLۼ\co&Ɲ劍kߞ}y[̠ؗPo%dMI+akrһ9E إQY Y73{(Gdf :W+I4dsbR9})HR`' k _ZKSy; ΡbgMY=n4}-O*=iH1(3ur)uBZbc\*,@i>^YP*od" iEp&KZ֙%%ں?^ixKYdRLEVHYQCņ;WӇ?*p%6%I 62k?e,+4TW]L6G=}#挲O*Đh =!ϵ@` AR r)z>(EJ6ӕn#w\ie3E)\Nw΄"T)4Hi70}Efе4HBtShh;`*]Ѐ몒'{#A~.E�۫=Q{X2")iO2C!ZŲJjLU RL)U-b_(A%Ÿ%؛ .GNM02wQz)~#:Ks|8l'fF3G.rԑ#{ښSeR=u<*]6^y8wIwUF+GQ`SR^%5<[tVy<MNE+2]\aձry*6Sxk^C鏤D5fu}3jho<M/{-e(G0ZZm,q;*H2'̸֓".]O|.뉈2G_mFە+/U@Fy~JYR"fŮΒZ)7)_E$*!Yk Zc&'y_dR0ekZ3VX6o4P*,-r-k161 KW_|pxƥ#3QɇsR/ x3jO"M4PuqyY2nH 1Dx#±ks]bMC&Dxլ7ߠo8~thg ד5~߆}k8UmF7n-G>8HBDz*JSjZ7.!Fi0pM"QDDXQrRvu?RvOq1mc/�ZFy~>=P|MP�f& ujKC6w2l&l럢\iDlǦV?剼NHli%,qUг{9!40WKЅ!>G7#b r2>[�ShƮ0 'N^,K4=0BMB4jdbLYxN?ှ] 0uX&zgl-jӴ3q'a@L gM8NJco#F7PƷV.^U4R>37 @#YFlTJpy-~H/p,@t=g#;n[,ޢQh5RVZƑ�, $mKٮJVc1py[,Ki ~p!3|q\qeSVݢIhR\ mo;O;gJ hթP[EgFe Q#E} q֌&g&ُJSj>f[)xƂ'Cg a;4m=q}?LA|js񍩛f|^:*z#͎IE工a<3r6Wκ7/Ո yk`t!;z{Z<ҫ 66G" 1m֞h4{ֈ&e endstream endobj 145 0 obj <</Type/FontDescriptor/FontName/PIADRO+NimbusMonL-Bold/Flags 4/FontBBox[-43 -278 681 871]/Ascent 623/CapHeight 552/Descent -126/ItalicAngle 0/StemV 101/XHeight 439/CharSet(/e/i/parenleft/parenright/r/t/w)/FontFile 202 0 R>> endobj 203 0 obj <</Length1 1612/Length2 12802/Length3 0/Length 13638/Filter/FlateDecode>>stream xڭweTݒ.!Kh4kpw =9ΝgkSOSj*2u&Q 3K)++'>XI nDw4uq�KZ--��66�+///@ TӦc``zoX G{K;%h YĕUteJ�iK)f1(ؘ[],�V�?�s_0c�L.6,=-r1-m\\޿6.�kgS{\�6`s_Vrtvx8;8޳HH+.6n{_%{yڀ]�2Xظ8Ls9:MlO�gKkSg ;;_gV#GWK3+{Ns6`$El�`-n_3CN XXZ!(9Tfo"E?qUBK@Jx0�_;dۀ mYWfF)OK Ws ީ` Kg ]ѿ `b_|@s;_lE:6ÿԿT޵wr|'(:X/ 11O7 dbcp'ae ϳ'@O Fl`׬- ݜUſii?6-3ݵ~o7+ǒ:j.M /|-^_vq@4])gyDt=km {A,F%ҏz)lq}SU3*~#hcwF8 puD3OݎVQ[ptLxxwK?<8g!'/,O+;j0Z%4qRW!Wsݟ 0AxTӪ9ep؆H&z?]o86ꜱ 7`Vb<Kw=8v \$"}{2|'nѠe3�26ѫkvnKb1 >]9qts,UqD 7ʖ>>N�K]FCX\Skivi)omM=$bqXt\~3G9ƪBxb Q۸N4G Ƅ=:MWd $=*S/<s]g&bDKl1[RFCy}OwQGŠ磣x 7[zU =in4,()0hƬJ| i u\Q$ºJʅ~檣zRlTE jLAmMjPydp@[MFzsϿL=aۇ\#dPCqQ)qyiz\Qr5=mw$8s({ǎk! ޜRąA SAt[L(f64UMzqNM ~9[+3 Z9<;UĮp q9FaD_ m(PAtg(ǒܳkEQ.Tw\>!A-Xɼ~B*Y,b`)bf<j-J\C$g�:ɞ ob˂A(Y!g~W"9^ZDC%�@]<iV Ta:l:O.;Yqrߵn{Z_>(446f +0J?ŵin!/l1 q&`LҶ*Xоs=O;U0@n+a�8[uoQQ@\>Z>dy'B(:EDȽXтrW#>m)UJ"f]X )1cc4n[FmذR#q|X M-O^Sn<usy޼nkg�C@9^ ן2q`y~Xs1.?<LTf݊rW( f[*D& ';R ϙa 8 X-xmet_YӒG$q wWf~+H_&Vo5AU*︈j71NX8 z[vSgQwytէ%ĺ<|cyYɄR&B?IqaпxP7 qm.%§w @e͕G.8K?q w:-q7j*(+9cr+ke!wrI<Q\A?;fRx- ']ǎ"p1= \ SFKFPFixF&J Nx^+m`YMTTgksuu|&KLUGgg@ |[*ixUpH8MnM3)Ggc.CZةmIGbsYrغ*t�AS41Koavf7s77/'y29(c%&,һed0aWgPQ^5rL'V]IGHzZ+Mdm.x I`7NpuU'(i$E>yhN_ bƢ-r[Q!lY؅{O'kc?"Է zDksU ҷq@py&+D`l}N| V{[gifdZ4U8H:7?S}tQHC])6Y{ua `[mX~'i;h'߄{ZtڮKV +.($}ӏ+"&@ B3cv EO|GFH Z% V܊5vxyE~)4v5]"teًKO^κW\_, W;g4𰄂&7)˜?Sa~,Y/fZ\rdAP"Ecy^b +m)a}tCuŤ-Y= ~|YS*n\ȴ/蕃qx IglڰlIӜ0JCL*'=/ӰLI:KfdݮDK+2}@{akaoY�@p%y>唞ph¡H \ Nވ!$&C3[}f{G[Jl\QKJ7(ɣLm- GS>|1 jKHauO9|_6/L3!H;53Ij]?9fGbTXt/\6\:N(-[r\1ճJ "|@sKĴP"`Ns*̜R2DEk0Vq gށNom{J|(Y($I~Vwa?Y&ǟ VMLt*̕*-;& XFL$t: n̝pV,vLrrJBp$آ-X>8oekHM${M,[2y"3&QB]pa/r8> D:Έrl,s,7ˮC/aïI/0C@7Rk_Ip]kÔ<$9= '8v8-*4HzРD R1G _Pَ)y~;p![f){=h1( Ⱘ*Խk/{MCLC*D6vW꘷úN1"Cv뚍 zPx};j2qk01{+aT3<j*>4"&d?_zjZXZ)cӻ6䭩 C"sB\7т3P=v|v7`m",ڠC(5@B5u�RQv蛘 "6PgCѯd^kfq8O ^Ƚ\'Q^|zTfF*ZwW~ү--ؘ6cG2)MMHOw1Q'4;e|]H|?(;(B >1 `gtׁOt1.tr5G-jͫmQ_+–Жy UB"W\&@;]+ߡC8϶:h)83)^u2}ȣ|b| ~:xtDGE~נCR&[`x  us渹 Ωzko��g|&pw<_1ܢ , BP< ƩUq�e$46]rfCQ6vӷ~E#}}ē)#9'C|TGԍuvu=O m0a8x>gj;/?]1rͼS FL坾()3-;٩D2o6w2`D1rjju;@*׈i_%�Ijf竵gjAlŚ>ixMHcNjIjv,ɘgrH~\0OgjR0 f"T|^u=ǧ 1$b~umq`[n/;u 7$+ee:clؕbį"iNǍ[ׅå=b^ث ;bI쿥"N6Xݠ#52q4XRR2p8?$&q14ޖ Np$ݸpŅGry n6V`*NfdQk'>AKIψm7.mxecYl8wF9yd[Ԃ~uNU>Bl@ǨYlN+Bi"@:7yL'or !eیd�UYH WRnK|yn�|o<X"h$(,De-k`Voj?z,t16c6XC8_MTe /-^Eyp_HKXjj'9us*eQQ˔kE۾^{v9 uQ~NDn6/Tc%(=vUBmj kY׈dW_㣑Iv(/{u%'ZFBiJ&h {u[{&@}O%־GW^0'=5M)*v7-kާbq\|=y+30l YVW?*!޸b+ qjʟ sL-&$:\Pw05z7}We*7Hm>S֘HZR)U?VIV9q q8|D$BW'?\]Ud!.~s]~PU _鏍$ RvMO%^B#?+Xjy ,z_aa0!Zg]mee]/ϊ̲Ln&4q]~ZS3T,?V]@ sFv"ydnQՊ'0IffI.dU{n@Fh K@k!4KFy$kA\TX/ZKygDCZNj8ۧ5KytDϮx fGebe]ao3ccO:e*>|ʝ7>Z~  S(J!EM7ɼM񼢞MȽ9( Cw@ o>cWڹY*rLknՙ `kLgP%8i<7tgRrÄa^r=wvoTzo,`ƟedWW%C`d wlr!{V1Z̬L–ڀtɭ~{N(]F}Wه+ F:e iL7l[ꊆSQR6Gƌ�XU#?;+7l_[銘ƭ,I7ϓ]*܅|Hvt|~ [ ,|[ ZΡϩ=eքxMZk0pᑟB1W))D!{߆`tmut쌇.dymy$R݂ogQ b&ԤJҤ?nDmdo-~ *c="zƀ/7<oj TO08@y-$   u>a8X.#Waҥ9 G'_u0x8/V܆.VZ&D1 QstdWSY~WTpt F\-}}]aPoIJc{Lv*Щ)2<ku'ΧE`YS*ītS^ =[TJ5fY8(/2~ rxt[$2o*^|jIG'=}ZV ͰպUUI9LBO0hK:+o,8gތE*5J 6a[jM%p\KGAtWKsO4\.S uޒ޲ ݝh!;щi$k `Ă{4ȝkK?t4weGrȌPgS>碑F-fu8LI>1L@˗8e 吓˅ta#/n]?x| |T02Y>a$ЮXY="5WtK+z_z/u0֥P:XKO٣qnV\$Y|Ns2b4דw.´a@ݼfz=wd T/gDSpdsWČ7N6\ ?ai80'r2]TA䈿3ܛ+cn9@.g\p! O[ۚq~-d/uSHG >\T7E8=mI;4!K`Yj؅v(}ۉMIh[i?-4"ENkO&)(T`Lmc4b$[;<Յ2;%ZJ-C ^pJH\8v;zfdOM݊ۏ~CqVV)9 CѹѲ}v9KB꿿ט3|ߒG>,ʼQ{Պ�09K5qSECG)tpwʞE|Z7`{ @Ɇc{!X򰹥h>??!8Igc(uug4TwNRHslsnx$n?@Y[L581Bv[򪜼taQ}uT< KQn+SpXH7o'#FwGSÊ>0wn?%H4lw ^iv}_f4!z<eݺZd1Et5Yx :)=Cm ) j8-}8~aMR]<"Pʪ|dkl٩gWi?KK_zfyv|khPulĒ=Y?9M7)ʜ~eG&%(}慇[[C}Ɯh ^J�ƨFTG3C).S{�D PTQdRM7##{8U(Я gx>,P|90$޵ zYUodw?xG"D ,FkVY46}-'~%,6g9C(Pηɪ*S/NRh/gx y`[YTVᤎMGƺ^{ujӛ'A u>$ b^S#t;@υǏdmCzO4.K@?z5]R9J(|l.-,߬At<5 >f>O>|02#ߏsgfsVr7ΠHq"1r5^kdeWb,e'Hba4冲Rg<varxo#dž2SJ`Blj�G0Eڤu5q1Z,RM7B:3z_ۧ͗©N0%6!*hv A&�p0n{**%>!?|Pޣَ\4m'vP Nj$1#Rn1h�Ye]š=$7`jZz Eɗ6�K Z-C\KD s,kkQm׉1r?�n-y0>}՟7iZzRZi5{ 6/a;;eW˜}a힜\0kѲ]ǘzXk^/]Wr8N [*<6dUT^ݲ{EbjI/qs1RnTW_5wjD֥88`g7[wNvtG� / 29 Tu_ZnX1r Anc9%Ey=Osɛv\l=C"|1_bMQҭ _l:omZASe\.*\ K>RgrN/i|3Ls{Ƿ`A1'!u#1aP"+rυƑ诲f5f!aFU0=ݨ2&HScÍ>\ lڋ']gfI(g$;*A,#kֳD|xf )7ٳ1T b|i=n`{Bt9n9(`*?t7i#}fJ8ORv<{~['>qj4YL] ]aM1 i2a,KpN`lMs :HҶޢ >wD;c8$/![dBh"l=t֍{-8LACʧHxq~e#تnOvzK+:emsiӊUiH(=n5WQ/ lt!54p$ÀE(p!`Z Qrf%f֞bC)ϋΰjl_M"~XqcݖׄNʯpxc}z| @gobIꅹmlF6Hj+_9sc>;XBhch͵54ibY{UE7 S؎M.oA^ؚS7 Z9@OxՓB1TJ!#g8J�H'[>8ӗE&qc'5/T>+-I[}v;oDbgɅG[j e$EՠR-,փn+2id빐PC[xץ.ڥwySIr~soLOa1N& \޸e-+]"(G+X!?ϖsՂb&WyfNÉ�j"#r<7`dD6Ъi!/)k*lˏL]Ђob{gߓD=)Xeݪ6z<(“}&@#Ka#zFjVp.IH8b~0:~s+j u@UZuڈ>oVaED go"{ӜdۛU7iUO9XoJr/Q#p�~u5;W7=K; Q{=K ^z`c}N_4v*Q}rr "qH$G!ab =9"vEWf\GmGO5=־B5rqzqLzѩkO+Y2CBH4sF3{HDhSw.I} `*)XU.!pnj T#ց!"duL9!gc(n\:2Oa ^ü/_r ~=n!"Ph $ h.WGR~͙ѮDnaM ^ՉJO&i4fM: [v{D(z~)zxs6! H܆jqe>䈫E 7"WKGޚm<b(SLן)a79|XW)Iyֈ?bsVilsj{%;X~vklaY-k=oJ[5-6:WC8v63XEFFAx=Y)zWA4S GcπA[ąƎiK{Ez]qQ}΀GfhOljy8Љ |W}a-ꉳ𐉺p0B<-w9y~]䩕;S(# ;y|AkD4A;y-"siնb6,f0sy%]Ȅb{f ?f\pz>zr5L3b5r&W"QAN ^ jXkI"ڑoِSb -n/n؜;=Lsa6;,|nW<7)83$c0b m >a Yԉ'i |ngW?J4 Wt5fMGcvD1ƻeC~i"cDڻu0ci+v7@Y!4p2,tHǒOBillʟbHB@VMh Va`2?rVn^⸓T~M5&+,xFq4Z$"BY=/`|3Oϻ'tuKf!eM'З@E>!kO A('Ճ#5˫29NaRȈbEej)NG|#m<y6z]9u= `ˆ+yݑ3֧\לZEp4#bY6n2*[fl%-9k[?LvMYMӵ5k\!.Y>;G7 Za)gFcP ʀŽ$Q>/Ʌz=?_PJ7'ԤLjwԅX 9VrҒG әLFaNrv\­5FnQ܃&"Nj(/oN?J\؛3O(2&21\-Up(Lvb(O; f2*jۉ}|Kpv+]aFZ9 (L"O:? :yU8* um",6-3ܼ0ߚht%1H뱙jWnXO@$-CK խU$oCY2Imѵ;ۈJn &�ύF\5,y|l#r̷amZ,B[EjAJ!&AŃ2ϥAsz߽Q{N~"v[\c|R\I&ؘ\57LXW+%2~ .#t\4*8]/|Hl&9NV,ڲ^Fݕ>D= -$ (( AO 0,V:>^P}v!w^FzkJ&/ ):[yEr:rR<id7(˚{Eqt`he:}eX܉>oڜ^\Igt\ŧ3]Mb徳bG0aC  ոᔽ=XI=NCaVP/5 u 'Pc‹7e? Gri`MKcYSvJ9c>D8cc껔_(FjW</9*$C2iƁa徻p,8ūd؛[$fsi jGs')ض2ح 8*1ǣyG"\켟5Loq Vƨ8dWQ-3 ҳS _O h9,##sOؾ~F{=0"MB%SzKo(zD;$l&Qߛ ?\b:9M(gM,V<PxG~)Kaa8(Nˎ:na(YѪ2{t K14|\&T2VqZkN#E87k%I// e\fQOTQaoXXP&z.4! aMQ|F8oCgG)$_O6 Pxm01,"%̛ y8%,n7{|s]3R2M8c_QpYmj,+f7?M B Y7fgC?_z)6RTÞE�֩a$FLXZ$32b/twb30ZgDޮWk*]ga@Oq*=/MB@I5?;U'"Z0D^[2fNDg/rv^-k'_V4jnP9>lo+9*I@2dxU- ~s:ȴp,l 7{' 2ⵌ2fe^?MeܶaM Aug#C]`vV$~>7lIo]d6TʸdrQ%h &K~O"@s<ڕM9*!_?6sÍȲE4}<[ t0UbߓlMYYtG;8EJ<V꣚T%"eJH ByW*ri<V|DCa%p|S >$M .1H u6ƴñ6֏TI#}% endstream endobj 67 0 obj <</Type/FontDescriptor/FontName/LEWTPW+NimbusMonL-Regu/Flags 4/FontBBox[-12 -237 650 811]/Ascent 625/CapHeight 557/Descent -147/ItalicAngle 0/StemV 41/XHeight 426/CharSet(/B/E/F/G/I/K/N/O/P/R/S/T/U/a/b/c/d/e/f/four/g/h/hyphen/i/k/l/m/n/o/one/p/parenleft/parenright/q/r/s/slash/t/three/two/u/v/w/x/y)/FontFile 203 0 R>> endobj 204 0 obj <</Length1 1630/Length2 7362/Length3 0/Length 8179/Filter/FlateDecode>>stream xڭUeXFQ: AA;$F@:.8wu1sϺu׻^M"<|�u3B Ujl5, `= "2GaPys$H`�ȃ,�~111\ E 8e}`X\@ESu@ �C@�9 ͗J�v%u= r2�4K-AP` s@:�,aP+<\29�Y@n o�99g�q2"{PKְ? `7{2MtÑ剴5G��M+`4( E� 7X Ǿ';�Cm7 dcd!4ܿ:zs8?s�# 5.}LK}l0(Ca�~V`. ? b=3I[wW `? D?qxߩ!usk� g N߻oH@m e#n +M0`m CAi0][=A ӿOF j\ny? H]w8a `V<摕<"�(@TTDC9 0(@-aV'Gi~ÖNNy؃@n K D]wr~y~`x~lRXoJآXuY0OՠmfZO 9 EњhAk-$ݶARI5&`K )2"amͳSCUj sr-I-ƥ뒄Vb4턺H$NjOt�; wZO:dY~mU|m6lQd*iittE401 ֖c)oaئqE^ݖDR)ڈύ*rG{1CBm"K8GUtXX�e+T&1-~[FF~j@ cm%H+ThڠYie1,>ɑShK-]=#(OGh}~׶D/?H^7Hr%%=+nM2v 9ؿڅ`ZWNa bO/Q,r] s6J=<yJÇ:,tz5,5 y偹IZgBqjz?9�=I$m;�۱C }Egv:_6\o \ҦՐ7ED8dsƣ n=)CS ^-V G3)m%[;G@琰->R*ef5'=+QAΏ_¾uwO�DL`s&"*Xn mK5Z'ŧkUD2Y/=tZDVuUX<|aO1¡0ew[ j,-+ 6`&kc}z{^5f=Oe6 x`ihHNhe ɋ: 7B+W1Z{k/coRY)_NNczkC^ eJ!~"C/(maqx=A `!~mKQ~gCSȧ{8C8Hԏ}p|ǒ| 9#XJ㩃9W%|<t>R锻 :a dq?{ݛ<;l_�FYV>aulR|$=`BYHR՞ZǶ|R ^O1DIYix6nUSRl- kŒ^c9B}n ksȩN~dpċ)pԢG(Pw@R5Fϝ"hܣd-P\ xގ]mJR29rmU;E/"}rrrv<,x?xT*0үuWk#?CLQ2B0lSqQ0OY^FKoΕa\Ki~,=m4NnjKfQ ) ^8Ёz)eNLM0>L0 }2^d-c$oYjO( plM2"(&94nbQm\D*ϱ`l mş7, _˥"3{J}s1 |=Q_ j,khK}sQG6|h1pz;d@څT7{/ʔ 7@,,5#�[0(@w3ȼɶIgwp v<-ʿf3(A'M^1[4Sp(riphRž=a'M!aQ3|Nw{[|q2xՇOVtm}1/58vF>Iăsu9Ɩ3/y{#W)t \B}{{ X,c3)Acq#iۼ/ޥY$s%[7Af Z <,lX󣋠 EHƓɣ&;{Is?M\7/u|zP.JNtӸw4J_fDG|bK\qBi52͖4;EΗZ^Qcfrk\YRpne7py!8q*< 8SڗFuDts3j[V'JZLX=_3\IHWX,hK`a|LGS/|ͅ#ZbE򊽵qloHp)_SThd~bZP.6A^E?As9輷pi*ЭirD՝7Xkj5ɺ킎O~|v4ǫ!7=ҧéZtؖ7(Bd_xT{Gڪgg/dĔr{#˅KuD̓|2En(lS6HD%c#W'%Ӆj$ŝɝoPW#G նnB-}ѣ&٦W/GMPf#)F۲3pu#^uqS<+YUAH'k~9 #_/y y?Qx L*YDRWä_}7ɊGvfɗL\'=8.T׉XЭ^(:?B=PP1/@ރ8180 BGr usI\0ω/(a1}3+VGZKTwyEI=5 bvi7(ټ%`YXy!R @Fk=mJ(@7w؇:gz8ke!N-2E *9X1"/=x3)?[I*1QD(uVb C=v@A-H{JPtpnTvΖy]36"bMM Pvv! 8ig$?ߧ+l9 7O^S}!QG`W2+L8f_g~ӣ97-[%׽3H7vrPPX($?*°pԻB& ҽPHl>@*atqZm2!r2mWyՕН!u\ֲnlݬ$ I+Mt^^C+G"o"d4H`U?`̩KjW+堆OvOFHR,T"٦:ED,y !-= u�@Y\p1&(e/[3fnK%_Z4~+/јmx$NS_sa3]껒N"\L}Βr�r UaFço<ģzcp)K 10qU%Ob<3JdQn}j܎8(D5=ӚHzq\"Y. 94#yje.'G|7c6 AERNǶox+UJBJWW֫=aT ;߆/Qϲ i#BK[G=,PR^ ^ h7Vϲ4f碯1Vx -FnBumV+z$]1ѶX.mQG,i\Y(nK�w,eRly* @glɣ&*cF layx_WGM}qT:aeWE)rˋA@oZjem{pepHy~NݒjC{~k_|2>Ol.SљRc7*O%D#zkϸ0#7\6A}DE_iѫˮgL۴?pDGFmN s3pq[<geUa*,)c̟lA@]QJA1}k$Qq@TaG՛:CmrY4үՓ<ba(r<<OIKw:ҠjױPb_T:1Z7عx~kuޒJ(f<)2EeO R7g{4)�pzYNcap|ީhᧁ=G:�'N%c|w)>[ۉO}|ZZM?x\b g(!ͪ -T)̯ɻ (V7$U² jUzle K8WsrZ{6ss@~} ?Y o;vi$c\;wXy1M3@8s$P(s6DGcjvm ;y)ϼs\ gr˟vx5@˙ӉʳHyŠ#qgwD 8v+/ QZCU6;c%?=ΘGL(ڳP} ]_n1num;zruMHH%/Z>ɂ@#+2-!lԵgV+P@55')X8YO?8iܘ-#玩#"v[<%-A7vŘ:r&GUilgeLTCM{nQ0Xv2 8L<,zd&A'1upF<GSՊCxY㾩|s\͈4 }t/ .1JǯctzڻI0&țBXvi=H=^b!snt96]y!UUEbӫ:G`cyc>[%Ud ``腹+ّnO nTMNܸ͈31rMϠ#pZNvS^RAR$EEzXhy{VFOL% ӡR?POcr)hA1ƭF_d?SiT2ν:k} [B.w*lX_mj"I뛺ޟk 57 %Ϋu} xR*eكG۠/EK,1RZ)'R!=SjMU$==:%IJzϋO6ԮpHL8B'N[濾sۚ-=rn' ҆^WKB%Ŭ(~8x@>Wud$0+' IFy~d1K7Q nhヘ;:& t;3-Cgn,e6^C8ze;.Z|rbZu^= Y_5, 'a?l0Q"cpɌ&r+7]T㵿Papho>>N| &1<yM)\K"mKܣ_QI⣱Esq!f돷zC :dmf20p2Ү'vx}嚔Y,"D�|ᵨdN0G6Av?==vV/oUH$Iڴ0MSWz,%Es2!$y"^mm@ @{eGjbnMvXk\9!;fʯ�B7_BCTgly i]zz%+x~vV}qSaطv&W$Wv$S#47u8NzB^l.3ǦXY\ ףNt"}O.<g.9r=m8t_Ead2'+p ^n@BIsKfTLqVvm+{MdF0WkGk- hj%'~zN^M**ZbՔT/!''E lshK̚peZXO٠r(< Ȭ YyuSW '# BkŘ>:B>o EDj<Prɡ{;E{S6AT�DzfaqfjHָء*aj_M:$p2fzX*֒lkSˀKBo2sNpqs+H@B7"UWx0zSh ӣ\XLR9)nmV39|ސaC /Zbo9aŮ+F.x.[/gJ+^w(Kj EyG8Av Upj[pbz>C?} -|+q5MOQFk!-^amCeAcD-nЋǪ j8hu|y$9OIVcYS~Qkj|pZ_ m'VyF}T9hO6'X& c s" [b%b4S8`i 4·%^s;P4գ.!UpIJ⺫O>Woeu!JS"ݕ2#WCw1[Ѱy\2V1;JlFdUz_H,W?tc8;,/uk̶ГYw$rA ^UYɏGc4⩀}Җ={o{~AvzM#S|C1UQ.}L8pvҞ/կdž-IG*E[E X oLԉJ-;dr�Z a6XmMr.{F&na4]RkkRqۮ6dF@\˄u-p:K$pmr躹t,`AE] W ~۱>~e$KuLi)hr{�19<IWI `ٜcCyl.w7wb\E"h`JCGAoqёMF' d%NP_"Gi=%TG;Lܒʧi)q`*a2'cErac1^e⺐&ػޜ烘<d1*8FͳP6OhU'7vnҩ,U݅54'쏽y9Ue#>[-%76~GW&Ib(  J<l}ki9nyG pM2`j&Lj y$A*q` cVK}`HcEg UR Ss ^Oȉ^%;=U;bDܺM:pr$p;$T%zuwi#sm*y%^9+Mu|�B^ endstream endobj 108 0 obj <</Type/FontDescriptor/FontName/ZELEMN+NimbusMonL-ReguObli/Flags 4/FontBBox[-61 -237 774 811]/Ascent 625/CapHeight 557/Descent -147/ItalicAngle -12/StemV 43/XHeight 426/CharSet(/M/N/a/b/d/e/f/h/hyphen/i/l/m/one/q/r/s/t/three/two/v/w)/FontFile 204 0 R>> endobj 205 0 obj <</Length1 1626/Length2 16063/Length3 0/Length 16902/Filter/FlateDecode>>stream xڬct&NE+m۶b۶*YmIUlVSاϟ>ߏ5sO^s^3"'VR6s0J8ػ330�L\TfVrv8rrQg+ 4M,,�fnnn8r+J]E$�L毧=;h7*p̭l�QE%miI�:@ht6(ZL.@j3?0%08M=M�@g;+�+:�MmWn/@-Srpqu1urtͪ$&oƮv84s0uu5w=]eY8{7տ`X[:37to;:z_V ֜oNS׿-i{s�3ӿfnStW m�f@s8F׿)Tw,3@ -#?9.%lmw 13�9?blge֚q5Oߖ[B+ +O%o%W7:Z虙CfiejcV_QLKJYZ\ew\ռb;?aDD<>\�zV6w/ n&Cb'@oLaMUWc{Mݜy@SٙbaMj%E}UFua ͳ<^+?ehl)Ҁ HP(8i 35c}v t84U ޡfXaI݋1Mcv4>H>}z黁8ģKk8՝D}QGfM!Ѭ3K޼f& qFaoN>U#X?VLL٭tA mxx�)!U=)"bnR*ʢ= ⇬'[„7? {\IP?»IQ ~ G$B;{:hjT(N e@DF\7Xx=obV|ͮ x#Iv>}5Ọ4{j/~_! S/˥ rDȢ*`7q~JDe)n v. Zl<w�VEXzFez2�d#<OԂ ` 7hWrfEgcn^v_�!j@7 (K|YqDZE!+)VKX })G 3oq U҆FzNP! \EЮ .`֖OvA?u[|sv9%vJFnb,i ?ӫ'[nЫ~{pxc3[ %^72iB,ĞDN[ 3N6"Hd/HSLODu%ϱ<AːD&p6~+'X[2LCg NN_",F:Wl|@?j'doC5|Æ:OYXyR VZL˦H<}->yEK)S/�TYgCT_$91"f 0]J0WqݸXS^^֙lܹ9NKZV7x7'oDMi42_ы wWWӰ("`L螺=3# 'J]ľV Fbs|0Vn*x,0_J&Bza[ale)i92u<ףJ;2䵟?O6faz!q^-8́b$z0UxuݢuN5LHwnJ4V1}h#*եݎ et$^=)`$c:>PXy9/!Rqx'2]Et˞H&q:P1X"6U ~ΉB<iz1G"`i+$K({b^MHw],dw@ۋf=UhK>f &S#oeCP; J�=zǻ7E*]|*j_d"ة *eoe8z)vS@]O1wkHrԖ-*;閚r ;vE帓!ayAF&>�şAaEPfy"Fv &gq>h+kX)0m[YIxnaYkXfr(yۖh*β^/0O& 聠N㈸:1ZLաX1 cPeK,jje]KФWXS8j%dE6% #ZeaCQPlb`ǚBibnU}utBݺr)װ"MD7{NuNifbM-[~T]T"-O7U!#ں)ʳuڷ.<7弗_G҂bHk{L5 Tz.Ξjl7ܭᯢHFWCjP9wkG y~}+(k|Zu@"8=q_.{iƨPφD1ܮүQͬ9MK|G80U6^UG?aU3ƻ6tSF>B%F|?@SL^-4~y$DH5ƓeYzAx^4*z|{*m56i,@CJ IlF7!?=IN^Ce4O+1gP4Yuy|6>rKw}S:2A@ry[kqe1j!.TFHCnG~TSJ"a I/}8 v;rQ|ujl+H T+@5殺02RKvi%YZQM{bÙSv3k Z}'yw�tFsL&XYS 5\.* 0& 5AI ƅa0wݦEhb=u6S))R*N o;M(7@h[B=0ƴ'$6If{=9z\ )!{€TvT# LK$*s)G߄2'ʏB_?vqI D'q1:1]Ll %Otrj]Bf}h Hf}n/)oOZg߈<꾜c~_7`.f<7yJgSR1pFJx,^J=j o?8kwK>KFxT-WEZ/#٥kH&QqA1<a(5&X>@n {TU,C|ȗեog es"mdڹ+wY:K {y _Q64XוH {*+OM)؏WJ 4jYZ8.X$C〶i!ǓEqJ]P~ځpAU-fNY˟kK30ձXڹu*V:4G*dH/ӹngc3|6`<uK^k}#5cθe^ չ;[[2\vrOPO>1exs>mM3U}R+ Dd%,4Je]!)kcGƊ?�x-"U|ϐ*U0 c/&fGP®`A�<F#}cހ>އ{u{D̳: 7DO>sE֨x6N8 ! 幾\bܭhG1Z({mߺ} aAN@_d ;h d <`O!+˪g}DÑ۹Ta&&I=2.ǴY֧V-ԍ7 !^U5~ Iw!42Ɉ vI{jAŜTuՖT'DƳ$̉X;DzܒO0gjUKpVԝFe"b {g\i(=jS;z:Mxmk7QwyzvO]`A‡ZLI90Wl "U+ R`7 ,*$D6 P6}rvLjX[ѭa&Ei#wԉ/mMf.S/tZjnv0>>Mzn&ԣ<TOװzd̀y)B5%TU (9j(<g229ӫWRakf[ #S~FUkQlTWẗ0e2Ϟ-c 4zԦG!(t#J*ޛ}xYVY>tԓժy.K xM^۔ak㜳AOGL( VJaL:o\LIf::y&1\o˛z5/2�;}hKG.5[gfVm71=6sˎajg|#a2]p5-Bsh\Cht`_Rj"u ;V 4*-xv"b~bTIdσѬڙUttwqf,| #Y 36]1 y'\O*w|l-SY6=e-Z?ŹÇ^{3ݝtѸ:daGO4"HcSl՗00olAjv:K{2^%s6_bl&<AR#1U!HNw񄎤M$^y;EB8MfRF}N03MfhakIq$^]hEȵlb3WtmmFVKr ,>]㑫 :z" 7lABq (.edvݠiΐw %xpg4B4 :r*USߴT{J$h%wLP lwPADnD{(@.n >oo&//` Z"zeo˿tzw)KXe,@6 c[Ή[}RO3x4hB(nd<mɑB^qj QSIY;t^�=Rz n@0!WRɸ'çHiQĬW3<o8ZSAţ@&~56SBl>iϲ쿩w+[WR"}fE?d L<U&\f,x4K% ÔjkBr o"|R=3iPE|UnC'W̽qH:p~IH+DPiHluvm% oY]F%1> ~C6]M|s1唉C25Y)"Eb&vVsO%.ͮWPa"2FDxI|髽Y*ALmaUFvˮZXSL'` p)w �Kی|~K-(mǸvOME&Ɔ/kꑼt<h|lj l![qt<YM?!j sVBڲYй40aEG8-v;&-AtDq(Eomk< [[5/46siLwY/xֻC?ӧ"?G=$axF-CgEYvۥ~\INvi蹢 ܞ/V93J`w%xDvd <vaxҐ:ʔ~xr$=~1tI0gQ:Fk2Y5ɰzGܧz)!Qܛ7G=wLf;y �EaAn ᰄ6ΚamsETgTr)R  ^ZwP"|TR黊!ٛ?{ +vh.юj:TgFh<UwK/1 �R q{nhZf"Rmr$%)š:3TJ1""-[SVal LZ;B }ZS=13'V.߫\~b%&cQFl"l&oQRz'OFoaj6Ig^Ѹ|lZ1,}r4Iͣї@QʊI v44zL~i#rpⶒ2wEI|E-aquT !Ȟ5WݪӞ|/IoT >\1͒8 ?a*GI1 HLdlsp8|pN2zQ Pr󹇘C^gyJJxGɡs̄L&V!H1;Hush?[b<(Xn_ė?jƩ7V cMRf[TSlk@hvqg6yo}g�iU{q=3ݦ7 /H~[?v-;RǤY4:/ɘqS] F;?gd^rRX.x}7mmGjQEJ۰+]@l"DkV"d z1Jxa2H3P3fjqwri VLNHsF*n,HQT"1EXP.3>JܚVLG*bҪ 9B"Ǩoo+i"S@:>kY.$t:%,#g{+ET/tŎ'GϷ9eYe! Ȋ0Zg%$<':;b&{amr^q.j,w7Y~thz sZ۫2Bְ7u#Ƶ;M- ; d9Y?bBBmڥk1?|Ll{3I|F6bUUl +\*\7KirDwBׄ ӑ='/3RĴ_%i[h;�hOJ~'wQA4{r:Y=4'SާO;% m1UOgC_sy#w?\.'- h:ISi[7JNI-1mM ?jb0һ&o H׼GG:86Z]ԍÔG[NB$xO}Au&q\1[Y( q>elpO�צŒxXq< fws$cNlWT*T{b^2;XExuvv(h:C:gW *$Z;1oDj5f9鲥EqBdyk㷤e|_]ut>,`Ne5%#Ìh*BR] u0q1:}: <U9Ӂ?wx' ^AX9y;7bƉjPU '@vm'Wp hqfX||(7 !n2jOv\! NNE<9UOt<d87 ;bq0U+C~qCyc 3,(d9JdO ICH5!#|3}3!zo@"iLDZ F*!;dآ>GɲDl`')7˷'u w_q*l䑨b#U'\G Y$JO*u_WQ "N[KſkW*$ml!gԚl8{TsGoC҂(2 Ybs#kF lTrY+hhڳ?+L'ۅ|[7n 2,_ݓf>ʵt"?@fY nzLJO>C[?c6 #[eB <N7ӼsIdi[SZ)R\@R^?Qtꚨ](Bi9y2,(p-5gCg3yS A M2&C10\ӫe~=H]8뮼swꞁ4QGA߽eOZ>4X۽nD&soQovR6y ˆK}p0?}$NW;/7T㫗tEȺZ >Z t8H>cLԠ [R\`H||1M#.rP&z*,mIMS*]m;Ne-`I 8IU ?oĜ%%7i;0s2ӧv<OPVhL UrTڋf$3 V$ Ɏs.ONy̨ E!Q-4g8 k`;wƝ)1q àBr#׎B(iZ(H< đ2a`UZNn-h6H4~\?!S(@P*U:C'd x –Ef[wC~(cVt3p^b[J*4"}K9|p/-j`%W&,=/m)|!L&@tO *<ZqqD ŀSZ!pB`SZ}>Ag%mQ0XF2Hs0߮4k+lTu=#־?H'NoM­֧yfZUhK's}dR[ԣ�,Obu4m0k*ȕ_]"DARJ g)tID'e7pora K!^G8D9tԯuru" TgؒՕ@֯:/q&w{4|8h6qIc 싄NM$CG;z ][(�b!z{k2Jn[#f0Mݓp`Qcwfyp{&Ii;}"'~o2t#C`LĖdz_8a EC|(]KVl~� P ]<>su~럱JhjzSʸ6z(KkÚU)1d1ʙYw h_w#V;'UGU\QrY[Fe:Onձ T1X΁J6};E*vYkraUܝlE} ��qGɷ1;j_4Rv9Ɖm!,"@IF ,p6<kYϩ($#۟9?5Qw64;`g()a7iFQƾԵH+`(060tƹ)-a8Tr lPaDŽ�&d՗e X.|ΧL6-bbbu3d-gf6=?uO"D>K{ZB4aOՍL(j!k_DAk1MWv1q@3Y_]#  HM"LN!. UJ:e"-Nۈ�!ݏnFF%3 &-ǘ'ZO|fViyfd߻}vSh]PיQ~?|GߡNqvRGJp~EN,~oѪRęv*/9phUr�azV+\~ƜpsWXK h:(z"(b  &sߎP `EG'cdo x`I #Ux0^3VҰ;Fi*Wʁ: \2VH{, iۓq%x@-u_t`VICN[x돊,bi_2}dbBHY0g-,� ׉"!1tpk0QHwhN>mgdni`[6E^^cѹCOvFgO1cSƩhpfex1F3H'y"wxHXoC̈́nE]aVwç/A�R C"nNC8}7(3OE#I6SxWOޚRu-N _ $b(bSo#!Ue@W[PX#ET)h1TM#Mœ@9zM`%[s`VhlA$mpX0QrM 8 ~�זǔ5'.3`(')dt#2M6ҡ\fcY47 'WW8yDZm8I(k$~sj4}zK^zEqOnߡON%%Cg$DyRiPՠn|XHPW$7 P4Qke5$? F<tIŕE zp qofXG,C(#LGWMȄn9s?@Sݼ7ٛG:\]3pۂ߶\ʟ]$UV@Ԧ΂ې=ͩ#S 3hн}^;aܑ{S7lAbCS>*H@`{"l\Qo,˔<w'u=SṈR19`Tg $,BnLuM- E ұٝ1N3J qy=!0)qaڴH7|#nؚ~vPX78E$8jM}8y};Gv[޳|sDǃ ˕}/{ܿʅ&]Ɋ@wa`/x."-,q]Dn s#Rr0UAP,r>y]حDԋ?�,Z`N*vL=qL:P z}z(0߆.dԀj8XqSuB /4ٗ$yvM:;s@fPL@CY N*ۨ9*\_GFT؂bq*{I듽=<go[{D' |[ze3L貉yh<~?@8}+v OsCP:x:ЌDl=5V#'DUV= 7>+b/H:tB YiN3RL1Jp)][sw|.Z~J[e"�l:*A!�.U}Qj;lq 8f".EV\Q;|'{ᗎM 𶣾wwˏN1%n] HrzSvʢ{m䝃޽ϪAOoʙ xZWDx*_,55v-aegqml niF"b{$_8-?qɇV+]nqȪGJt'h"'c^g]T+}P'$p%nC-ZCT΃9 &H duІQ/2 hdIqTm^#4xkD=U >J~~c ~@rE3J lqnXKC>Ӿx_KqpIՂx{d' ُS:z)7 a3oH2e|Y|8O15)QzìvQ)aM{=fF{b̌RGmzju(M"1NCVٺwzd�ĿwG>whb3hswbhq=@9 ?Hz,Ai8-<]R JĞ 3HzFLJiI@\=T;i�#fϰYŏ̰얹8$)D 6>ꄴ`ozj6n&f'q�ǪuFc_-մKLoePEk(%Dz`"4$Y M}I[MJb_5.F3aNɔM1"h&aG2 -7"'Y@ x!m5jnz |^QU X^ձ@y=_zK5WZF 'VincӪ㟖}veEXЂ\5ȃ+@&~ :eG ڈsWD'Ψsڲj s$IɏFgeqHk X�:%ކV=A^yMCX]%eV}rqϕBFC[|cL`7. lW>Vb~w@EsvNOŷ),ŏhLޜ޴ ۠i�!V^5bf+I8DБ_ȴſqS \1eр򭶪 d�Gsvrd[6d݁Z[~:;@]C?|:UƂ]gPzD]"u_h.gLhF̗J{g:"ϔB?:tZ@;\AmT=SJχև/[4Q*GK)٦ eW(Xqߜ0O>rtBxbga'.>e9K$L`$jub/1gx8l زuh>%??KQ\̈́x- ECd �SZGF^+MI.Cљ@,_B\<4'+z)g.gzgXW?3(@k6T=*03_mePUwz?t5KW3o4 NfWUI)dy՟OO BL+Aq@<TAph=uGj"{"B,brs('jc؀%t;{md[Y(V `ٚ_UP1&2AFL�C�a*1빭k0RCF'5Wls7)p&w/7iilZ6矧kXPȒf> oQWIt|HV@ՃFZm#| Ƚd:f/]˪�v7g#;7#}uY&�u-mKDdGΑ2@lȭW\Uᧀ6f~sn ' +L~HzZdf9 5;R1A*khk ή#QLƘ'pҜ2 B;پqchJIFa 17x5یe5THLj_Y;kOc3Пz� ..0Ykϫ:iND3}׎B-[*k;Z'5X һIYօLa`NL:B<'ek`4uXÙ>e5~,e~e`W4L2Lgd{.5 X'4ؐX;A^*UµHZ8}u-/##VcWcu|FH'љ] >O|I359NZ{f!p aʹܕnhlNbpo]܏Dhb$̅;([�9A2ahJ[ u6YƐ:Izeܸ[ IԻ8WA_Gݞz" Y!ðE4 Eu^g)QY8C?ilyQESwEŞP@X|W#ߊM~Sq`zCPA\m<:Ka-}7J} };b`fFsJCNºSm uG铇' Y)3K}M F:آ&ibSzk_Ik%$42'*)nz�mY)Elxs SQecywdOn{%42>pS^*"Bۗywǣi-L^dh߬0.+ƎW3i]S+.+Q<ʛ?MT;DM.YFh6tmh)Pئ髗* ?^ya]@lrVnx J)ՕGw5`cy.w`6|"=.^ mӈY[ g=rKVe6)-!^s6.tR)�RZj!ޖmh#H[P\gk͌gT@F8<�AiTQ3PKxK"lJw)KLIumlh{:[zʮA~@=4ʏ�/#\i3 lM/pI$%nښf[]4�E4NF G۲+4G|de7FeTw\6j+Uԇ(*) DYRx<Ś|[Z.sd`�LTRDt[=Q[JAG97noϫ5ZJقct31r-z(eSfrp\.v1L^]eؖ 6lo<pyfjx- aévHL5jYArWq-M'!r섥:oپ15gnݳ7[]t%+M;j=A;N.)NqPo5@+BuUb F 8fO #P~ A)W6"ĝ-Dsx|Ee5HvDM[za3<QsM'/:Սx%H?s U.r&-5>aǖ<@w q* >M s@fVj^{ 뻞o%rgbZ8U=.UtA2S]j,2rHd"nG@Z3s)IzĎeY~}|T -]= "ԫ5z?[%-B}1k>$� "T̂4zuH*s="uEq M0ڝd)C\/ e:& eG~Z\B^m@vHC`-$?"qv޵b=@ekZT&! 0}qc{of0<\čoi>7EUNJ?dc.Ăez[hz3d19yl}JaA2'đ OɤI\yXcyUN2:%W|Qnef"o)ؖh9@9 e%gw/^jƧ4M*vz\q7gGNRbמVk$<8ކϰ? O§1ieuWPC?){-I֍ =Q&X2{sKy2ScajPȣ ?pi,t(~Цe~y ;./~J}}J+RMlu )~IԒj\5EX.+' ];1OK1^Tj&Hi=HmlQ_ H<HSc(UF[Yy'WY-&c\b͇g` l- afc<2@ًTYΪ۫;MN6Y<dhUv,*7j8!JFßtc' guJ!ũ2qy, $<Oݝid</q<>5F_!m`o@ǛXL\sW;}L*cO�ĤϧTYfmڼp'Oe v"PL9gxi GrlzK_ЩMӰD} 2p츳Z A O��E]¬դȋ*&.4{kv¼dLa$kb/c4b_s;>"r7Hcbnpǎ aȭQ=ήg*!V9&*�ٳ8t3`&wS{lAb~TΫ\H)>q4Eg�8eύ Cѐ IVm9D5|)a@R?>f$O endstream endobj 42 0 obj <</Type/FontDescriptor/FontName/DXHQIH+NimbusRomNo9L-Medi/Flags 4/FontBBox[-168 -341 1000 960]/Ascent 690/CapHeight 690/Descent -209/ItalicAngle 0/StemV 140/XHeight 461/CharSet(/A/B/C/D/E/F/G/H/I/K/L/M/N/O/P/Q/R/S/T/U/W/X/Y/a/b/c/colon/d/e/f/fi/five/four/g/h/hyphen/i/k/l/m/n/numbersign/o/one/p/parenleft/parenright/percent/period/q/r/s/seven/six/slash/t/three/two/u/v/w/x/y/z)/FontFile 205 0 R>> endobj 206 0 obj <</Length1 1642/Length2 7677/Length3 0/Length 8516/Filter/FlateDecode>>stream xڭveXknq-RE{%@  ݽ-nZݽS܋)V9|:g\3=3=$Ԫl@Hjgdm͜`P[2B�/&=#IAB� 2pq81P{7G _c$ li`x|pA ;#8P@� 0|+" `UȂ@M:A�%9bX@�s5#8 ` كa Wso`r`�0 `hj8�38h)a=Bap#x̪*%Wp+S0# Z<zN[=<pS �2`=1##ON0A@{y}; /ݛCDCx0Xcrr=4?a=/vP�'_v0g b=3̏Ev7�dJ L `? D;qy?e Sk�q��-?`Oo_EK@!bY> `xqZv@#lz>qpӴV/dg)8i8>N\t~SIH@]l\qs��|<^x8>+�}vN> A#mg! qm ;9:>g<6\AsPs@dx9IFϐ~G;'RO}^fvo)')tE,zDmjnCf_;1-t‹k%&~MWFy8?u"='87WԍroQ_4q;^0:gҝz'V~ j~Z<k'C9cWoOw/,0MIwn&U(g⾘P\Xl LٯsR 2E^pc.,:&a(FXFɀN<v҂�KSw{&i2﷞ |mBZ$7Б^NhC:(ªFE*BA2{nQWȵC@XnE HuY/9X}L\3v,1nT?kےUCdNzr?}7vQUTM/qe"nQTeľ<j¡>BK|�5LA I2Յcu*Z5UzO AaW#kkX9AADbX?(wV'6h/�ղJx1�1T zS:Qp}6W j2,2wQޡ^#TH_ ";vVK^9ŴWd{x]CСdkpTx=JPQ޹p/'=baW m[[ 2U1p n4ۄMΕZpWP'3 |M7|?9Fmc!9I{aq錊:2$&-EwiWZܴ,W)> q`lb!sfe1.hۛ.0o1@Qc꣪aZ /Q&?+&7hSbJY~FОzq']҇)o�2^zrvMȖ`^\PGvTiT+ofT{RAIa#JwUM}]zŶ( }"hs~h0\{ؗa!Eq2๪;mVl5qb�[% <VO+s3Ze{B -g2Ӌ~kEhvY\CȢ�vBV?Esm}<:[x|@6`x Ba\9@X=4 WvTdO8[M/KO+y_m 07\ƈ$|colcY0von ~qM+kFz% wT1ylH<o*ŵ ,<8݊Ku% wL([.am`ןgAS j<W#|/X?"/9|XU&|LjX8qUU"PC @.:mgb"enhqƿ%Sw:tԖ}9Z8a LEi 7^) yǒ5%!-b. >:]/\,!1nСb9(\]thɡ5Q%0T)ZK}]+chK*RS:۳:1x{@J֐wu-yڔoZBXvHN\c@g*1ۚd9TX\/ 'WU@t2-vJq$a ij`/! M ؊{¶{Y<`UC1y#ݲgXeM(Va=-/c΄)lD/fw*˶^n%4 `#ML/&Ls٥ۜrԝd+.L {Ѩb #_,A'jM_L#0.r,{Gwr~N&"o @ʼ+,d/Rwa@RKt :֎.Wܮ{-uBWh,<z�:wáJBpBΑTs?ɽ bXf wuy[f"Xj}%d-nE,¿h{uUҹk`.e:6;c( })m7l f? dp= qw :O`2AM ףB"NYqX ܋hB\?']CQF'v϶ȱVPT}|[lKT%ya_Rߚ_4Wk_+Ea8ͦQ K#ţbD':j%3[>{/9+پ8t:9qpג2ZYSY>|H##$i&Țo5d˸`KQQqĒēZ1X&ɘK%'?uҢ8V-0L@M-31u'!!S⃫_V�%f FkنZJ׎?s:tECd anOhqNVN,]+7F*jGxaOD=1o+Rory0teYucMҁ8PC}is?B2iV,YNv*<*d{Oֻ@S_e`S{EN Q~59аOsJϊ)5c׉!2نL{͎:e)l<�QGy1[n418㉒7Z<5d<+ȯ!]e&~91(:!C}T19}VUU@lv2v!*SE"ir"bK~;Fm:7k<p}8,%9苪$n@^~$ z7K _W*::P&#.6pInl yz·O7Z.4ޓy\lيG#nZpdAcW"$-ޙ&oYJeI#c:q )֥Bx]~rcY|(Q`1ta0A/qUʆ΄WMMid /3g8wf46^.ˑAl<XM 4[`<2@؛M1*YuRyQh2'Bx sUY:zO*뤥Sf%E&X85@Et6FvW~d3ʯNnXbH2> 5"Dd#^5;k dM).Eʉq>bnǾ9QC{Z}7t+)K ]Wbvi^%zmk|D[tJlŢ}q5-m x~DL /JpVڈwc @laIxu˙oXD 8OP OM 0~$>1'lWq[G)%^�ȾWSO@n*Y�QJ =,4᠞FH Ԋ 7t@9VXƓ=ۃqN]]w[g<GO~*UBEaG`JpCwjF?gBt8?G$JwG3͡~<-8ʿrWG8ǵxln,nVo˔$6-nZiv`h36KMzOí+0aKaUyѻ3bKBڅ!1##4TbBU9 LM/ "XTP\ī0&|Ώv~qRX%. U4ve +N*j2Ns !# xF^x~OFVkYw(]V*兾ѝ uǗjY.3^ɟII{򵥠tO &L l۟q,&8S�t:Ȍ4 Lީ{zUP w5F~WhkwR[w9?|q鶉,%gwy}`1>y1aQ egdSj^HKPiӭsdd]=w),&8]rʌ%n߄/5rgbqɺx iImȝZ؇Vh]L'=>ۺec ˹rTYTR" Fb+?ړ瑎]CAL} h OE﵂pL\) rӾ&NO^4JU2=Sn(6g=k3hik*)ݳM Yp<^k2S7YU64rWq\)cpߩU'KUrnB7/ &M`:Z-@ 2]%8m_QC^$4%j'zS̺>^Nv&DA&*Cmzs.7{9ҎF6:tgJXPsp_cdhF 7l n\OǯwN+F#f&H3' W'(ﱞ0ZVk>w`CulZWIFtLWd5+0W9 ٘n einj7i8\fڛ= ۰(;lKy;F@xzWɎTlSXT/B#tD4nkp, d.ZEr 8ȽrXHռc# MI^4JɸPNS0�`횑yx~Kĥ�\s=E̲74PWVKhe 4Og~qfġӬȸG| vqTl}(.[V*@^?jM VG'D})*iE:cV 5B\wSFfEi $@Ki,ǽ;uĢo}DjǀE[>._{#%m.iJ[2SK!˸Vr%]wzzL+Dm㍱5^lMW[/V6 h#՚Ap.ax<(jo|2 m=il|*}; v:on2JO[oj0#=(]kr0o%R9"K>A02r Od9Bx"-+M|(o`=*'uT{aZd}j&=kS|\dya%:%gI jUsZvR)VYv15{+_s]!CꃣgҍBg*?LM$,iGP}e-BkV+巏ac% R7g~X5Lnh:`-T&G�^#"5w) j֟娛PaC01V#'` "]%w|K�X"T7N2=B' W;7d/3xR<,O=UK=yS,uEe{9쐵ž=u=>e)<.SvYVkNQ#h ɴi6yZ(g翬:\:~XlIΣݰ|rIRx4oaYzJk$2)Bg>3iO{ylb֖Sվ^.Tf;Zwyl# Szg;OCc<2L:DqN�i#e \IK%!h-.leo!zX�;$EO , bsi2zlߦ%xg<3ff',"i"]M\rd+`bsC#e [鳴GPp12._fD}fJLY9ƥky|L2\- `tFs_2dR"r {鑉#)1wkMGGtM<]t7t ~fuݞU-"7CM&bƽ#F-tm}/;unVe}#1[#3ݷsVeh+Ln;ATͱ2xŬ6QJ~Gp CyUȖvQw֓ǵrE:Fg}$:kzuK.}v؏ց&X |f)p>lS }t o%PyG<͸R")*F^ȯ*}gnmE"Pm<< Ey^t5&Rkĵ]Z!Wb5{@FreҼ/F|MJFJJ$K<hqljow/ZE;abH^�)^˵ AƑx"w#H4O?Ȋj;c4 �B4)eg2 F x9;g- )BF'R[m} IQ1*~k,%,:JvtxCH]%RNk> 0` k҂SW_z=vкm=ky�޻g)дF)`Ԭ'p:䨶Xwau-\3SWJq [4ly&m0o~لpбPzA a: |}c$=}nflF,g+7!Q8[ӗQBR՛i2ĝ=| TiF1+彧&.QBs]:7+60WonZ34 0)xl -1=hQb⧃LJ� cI� ¯t0M6#}x@uʧ/~9OZzz ԋddrЍE*%M|'Tٱx |[/7k{,up(#U]T_=�4ŏ8<rAk瓁[x2HH%VQX1g.@NP/!YoCz1ŮK%)oߵȔqZB `䘭3Z1oʒ̨ r+i2yoTx> ƖKv>H"cr%*rg;6LF:+߁(5#c'v%tJ)! XN(XK43P1 _ׅ+ddP^I.şf&,Q->c\U4o<Kg!A5¦V* C̓:ʝp̅,/FOC =Qq5L:yd.PEC#믎TX9^̅t -9jmރ)e]1Vv`)E ֆVR^S̔@ȌW謣/LոP~ZߖV1QR'HVkbTƪ(8ܖdlv}z4?* endstream endobj 110 0 obj <</Type/FontDescriptor/FontName/HQFFHA+NimbusRomNo9L-MediItal/Flags 4/FontBBox[-200 -324 996 964]/Ascent 688/CapHeight 688/Descent -209/ItalicAngle -15/StemV 120/XHeight 462/CharSet(/O/a/b/e/eight/five/four/i/n/nine/o/one/r/s/seven/six/t/three/two/v/zero)/FontFile 206 0 R>> endobj 207 0 obj <</Length1 1630/Length2 19105/Length3 0/Length 19953/Filter/FlateDecode>>stream xڬct&;[+m۶b۩ضm۬$fb|ݧOu>?5q{ENJ/l`pwgf`(Xٙ8)8pѫ�-�p@cW+{1cW @hXX�p�QG/g+ KW�&5--jqxo=;h:PZV@$JRA :LlLrV@{ 5`o``ofOk. ]��G0)lg`p6w;W?՛; Ggvm\\]L]*INWKcrX5z9ҿlaZ]]�@Or�fV.^sstWn.VYhalf tq g{cGG[E;`5gcfon +{8vEo܁?;Cc3{[/QoJ� }$7PB oWG+=2w ;blOQ[r`Ү"lo&+\$<fJV�scۿ3^ lke gfb/65K+SH` ho_;K׿gTSP9*]W5/ǿn' @-_@):[yt?%#no`꨺ۛݶl]=p+!ٙ XSbC࣡j%E2"CZfx>;?dh0m)ӀW~ԃE(ݜA则1>Kr:LS*eP3ݬ0}G'{roуP|vNё[#<ڼxXr^c,3W/#fOWwNv:duo7"Ǹ=4KVB/aC&xY08$: Y5ƞ5& 58+_-+%;(TBRK:afRL\șlߢW tԪeh=We$$mFsR'x ݂)R5m]0 -׋R!{uH"mB#O~{jޯۗ!(h4{2"[<-w R! +Pϖsmɤ~N7^5^NHZvA /A6 ]P0m13bH8!Ll['[׼0J,dm?@}z Z L#�,c1={9 p#m'K{Tϱ9qk*g3-f̴@3 MmZWU3SfI.Gy(G+K_+iFZ5 T*+VnlVa袁X* h~BC,|%Ab5S6 ;P3+P訿|JR/};bgu1dۏߴP.1Ff_,W; ջ8~wPI-m1k @F/é3ٖVPkle`⟁\ 3Jpl%lOc8_rG.:Lҝroe {HUj 435ֻ%9~ɵ,̜E7[]B٭Ӡx.̛[g~MrY`ts8kѬs ƜYFђb|f7:eM u߃/ho `>}]E'W9(-=IHB˹b߉(My|ڒ>}ɣO" Gz!C3݂Ng%}_'*(o%{7 F#vF3cJ$T* "ŠE-UnYIJ[yg"!Q^aCVՊײN!VâG:Byq&դY5n߄)T&ޙq}(m_͞a}ә�@ mv�#1zD_H#O#gӌ8u8?KܯvZ vY͜M^^{ 4yDb1Fn_ЏLcw|F>9}LAt$iV4ܵ3V;<#ei|ЇޜhJz:|kNp͞։S$nBwuxţjwP@BψD|^МU'^5 Ey3xx0)^݇"suZ*MemP3)hW"3 6/,ibz38nir`kOYott11Π?ӔqB&Xd6#ƯF; djI=fmÊ>x[Křv�K 8S'c ,2FZQBȶx<D�dMðƞ:_1F0hSb{bpnO#kA;ZGi^"Oo[th,HC<'QʾCY=w֩aKKdpw7] )bÆ'rTs0; g!7`)=z*X)gFq( yCdޭ|>gVvynڇ !ފ}Ex@'mU'CؘxFl#&bU݇usӅ8�##rH"u4?J1wS1,*VHbU[QBuY_!;$L*(\֒5RkG|QmZ(NG3rco+(y VIW} n Y �!ҁbR#nǺ :E/1uŋ) 8+DQYb<V. $f[\ϻ9IA)q(U.|Ξ@]V>HKS$;|tB8w~¸܈  z J]wOmftߗsJym*?m4 { ]d:̽GN*xMwi̪Au}=mdԗK>'`ȕ+u,|&JtG3˛hT{5{7@ ĸL W [ ŦJ[0OMWuJjmW拨hWll,Fj/߰=lڱVPF3u)ru{`A C=3qgf6&ǐf4VkE�䃥MQ"^ p4:OF'8Q;.i]z1b^+N 6MvbubXeLX;a`FgՉOD(^< <MsThȬc t39 Ϭ(C /YgpuKz)+{[]Yg84X:*J=E:Hn5E:^8w5baBt5ғ?%'2|GSD#܋28%+.VfPGae]1jC*b)2+/oItS଍TZR\bGjXcB2CI@7,2a`hX͐fj_& vY+Z GD| kLXP|<dqo#@aM1sA[ZGr-ʝw 5͈ S` s"6~hZw3/Bl>%.S"tcpIC]Mg PXMIDtS=?FrT>yִuŴ m)iaz<1"<5>2Ûlf ʦiv;OTe31, /lb_lȹ\4+"bRM=m<go|J(Ԥ}^ 14"_즘}v kKȃ Gʐ Gv=WA.!qIp~楺][ W@18 yv0CP@s:{[ᚷb\C%v !Z{ܾ`GhW0%') O ȘB)0h<:\I'±Y}WJq/)y�\S=始R~:EX7#U,#LU) OEcSNʭ0K8ҙ|c;1 $Qph»-r"uRPl?.vsg?sҏ7zv �BhɅlhM"~0#6jÏ@π)@EUԎo <SpF߶-#Fx`]O)Di͢{-Ӫ IHc6=ks5!6D8}OrBzIჭ>Wy۷1>4Ӫ@@)GaHXً/oqJ01AL;.6r-6UU({R奠ًYzΌ~ěA.oflKŞkOK[V^YQf4 $*\9ũׁ5cٖZz_8)V,4]axO0VgRPGV- x6> !P�33 v#n߁[�H}}<*BF%MժMgƼ5?P|arL]D8sG?(,F[O6^bwiaf\, Ű2 H(BHU@89X*yBemtvtIټG Gw%O?зLeB동 *^Gs>ЈEvxjNs2&p^(=űsvKͪ`ň�U^>:پ,'1Ŵ'.$SpA%􇠧:7ՠA{kx,p<(ɟ`ZmlnTK�?sZ9ݝ] Vc!Iղ3EY)aIEjRQ,ƌTok-;^\(W˦,0~\g|e2_wH<6CdիegxuHW/\T|F养LʍsԻZO-j/gVrw=.9q P:$:mTV:}+kE= ;'}'ߟ" ĺ:4Mx5phQ}q\}uԆɯ i@SdGҨ,Y/Mi!x]cNX9+V%A$5lЗQꛍnZ‡2~{] ܎nbEE(' /F?+Ѭl:Ц *lŭa^6>9p% V!i\ d83w~z!c ’"֎~5?HCAuk[/y�Ih�)Ѓ)"|v�NAH_:;vt> wLӌ-w^!Q$λ {RDJEP+C*s Y$WS~mLE^[҃,e._,y49mʣ;,s+ /iDyi(+̫ћҟΠK!uG*% Lgs7=?< .R1A*Q(DۄGav܀PZPa9v }EיB&nr� {[cb a.B50i~'Xp"uAL5,'y/Dtvj$ԩpnq=if1m U1b)ۦ,irg͟$jT|Eޟ뉇KHL# )BMZK2W}XDk%"U#PҶYNdVV/ ~}ͯͅ31@MG_DmH+VM*-<R$Ĉ+ l~Z8D4TS"F-+>Y-xw{I` k3yd;-kYu'4JYCO?n`}yBwuMl- X;`�h3P>XG’L'' ňghZ*ҮUTrIoI1vjPѶcz)&a0/CvSYaVXu{½j܈ 1~mu9ʭ@ 6lr:}7Q{*3A 6Z &:f.1ޜ-7"S$EH,-<"ߎ_ER ZT"?ച$yX[ _]:W=Hx?Y}rn cQ#;$V*!<i?yEд A˚_ᡇR C>s{Xܩ{rZLr1wHN}ږa|=@DU;$ySRdç=|:lVeZ.hFK1Ҏ O4\<߷3{,YEygVN l u.q4n(%8V(ղDt^OC(;c‰m-}<v*نѯǦi^JorB!)E=U> 2p A/ܖ *Ѧf^R7"^2{8qyHرUEs>o5KIW4 "a88 5|6TYJc4+QtYjH5л'p{,p",E0r`d:L&ȃP,$_>.{*N0ANO_U}}ǂU(Ҫ5VBe'>:HU?.:B<22{L\Rm["oڷDFn@b O趗p&5K'^1XLO;5^R4Ο@mGBۗ SShX~?�*0thd߮ q&@p~XaUPq{*jv lX@>$h 6YԲ'QP }_IB2(fYHcROBԘsR#|#.O|z8v K'{Gȋخ]Ɔ֥[e18lW)O>(Hř'd' ԝ:th´FxV:Dդdk-.\Д!=W֢>څ8Raρezybmutե4=ɝ#INz<8*5mmb V=4VM& fx"ђJe*P# kH}#xIz!bƓl &Y{Ia q1[>g J,}^{C~nN'EmtS-n)aJxԬijMMX?[Q7j=> WY><b(  ps 4?k csGLMrȜ~^\ia~3%q(Qɬ#jG/~jyF r ԢE /U}PChF&َ`+ЋfpP Y̓$"4n^$"gjJ_73UI 7@UOsǰkjY#иh|!b+}J1V4b]m(:</C߼Jjd^0 Qy_ [`?c_pt9o_Lַz+r6%Dɀ dFJ@`]P aॡ=ՔS6i;oNTH!F&J�E;B(Ae&PIڃyG1,2 tthia٫m387=D2Y 7#5B Xnw#mq] cw Gᶺ I42% %J#p@.SP8�$)'zWF2qw"f!a'?'T&9py̩ 5- bQ`U r#\6%-RE/O| H b`^[U?񫀉 "O a6}u'xmSDd7|%|[0xaI$\sK.UQ!9[ Úćjb:N@r`IܸH\ӮaiIf@¼sTrPLo >m{7.Hs">asm}B]0){ P7rH t]8QeSX\m b^%LďnBgN+sۇD}HGǣ5e<鵩.0=PЦAamQ䟉 ݧՄH;,f/1 ʥ~a:HQ_*vT'OW0LƫK"Hh#~6` (, U8. mp9y<)&Cv|vt",s|&Ĉ1{nMamnMg1˹GZMVc‡zܱ?{D) {:%؋<[p j ]|J yY6wV@,!s jaXnjPZޘrvsS' O>ۣ: &ιĢ)s|c+=O&0UqH\cDؽs"*B[:.l E^.*-shW}F*9p7@-rI`(KkИOY/;܇>*C/uS ؕ#[ɔIdpB+,/d2}Y"OŰB%h *27ʼn_b{ ؐyO&UgaOՃ?UCFhNLqHۇ|d+qu-rL]2c8H"N6\$- $X|E3NaN<JH~h[So;QƵ@tutݬ𜈼V,*OZވEv5baPV ZV>YjB^ݺS Oɛ3yRus֓~,G7uۍ@x>D�oԗ/k%@^>1CHb|9 -˟_ͮ=ɓfW야.pEBt<"<_.4Swa ~TW㺾HR9J=r]UƃwN�X: JYƀ,"%Έnf7 h~saaP$dx33tafLG?r$Lΰ6N?UuVbȚE"(nXz>ijvg(֫E,5?RK*" [ZR€e;:z'0ܗh!}BW`4ȯjz&"vQ٦'O|Cz9=7F{hjөPr*Rm-/t.<tDE'4dW6]O'\v!%Y�ڔMq6 XA峋T`&QB[ө$ܔS9Iڢm0Mɐ eY=&04?]],˫~#Z)6=~Цd(i f-sB|@5:|M|7z&/lU.`Vn&�ƍCMARLOV [6Q 1ijݗ=9hI!Սˠ8Mls WAѵ@âM _o̡5\uz}H$7;qϝ[j Ŝf}.޼[ZH`@^ߒED%̛g%CM]ؙHK#cv :սɌ_=۷9W 󺄆\RH90$O`M&Ds OF?fY(�o3JPbb9pIue,tl~C4u{Miv dTOb0Tr\YL1; g iPW(ވn!QR6-XPcbM*&b4GS Y, 7(T]/UNjQl;럝?>ɠby "%g<*- {ؔx wGO{@njXPX1'E7o`8Ade7XEXGY[6h+^CcOwr}/Ruj8 lWD7U|:k 5DS+o |G5U4w7љ13\MUuϖἍp/V"L$CN9#T ʱM:/ (^854 EF{1Kt2jdކ=O ,n\ 7ϼ K^"oKW"밓]tRGENɵF\n[vWj_ݑ}xazs*@z e4Hѫ Yb !q0?E͜õD.Jy?GfWM/SګflsC拙L`G MRg:&HnO2ɋCChcެ|bhFCᐺ׃Cův,3y\ e{Aw\?u78.np(059߇_^5b;Yo; LG 8ʅ3un y <#aĨjpf',{klyJwŊ@+pvU Z2@ 6{öcFxVΖ$'_pOG!x.nXo'%¼Gs ^*aUP<իQZҞXWeմu)CÔŁPAܧTF^xq)$KȃÝɟC2K3k-z& b~oA 4'B˨LIF CНLJ; r]&\2W TuGyV\qǹAQŪ_<6P94 ~M NoT)"cL:>Z:;N=Ü("\N)"ɚyQ`Kը3~XL|M.,5#kvӋ ѐM/P32S|b<Bl>jN@ <W#*Qk*D^ t>n6}pWfBMJ+8 9lPZ76q*ow>;C8zIfqfpY6Ğ4%Q@ :O=fl>||WKǀ֑@CTp8JO$KɮX84BEqm[UВ5ЉV#hbi`Z_kŨȪDvMQ3 CCrͬW!AL<zzi@ -="N~Wō\7@8S$XgXkkwDӚ'|dGV`B&yS@R51ҝx9["%kv'Ӝ(OFX4R ya8` 9M$?,Ős4ɦo1;4y*Y-q̥9V}*0 CGGeea0I'X]P.\lp{N{U qO mpG.Swik+[5m ^mf$+>6\ =qu0- ;Je9Xo|AX^7ɞU~L2YE)u:FSADp8ݜsޛc?N%L`�I}z;oEnz!sor.=2L!6|hahl zʙ]OV߼(ic!1\W~'m*Chp)ZLlR;ԫ5ࡥ~ fSCc46Giw"'B1{W,@K7A.# bV{Vg vyȿ 'M *V,x靪:O UCoG&yhNSӻ#]XqlP)Mm2\$[0JI� :wY`>n#G2Qe0'fE[%4_J 2HRa왪adml@εbrJiQR9t1&;Nq,jCBd3Le|^M#VS| 4$"pV_jK2<Xݢ1^C"ꚙ&+2$w7SOM1PDN=g w(o2{}@롕l \g'j)Bd*%I𗾮O4۪ػIODk,,xYao* I7]WfLIq{P+QA$"\blL0?r׳+ujT?}hCt@dN'+F &ۥ fG :9zڤws, A;NO-IӒelG99\A#ƴH.ΰ$= ~a7,0T!b{umnHՎ_s 2 =̴Z!9h御1ru4n9O, PtvQ uK7Mn2U-V1R^(FWjqq]O2Zr~v<4=|CR{ieWd&e?# ,}ܯG*7\ʯI+\݉VA9bT1 Eּs!*Fc٢>@/:JÝĐa8uQRh/{(u`&61Pn$XVpuf>h12|E.8h4ּ;;* U%1~kX<s\bf?4ǡk )/ȴ fKzk9J-̩ޅ^Zms!C |.=]EMsrQ+*=˿ƍUQo-ALS3#AV_}p{2\30N%C( ˓dgusb./jZ+QbՃm')^7Z;$4g788H}8LUU7;R&#\4٥E3 :`!Sqb+wrV Svtb)QܝyvJ@UqCZPt09E+#ce*+lW@N=bq> :J"?R>twc ]IܚOK6{|kE"3, B̌""|*CJ]Qxv<Gh{S,b{Zv٩M9iI!ZM]Lon8q25K3CȖwdcM OR2ܥ<!~mEM n: 5#kU3H{HT6@!{xzLFGf!~N * Flʷۃ@i (JZY\UWcesv  8y~ٱ5մi5A_!>?38XaH؝TD!KM*`N;|ɸysnn.8U9}+^Lp4ig `fH͕_"Is5[&̶G�͓Ef]:uXŽts?%^c~bnbKI颮ܦX ڢEOPiM~F'rvϣ|G%2]fhVPy]7ȑ%%"` "ֈk@ ToeIM'G{"jѢYMQZ;ߛ{ۑ9=FmݸӨOURDn3cFoT4;xbJҦ޳?f~]p9gM՝ցN' {z<FXnY;R Rb!aD]-(1(IY!}<ѩ,W"9>0p@ƂK=Z X%W8f)Ib/pBz% +MaE�T2uRn2Eo^4\ȋHq.ـ ~\[=4I6'hȚT<*N?Օ-."yEOų(9/Yao+q1kGIQ.lM=&Rs4k͘THOs2Qf؟&hJV4~Dգwc8m Z a{-xf䞇܍t2zP3JHfi츩kӋo, Bgeu3A\ms-ihbg&HU/K٬:$l_AQ;IuӦ:ĝg}W;$)7`4”#&|e~dSb4b b" Q%/-.oy�mKXvItq9X@iFc.J1#7O*[+\PxzlF ~tdI"@uN2OvLD%-4<TtuH&H&=;]q3;%9)buܩӳ*~7$\.{=#ͅԬn}9X)ÎZlmw6=Ik%aTyo?=3ߝP=GUpye'MAՆQ6Fʇ8 4qKp#Erf?AAeʆl y΄dq!&,7>Lqx$tQKQW `pJX۫}btɟ7ƘQ%Vv:r�*mpiK*nIq3Ks9&g% !r_ o^Yc8y%4ĽCnrDT-o1h lSAV,#s8_8~eJ?5FZora0Ȉ;pX_]L dq ?gƆhX9\�Շ.eeOExƒ}g'1U{!o u}nfT: o7^߱a1l7;-nTϚn4սeShHɠY9gNA<_RsY"8d;LO96?"koAg=po:]F 1i܉1<%VTTݴ8osKU#h>g-Ȑb� Vp{%kWJ 'XU*lE^"%c\Sd[0y="[;y/br[ JNX;}JT);]")!󓎇EF'7-*|Rų銾i&=Ve.=5Bٵ@AuƬϗJylNaO~1^˶5Wl\'C%d dBӵ\}(qaɜ<Sk#,8i Qsw .:U4آ~%HebQz Bw,нEė)K #mB`f|uM|v4JO2(m#:3#H 2_uc5;J|4_vy1Y)ܨjЫ85=ҸtU٠7Rh~LvQ`o:x� fByZ7k-U/+q%fn2ht\U:qP܇pW9):tv߇ovdL W(4h] G; 2÷Z1ÐPJܮ' _wmHe99LgQU6m>?:o |z>-|@^m΋PZOiF<ss:37LCE}07`5{vȑ eAұxk:+CLvlM̦L8zq H2k}wgDl'sSY&e!An"k,^EMtWO&{ rd?4 ƅhaD |qY<QD$)ӧBppX!hzOL3!S|{8r!7y{&W #zzPhwF�MI0 M?"bKrCMev$G__H>0h^ݛ !bW$OƬSqZ"^WGO6@&]/~A7B*늧ٲܫV-eiKFDٌRG$D+!ӿ 'ۯ_-NY 7@:Pigѥe"]Z#y *Kr臨{#~`Ufw&y;s1U#R)rP2w62Ro˸fKbCN zs�cM:yMGl̺f/^p0wUN+94wǮ 0pD\jFʨJ=Nh1/r *4}2h(QJ r)֍g\:XO-qVJY՞TIagvI\]�Ps7~HGr2Y۴0ٵ}֪F鹆\.�!Y!́5eMӥ=`|K!GDK-\/GHR1HT#}L|i\uCwyV~[ -ނIrd]{z-yB"vM,B4šu;G"K`6PAքJ%|3@b2"Do8c}T!]#IklхwO~ĸ wR_:AcD%0SRMݭMaT[;}찥c*eĔA4iXknsWJŶ&An/l4eFY*mY ׊'ػ˭R`aYܥ. YA/܌AFLy~RZĉ{nd=͒<7"֐x(E-*8/Xͺ_ltt"NTJuk.^1m\#lrF`|>2<Ւjaꍏ,X`RL !xZ|7)F4G[R(|ǖ[C<uh`\+WkE~o)^9@fۯb9;w%.^:`<HaF(HKKb k+ŵu*N VUQ# x=aM,}s.R q? :"/L?i]$4#\[ ?Ī*S&V;+oB `mK'* +8epi虽›u|Js)]?Z EuOoH{!Lx<2nt<U׏&p-GX&DZMT9z؊1& nA~ ؀9ͳ_@J9s7k VK^f > _mӚqf[˞C@p2U-?qvVy;!=!X֝tcBr[I7+Js\ƫ!KZ !8i/J~- Hؔ�M'yI:P�|=yI�p,7V�m5oMw6}֤$P/)@aeeMsWtXb@prf<U.ZqT,iW流8a8p3vXG/cE`/SRw4SB<[[;${3sڬy=P6m6LЭʜ5xQf2Ԕ@N,T \SC~vK&ǿ ?(r =q3d4(,Ӧ/V^.L~IsHfsJf`Bn2c . CT]5xMrLZT~z=0T7pCZt b8x׭qJsy'F8I+ps</Ó'5ٍm^;K*Ox<2jݭDIo ǾƤڶad}]V=cP4@nWU#4e@ )L!4-ENV(qYr9Y2EL3fff`(ELN�3bf.,"S8`Ǿmq"#,_uZi"FխZzhuN]!94F©))�9t$gOIH7Eʩ˴6нxst}a(-K:^R-) X~PV0[q ~(]"p:*jJ<W,fS&1掘_=<4RyN$X{ߞk? >GH>Xt3:IQ5(5ңN߲ Cͩ<Pt'޼|ayy雊Ti~{^XjDKL' AR[&n 5+X>WȣC:.(_ٛlɀ/;DGhfBo+ץObJUA5Z03G/aRQ+Хyq}h3  Zq^_`@&v27G?f.+!y%8^H26hd)k-5xjf9&|0p}ym�Tךsp>1 bلxrn8\f40\ؿAjV떁-5XnK.2 cyaNXRlXy@_y |ηI-1Z##EqXxwH\oMON:s 0x'&0?m˂Qa59 }aT;iJtp}m=~>ٍdwsFA6VJ!5!q 8Ȩ3·�~R5 |V~C4/lH|Zdj6B(n2WfGdF^ڀ֎:Bi:$[!գD%˥}b^ v¨zUVb '" ~M4# Ф!l#V# endstream endobj 45 0 obj <</Type/FontDescriptor/FontName/NCLNVQ+NimbusRomNo9L-Regu/Flags 4/FontBBox[-168 -281 1000 924]/Ascent 678/CapHeight 651/Descent -216/ItalicAngle 0/StemV 85/XHeight 450/CharSet(/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/R/S/T/U/V/W/X/Y/Z/a/ampersand/asterisk/b/bracketleft/bracketright/c/colon/comma/d/e/eight/emdash/endash/exclam/f/fi/five/fl/four/g/h/hyphen/i/j/k/l/m/n/nine/numbersign/o/one/p/parenleft/parenright/percent/period/plus/q/quoteright/r/s/semicolon/seven/six/slash/t/three/two/u/v/w/x/y/z/zero)/FontFile 207 0 R>> endobj 208 0 obj <</Length1 1644/Length2 12541/Length3 0/Length 13397/Filter/FlateDecode>>stream xڭvcx$\el۩mc۶*vI:vұ펭mvlx7y3sU^k]EEQ^L΅ 5vuV疥W6p|Ě\vF.f<� 3S  GwtP)kP@>=v�/nf6fv.ώ*ff�K39 %%/WHٙ9�]m&�Y5 `4gO.!gfab89?� '#;v&6 OL (*<],\v~�{ϛ&KD]v�3�@go? �NfFN6fΟ4_ulmϭlfc�3Hٙe7uuQ$LlL]>CTfA[5_{wjqWy#׎|.#;.#h5L-Bvs3q1q �:=L.&�s#ϾcW35sڙ}OkLLZM /߫Eh%Kb9lrQ ofOC? =�^\�z.7 C,g�01011d? �37;H*.Fvӧ+0303[^7 JHsf vYZWe]nZ0۞4p7 eWEwR|u6N�FiQ^sLJůPmN0~n~dH>&)u1( h5' G#C]א={1F>I'_]:՛C>q:Wg6kT&{,Go3Y|2 .ȸ_G LJ׶/H.q Zg,qG"$RI 7Qթ%2CL%ՙPҔ#8j񾒋 E %n osN RWuH?|q$(eBs�\u,yKlª2$jw]rgPTub([$.T1�ٯS 20$;AJd`s}ؒOߐ挫t"t3[3"8vy 3�lX;n_GC<"fjФG@ -l4a�mڑ8:u"lb}e_'֠E^}r9DR;zeTbvͿv7z%bk:ԧ9MAe&Z[Tj$CNt(5JzyZo<f*ٛɒkDxvUJkJfVۢ{:<y:`0+ (4QDo 5TCFAMq wLsW| Zs+D.X <XlQBnbV-zhaΔ P>z*!AGeuo[EBovJsBQ ͦwKr1`r2in.Y_nHL>vgkU0Fw})Q�|7ҍ&1|ŠY"RS6vI&sj0=DyB%3'oZs̳iF#(5~3+&:tpp+i\HvZvTe#Nn`aAZ/"e3~Fh~?9]md7l[~yzyC)u=BG!XxOIwaud{L]sC2u1G6f.\6)1|!T&.q<$1D!ℇ56HK7*rjU;ZoĄ( <>` ^@,(gHaNsD]q'In_)Tɛ^>JJF<XSdWSȕ+Q^jU1f?pخmbHNߴXp@4MAQ^uX`S'.UeHkwH#&ˬdƶt&8bo\<r56+:Qd@()9Az~l?|C+7ue H=@[ +F܉K<Bnx6PcB(6 P"eNHfWS U9Y)ѯj1'P}.Ǯ(RL`dǍ.JNT,;?m3STsܵC.ӌ-H}4&K 45:wnf?:C.S7XӺZ"Pׅ\S7_*Fˬ@ f?:),w[ɺ<ŠVS1V"Zn'D`#t8B$3-V6cE+d_lrhs s05}*'RXZk9\>IY'ݗЕFg ]kǎ~K]#0IB:4Nw3ҍ :}/3g"J/1 *.j>Nᭅ ^-8kWrw G%v怱g#<ՊL=~4Mxś>` `(v\=kOQb=*ǵ(&RDfR.J!15Q Tjo3{fVd[աt`Axsn�>}Hg4h?hu1lZ 6~y"="(3u҄+O:ޅ/. (+mf+<JՄ-> ! @)}Je١YGi/7[n;-~)VvGIDtm{R"y7\>'s8>\gvͨ*0CU{| i2YHOu?k{6΁Pjb<* FBQfn_Px`H@+-xJfDeAa%=MCվckdb1�}'J\j!8@G6 ~:i54X!Hmωj]\9A*={q,r|==L#\ßKUHT l~0(|mNI\dmi1O 55S ]uO$AV8sQܭȷ[phF?41Jn}7˅PlPBtz0S^tw"TM=uĶ94*:];*ϵ*hi\s5"{9zl"@haޅLΥӫi07,Kl.}Hxڴ7Mw҈(K15-CKJŶg8-dc$Y?#w:AFlj*{ vj\Z s%;; D4,nsqy(0d=B?YxGƽx'z{e!ʢ{7Z{8AѦ֏QdJQ m|6!G0]pT }')ɯ`%J></A/8{rEr$dAbu\?´Ւ-u/cXV7`<TE{w|x1i)(?\W8aIsƜ|n <۳pߺC X;Ldϛ4s(:E׀ۋې礽4m)ڍFO?P7H#v<PJ;0CGz_)CX~ U@g}lRhlxSuv;Jrb)/|V_N)`^AߢL龧*&O]A!gژ5:}RBBUA/=oJ.g3ui8201gT!f~x3Vϩ) ի|]rz:(Pt/A /uQ;Z2}0b(4ZR'-&E\d/Iǁarl^yzt?[ʑvx]!%$%NGl|8v)R5* ume\<^TsF <g1[zS:}u$BS;DvVýy;T1E4O*E7iG[$,>]2�x"㢾= {H&<$[[ ;;5<-hP+Ѐe@.Z<|phIzPZDRzۇMzv̞s0 ,ئB:.hWEq74CMxHPDL|^BFak:Oϩk\V6;0痌Aù%3`Գ>k5jT= NVKz #+@o2ItO?l$ kq5kFHP'˖r~aG4L7taBӴR$"T+bg w]Y=;S)Gis7emgFKc†aS|^y H#`a"څ`RFMJ^ڹ^uAm99/>b;HƁ7jwaQ-eTU~ҞU!j.%fsu _N*j-@{,-|)LѩܵK mwu%J9/XC'1Qէ8R}su'lSdwv*Ş3j݇ ΋OuqaapsAD�3\\+L Cf&(8Z `+D2s{ݨ>Z]t|E%Lԙܽk<xlIQz,0]/EN<1 J\r]4\>}V~yPPPJ 8"FVRV] n(PR!gba^E&8l\l dTYsVǨ""2[v,Xx+sK`c%9>gw?Q xoM-8I k4Ez -3k|G\Fsܯɏ'o -67ND/~CwReXQFG腮bK%ݮAr\.( O5'72(hB+rI]6ӝTK야K_qjH[?*'y-ȩM8*'lרߛYdbѯ1- OadDˠ+m  mPg+QمSZ҃7"ݝsY=s{=Nb5N|y;J+R<X.* E6 D;d\<Y^b*GP.ڬ�]`>O kqkIm ߸̴H{-c A|:%T.؊^GR$Re>xgXG1h-`cu'/f)Ša("3%BZ ^VmTSW|EܜhmG;hkNI`y3 ^Ȉ ׻VQqdWiìi (z!]*- / by/`{z_eWa9tɔ|ݰS/M) Lmte YKL ARF٠ ]$JjW ศg {6~Rc<u�3cT^O^L޵ NXe. &tvO=HOFC9ߟU("*3#jxĨM^-JlmKEY xUX=Q=.q;W;݀ThPb ET4}#V:~)$կf~HINL]fS:!UU+ήGlZV#ˢ""f",nE/? r W4;{5QlL;uVwEm'uN|#Ÿ́x蝰ԕ QR,cw0щW#": >DwqL#[Z!u33hPɶ]U6A?s;T08m'oʤq"cEσ]sat"E #{kjб(� È+4F.Vx%=XST5 Ipc3Gyk0VR.9PNQ/fdbiy+mLk�bq@rb:P KDkIk/,73ne˲FU').=մ{pre EDKtv#(9W %&n- C4vH"X|R.cp||gL8Jܾ{DJt\)j4iHƌ76?:'jc&ƴۜgvo_ qNPDR("6ᐧQÂ%ZXU 1ʛSk=�w9WD,G_c!6,ӓd((t~a}7 )'eQ5W|t/"˷*0;Sb MB7rYM\"Lqn,4+5\&_pMӞv!H1n-m1fޤ,an8c"R1߯2b Sڦ5衝*meBqHP">I_*,Q?~Qotl&(L:39lLB/Ni{|BJ9p I/?h]^zN>mȿ թ'&ZZn<` AP/$3V<Ѩ$2_x91!8ٟI%UYKKMdh*j[2j~> VZVUR<\S[_Ƃ+h0A2O(� |w*@,>IyIxτͶbw=&cY Qϫ'` U ځN[c;C qô GV_I:ھB{X"ȑ {D%Gwˎdc2ܧOUʹCkӋ""U̎@'QX @5n ŧc8L ~|'/}IgiR3Ih&3Mp8.Em7d2~6+73>yEI;{R #Á|?9u5YZuI3Ev[;I2?*#JثI?vCU0;s<.V(Ǻ:\*(oQlQQ(y^t?/w 9`trfOLN#m3ј}�&n^.) UG4z(PCb:me]fVx;W(@үn1yTdl!S`9�+TyI).-FFfa+xhjdĊEXYKL,qLr9 dJʧfݷxBLIy=ՓK#p(tI<$;-eTpwCUт6N{lP\MF*CuveBuOp2I Sa6&brn8` 휟5nSD] 0C[_j)_T&E(+>Q6b&[XoӪ\t}#Mʔ+…ύef\B cFmpF6n,ȵ<wAU!C5/'럽vV_DrCJ`VJ7ڨ 9emx8/+SfR"!<ŪY#-) L$J+& ZjՌ2BLk*h p%lEEZXX%Y1DVs3 + eW'=da^XqY l.Vfl8 C&hh1"󈄋#Ʒy(W y01MǙ�JӢ {SKMml,xnnz2!}o5qhnhu" yQ׼oaKvP.ymdcqQ0YUOS<آm p./8$y#Wf`CbET¼.wp`=uC}}#M!c5ySb5)qO$px7 |H=3xaf?ovK 2T:#Z]~$X=MʍEz=\2>u>ZV[T6%R!J:W@ZjnDv#ŚH(980|\|"/<?)$Onddk1'  $YamVy9N;p네DLvxO/-}9n{e%ŃFeJl)z ۤ3`t– Qts 6$v]dU^&QIqs!@?r(q@A[]KŸqd7e{!1;u,GO0qzㅉK<sf3f]#73&2'XȘ�Jߪ{̩O@\؅?++pzP@u ͨIpwu ZZ3`U^6:($HBB _q@ ~XY4t}/Mץ8ij4AI5 N5GSM-M]C୭L.1|v{ ATP8[lR$foYL#zamv!x%U3@1wκ 9ojXUe?i>:lH__$]gX ^C섽h_Oh]v]&3!=}w@zQ5pdz4xdV6,/ي5ykڍ gg�-Sq|2bl |# յ$Z!†+qLiqOJ*<D8E(Og*@߳w_7+ e]+)VFs,^E 73 9 V1G2IyVv8#V~ج"QhD8j̽aDqi)No0,rsq.Gs@] DCtEhYDЌTYW@}f=nC"VZ0d&-B$[LLpuXInNMHlC<딿F"ulS?'1űAҺf&=RS8eddSt,95^=g^_}n"&Yq&Tm[ , _y>6}j F $1H4BRm)(7lisEHh=�a'.c6�hon ;ℋƥ:ʔZi;/I9wo@qm,80�]ƗGlΣ%">)&<Ғ'd[�z`gdD*cx\�n0,e/%sTkrO~GgOL"btG%کz;L15$E4RQS>ow\e]t603ɧU,\&ϷA+Q d[2,A2~&,3(Pdp~+ݛ_F.vlbB-KءvT|BTI[71چ 42f QxD oaBV^WFZ0ZBQO;yZn >" 'pxNnӴ+nF>|0m)TBxAw<P[*}_^A@4Z]6 v,t@qPzmxTr2H *P1[\)-ܘ23 3cSJ[0^y&>iUԴLj VS[*plP]15usC]UDP}�$OկfE։D$+k8ֹw쟋۬M3Ң<JÜT3 8ҬȆ-g l6Wa}=ٵ2 _eLgʙ&*@H T?% ;Q&0!9R>{x=#x\ާ=FC4 g7_~p^JeAhPmަb| |`M+'M5WuIH~g UE*:%zn`DbeNZnF  [mv?r5LZZ;tE]x˹YͲ_~>d3׼eғ\oUv^ v>KѐFۨckGIgNG=�s+p$?=^�v:j"/Vg†XD𧋦;fdIX:Ƒ_jɪOȨ! u9qc$` G!Ik! TDI TF6ATkaQ0Qcj9"V41-,44Q,R?jlY'|0�1z%_-a@Vl{ `Nq#ȉhzdKBk`WVׅOt VƌDmFo :k">^_zR*5dY/L=7+<'DNI,NԒcoSӨ]o (DmN1yfL]hfąkN<DleE#D ;[%/.lt;-ef1`@oc{IJWbO74)~Z_E[=B; n8]!U݆5?<I@ 7߲J:Q9ς4 W`4hC}4!% ` GJY_zYr%prgXRˠ@!&V4Kk^/O7қN1N@`Z%&t-]rLJg,I2B3Y>rGm'YSՂJ&<toۿ;/1<CVof4UN?A/XM�`KG?: &b#=+|lH ɴW.7n!ɷJ(òVLD5Rb#9~_8aHz^Ty1mb3H/!~nʜA[tP5w.%" ) >w:sZjDd3Z~!NxuȷPɆ�-Oe?fox+]v̴r ,HɧB7,RRd|t¸e=QƝ. }*vΗ j [!brU/PB2Wf"`;VY3MѦ#muPF-Q.KSSR4gԢ/j$$ؒ &XxWVֺ5|<rh-VVUOxp# ' }Ofk@"(Ȋf2 N&;\=:m`'"~ c)S+kdm8(,MjleVCq_-3fc~Bis[_%0>b~LrY(t=c jɌ<SÄ*øhr׏FENoS -pv+K0~Rk"egQKTc,jcӎCX\zsSJks><f jgD@LAK+Ρ_|h5MY-e`ǃztA (ECv8Nuwldak W4!k T+31_ٰ"t_Kz�A4HxT;Kݱ#MG q[M&XE~/8d5>)`]o7w#- 3aT%](VYV~^z9WTfi/7WثVt7nJDLQ qI_PaR\NC^ircTql݈ۤwK\A%h>uV?V{hW\i܂t82\a\݅n`Ź=q׉{?(\^lqt3Z&ZLB^"6b{ݧE[uL d] zUꚌmEkc}X;cu*#DiFJҼD=ks\^F{5cC*QM]G3}CνG#PDSe.|?DJi*& PUVrn1Tt$J왛d )xjjzG](lIt~z.kzY6 ,",g_E̮,| 0Md ,銹(vmfe0Ċ=*mbjhԅBI*#gˤnEP+/n i fV# * Yp ^Q]pi-YHnG`?HBe;/\AMΜ^ff!+pNo֔Fbt|z*2 0nrlL�JdQ \E \_Yc= =`dY{5T94Ncpc/<R .($78VV!ۿVHD<o&^M IfcVgX/�6G*[ih>#"T`,} Og׹oUCQWbia 1 [ܶ+%J)HY!7Av.V).Yg-6` ꋹYWCh<h$QR}MBHMI۝FEgf[Bψ#djٯ",P61^[BsD2Hpfɑ3? Ɦєa7?}fDQL!w(191PE 6$㍜[']?-+{θ nkL=S.dQHƅįai)Ts endstream endobj 184 0 obj <</Type/FontDescriptor/FontName/MZCEPE+NimbusRomNo9L-Regu-Slant_167/Flags 4/FontBBox[-168 -281 1000 924]/Ascent 678/CapHeight 651/Descent -216/ItalicAngle -9/StemV 85/XHeight 450/CharSet(/A/D/E/F/U/a/b/c/colon/d/e/eight/f/fi/five/four/g/h/hyphen/i/k/l/m/n/nine/numbersign/o/one/p/period/r/s/six/slash/t/three/two/u/v/w/x/y/z/zero)/FontFile 208 0 R>> endobj 209 0 obj <</Length1 1647/Length2 17228/Length3 0/Length 18093/Filter/FlateDecode>>stream xڬctem&Tl[;VŶmٱmJ*mFvŶt=ۧO}{{⚸=XĊ*tB@q{;:&zFn=,2UWKN.4r5rr4�Q Kwt4pP)kP~O?&�ct4P}p;\B_;�� QPԒ�PIȫ$v@E(X�d-Mv@j?9r&݀&@T@'[Kg�Kg,Ll\MI_ 98 hldUQTyX`o Wbdi pz0tv1/pu3 FN6@g0 ߪ7rpW.@3zX&1M\6eg^L: Q3302`]P߱LG-FBEcdk�ciq5?9Wk ࿳UBvcbgYhhbb03ۼLN6v$N6ڙ TԔdi_ƊE4M(aa{7;e{Y} ?rF.N�FzFF&Iٙ؛3G*.FvG Q:9e_oq%�=&K&<!Vi.X9:L`á% =\5Sܟm46=<|_R|Mڃ 3W [;JS,NWOnd~&)0:APk N()Fzn ha%'x:7|Bq¹#x}ڑڋyͽ0PuO 87Z42z] [C9|kIcLnyJwi<4$#3PKyjiZQ)d< *5e-L D5ىQCmaKȍNB؄z弞<; +W1֭3WXzRFFCaxjaj59Ӆ]>Z2!F-I\#&xy-V;s/[_Nu*o\р0<-[oJ#kC pNmɼ(n(zT?N 6$˯%~n|Z?cD)^ GPc,>0˔.e:;)dB xL^v!m9CŦc>JHՒ)^h{ezq�ϛ"TO9w.o TQTV޵4Ɂ}YSSErQm~76ѧyDU"=DMrdq= \?ru;C`j-o,52*t*<YL:ok&D׍B''y0Ls<-Up̈j8 1a(#w*=SL!le8j֒=*SqPBo]vZ1^M3L^*0/o}B[dɨk۴t gP?pOk\qT_$@U (x{fqӡc 8=q,lRm)NLd"=R;i4r(`O%ܶU~a3QsHNV~A0$#ąy4ug'rC/cZrːJp%́9ZI@xfn]$ �K =NOgMu:`d6$Lf<jl;5@6{\9z2``s1;VDf&@1~C {|_y7 UXws.lw]Dl[O`Hfp[_IȠ4&̩=3}pTuԮ+?*PϞm&HLvGqTj̀ޞ ?-cXέx]F_mCqfr RfzYo\i;kPw{DSI'olC!b} kb&fo &N^$IrEԕh{,wݾJ!g Unfgm9|,eE#k1 MfZڸeٵ],�(~m04xCo Q5 &)ű뷨(Ff(=t Jt!i_/Wjܜf˹il? V{f L~@u 5Tm[4J'9Vi7},Srb r˔wtQԢ5}u?\ E&FiK۵�"c2:=z4Rw:A oeMM3@_(w5CF)_ΰf4!{5l}ΦA 42K!q踆ƪijIt;kc!vG"e&6/&~>FUYgK]Ąvkl@UJyD^ D3u`0]pjBgOˆokIctD8sA'0<b%>':'x N!ZmoC$J'Be,RwUIN[ظXI>vz[v)=IQ-`�୭?q?]$tVr'MT9G20ZĸL*Bi3Cmi:/>G/gBz3; -dW>WFM)7 `oLE)ۛ>ԋ.Q(]} k}ˡ8U&8mR):ؓLGgotKݎ@%&wr"byXZb@܅s1Y:zeYДɨ iQ޶'_]#յ,ݚؿ ("aB%>LizMqV٬y˳X$J0olV_؄rRU[cً[<tW$0@]7KJ5vv-!~!S_DXdb*D݊ ~%_J*0<lM54kϭgJd:*�븶oL]:2r@PItf<_y ͨXpaƁ[jsw;{�[ɳgj*-=K[ %T`x\K9Ny&iΩy-\\UV{`[>΄}V̔ xZj'E@LniL&tYWf'3 W%Nw-}bT�G~ek )ĈШݏfr:YqziH%G#x#A D$׺n_f6 'Q[i=M` 5ߣrĕ4qu~H8K { R(/F0c0]yV AF Uqsfc`=Zme+CI[bH-"^*sW0sFRP+%'Ly\"2_d\pt ̴u7+v*2h=G`�;kQ +S<[|7~殕Ãno(:XM 5WBS6}M[H_Š hQ`Yo ./ Tљ, f"D''ea9訄z̻p�~&J^[ya:c2m><7J`|p*ӻJQ\1Dzߎp_ X4)7KžB?쎯c)~!E;*[\R )z,#W`i+8@j'SBz2G)9= Xospa4& ?/P~Oy&j;il#2w"c}-bvb<>qi>4KClv̎Z)5g ۡ3:ҳΜ3%aqj ]t’L[rbNOw:3aN嚮"K' k,CKR jm EKݒ 6:݀' o *sK"_hEI^mH4#Bbi!}K c] xUA^ nX(c)}E n؈&_>@Ou$f6j&mֺǗ7-5`',&T$FU~n} 7~5db6g n5\&@,VWIBZ_Ng^?'6" yjO! bt׵"UtϞ/LO$l#]"+SY^ѐE1K}]c'᳭ !TQI47&jVɸp�I.ٴ 4U2\ƿ>:9WqSu uّbͥ^{4*2LO-r wT{$K3<7\jݫu./J F-!33m5t:gy<r>6Tn (XF<Iefٹ/qTod<:Hf3(Q 3e`\g3F ym[,.XN䁯gU؅QkaaΝșjrV2]LFu%Ə8TMkIƮ)࿀T�k$-['}%#. \-K#hvqJXvttOd;Dbl<?:.auSQ).;P#l/k$e~`tx>*eg^#_IvO]N,UXOywj\L2[md&j,bm�!4ǯڦɏWChUxY0G.ָK*Qg!E5iI)YD8D2/7 $t!Cd`ru=kW{� -W?ln3:WMoScs%̅ppfA8͛OwRQ:DB3~х;H8V6, V趮RMw]bo"U>g儑c`o q>~Y2 )7~v ˚[f΋yU9mlH/z> X]BMeCW+f}<S"xmE[W@Jp<s;7Y,RJU[ [G#kdk%W죌 z6UF0Z̈́%"+[Gtϓg "K-k*GWY } ? lG4B۽={Z.!jmG®;>c|T vJVo�jmjU̮?cf:="15 0Ts $Ov3U.ܝw ~s)#(03,: #6{m'>##� 7a>ޥWNO* 2 32~؂iPxY/b"R6Kԩq3x^ v9a"rW̠ S>o+Q`4^`~?S.@+Yz{ylßx;0'uv7KnLao"kD] & kj!|1-)1,EZLnpg+.XGU6bq\tv-W9bh+N7 ' zxtkCt:ʚb PETb$hxΚAFlq+y ~(�a25+I]?5h,4myG^A=1j\(ܷQEmExcEn{</Su_C'fRRZZRMKq͛[#4gh` ,`Ǖ]=PY^ ,u  @($3'IXWW`ݺN с)E'y�o1voYbHHxZbDx^{(+I +/2u;@m ca<6zf q"&i8|fk%hu)<D5 2z1"|,ޭIEc6C6"V~'4b Y[ Loi(> fu\el@j08H,-uHgmA[Πֆ7?g` 3J;*{#Mz,Xuʟ10a/}ev!3;f9T[G!v�' UʏLԉVjRĝs -8 sn>\ft'1׼25}Xe/Ӏfj#6=O0R�h$9*D{zK"tae%=p'*=r8mJIhQA aoWWerp�܍mK@^/]=,"+i M)e'[}‘?}&AH.\ӟQl{BԿN/eI~^ f*fް}l/4ag~ /rhXev.vu㔃z;'ciQ*Z7ѿ pK%>}r'EMv\ ۰%IHy C<:9t*q'R S 3-#,fi0h1zo2+IR@ӭmuA2g Yp1U Čg tAILP7J bk+<A71t1d#&L#ҭ9:u3xf yAvT`92iP׵r@/ɠvk'6 cSJP~Sw~at;DtUbj i:{Ǩql1 ֈo ^R~x {kt],`)]f8m|UMB~A>ĸ>״}.zӕT6"eVe;GUǙtu)OI7^w9)T\_8ǎߐH RB#dSiF}u뜌qϹRWT` q#HQRͮl$Ѿ&+,M8`?7uķ,h.LFx2lE؃aѻ:QXWWCךt{)С~ EV!79Q[hF__zhH),sY{TWST)t Y2%'\-eKcejq."9\|)q@cγi1u�M??'wƧs072.�Fy7s ͡9f뼷n<jͅ/<f`h 9,n+?rBL۩\!I{Gu# J2ꢈOs8eqKu'2|�w=AAv/["@X0ß"^sth3c:jkLr4�|b XRwkW*^A8f)'k&f9shoHxVFV,ҁ/gƊv(¼17%υo'[?}XͲ@+nzN<g ҆=6an 4^ a]7;Jt0r^Qi[tupR=HKށћA5"X>Fdu]pdN^-BVv`\Ыly^lg k!95~ '/y5.7)]ʇttXYH덎F.#$SZ@x,DhjHcӂs?|&(N|B^ʇ[Z&sfa|a fTtcYq*.%c;Ԍʯu )smy.N:�‹Z9^ w^\RY ]'^#/a4]ޚl=%,Ax g5ey\y}^|E esPGFW.NمC:<tˑ!Ԙ"wkUuv`ŗfmV M #e7[0ҫɂ^lHɴVAbg/ybz;8�tc&eɫL?arəJ/?Y^rxlvIXd|Of^o3C1Ld`T _x$C V'"9dRإۛA@8PJ~{g>Ul9:`˜˜,..34p _l_%04#߷X"aοAbݺC](HF/ M8=Z$R!P%y|ۋzkn'uM@qd-SOd)$M:VASH}>ɫ]ɏ2`WGQu~4#$YjMpVۮDGΏ1u5JդMUJG6x09%6!S(Uj[ 7^Z3+Ơ‚-Ax#aek0&?jv h~oQڰ)XY5rV\v=nfM[,=C:n$Zu%E4Hhh$(T#ӽ|pnHؑg^{0%NzW Y^}̨p^<o xUUVz]hKsڷ}ic@jDy䥤|F>]½~k6g3X/>7We[N(ٌgjԝ'?4TAWjGK+}m i{振nvS7Ꙑ9Y˚]D^30uבԼik7Nꥲ?h)PNxUWߕJPԄ\Wp<*["]A˼) pkx$(m18`1%iy[uyy_;SS{HU"LIRGU0 Z($lN|#5fhc:h6Y{(٘t9K7AMX%:iW7i4 RFf)x)e#:ޛ-,$ǪyY1uCyJbm$'[LrqӼ+(Pjɍ>p?cwl!<Q ތGI.fv2"eZDḫ�Bi,bM>>*~%sTK>Ӻ/*=tb녿uPƂzO Åew dS~0WPȤye%7fZ0w9C!aKFui7E[xn2ԑ\;u7Pbݑf g5�ZKSȢ@;<Np}:|JɥHCSUR~ Zh|$%Vf"iz-DIZ[9"Y͔pYdn34J9\?GŹZA]^iG?d3aBKVi9 Hh[gbĎdioyƻ^* )'5%XiaB/1Lymh^f%WY9WM~]j»+,y udl[X<.8 pR!&q{l[<DZ_ndtb̟/JDp&GݨmYu@F66C33HϼIVA澘ޝ?A(zNth<Y&D鎸%OUb9Ĥ^)=H,lR:C-^OkL"s8ط�E&`"Ǯ9%b9î+08:F^N7dtO9[I]Q46pv;Xu5ٱw?e{w%wY+~-Kǫ03A K"8Uz"TН{σ"ׇeO\TA>J TODlm7p* ֣1Qt>'PyhK20lL]b|/ErjNvr%׃SoI'!Ô6+ b;$b"6mrCl< !&Rhm@01aHoxTl| 7b:_m_l<!7@݌q#m:Aapج2W~R`'55z?Nձx>~+O;TJ/{4�U x| ۦƌ t.Ҷ731UdQm> >~~6O0N?oIFHQT\ix| *}l5V`nQO\/gO:pЛ[3+!EnU>9`n:fc2cc~XqkO:uƟZ(gs|Ô+I^I"%2r';" W" #:-]v�pо!tnT?ԥ9-W,GI_|#Һ&-4_4M{)AF]FroǤûA,!^$f6B;'-+"njޒp܏x0TQq(xi(NZ|'fJZvƫ?]{ϸ"|LQf1Ii&<`j*88}zcEd 0I&aͪ(_v 1$KЇXzP:Qhq}<vr Z)ݫ:W`:A i1|3Vځ*Y:v0f62Q+G TT^_Op{򂢶yK4 KgZBغXN‰dKj;x+ ~7S"6#//v=&~be?&2ȺL2\<.tx h Q0U1mŜy݄D.5f:+P n'PawKM6@Z B醪ysYV&"OpmeW.}zp ,.k&(+Nóp6(catI cz̡-5:bfӛwJLh˹+̶~4Ko[v| F.JMM<*pT2<xræM31*Sar3ft^DD†OMcn05f|p`6%g홖Iv`tY J )VRfjز0@9q 0g>~gQ5DBj Ͻu)hv o6Zٰs^SnnHXލU<ưe_{D<͎}H%bhAXK+f};ۍS%ֻk cjp(ki}{~81~3I;w3B(XDĽV8M4>$&hRJ2B$&f&kbg<h('t. >pF]&6۽wNxؼ^+\3V$ݥ]G&-[ZؓC.R*m̂,Dqrn`*Íļ1UY:c+Sn7oa|BMw#no\RW]ĩc7ڴ䦊?&P;Qh\Z#3{SG);źI֧&3OA"ZNZEr!Q8wG;kD@A("#J0ۿ?R;kIdV?y9XLh9{EnȨn3ۥ4mU1Nw*%9�Ǧ@OU/>i;˂1'V{UÔERVI<C2Ιm!$F xݬYN3[xJ캍s<08bb6D*~`ªI@֐>?ŗt~�⩒84xׁtǡ4;e8=Ĕ|;R",c\,Qg8[D+r*OU /GBc/To W?SKzrBxsjDMҞ"rgR>TV7N25-԰O ǥߑK0fg0ihjҪoϡVULd-b1 9|"|')$nd3]fM 0ۘ$ >u͍%!`)4뱎3>le.Y zw{e %4g]3$ /W pARyT9l\m=ced`5Q~臾/ dtTM 'eFSBx11LI]<#/4lNs#lNRbh oQG!HUeyEt`EO$pOQ v�`e ߯3cwrg ln]h?&lBk[~o ȕo8WKecF}k~+LeTgFR!FZjoN awy?;h'rCc-F_hny8%2�=7LgnR{DT_F;s9MIϠN: bsbƢ lы:8V0[0Vc+&˛b! ؑ7qLpZ\٫>c 4:@M3nYsÙ=®J⊼GD.CsؔRBR";YF K_J�~I~,V bY5k}};__0i7t u'A1whC;֥a` EF>ݟhE"}{gLpG[x:K%q.T⍔�˽~H%IhLvx.Po>wߝB<F P#FE 9 ;6b4͟!3dpLct (S#l'9o,˖8H9!NC!>$@{hOpH;nf-Pc6 @QȂv@,~Zb bxȸ!,$,gFj2slFJ~?kN6r{T!8C"4@?)|OՃkxK*tzgit:6Gb&X0'>KOyM|^#rz˰=Gs3uU%F%kQ$^X,BYgNN˧t~QUz\Ta"jAE>TЉ,U0z ; L%nضk 3 d�IIh=u0.~FuP O1^FhQ:^)U RT(;~d^ b2A9㎅xʤn\Z4t$4M׳FUh.~^XqMiðǥ<sauyf'w�"ucX0fnnnϱP斮`M?":c۩%dsQj颶02 .=m3OЀЎG`MH}/8JIe,L-Ҭa}CaMg<~3 $i!ylvB{Va0!~%ao9,&Q}_G)e%P{T@T33OErH5:grc룊MsrCrԅ6ex|%0**a) ~DJTrAxO\T"=Pۨ5Ԉqº,1&nOp6yC{l<7~I]]d 5L<cf;᭜jBM8-<l)d*ȕ;mk"Md /TLA$IwGWhR睂hx(K.F1{fsGy혌>nʭɥ_� ‡;=\{rlM5G|$ki֫<DJ1#)W}GFГ-ޛ �ژSGXvS֧"5&J WuCFHXTW\ @;4t= 2 9(j݊%�svR R8qmXj, Mr$Crt?BDMMtBxrǀ`2|LDvq-(܊[ kH{4&ڃ&=;SIbƃ%2qPh6qp{)Ρ kYA/J]{tzQk! Nr$W蚦5tɘJ\ ipDVg'd#[|=\؍ô 1bTZ_["3'T<kw69 YmO,s v8L}̊@EٍDVՃQ8 ]zΎT,N$Lsu4\ EJU﷗Nn`A?b򰮗)nu!}pR/dܘjmN21uӍ_S!P{"/ҝ>�LAF<&1)j%#:Wa>YnS�\tNafJA ,]:?6 hKpêdJ)^(TUF.s7I &ogm4LhXF:aͦ.8/nGRH|.P%}@ٽ -ޥd&\ U1|ٝ~8+eƗK ?y˿SЏFp VĪyR5i8J%F[|ͺkl$*+g$l j'lwιK?pA D9c7QRR?4u6AV|]I<]`-PAdAvmj4 9 7vipzC/DkE`}�\2T1s\bBd|k]ªo2eaϻX,|$ϑ5H!#%l$XTCpQ.>'͈NZeΚTT~k8L▗ 愺V_c<Rkeg-!e)%^K};tD$k~ri0cB* e=yމc+Asș9Zqil#lMH�޸_ (G97xPK}q *WQz1BKL$GW[Jm/էezF`K2#$+X%INHL9&h(jx7u*a(\vsjY{]Z .=e `.ZU/{ב6(-<Fx!خN,fb$KOxyB(3B2َrףr! :V;ў n41W݋&m[7I{,O ˉ[%zΥ\ KQ2b0~C1 bvI"w^�&IꃊX?F(g++|>GE2rp`AR欞KxArAHԮ9 |#JtY z&C(?-R~@cSk3>wUen($$C7d߳0>/b]I ezS[k0voU8 @1aڙ\ya#\LKWRoӗӚ({`%�d &Yԩ\GHy*xG~ +R3Qh_iN5<2tG�^-9Dg=dlj ²zXGNԺ'#Y/xHDB;A Lݫ ytƇЊ46^ m/r*G\ nGU|)j@M3 vbg}|0fԊni"fde&M%C $UfFE'j7'ϊۈ;c>_pD$k 0+(׹֌E 6J}AWװ;5Ukrc糥0Wb{W]z$.Tsܸ)Hӝ=EEc/ iM[}ᾒotzGL]kUkW̞SW),@L!_Dȓ3IJl|np!~Wao6=&oXM`8]n-?Xy O70($ 16W@Xݦm`mcA1"5iu/D i-ǫl6vPP;~E Kvɽ?1"\]a.dt~ e]V Ge*D=ԗۇ,ٍj]=HagIƭNȋ0#.NPɴɌK ֽ1n;,8*OޯٵDRwpc#[p]XOrqtq^a#BSԋu%hˡ9jlUB(\RqttY'EafcĺN5D8NG;0ƀKyWoGFvi7,|2@K('Q:k2oJ,%1, O lՋ:nS#lW9ћT oN .%m+R)~l^@'5X"پ•#C oHl.'^d <.4|(`j~I!cA/ STˠP1'Ó,I,}T߫s!qr CcCHu [ ʥ..6W95.LC7es[u ~TU-D-W ฻:ۑ݀y-!)_ $$NًkV`g(^嬨Ҽ7 -|]-N^Q֙,u^�ߑ7M9T8zڲ/`Qbjw@ xVH"|c:^GwF�z?ؖaOnrchHUaڰ{` qoiZ�-?Y BvqG{ 6"w�<p(NuF^ƅRAs] 9B>ɔ{6P+uzT:)obI'TLj)d&1]n8Baֲ endstream endobj 49 0 obj <</Type/FontDescriptor/FontName/SUQKZJ+NimbusRomNo9L-ReguItal/Flags 4/FontBBox[-169 -270 1010 924]/Ascent 668/CapHeight 668/Descent -193/ItalicAngle -15/StemV 78/XHeight 441/CharSet(/A/B/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/Y/a/asterisk/b/c/colon/comma/d/e/eight/emdash/endash/exclam/f/fi/five/four/g/h/hyphen/i/k/l/m/n/nine/numbersign/o/one/p/parenleft/parenright/percent/period/plus/q/quoteright/r/s/semicolon/seven/six/slash/t/three/two/u/v/w/x/y/z/zero)/FontFile 209 0 R>> endobj 53 0 obj <</Type/Pages/Count 14/Kids[41 0 R 121 0 R 191 0 R]>> endobj 210 0 obj <</Type/Catalog/Pages 53 0 R>> endobj xref 0 211 0000000000 65535 f 0000035071 00000 n 0000000015 00000 n 0000034892 00000 n 0000035460 00000 n 0000035774 00000 n 0000035850 00000 n 0000036069 00000 n 0000036145 00000 n 0000036365 00000 n 0000036441 00000 n 0000036660 00000 n 0000036737 00000 n 0000036956 00000 n 0000037033 00000 n 0000037252 00000 n 0000037329 00000 n 0000037548 00000 n 0000037625 00000 n 0000037844 00000 n 0000037921 00000 n 0000038140 00000 n 0000038217 00000 n 0000038436 00000 n 0000038513 00000 n 0000038733 00000 n 0000038810 00000 n 0000039030 00000 n 0000039107 00000 n 0000039327 00000 n 0000039404 00000 n 0000039623 00000 n 0000039700 00000 n 0000039920 00000 n 0000044681 00000 n 0000044838 00000 n 0000044995 00000 n 0000045122 00000 n 0000045283 00000 n 0000040024 00000 n 0000040229 00000 n 0000045410 00000 n 0000390684 00000 n 0000288194 00000 n 0000284370 00000 n 0000420072 00000 n 0000287619 00000 n 0000337123 00000 n 0000287589 00000 n 0000452679 00000 n 0000287016 00000 n 0000344394 00000 n 0000286992 00000 n 0000453173 00000 n 0000047008 00000 n 0000062066 00000 n 0000075515 00000 n 0000081296 00000 n 0000086895 00000 n 0000045512 00000 n 0000052849 00000 n 0000053106 00000 n 0000053148 00000 n 0000053537 00000 n 0000046892 00000 n 0000052694 00000 n 0000047225 00000 n 0000364784 00000 n 0000286646 00000 n 0000053786 00000 n 0000054070 00000 n 0000054156 00000 n 0000054392 00000 n 0000057889 00000 n 0000058909 00000 n 0000067543 00000 n 0000067802 00000 n 0000067844 00000 n 0000068249 00000 n 0000068522 00000 n 0000061950 00000 n 0000062283 00000 n 0000068787 00000 n 0000069093 00000 n 0000069338 00000 n 0000069554 00000 n 0000073247 00000 n 0000074936 00000 n 0000075401 00000 n 0000080903 00000 n 0000081031 00000 n 0000075730 00000 n 0000322077 00000 n 0000286572 00000 n 0000299097 00000 n 0000286322 00000 n 0000081161 00000 n 0000086518 00000 n 0000086648 00000 n 0000081530 00000 n 0000306496 00000 n 0000286297 00000 n 0000329765 00000 n 0000286226 00000 n 0000086777 00000 n 0000092377 00000 n 0000092539 00000 n 0000087114 00000 n 0000373404 00000 n 0000285907 00000 n 0000399719 00000 n 0000285604 00000 n 0000092704 00000 n 0000107485 00000 n 0000107708 00000 n 0000107751 00000 n 0000101976 00000 n 0000107425 00000 n 0000102170 00000 n 0000102323 00000 n 0000102579 00000 n 0000107316 00000 n 0000114098 00000 n 0000137565 00000 n 0000149540 00000 n 0000219486 00000 n 0000263985 00000 n 0000108187 00000 n 0000108565 00000 n 0000114015 00000 n 0000114284 00000 n 0000127666 00000 n 0000142495 00000 n 0000142719 00000 n 0000142762 00000 n 0000137230 00000 n 0000142435 00000 n 0000137424 00000 n 0000137809 00000 n 0000143204 00000 n 0000143588 00000 n 0000149389 00000 n 0000155468 00000 n 0000155626 00000 n 0000149793 00000 n 0000350797 00000 n 0000285265 00000 n 0000313798 00000 n 0000285240 00000 n 0000155757 00000 n 0000223666 00000 n 0000223885 00000 n 0000223928 00000 n 0000168618 00000 n 0000223606 00000 n 0000168812 00000 n 0000229797 00000 n 0000230026 00000 n 0000230069 00000 n 0000192277 00000 n 0000229737 00000 n 0000192471 00000 n 0000236719 00000 n 0000236948 00000 n 0000236991 00000 n 0000219127 00000 n 0000236659 00000 n 0000219321 00000 n 0000219755 00000 n 0000224356 00000 n 0000224703 00000 n 0000230515 00000 n 0000230908 00000 n 0000237439 00000 n 0000237834 00000 n 0000243881 00000 n 0000268740 00000 n 0000268968 00000 n 0000269011 00000 n 0000263649 00000 n 0000268680 00000 n 0000263843 00000 n 0000268510 00000 n 0000264231 00000 n 0000434111 00000 n 0000284765 00000 n 0000269455 00000 n 0000269846 00000 n 0000275671 00000 n 0000275766 00000 n 0000275965 00000 n 0000280384 00000 n 0000280545 00000 n 0000280461 00000 n 0000280731 00000 n 0000288670 00000 n 0000299333 00000 n 0000306699 00000 n 0000314003 00000 n 0000322310 00000 n 0000329983 00000 n 0000337329 00000 n 0000344605 00000 n 0000351038 00000 n 0000365119 00000 n 0000373674 00000 n 0000391097 00000 n 0000400011 00000 n 0000420606 00000 n 0000434478 00000 n 0000453243 00000 n trailer <</Size 211/Root 210 0 R/Info 4 0 R/ID [<38f81177f239849d69c477e8e6c3bb41><f5913c882f2b13eaa98bc56043cbd120>]>> %iText-5.5.9 startxref 453291 %%EOF 4 0 obj <</CreationDate(D:20170131183532-05'00')/Creator(TeX)/ModDate(D:20170217150125-08'00')/PTEX.Fullbanner(This is pdfTeX, Version 3.14159265-2.6-1.40.17 \(TeX Live 2016\) kpathsea version 6.2.2)/Producer(pdfTeX-1.40.17; modified using iText 5.5.9 2000-2015 iText Group NV \(AGPL-version\))/Trapped/False>> endobj 41 0 obj <</Count 7/Kids[220 0 R 39 0 R 54 0 R 55 0 R 56 0 R 57 0 R 58 0 R]/Parent 53 0 R/Type/Pages>> endobj 53 0 obj <</Count 15/Kids[41 0 R 121 0 R 191 0 R]/Type/Pages>> endobj 127 0 obj <</Ascent 688/CapHeight 676/CharSet(/A/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/q/r/s/seven/six/space/t/three/two/u/y/zero)/Descent -218/Flags 32/FontBBox[0 -218 775 688]/FontFile3 219 0 R/FontName/XTUCJG+Times-Roman/ItalicAngle 0/MissingWidth 500/StemV 116/Type/FontDescriptor/XHeight 461>> endobj 134 0 obj <</BaseFont/XTUCJG+Times-Roman/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 127 0 R/LastChar 121/Subtype/Type1/Type/Font/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 0 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 0 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]>> endobj 164 0 obj <</BaseFont/YAVGBP+Times-Roman/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 169 0 R/LastChar 121/Subtype/Type1/Type/Font/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 722 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]>> endobj 169 0 obj <</Ascent 688/CapHeight 676/CharSet(/A/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/Y/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/period/plus/q/r/s/seven/six/space/t/three/two/u/y/zero)/Descent -218/Flags 34/FontBBox[0 -218 863 688]/FontFile3 218 0 R/FontName/YAVGBP+Times-Roman/ItalicAngle 0/MissingWidth 500/StemV 111/Type/FontDescriptor/XHeight 461>> endobj 178 0 obj <</BaseFont/YAVGBP+Times-Roman/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 169 0 R/LastChar 121/Subtype/Type1/Type/Font/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 0 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]>> endobj 210 0 obj <</Metadata 217 0 R/Pages 53 0 R/Type/Catalog>> endobj 211 0 obj <</BaseFont/KGORFR+MyriadPro-Semibold/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 240 0 R/LastChar 150/Subtype/Type1/ToUnicode 241 0 R/Type/Font/Widths[207 0 0 0 0 0 0 0 301 301 0 0 236 0 236 0 536 536 536 0 0 536 0 536 0 0 0 0 0 0 0 0 0 636 0 588 0 515 509 0 0 264 0 0 0 827 676 704 559 0 0 519 525 666 0 0 594 0 0 0 0 0 0 0 0 508 585 449 581 516 319 573 572 256 0 0 257 0 572 564 585 0 356 417 351 569 0 0 0 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 236 0 0 313 500]>> endobj 212 0 obj <</BaseFont/KGORFR+MyriadPro-Regular/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 237 0 R/LastChar 83/Subtype/Type1/ToUnicode 238 0 R/Type/Font/Widths[212 0 0 0 0 0 0 0 0 0 0 0 0 307 0 0 0 513 513 513 0 0 513 513 513 513 0 0 0 0 0 0 0 0 542 0 0 0 0 0 0 239 0 0 0 0 658 0 0 0 0 493]>> endobj 213 0 obj <</BaseFont/KGORFR+MyriadPro-Bold/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 255 0 R/LastChar 121/Subtype/Type1/ToUnicode 256 0 R/Type/Font/Widths[202 0 0 0 0 0 0 0 0 0 0 0 0 322 0 0 0 0 0 0 0 0 0 0 0 0 260 0 0 0 0 0 0 0 0 0 0 534 527 0 0 0 0 0 0 0 690 0 581 0 0 540 548 682 0 0 0 0 0 0 0 0 0 0 0 528 0 451 0 528 341 0 0 274 0 0 275 860 586 577 598 0 380 434 367 0 0 0 0 523]>> endobj 214 0 obj <</BaseFont/KGORFR+MyriadPro-Semibold/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 240 0 R/LastChar 150/Subtype/Type1/ToUnicode 254 0 R/Type/Font/Widths[207 0 0 0 0 0 0 0 301 301 0 0 236 0 236 0 536 536 536 0 0 536 0 536 0 0 0 0 0 0 0 0 0 636 576 588 0 515 509 0 0 264 0 582 0 827 676 704 559 0 569 519 525 666 601 0 594 0 566 0 0 0 0 0 0 508 585 449 581 516 319 573 572 256 0 509 257 848 572 564 585 0 356 417 351 569 508 0 0 500 450 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 236 0 0 313 500]>> endobj 215 0 obj <</BaseFont/KGORFR+MyriadPro-SemiboldIt/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 251 0 R/LastChar 121/Subtype/Type1/ToUnicode 252 0 R/Type/Font/Widths[183 0 0 0 0 0 0 0 0 0 0 0 238 315 0 0 0 0 0 0 0 0 0 0 0 0 0 238 0 0 0 0 0 596 553 0 0 0 0 0 0 256 0 0 0 802 0 0 0 0 556 490 0 639 0 0 0 0 0 0 0 0 0 0 0 540 0 430 544 479 0 0 550 249 0 496 249 821 550 543 0 0 347 397 333 0 486 0 0 472]>> endobj 216 0 obj <</BaseFont/KGORFR+MyriadPro-Regular/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 237 0 R/LastChar 120/Subtype/Type1/ToUnicode 250 0 R/Type/Font/Widths[212 0 0 0 0 0 0 0 0 0 0 0 0 307 207 343 0 513 513 513 0 0 513 513 513 513 207 0 0 0 0 0 0 0 542 0 0 0 0 0 0 239 0 0 0 0 658 0 0 0 0 493 0 0 0 0 0 0 0 0 0 0 0 0 0 482 0 448 0 501 292 559 555 234 0 0 236 0 555 549 569 0 327 396 331 551 481 736 463]>> endobj 217 0 obj <</Length 3451/Subtype/XML/Type/Metadata>>stream <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c015 84.159810, 2016/09/10-02:41:30 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"> <xmp:CreateDate>2017-01-31T18:35:32-05:00</xmp:CreateDate> <xmp:CreatorTool>TeX</xmp:CreatorTool> <xmp:ModifyDate>2017-02-17T15:01:25-08:00</xmp:ModifyDate> <xmp:MetadataDate>2017-02-17T15:01:25-08:00</xmp:MetadataDate> <pdfx:PTEX.Fullbanner>This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016) kpathsea version 6.2.2</pdfx:PTEX.Fullbanner> <pdf:Producer>pdfTeX-1.40.17; modified using iText® 5.5.9 ©2000-2015 iText Group NV (AGPL-version)</pdf:Producer> <pdf:Trapped>False</pdf:Trapped> <dc:format>application/pdf</dc:format> <xmpMM:DocumentID>uuid:c1b0e4b5-d14d-9342-9145-22c99ffba43d</xmpMM:DocumentID> <xmpMM:InstanceID>uuid:ecdfa650-f2c5-7b42-bb3d-654b62e4dfd4</xmpMM:InstanceID> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="w"?> endstream endobj 218 0 obj <</Filter/FlateDecode/Length 5836/Subtype/Type1C>>stream HdiTYǫX]U]v_hApAt$ h {Pр " qAҶ{.Äsz?y{a8 (')>{&ad F`އ%a r>˩A%Db(֍0a; ={(B#cm8Z(#bmjufy:Am٢6H /St88SNd\0  stư$6fH0W;lyaw?lͶ5aauؿx^k`gkI%wW A)KJG=Ї|Z}xI8Hh70_JJH=hՠ΃9*O;s.ٽ!+0_1C,&EOK 4Y]Ȑi*g"Y-?6}{ksv~=t6(n=?XnG>)ȺS)I~PG̎JvխޚPwZ:TouKYpAeE[-2VON0lS4]Y*{Au6@JU99lSVɔlO ׆ l[mu:~`4QcQO;~2\#HVr[(x+\AgQ}jȡq5XƵ.� Y #_HY=").._` A#HMŃllZhTH=E.ϕ3 ~bRyC1#h7mwIX%5ue8ꑈZ$%!ɩJ#; ^>0{!|x!<ѣtǰU=kU̚Y)"KIU⫫NH! )slWQ=b1;KP{4?IMb 8UmOݏ-B6ےq?,em<V#x)~᡼Fgka-׹ޡШ(ADR۵;8Ayݲע1O n4C s>h o Zl}' ,f`=y(;+wP"53l!%pؽ˴{#;#Թd T +̍O=cqƔ(ecg-.CwS~hӯ!DTX0_FؼUD,;bFXct;{< YQqJo&=`0F,s9"-+=gySZ%¡ғpҴ~'.~*\Fܩ=\p 6&n~ml.wyvojEy;tWq)bKo-tuw5/uGCW^^׷=q|[#Lo m̶[ߩ \( ig-m?J,4ӟ`XpAEvC|0$r41+`ewnM壵!ɡ|пr؁z b{+-ܺ سme7œhKor46%WzPL[ 4eˍ4Qikwzȣza?FfBm?,2GđqۓRTÞph_FUB\'\Z_9 7<&$&<.ӸrޫKj6 aakp1?m>ǟ /{Ѷ/bOEpY';xZП+/ 7TxǾ<]*4Celz/u!+SdNȳL k EgGImNwJ WcK,F|s-0Mh#7MvuL&e`!ӃwdmFM(ҜSGq0H8Ff(L2T'ۻ4ZЋՔTn*km$phLuouSG\ bj^` 0z\do{.ss) E;0(/O Ssb6[*|rV1k+pCvEq0Mqv6muc-n\bf5I-HPa8d8k.s@De5qkm#[5co?}O+Ou)-RDt@ʠUJ@T5Hh>~` ^d 2[&+9ѿ`b�!]7nW1R&͟N-.m̵\ &pq0" wD"rڞpTl @*)Bf%X97z@wuTzڱPnL*ImswBw}R6ߠcU[wR*U~)Ԕլ&9R2z=*(wxeJrעE.J#|Yq, AJ#oneC׬;ә| |n7̹̿#'Gc u<Y, ">P21WHZԾhJS@jԂ޲O)aaTlMily2ONy;d,h5 V+IkQmԦMῢ9m ɰU9.FЗV  XDQ/V/B؇W8ʳk|?i0{:udDywyT`!Lމii҄`:@CW2r)q'CMűms]<Į�8ŭVzv<^vLE R:'4i??9DD>õ ˑ (SUZƸJ8�77'+4&XtiivrCpntO+(MQ\Zv8;qVZQ2bX/.DAx}23,#&^DO(xXRHa۲k@TGN<CVd,Cu+:T}>tMHTƄP57'ɭH> ]˜4I'N7Id\ZK9򡜁A ;`k8uah!E|uz.P"fiHUsBn}Ep>CݠiG iR%vYR�[ ӒULNWHs/Nx|\wHF!Gt чqǘVٝ7'+8Ɨ3︷fߕSLPm z5WmX#M Qp\̐(7?W5˗5p&n/%_)E1wSF-o(7QM֪b:_X XoMZCAܘsymKHSHƋӨ2)!)<Lnl=L{hdCOv,, -"Cš=ema`)7ΡzH_t?b9Im{̬a+ĥ %+$cz]*-T0dd5pB+ 5eXRjb9ܣ@[U?7 }VUD0šue*-lXӏ[8wwo^6G++*K]C[,ŽfƑҞ;lF[A_bkUT[wٶL�kwg陪G(Yt4#T4d{},;yba^Q&e ɽìMvNM/Btd>v-²]d$8VJv%><r` m#`O|x&zj16{n v.~C[u^!j¯/Խ]@.\htqrunmF7fDza?LY .gL[39%uӎj=.D+1q+KN: "~Y ʒjFb/bh-Q*ۛްdb6횟jL mt^oo}~62\n@.ȷED'{;.&ʁi)LKt3ڎRDZ:,Ry8Mٹ~9 1xޕϚzdo +$ZޣbE$@*ڤxY[O۹`ǻSGb7qzilwn҄i@3QAbc[X_liWZeUm%ɖM8C&4Ief&tinGW|w~眦n[ikų_8sb`CP*fu\>5U-hJ *a> 3pWp7́ʽT ?0`p8T&ESuܛ^X.& Gz_OhH <~r\3B{Oҹ4_%2 6K!s_:VLU6#칪̥@laizk655mMGNn"U$|;ٽ/+S8zc@Ћ1o 4b@;Tƒ7maя Pwi4}x&7IKE BiR yd.l:Ao1,˜xbx �]>w-~ZfD8w25ߑIݾ q_ +D\,aB;*bU6YKU( x#QŏٓV7cM Ő9|Z, Rav/@E+='ǂ92шxD< b"O^o{FjPqExPe3%r}Ff'4<ajiL{mR]UGHUTw2EƉ+%_BhÑa:4 9X CLt{2Zd8^<̃F1 p�<P^=ax0Xq@d,Z¾tIkOA"eP运O==I4x=o>3yQGk֌n߸-:ajQ8vP!|">O8qG|:1kOZH]SwWk3չW^Eeq깙b8 l>4 ^SMP3~=&&Sٍ}]^Df9(f6j .CLկU;ȎpRO>Am|1=Yz9Y7q%C(9fqJypo HiPh#'D:K;H# st_ĔeܜIe7Oÿ $H7DP&_nɾyDo<ihS)ï$/JcBH\cOZя>4T8^Zi7QT˚=+A'CA ?%pwPvjԞ:at79$A %䅙SW.L(S,Z 3 |ZuRmnࡂ<!NI'|y�6Qqɲ01h q@冿;?JQX+Wsq΂qDd$QqnA�`+&&>x6 ] .hj,}|!>AMu~tX}:o2}z}*۳ps*凴�e#ITꓰ6)ٶLpͱO'߃O� endstream endobj 219 0 obj <</Filter/FlateDecode/Length 5670/Subtype/Type1C>>stream HdyPIhqt@e<]A<mmnAE9UD.GDE]/D\utuu _&M/1K >ajeLo:0b/rx0܈QHX1 4)rйj Qa!17M>}o;ݔa!)E2\U+#b\fmh<p厡kÕ*0V':M>~yr0<0+aVط6f\ cH<a[0Ѭ +x.H%Rx*2k+6}p9rROi(nT¨'F:Mc$YcTc([.gIxۀLNR?ud6m^-bn_&~U? Kƺ\hѷv}d='k{`o@Rܙ޶Uu-51~"m:4guq }|Ŷd'Z^q *Olvge֙kW gCcxYRW/ 8+ەo*M~@YO`bh^!9dպWuDwqM5Bץza$ v0}gF2ϛH;8(mɀ */gf.y3# Ta᜜tҜC\AJ h=|_L-؟tSҬ>Q\;89͟C$,�7図}ُ'l̫DcP{mh9"RDjWm瑲Gvf3J޴BwY嬣Rm|`:1?Bb?jlZnIcBE-N뉺';g*U!Zc\n6+<C!QQZѯY`{C\XnވL$5E %{?|" !RCxX%>f`3Ys ?[OSwqHI"f,8ݐ|qTv% JU̡8.l~𼧺\qѵ^+Ѱ Zj0-o D6-a[FآuD,;zn/),{(E\S` W(e."kdc oavGX(8u{qq;f$!tvѢKIe qPӷRMRMg{/hIkf^Eve_fs _~h0Ͽv40xzc7j9]!-lWsMo%;3+kO&-&҈Er%ˢKe}`4p,8+Ġs I{v\IE>9"͈ =i;hMpr \rJ(/=q{EZ H{es87m,cSͱ$1MII{l)⋌Gr"lNj# lTb} d8�F΋6fKbčLqdܮx.MJl圝}k;C7q-O G N�=HXX`7UŚ2ڬLW'\+I$ 8㚒x7/ A3+u/߳}.wV P%)eL[?aN.h?0 r2۞r 4`9O-,d4$|DNKn vg=\B̢'; !3wdMV͂(ҘWGqLo_* 3a"1h$euT";;r7藙%:J**k%*F߼N$]84y;DQ)C2jQH 0zR_hnC? (/1W$MtqZ^L�@j]-v|wm]rV8)ўXKWBĒ] 8.5);?A(V`{L^ҹNX~,i@Aj jLÍ|uKwf8縮tV+wmgAܑK b�r!! @ȍ $[GX" -/U٩cw;a??vƿy{}-)3}^8i ac6/ꔀj/iZI5E9 It( 0Z80@"ֳLE TwmGz<ZT{0Ǖ_y}, =~ܢcy"cmpl~PIbh߫vK.?mvľ3BQ# ) DW*LAϐV0la-@+ȡoP,M'ȈBjzI>zOg+EH^q<tgcʗ3 5(l+Za\ *.jPl]Q! .ejd�~ykM,#<M\5QVf| )%oWԥ_p. oĦI$�AHJda9=wBG:%vM"ç~{up=LqUVV{x D?Ӳyyc(<XSg=q-o1bdBe|em[E֮ᅅ]ec#,okԵ&y=sVt7⨑'hdHcRFW9 _Wm 6XQYG+( "na)UM koXY]6jx ) ҂kLT+w<njPde"LVb4Å[xxlJ-HI+v {# xye* WF)?n<''DdF4%9}#;i㩭i"C/CX"wep[xpb͍e%!yf{/F"9rTg<$<im]Z/[.T3ۮWXm7N�kU+;3RdAA_^8Ÿ=>4-BᡉY9 rOJ O9aY}qαRUNtu"7sJͪP A 97p{T73ݭK&UVP&(7,77Rf0MT2_Kg[wzԋ6ҿ45[jpI[/ G{yp n^ƭ=6S;1j\ULg(&J0J*J'OLs>>3҂ݕD{%B%`l'DB>@G?, Ko;Oznw\ 9&-Te*݊16U#7%OH][֯C+h; 9x])r<!a4vȝ;?ągE3j:su ^pt@IN'21AL'i3<>Cц!K bfi Lvq3B!]8A>0piP["$iF_Q q}[~ 8x(=NLD/@~'>,$`9ߡz O?s sVqY66I1lk\8=Md;~{o9?&\Nunhq=G>Zƹ @'-8ḳ$5V2a cMS(t?Hafj>C1ԷDVbi߂ |兄(s psapW|( ?qEDJKgpUwO%6G idBjξ w@t~1LEp787[I]0uI-MQ6mqp'}v *'1MҤCN>D7x*2BLP1x1At~doC;6,\pЙp`'˫kdk1 w]$%RTXE9$&EdǢ/ ]ص=pN5`^jm .=ƢTݽr14*xH=9{{}wZ=h;V6l81NEhG"$sbr<};ynXK:#O00:C&̋ܜ2MnFW g<ޑ_NN8QѺ`e5YTW7E9j0+~~Zh}^ 9}ԠO[ Zxl1A@@M~UoM$ց&& Y=ΐR~^f F'v䐋 Ph治n{w[h,lUcΡa+Io^vE(sӎ#MFhʕ7 yJQY|z@F>fr:gd[@wݚ~&/\̪٬iȜcsrw P}ڽ �l@ &(py=04ma E[Pd#$é$5=ϠZPd2 �]K`\V;ͅ\g}Rq|`vwJؼ 䅬_ps:/ag r"W\'n68F“ө`lΠidnT o$w.[٨ٱGđy|BܐJނ[Н/OшʠOV2:<}y-m?<ƨf׮>uMD; gP}Ur:q{na&VkF&. 0:ȏSpm�ǚ⪣UO*mӕhMc77(bSL0E_l&Ԧ .oJ_5XUQveKOe.PnK $h=x.oxݡn7RGvp+$\veX֏x$DRAjdyGf$s$Txh/` QB$jhj\\1񥘮_vnqXmA%1e]=xfꭢC {K HK#YuXU^ۆZrHt,%Etm%sA}-QCq̉pbΈ,4�Xn m6Zfd^m)`0[Vrws/O28u6 >b_14 b.,[DDl[Z>�ۃqZ}Qe_jLl%z[Sߟ:|@(fbcsaLoSZ2%2NBґ>D�J,a[(@FτAE ^ "n$ղ$۪(fNQg<n 0rt۩ X%1ޜh ѾZ �;U endstream endobj 220 0 obj <</ArtBox[0.0 0.0 612.0 792.0]/BleedBox[0.0 0.0 612.0 792.0]/Contents 221 0 R/CropBox[0.0 0.0 612.0 792.0]/MediaBox[0.0 0.0 612.0 792.0]/Parent 41 0 R/Resources<</ColorSpace<</CS0 245 0 R>>/ExtGState<</GS0 244 0 R/GS1 243 0 R>>/Font<</T1_0 213 0 R/T1_1 214 0 R/T1_2 215 0 R/T1_3 216 0 R>>/ProcSet[/PDF/Text]/XObject<</Fm0 222 0 R/Fm1 223 0 R>>>>/Rotate 0/TrimBox[0.0 0.0 612.0 792.0]/Type/Page>> endobj 221 0 obj <</Filter/FlateDecode/Length 827>>stream HTNA}Ǫ鞁'Q0j4>H!rvWvsz. S]}ԩE6z=.*Jj}.loK'+S?g_gƚ(sU6+7 ei VVk#;f,к6k}_ɦ'ӥF6E+3ryœhuh#c[6ˢ,Arbn~\_Temrt{xfe5ռ󥺢\mD1T`DI A@=y`o$kZA<%*j{Ev`גEѶ7Uhz 億Z9Xr +\Rà$MO}1g`u.wW9á@K(Fzp)S"LbڡhQjE6۬ ݙKZ6rz:Ka-P9p9ngh/CdUw<>tM> I`dʮ-\0 t_ n96|xakY+&59�*D]G9 [ Qy--r3y �%u3e*O+g{Zq`p <KdTfN�J0g1k0u?&B8XG8#\-Wpm>·HRΊB`^G 9q~nz C5S˂pz%,hzR舻1딀oj%j$QBap|ZO5js'œ#|.(IaelI`5!xjw:͏VQZN`�y_ endstream endobj 222 0 obj <</BBox[0.0 792.0 612.0 0.0]/Filter/FlateDecode/Length 127/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 245 0 R>>/ExtGState<</GS0 244 0 R>>/ProcSet[/PDF/ImageC]/XObject<</Im0 247 0 R/Im1 248 0 R>>>>/Subtype/Form>>stream H @ yynn^ ⩊g3$31GzG6A1Ό.xܟzs0ZF׌V <xX'-R'61پ`0�O'j endstream endobj 223 0 obj <</BBox[0.0 792.0 612.0 0.0]/Filter/FlateDecode/Length 12291/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 245 0 R/CS1 231 0 R/CS2 231 0 R>>/ExtGState<</GS0 243 0 R/GS1 244 0 R>>/Font<</T1_0 211 0 R/T1_1 212 0 R>>/ProcSet[/PDF/Text]/Properties<</MC0 235 0 R>>/Shading<</Sh0 224 0 R/Sh1 225 0 R>>>>/Subtype/Form>>stream HW]o%}bg$lWcXFkH"se؋sjxcdY5]]No>8<{*L)oz88<ɦׇ0]0} & xd]+4jRk.~<|=_,i淋'%bP^Zv]~OK_|C:^jכpRbG/{bu8(+liCowXScXChLq-æ) N["^ml,1mJ3_l1Ö/c㳅KJcln<Ǝn 'L7 5Z>=X2WyZ.~3M^؂xD~_"KΕ؝Mw Als/NJi=a|{G|g2S -vfF`Mn_\6@I 1%6XiKc ovY̓m`/(�ݒ7,].1"JS\AԖB$Ê #|r ~͏08zT`sQn4b F']:]j G>FƈӏHP{k^@ � (у=(!<a/8B< `òc' :bŚ<Bls4iI Y3�Rgt 3D< zZ+`&7ˑ ivo pbuqO(w; :dHAn4y#PB^U$( a)BF!)|?jxC8C!1qHC) I$xAN$`['9s@"a߁m/{%dRPrPJv w7ιc"Rich6%xmazHl Eʌ<lC w(zU)ULؗJglpʔFW <]_aeXL(t}礆mlʘ!T3Jt nиoa>!#SD =5扄Nu�.HE2ۥrgszZ:ƨZV-,9MeMj='hPOش*A(!JEQmmGR4bNZ_i")qtR98kCh<m P^È2!�lN i<$1oWe/{�0}w?ot5=A9*yp479R|8}}U�_͡P5*k3!)( -cMT$D}CDWƐbu_0BɖX8W+6Mg#j.bYbsA)f5a|F)@HH/uk\#AAz l@+ze&8ȳtbg##ԣcDrվ=iJπ-kh"4,Ckf1"2IzY\UKF5:QKbA:P`tjf#P9g'0ɦYA %+T(-Ƿe%RX2!X$8R*Ȧ$ 5H)]A+BLm-b  \ 7T <#Q-UG_a2HZhFr3ET5WdpFw'Te|č\(w eH ɽQ@ NFߐ3`ؙZ\LxQݬm(rG4&[iDMpBicf$k<wJT/;2Q,JtJ;#ṭ=,Dܠ~SX62D`m[]kN`V�ڷrMk͋Ț:jˮoےĒN^/c$�tHu*g U7\lmҵQ܃<sdX4Ԛ2Sss*Ãr Qe[ȥṣۀ\7s+ 7*%n.WOm>ѧ6FSGc�B�kHy$)vGT)w۰} 7!mC-:S9OV_R%,݅`E%#iA2~0h§kjl!P2#%1ƨ$R9n}?^*ÁXF]ˆ4cȒP%iKE%"ֵ4�$d7ۏoF[Úrj ߈?ʭgπټ桢cKJŝ=wAaUnuX(N8аiME,0�C"6q]L1Aia EToHHϙ3fRRr` df*pkqZțvǪ^tCtҐC[21sPݥIĨz'Wg(RL)#()]u%a7]c/Ւ#ٍ>E^ E01Sr701 .SO`0H+CB̙$ٖ4 0*1HB6ȕ%kBQ< D,6ᵹ~7]Z7 jde�Iϔ]g?|b_ `/,O n4iKىHQԦcZk?~ #,w_X>~KQS9IBf@OyJ0lK>U& ĠI-Փ{sU}Umbm0t{)7 YmJP[VC*C sߖ?;үlUhLIyϿ<Gұ}~'Zhf)OδxfS<ZH|+ Hӱdnu.豵W0!Dηo(Y+izޒ:CW=~/ ;p^N [ku]^\9˕վ\-lvo=u^>|jz�rcqwqڋzhyp<=Zjzd9E+9ds4؋1ԍpp=瘓HXgȻK3EW܅JlۋL)l+�I3EF%Jud7CJ^kBFb7{9fg�BAQ-1ئX5Ι,FRЀNLXԳ&+Yo5qfQ]fl‣w$*n~XcP;,E+o ]JcQoqU4w/+Ϥ!~4Iz2Skݳv¸ 3qU%>t$$Ǩ5byez 2Fi&S9xBU+F͔ch1SVlVI|_ w*J?/+{y@̠Bz' >}2a ]K{F0E'jʨQ42 aYe rTAA{BޤF@U7G1`3ȘL(fe_!EpZobm~l>`!>nrSન* .$ f[Ƌ95f3sdJv&㥪] B]W̊0E-e{ { r5}@%ֈ_j\E(NN�zi0pg BN:/EL/<G^{׼nd V2h+DžlKXOqؐ!�)tMkilJSM1^jsKE܊ Z>>?JJ c_@觞9儝rbOrbr}YNߕv TNCOCO(;D(c3WYS$8BaJt5oI/1YgATTS !>9g�_.h dOtGb: 5c,Q@>Kd�(gQn.#fаu.N-fb lUyXF`jYhSG[&,k5"MT# iMIIT47̹55:"z; U3J) $;N_u"JIdG 蔠TI'EWQ,# P3KEC4>=XhVy.chJ^N�\ԝϫHe3$p5|D֨|uTCb|I=eM˶'&;fe5ZC J OkVjh8oM:vq=7к k'6ڄ]XDg34&#SX[F"]¬FK{Qgΐy AQ£GZ _Z/u_oTZH꠬ؼ4X/AfU&26)a? xSu}} P=6 64K@TI|H )/x195;*4AYD HP�)*(e+|s8U*\2930 ה2DLaӣzmrAiguq$'(lؗףY/=jTɁ-gK6[2Z1EL^H]HM_3.SCI *K)l4qۢ* J;`&'U't$I#DS:g⹾W%yYy+¬\ RP<tX. U.nm$58ɃSoEtvYzEjǯU2.ƥd7} K 9$+6ѻAP*q+XT kwc@* KNԌh#tUӋ2iRtb=z^J)M~7wBۙgb {[Ml6却)#m_KS|G ;L #a3M)Cشa3afmeS^ͤ)l2҆e['J*m6f#ﭶ)wmW:͔!H8n[0sJOM_%yNy4WySJ7+fqrGs{ZU7QZYz*5J0`~SE}pT $P4Z׉KL}@RAyr6#y&eU h=<tBlk{jwj Ix퍔1qW3w@S ?McPSB !@E۰֤RR~vz8ݮnny,%~#؉~#I@}^1H3 >hFhi/,ddvUR)݁JFGvS͜2h:dRY%rH/)|.-D481L/4LuQLڟcY_Ŵx J7X5c`ؼWr B-\h}QOD[cSiV؂MJp|z2H)w9uRLn1ϗEγg_uht+`72}Ws=J|Hs(swH JJ>s::FNWX vβH*/p(,cNB`]GOJTh$\9Qn6/*K0%R5"ir;)cX~2<N؜.M<LP%ܫ&ĜNa |hI<JN뼸T5n�) Q0;gE:sM׌8^ř`Vm Õ�{o*u]dEu5h`BHϚJNfpvv~f0Z}cznN͚y^|"b\ruwȤ`bnv%ؖA(FzmO·vp!١Z+eyeu�kwH@y+9WN Y*Q XZ ; ] (gˆBs/WPĕ.R|4ߛmeU {R? jOjWgC OƖRd0 I�]3d_tQg)ϨڙYUARC-w.q7 b̫ rt?和[gv]aREO[zɅLO1 :{7po߼r,F+Z<gSAɕQ{O6z<r.+73}&F"B ~kzp6 1hH!b(ӟ!UA1=bɼ&rNzeߏ8P&uci:{HrZ�PWqb82TbK!}cDӤz2x3-+Ch㙬/!@#lU6gVCG~9D7Oq Ea`E%g�[w; [6"U)i9q֘e>a2{ڞ2TRo˃֮[R}rZ)_\x9T5kZ >fL;͘]蚓&wB"&=믐~~:dž׼m=g`T1E>vwg}jB]TT[ĭq7?foCLRM}uiuͣ*!u8yHUx W@FQ0T")yPϏ]XEgpW734 "c?ޢ4[X6z-{8v ] I9ejp&"q [J5Ymhrp#~zkMt܃ukf{~j a!^p!+ ;%ƹb1)*w KK*i/lE׮~Ẉ+48C+&G6^!It^ 6u3鹒T4v4Hvs=oҗnո- 72*ڢ 5 _Po<74M䤱Iޥ[kC Uz[ u1@ @,0 f6X3Aq0oLHoULAFeme]AĠ?ʞK p/~Sny=уdm5 n$qʮjdC rl8Z+8Z9 @9s9$[Ԯ@$bv_2=8j9_sB"$y�Nsi ⤆}tu^A|]?OQ3 Zd4KV;cxu$ؚ8ۨR+<BjEQpHy/9ApmSWg*QҴR>vj? ތv)9bq-3Fd:m ۬bc!Ņ!kJ(wL{-e +(7yCп/UEM;ԉi:@]^j~qj<:'4" RP7NÑTy s8/3!Ȍ"ԫbU %X 8ٯz �.Q.Zce;(4Uyt]pAz`&j.֯1M)_$=DU#&R n"w,sp'$EScDh0Xyb)Ckr:*Vuu#W<Vq* h]'ϣd15cT&K̤$]٘G_Ck^W^[ָ)qʥNS5$k}f.MN=StZ0E|ΦФԞs8 7|ȚrPdS]ĚCGѾoG ܵ3=ߠO!c\LL9(MBE*zT ʽER8u(jxyFa!v1.҇菴rksHsFht\% StrUaRV#g [d,,M8=.`ı2/5|_C5߲{  ǾD6>=N\Gy v䲏�lt=/M,FTp9<Z\4%Sj&;M`'aNwpH+K-,Fd}<ΎYF  -25E&TF 4muImSYz; N}!4,? l!lǔNgE9I5?%G{BN<�-J!73g$j'Q3`zH2"sㆃ/d婇nnQۈlFqw_& =T4T2/hW]% ר]BSU8EԔy^ ># j:dL \,*VVeI/:fe565N%n2 qaeAP X-bi$ĝ3T8BFDxxDn&Fr?!yrlE5+G5S J?)SF*Ff]1%H [Z)QA^!G(|⣬94$oO1?> <8UoƗ3~;Ow/8I$?o$/|ayrr??N'ǷǗ~r|s|r/'Ƿۍx;8~|Ǘ8 ?7]?�sOF?#|C {HGYn_K.lNLyهF߫n*Vo{O )hE xkR $N|s5,}i8T7j'qs i=$Eb)(F2KECɋkǞmu9d F/VX:b`үu>:6Qs 2fe*Ar6*Nd2n ]W3g�I@�Ƒt}Zʊ({tC7z^[e˥yϔ*nG0ҸD{'K8^N'<Qw)h'N?'5' q¾'R(PW|/@1,PE>xb<1o<1(_Oy:O;O'GX8گKyʳ_<Liy,[yW U�Qq!w'=Yj&oEj(k%䧬&5PFQ 3ϏfmXOUz!8'c2վsmHa( ޙ>6VSUkvFd8X[84ΌūHLvxKXaX SimBNk?=pvOnXq(D<;aECJ VNJNDw-r3 ] n-ř,`zASK@=P(z0~KVrw,آP=Wxkiu%*+ԊWl$3z?iChk@_q* Cq4XrV7"VVq?Lrrc%kCȕ|8{q@(]Uc u�JwHODn8)0I{ΓJ %GD1G/Fep73>CEmkX7$3*qJՋHjC16RǒXiUYsО6wYL][̫[#ܩ?s`?ڈ{21q˩V-֡5qU$V�^7#q:PZ=2*yةv'>iQgb`hphX xxˤ[f=+~0Nt( wͷ&拃 e@|yL5)l.AFP)*( = 3~e0eofjjH ]tԾU:1PbC.&dۅtdY}qtlM"F?f;Z/QT жhCր8)IvrwDUQK4H%2Qus%{ؼb*wNՖ_7tL-զ2.zD yIW=H`ۙVO޵OwD8e w?º8eO'>%2%Q/9GЛb7CqB|NgeD>~\{s<Ux 3"3_lJx3SAȳ *b&~W! ElqO:-YJQQF ^ֶ˥\^8⻸|w 7}+8F[a|zuR̭<_K`ngUZ;VceWmsj>j_5G_jƪ\cj|yg5jVgU'hnwtV9A4:x#Mʝ d= s ml<U ļ샹&,yY9*Ptu "ڤfjmx%Z6z j6*M@rYTuz:H,U`q{jtW̉QB!jCPaC _p7Qge|v{ }f1Ь?\f Ŷ}+sù_{nƙ@ L#:7Vmn3^󸭾7/R<,z9, glLMq"O,g8{-3`BӰ 9Ary'yt;T,n8.htmB(~8G5:t*|75<#(r\;{8Yqk3oZ5e"q 0mu5pq%3D[.UQ×m  a.tbE Ѵm lČx[LE5)K3Z ™gZLVbc>]h!J=p9#3EFB=1&YRŸY#ϳ)57z~xob%10anWŅ9Xd0vɠa͂Wmogڕa`_1I &IE{Х& J..<.,HfϦc GWrKb ,5qrlrXr4J\+ '=~;affw]Qp$kSU'nlv$G`&;4E ΄@ª*a7 ]V |l6AiВ<Rkr8αʚoPJ|dl:.+156!TT ٳIc M-"$1PlHγɓ]g`,y+}Rqvb$ēY"23b%N.N$QȳFa>sQ>G&*(X7ND9Ie e҈ySI'Q$ m>(|Ssxb8 b]J򮲓ASU0K8c S\~]"f�@ɌEuJT @ =63/kX^eԈGp6Xqa7_kuUcQψ#H<wL4rFQ6*I͊6w\ @:btMGoM\gϐ (DJ`s34" 7d4)hSp Zz~SO;@"I65 Z=#n [UR5+ɺ2(W=˝!*Z:ڑxlDz3>*1&枵Qck'Q ߊ8{6y;z~,h$6#= 3 ;uZ?GoTP Yn6mƈ6b̞=9gm>G#c~�P]$ nC ;>o̦�pu(x3]b=&FxS{wÂM5fiL(aOˁOeM "O#23`W>+?_W5%_8F,R϶*P aq 9mth08YsuY=NQJv }|0Xa頾am�i14gƱA۸.hذ`( ,L`q8ȡS,n4H51Ksė/$˅O� endstream endobj 224 0 obj <</ColorSpace 231 0 R/Coords[0.0 0.0 1.0 0.0]/Extend[true true]/Function 232 0 R/ShadingType 2>> endobj 225 0 obj <</ColorSpace 226 0 R/Coords[0.0 0.0 1.0 0.0]/Extend[true true]/Function 227 0 R/ShadingType 2>> endobj 226 0 obj [/ICCBased 230 0 R] endobj 227 0 obj <</Bounds[]/Domain[0.0 1.0]/Encode[0.0 1.0]/FunctionType 3/Functions 228 0 R>> endobj 228 0 obj [229 0 R] endobj 229 0 obj <</BitsPerSample 8/Decode[0.0 1.0 0.0 1.0 0.0 1.0]/Domain[0.0 1.0]/Encode[0.0 63.0]/Filter[/FlateDecode]/FunctionType 0/Length 206/Range[0.0 1.0 0.0 1.0 0.0 1.0]/Size[64]>>stream H��?\\[ZYYXWVVUTTSRRQPPOONMMLKKJI~I|HzHxGvGsFqFoEmDjDhCeBcB`A^A[@Y@V?T?Q>N>K=H<D<A;?;<;8:5:19,9(8#877 �>j endstream endobj 230 0 obj <</Filter[/FlateDecode]/Length 2574/N 3>>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽�'0 �֠Jb � �2y.-;!KZ ^i"L0- �@8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'K�t;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-�[�0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c.� ��R ߁-25 S>ӣVd`rn~Y&+`;A4 A9�=-tl`;~p Gp| [`L`< "A YA+Cb(R,�*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=G</z^^j^ ޡZQB0FX'+t<u-{__ߘ-G,}/Hh 8mW2p[AiAN#8$X?AKHI{!7<qWy(!46-aaaW @@`lYĎH,$((Yh7ъb<b*b<~L&Y&9%uMssNpJP%MI JlN<DHJIڐtCj'KwKgC%Nd |ꙪO=%mLuvx:HoL!ȨC&13#s$/Y=OsbsrnsO1v=ˏϟ\h٢#¼oZ<]TUt}`IÒsKV-Y,+>TB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O�[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-�u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km � endstream endobj 231 0 obj [/ICCBased 230 0 R] endobj 232 0 obj <</Bounds[]/Domain[0.0 1.0]/Encode[0.0 1.0]/FunctionType 3/Functions 233 0 R>> endobj 233 0 obj [234 0 R] endobj 234 0 obj <</BitsPerSample 8/Decode[0.0 1.0 0.0 1.0 0.0 1.0]/Domain[0.0 1.0]/Encode[0.0 63.0]/Filter[/FlateDecode]/FunctionType 0/Length 206/Range[0.0 1.0 0.0 1.0 0.0 1.0]/Size[64]>>stream H��?h j k m oprtuwyz|~  !""##$%%&&'(()**++,--.//011233445667 �6@ endstream endobj 235 0 obj <</Metadata 236 0 R>> endobj 236 0 obj <</Length 36847/Subtype/XML/Type/Metadata>>stream <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c137 79.159768, 2016/08/11-13:24:42 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#" xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/"> <dc:format>application/pdf</dc:format> <dc:title> <rdf:Alt> <rdf:li xml:lang="x-default">usenix_logo_full_color_tm</rdf:li> </rdf:Alt> </dc:title> <xmp:MetadataDate>2016-07-28T13:48:14-07:00</xmp:MetadataDate> <xmp:ModifyDate>2016-07-28T13:48:14-07:00</xmp:ModifyDate> <xmp:CreateDate>2016-07-28T13:48:14-07:00</xmp:CreateDate> <xmp:CreatorTool>Adobe Illustrator CC 2015.3 (Macintosh)</xmp:CreatorTool> <xmpMM:InstanceID>uuid:6be068a3-6f66-e14c-a412-1d9510750738</xmpMM:InstanceID> <xmpMM:DocumentID>xmp.did:047e04d2-fa69-4b26-9428-764c0547b43d</xmpMM:DocumentID> <xmpMM:OriginalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</xmpMM:OriginalDocumentID> <xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass> <xmpMM:DerivedFrom rdf:parseType="Resource"> <stRef:instanceID>uuid:2e734826-cf82-ce45-903b-dfd35b207876</stRef:instanceID> <stRef:documentID>xmp.did:5f37efaf-fdf2-4ef4-b6c7-a42aed802ebd</stRef:documentID> <stRef:originalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</stRef:originalDocumentID> <stRef:renditionClass>proof:pdf</stRef:renditionClass> </xmpMM:DerivedFrom> <xmpMM:History> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <stEvt:action>saved</stEvt:action> <stEvt:instanceID>xmp.iid:01801174072068118083C67C01A9FAB7</stEvt:instanceID> <stEvt:when>2011-10-17T00:16:06-04:00</stEvt:when> <stEvt:softwareAgent>Adobe Illustrator CS5.1</stEvt:softwareAgent> <stEvt:changed>/</stEvt:changed> </rdf:li> <rdf:li rdf:parseType="Resource"> <stEvt:action>saved</stEvt:action> <stEvt:instanceID>xmp.iid:047e04d2-fa69-4b26-9428-764c0547b43d</stEvt:instanceID> <stEvt:when>2016-07-28T13:48:12-07:00</stEvt:when> <stEvt:softwareAgent>Adobe Illustrator CC 2015.3 (Macintosh)</stEvt:softwareAgent> <stEvt:changed>/</stEvt:changed> </rdf:li> </rdf:Seq> </xmpMM:History> <illustrator:Type>Document</illustrator:Type> <illustrator:StartupProfile>Print</illustrator:StartupProfile> <xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint> <xmpTPg:HasVisibleTransparency>False</xmpTPg:HasVisibleTransparency> <xmpTPg:NPages>1</xmpTPg:NPages> <xmpTPg:MaxPageSize rdf:parseType="Resource"> <stDim:w>170.000000</stDim:w> <stDim:h>60.000000</stDim:h> <stDim:unit>Pixels</stDim:unit> </xmpTPg:MaxPageSize> <xmpTPg:PlateNames> <rdf:Seq> <rdf:li>Cyan</rdf:li> <rdf:li>Magenta</rdf:li> <rdf:li>Yellow</rdf:li> <rdf:li>Black</rdf:li> </rdf:Seq> </xmpTPg:PlateNames> <xmpTPg:SwatchGroups> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <xmpG:groupName>Default Swatch Group</xmpG:groupName> <xmpG:groupType>0</xmpG:groupType> <xmpG:Colorants> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>White</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>Black</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>100.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Red</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Yellow</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Green</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Cyan</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Blue</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Magenta</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=15 M=100 Y=90 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>15.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>90.000000</xmpG:yellow> <xmpG:black>10.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=90 Y=85 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>90.000000</xmpG:magenta> <xmpG:yellow>85.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=80 Y=95 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>80.000000</xmpG:magenta> <xmpG:yellow>95.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=50 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>50.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=35 Y=85 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>35.000000</xmpG:magenta> <xmpG:yellow>85.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=5 M=0 Y=90 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>5.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>90.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=20 M=0 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>20.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=50 M=0 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>50.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=75 M=0 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>75.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=85 M=10 Y=100 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>85.000000</xmpG:cyan> <xmpG:magenta>10.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>10.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=90 M=30 Y=95 K=30</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>90.000000</xmpG:cyan> <xmpG:magenta>30.000000</xmpG:magenta> <xmpG:yellow>95.000000</xmpG:yellow> <xmpG:black>30.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=75 M=0 Y=75 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>75.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>75.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=80 M=10 Y=45 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>80.000000</xmpG:cyan> <xmpG:magenta>10.000000</xmpG:magenta> <xmpG:yellow>45.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=70 M=15 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>70.000000</xmpG:cyan> <xmpG:magenta>15.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=85 M=50 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>85.000000</xmpG:cyan> <xmpG:magenta>50.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=100 M=95 Y=5 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>95.000000</xmpG:magenta> <xmpG:yellow>5.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=100 M=100 Y=25 K=25</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>25.000000</xmpG:yellow> <xmpG:black>25.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=75 M=100 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>75.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=50 M=100 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>50.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=35 M=100 Y=35 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>35.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>35.000000</xmpG:yellow> <xmpG:black>10.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=10 M=100 Y=50 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>10.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>50.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=95 Y=20 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>95.000000</xmpG:magenta> <xmpG:yellow>20.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=25 M=25 Y=40 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>25.000000</xmpG:cyan> <xmpG:magenta>25.000000</xmpG:magenta> <xmpG:yellow>40.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=40 M=45 Y=50 K=5</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>40.000000</xmpG:cyan> <xmpG:magenta>45.000000</xmpG:magenta> <xmpG:yellow>50.000000</xmpG:yellow> <xmpG:black>5.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=50 M=50 Y=60 K=25</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>50.000000</xmpG:cyan> <xmpG:magenta>50.000000</xmpG:magenta> <xmpG:yellow>60.000000</xmpG:yellow> <xmpG:black>25.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=55 M=60 Y=65 K=40</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>55.000000</xmpG:cyan> <xmpG:magenta>60.000000</xmpG:magenta> <xmpG:yellow>65.000000</xmpG:yellow> <xmpG:black>40.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=25 M=40 Y=65 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>25.000000</xmpG:cyan> <xmpG:magenta>40.000000</xmpG:magenta> <xmpG:yellow>65.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=30 M=50 Y=75 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>30.000000</xmpG:cyan> <xmpG:magenta>50.000000</xmpG:magenta> <xmpG:yellow>75.000000</xmpG:yellow> <xmpG:black>10.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=35 M=60 Y=80 K=25</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>35.000000</xmpG:cyan> <xmpG:magenta>60.000000</xmpG:magenta> <xmpG:yellow>80.000000</xmpG:yellow> <xmpG:black>25.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=40 M=65 Y=90 K=35</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>40.000000</xmpG:cyan> <xmpG:magenta>65.000000</xmpG:magenta> <xmpG:yellow>90.000000</xmpG:yellow> <xmpG:black>35.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=40 M=70 Y=100 K=50</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>40.000000</xmpG:cyan> <xmpG:magenta>70.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>50.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=50 M=70 Y=80 K=70</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>50.000000</xmpG:cyan> <xmpG:magenta>70.000000</xmpG:magenta> <xmpG:yellow>80.000000</xmpG:yellow> <xmpG:black>70.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>PANTONE 186 C 1</xmpG:swatchName> <xmpG:type>PROCESS</xmpG:type> <xmpG:tint>100.000000</xmpG:tint> <xmpG:mode>CMYK</xmpG:mode> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>81.000000</xmpG:yellow> <xmpG:black>4.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>PANTONE 186 C</xmpG:swatchName> <xmpG:type>SPOT</xmpG:type> <xmpG:tint>100.000000</xmpG:tint> <xmpG:mode>CMYK</xmpG:mode> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>81.000000</xmpG:yellow> <xmpG:black>4.000000</xmpG:black> </rdf:li> </rdf:Seq> </xmpG:Colorants> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:groupName>Grays</xmpG:groupName> <xmpG:groupType>1</xmpG:groupType> <xmpG:Colorants> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=100</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>100.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=90</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>89.999400</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=80</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>79.998800</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=70</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>69.999700</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=60</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>59.999100</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=50</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>50.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=40</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>39.999400</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=30</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>29.998800</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=20</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>19.999700</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>9.999100</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=5</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>4.998800</xmpG:black> </rdf:li> </rdf:Seq> </xmpG:Colorants> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:groupName>Brights</xmpG:groupName> <xmpG:groupType>1</xmpG:groupType> <xmpG:Colorants> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=100 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=75 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>75.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=10 Y=95 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>10.000000</xmpG:magenta> <xmpG:yellow>95.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=85 M=10 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>85.000000</xmpG:cyan> <xmpG:magenta>10.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=100 M=90 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>90.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=60 M=90 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>60.000000</xmpG:cyan> <xmpG:magenta>90.000000</xmpG:magenta> <xmpG:yellow>0.003100</xmpG:yellow> <xmpG:black>0.003100</xmpG:black> </rdf:li> </rdf:Seq> </xmpG:Colorants> </rdf:li> </rdf:Seq> </xmpTPg:SwatchGroups> <pdf:Producer>Adobe PDF library 15.00</pdf:Producer> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?> endstream endobj 237 0 obj <</Ascent 881/CapHeight 674/CharSet(/B/I/N/S/a/c/colon/e/eight/f/g/h/hyphen/i/l/n/nine/o/one/p/period/r/s/seven/six/slash/space/t/three/two/u/v/w/x)/Descent -250/Flags 32/FontBBox[-46 -250 1126 881]/FontFamily(Myriad Pro)/FontFile3 239 0 R/FontName/KGORFR+MyriadPro-Regular/FontStretch/Normal/FontWeight 400/ItalicAngle 0/StemV 88/Type/FontDescriptor/XHeight 484>> endobj 238 0 obj <</Filter/FlateDecode/Length 288>>stream H\j0EY6 CIRi jYߑ&P4Gat%&|0`8OWx1VhmV5NHwpl0A9tƕo^7_n:#�4 hK^A&ٺՔ7aYϹ5i]h4P?hZ/,;.,@|d>yb sd.#o7;]=:USyO'GO!62>܆G=ٓ$1&S 0�Q endstream endobj 239 0 obj <</Filter/FlateDecode/Length 2829/Subtype/Type1C>>stream H|TiTAMOb.#@@7dqAZF%n@v)A3*K *(( . qcIbsR?sN;w{޽&<lvn&O奉7g )OWVvwL9} 3sE+#"(eV\V(i{ Md/JV, SxkjZctX+^(*ZWZ*u`QQ?z&TEqUnV,VԻ1 &5d&ba6fa+0luS`ya*;&N!c$?ŢQGGxt#GHIM'2=o:f6fIX=.3Eb>ik"tDp$Rq"&q]-pH{BjDt odttyޏ嗴AARNǗ)F@Cx0J4'oJn:qօЌeYn@!Hij/ wi|_>)^{'{aXzwe#\T kU R/J֨^lF5ɥI /lCvh<O"B} fd楎!{IRMUH̬pR]'4uu?^rGD%rt/琸2[Sr<0`a 9>Ad27?v.}3q] wOy `zZΨ^\t{W@wNH(G&|U3'vz~izH%I%^o`:~69ѥ ͍!χHR#M6sw/_;4*g%,՟W|6{ Pq.[m*k{Yib8t@]^iD:;0ؽ]Ewk߀ޡih<!)?f7 a])qzS U8KHJ<V,=>13BL`.RTA`f czѿ)p'dAT)D=G k63`җ}++ɽ$iv!2-Wf3>n3r+ϡ>CCRo_ l lrb   %RI:o}'J9\Pa N<2̦KVT/2 B+BI*"<6)%+w` Ĝ Iibb/_+e=֖wǶ10ozDd5g]_tW^SNu܁![ήa3vn57\{|v?Xi-$RMVfzz,7dk&"rKa~՟Z O z Od&_]KWCiEԟO'#%ɗ TݡĤC'S]ɜtSb7+ ӄY7͏AT[rPewglf,#+df]:I,Za89,!N;?sN>CG4zf 72"!T[wIɜsbK2E@<tc7>h9�L;Y~v#3S{2;pukQnn $[H3:}.VV1j45ؤL/eIj,iL^qe 1S4Wpiߒ3m6l=F!Ԛ̼R\�|>LI`ob>z Ds*gc[{!45 57@5?i6jmq3 BbEGHءldBtT4Rt^@಻[?'4+| ]_X  "f 5kj 9+8|dBv)le#OT$hQܱC+W�Lȥ5v~R;^ j OGj i* ìqi17$d,щi۟n[SCV+f̘E^je: nߑO3s}9 C#~"+ %Sܘ?+oB0Ok0}f7XER e>sP2<C#/ϵ ^lBbcG̣2_WZj:ja[%f}* <{#4tG_sC_-ȔZ@}Ŏ�L/*&$kt -]NnYנjq_~;\#nE: sRAHjj/{|[Vz/zEqP5 7<e*==8\gOn2:VV ʮqZaB?\3+-EVz 7]Ds PjB%gDy+$z8<2�["lhxi7yJˈ+["ȉL(ʥvQpPհWul"z>>:##w- "iβ(<Q !(\4H 4X;DHGj|!E V`SζbQJ@ ,G3�ywP<48@r*VYў5 ayмUr)&$6mݦNGؚX7`߻{3`7ݴn`�Lb endstream endobj 240 0 obj <</Ascent 889/CapHeight 674/CharSet(/A/B/C/E/F/I/K/M/N/O/P/R/S/T/U/V/X/Z/a/b/bullet/c/comma/d/e/endash/f/five/g/h/i/k/l/m/n/o/one/p/parenleft/parenright/period/quoteright/r/s/seven/space/t/two/u/v/y/z/zero)/Descent -250/Flags 32/FontBBox[-71 -250 1198 889]/FontFamily(Myriad Pro Light)/FontFile3 242 0 R/FontName/KGORFR+MyriadPro-Semibold/FontStretch/Normal/FontWeight 700/ItalicAngle 0/StemV 124/Type/FontDescriptor/XHeight 487>> endobj 241 0 obj <</Filter/FlateDecode/Length 430>>stream H\j0~ CjJBiFqyj4lvG3ff۹Yw/sjJC7<kwj,ͻyv?YӨ-.pU7w, ?neYjR;DvznONvaƞTKޝsG5E<Vy*s5 YS(-$/k@~\Fo]KpEll#kk\YQ,`7 ~4Gr z3foo4Y^ u:BPG#,,++ԗ,,o+,,,,Y,Xc ?~,XeUQ&6+,40?щ~粻G2i1w4N*v̾�&S endstream endobj 242 0 obj <</Filter/FlateDecode/Length 3890/Subtype/Type1C>>stream H|yPYǻ Fv &p *"$ aft׺! Zx֊8.*0*هUkꪮw~?Al5K"WS5zݗj֔]Ʋ;̚sgBqw/]&u"H~.O9xyy]Ueekfu >>[`,6B,<l8 = fytMJzؔ,6kSzk[[X]oU\вFD$"+  b!!)AL",Mך y""(BmMD BKG"‰ q#r'yjQ*AuuEPNĢ d@B;V$%̲Ih65vv+!aC7ґt2]–tro:I)("0nχC # X&(1q]e-3zܕb PCA`ļ133!i!o2Gf^ά Òb%zy(u4L2Xv ?+>�G`[{R YXz/Ix /a yCbZ{ۀL4?cj]�>-�qstS{GZClܖԍ)mJ:`!(%:XgrѥO ɍ#ҸgSgw~j+7XAV̊ N�3+!Tv72DMHKq9>^&~a`.~]Bӵ1f?T㥺n`}Tx΢ %24X xhV0%O)Zck /IX>Nrzr &<N]۴h<7h8H(0"KEy[.7v Zz$ %9NU C-@ά7iTQ7a )s;a͟6MtwÐ"^9 Q0a*0e WܑV]<PqS6zh(s?ֶ^-ֵşRF/[@xYRʈ90c} ? m Pbܡs s3 q`LP-nsF`&Fkv6(-5o)qQ}Dy'`O8$XSҞ%&~!&l$B0|Rq&pnz%<v.Pi!!R|7O*p4eFC!u"m \.&^ wdoڑz0hm"oDyb'3 qދ7<z RB?ebng͜tB:1lDŞ'"50. Ha-?Aы=J5Bq'30»k}Ei?&>C|R!M(gl gDb(?3xW{< ] @ 6=e )_7?[ͯrB,>)rW֓y5QѼiqoyU]Aꍞ<OZ>闠%kgoi8}-UV=y8GN(&Ѷ{zGt(=qsY#^qvYХ+eGN[OlY Ao+08uDX 70Z~ʹ'a _o`vwKvS q{;J!r< ׀F9ִ5JFA>yOB5\!7xGCem|Kx@q4 㓅X~-*Bzw -�;ߌ J;- NBn%n OgzM)!H9ym8"%{ _� Z[-ѸB(x*u#LA�YmWEsy+qxr݆{X&JƸ1; p[#?Uu;;8<f,kﵴFqM%1j"Uf 3̈0 "(2U6(iϙ>Hν;~$G</{4cQBgn)L:\5;}]7_v98]o>C%]xH(|u̝Z(Ie'bʄDBU9(?woߕjXabWʽ0 \k�,Q{$e?žZ_i%GgBz$.NdOQΠ+fMf6uyJ›9XSR"i*X])ST_kC/Z I, !%4ڴ}:>5cIRn5Fܨ`[6HjՂ4]>Բellet!'<R]3K"ʔ(<,zv\?X讜X l\ D0J@,֫XO7DNTjey|o+66< ,=CbqbiVEo#ẻ)B#D`#"3RV FQS`;_(yH<LsEJD ZuR%dkjxӟ0)sAk35v԰iG.c݈mzAY>{1'LyD΄"Оjd* XxCqz ³vS 3`.Ҩ5沆 =s^,z�^(.KY2#NQCn&!J9c,\,6'jkb[4IՇX}P0vZ?A d _b Lh<S&g ;`nko@2uL{yw 5?6LWVddK7u׫YԈ׻eSw|];}&hu+nH`d陉X{BR<wy 'R 3)żR#|i664sLl ^oۚ7HB+Xqx:;\P`H.ߣeh ~m ~bӆ5z{a@^R1׍蘎_՛O`+|yWX5I =&/H].O !"jO Kͽ7^\C|=~ PEç@H;2ΡדT?n,fhkA4h2@@딹Z9-u;fm[h6ۥQ w観+B;tg<Z}Բ⺳g:;ZLq*>@#bϺb[iY1ؔԀ0_%⿊bk>E}!!+ N`+zrV=I5itpV-K_ QWv~vq. 0DԲ|%+tyr~Q Kz B!խbs!,pg಩D>,E  5)T6UyQG(t.ury', Fon\/+=b2cS'+5w+22_7w /gx7!2ND윹ՊfeZ(sU39֍_sA:\eZ[�Z MI>PNbɫu-oNⵀB \<8( ѲW|* oחRp5K,)z#?%Х _`d|uޖ9*_i턠xA?�� endstream endobj 243 0 obj <</AIS false/BM/Normal/CA 1.0/OP true/OPM 1/SA true/SMask/None/Type/ExtGState/ca 1.0/op true>> endobj 244 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 1.0/op false>> endobj 245 0 obj [/ICCBased 246 0 R] endobj 246 0 obj <</Alternate/DeviceRGB/Filter/FlateDecode/Length 2574/N 3>>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽�'0 �֠Jb � �2y.-;!KZ ^i"L0- �@8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'K�t;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-�[�0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c.� ��R ߁-25 S>ӣVd`rn~Y&+`;A4 A9�=-tl`;~p Gp| [`L`< "A YA+Cb(R,�*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=G</z^^j^ ޡZQB0FX'+t<u-{__ߘ-G,}/Hh 8mW2p[AiAN#8$X?AKHI{!7<qWy(!46-aaaW @@`lYĎH,$((Yh7ъb<b*b<~L&Y&9%uMssNpJP%MI JlN<DHJIڐtCj'KwKgC%Nd |ꙪO=%mLuvx:HoL!ȨC&13#s$/Y=OsbsrnsO1v=ˏϟ\h٢#¼oZ<]TUt}`IÒsKV-Y,+>TB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O�[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-�u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km � endstream endobj 247 0 obj <</BitsPerComponent 8/ColorSpace 249 0 R/Filter/DCTDecode/Height 363/Intent/RelativeColorimetric/Length 23095/Name/X/Subtype/Image/Type/XObject/Width 841>>stream �JFIF��Ik���Adobe�d������!!##!,##!.333.!>BBBB>DDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD��5�kI�"������������� ����������� � )Q�����!1AQa"2Rq3r#4BSbCcs $%&'()*56789:DEFGHIJTUVWXYZdefghijtuvwxyz��������!1AQ"aqBb #$%&'()*23456789:CDEFGHIJRSTUVWXYZcdefghijrstuvwxyz� ���?�HỬwZۺjT*֝�9ް ֏P�.h%}`S h%}`S> mXԨ8:K'5*}`MJ`N{ԹLZ npsAŌY>K%"T�}`JD }`OD8,XDmXD G-�tX�8u>HbRT'CB23GF_C(RLxg&q8x[y+o2W2Mq9>L| +6 =>B_l^+74w*8kJob:sNba< "]ʞ6*ocqGwV_csGwi8"$^xZiWR 2네8rF.NBFEHN!&8Z`]a ٙo&RC2@o]+e* $RuQCfhdlwZ1&P(6'ÚS /.TrBLn*w+8 C`LEk Flu)4Ȑ鈿sy/qtcIHB|$Hl\Hr@.Np@\z{ pd`a!SrD60[#Zx:K$ΜR F%)NU[ hvۀ#Gc6n2cYՈ[QqOy\$�\O8$Dv �bR ԉtjDJtԉ"HR ԉ5t%)N!�)5�ЧݻTSRW *BT*   P*D*T%AJ .!uʷ;;NqO"z'%x=!sJ$GM[jL1] r֭_<+sXh.:rܫdu-~Hڕ=#›W9.ׯr6 譭pzG.8=#›|F [[O( ]p:G7ke%Gv4ѧkb;i<KHnVk4a4L*}r]\!@<)~PIˏ̹ԧi9ABp= ^JΨd��`K]Nl$bz-;+.eY0i`p<:v2o<)ZM˸9XcԯJMVr\G 2K#ѮOϣΈZ!5kG;m\G=ʄ=⁔e]rjuNc#ʇG8QY𮢦W lD-Uk!eBxTT^l @$Ne֞eĸ7حRqa$'Er7'YEQ!hTDa,$/ :%t*Sb A:KhĘ ΤNhxLʻ咞3cq])o*w+Vꎳ2L_+!ܢ!2rZ6-c M6F>ecI fAHƃ#B#H !F$)zZp쌔е6)%:HAѨAѸ$)В QeF(N8 ),$],sHYIedt%h 9],kEh;'4ƴ5jcZMZȤ]v=i6=hIǭ&ŭvN9!tc։;(Xֻ!(iM] nLLѧݻ jvn)T8J.(HpBDRÀ Pp*7K\Tb4xӘ8nJTޔ9$²{ْSI^U}={֦e3g*V!}SiQ,0)LP�s7IWe!7E'�&sM Oh1(9E!F޺d8˓$5uLYWAʩ!H<LSƓ$. <.uҺP:T'j� Ix<,qR!rtl:`w q4Շ*NjӍJ#u nN WJ\ 6Zۄ)AWy2oNc&_b(Iϩ7leXZ%V`w1KW;bmXQfMZ-+hU.Db7)hKMDՠI".7"`vF pTbsڱ#sUӱ x\$`썳B{MB Fu͂j4LKSLw"yi*֩Z-@RPm6]6I;[6.9nؓ1#BJڏmx@l0lvYT.-ȸJa6HI i |$9+&vNe!vNFR] $- dzEд$sB|AѷO$ .J|@A%$ 쌒J}!'9)$BK!d'JI:WK!%s$+@INt[%A9IҒNt$ eI.Һne;JKNW[ Вt.GNQiWk Вt"N9Z:Q'JaYnHJ9cԴA\ gҨݻKS?wYHg(NH'4`&+ GJ pZ\)iyWDg"ԭa7xq 3c:RAv9" e.tA9�f3$ĺ ZR4OƺrE w #y2;zPrD\ӚR[ Ɨ NQyWU̅O<ii."I{ƻ:víY԰.;8b Jrw p̀һeTKL\dB+;w]K;!S^.N74HeAɰPӥ!cz*goNHkНeSN̝d]- ^?H uVu w |6� Rɬ-# kroE[$bX<9-ޔl:Gŧ]E'=#Ҵ`� 7yd"$񮐸[m0ğXxO`7"- eye{ Jʳe Nӭ hòy LرYsI&wȮ<K(6RM\u.3qP{\Zf)lzlI�MJKX8LPt28 ]|13p=Kk1ֶ|QNLlSs�I8pc}F5ΨMܑ� 2P>W8q̢l!bb8!(9m n4HpapTvI$ ֧?'.I+: dg6ӎ8�VN異Z3Zw LqTz3g9fT,T7BJhҐ%$0 'PHdղvRӗPM-nљD{\K[e٘(jM\5@W�%DZIi� i%"KA%E@[[vHВЃ*DƄƄPlhI rDݐhI $MlBĹ7.rIөݻ]L[tQ)&9Ű:WAM9 lm̚ˣBJgiR�lmA"Bt1+y92/ `( |8Hl54 iAudiL jX p9PBƠ$� dmPr: 1ڇ0!tJ.֥,ε0K\t/)`plq]Q �_{\$D_*C2RDz:Z1#r6[Y]KK9<cuˏ6 FVW7q@q76xK$fzDLeBfnֻU3m '*"ȓT\s@%avb8V>SN CcPJ,1NNeQqX5rT\,AP/&n c;Iw.o.3]؜W=6{/95o]Z ]"$>"B*فH즥1HIKh?}L mx&-Y͞Tpyx!vR[ Mm di&%[fv"f*.7.q|W:;KӅ4 $뼷H\6fu'\HimdI,` J!pNAϙYYnθUx/m쮻^uΥC (s)$s\ Ȧ4bG:)Qk)'BFdZ9 ɐ9\Zs$Yrv K$�<8b5s 8AMtug0WdlE.Dd ,[f'<LnĒ{ #�5 3Bi5)S2Y-kXk}7jCmՑp\;'SuBCq�@dhHu}EX`<j:rHM!t!4cT kRRQK&.u4˪`jHͲ5 uѝtI )Re$\IJ֥=fr�&M̆lS{PC V+1n4xɄ)uJ.UQD"2\݃ iO)pԉtjDFEX\}RsP:V[<YU2Q9+KKfIk&jJIzN=H 01I$ƤNHP"*D9")j�DCX(FHWW�3h̺vHɥJ-iw.! NF$)Rb!]:58ԁRử#RpnKh]F:&~#s$kΕÑy/`u=zE>P x.NtPr,:$v-!tЀ: -0~xE;u+{WAIOƛlfKL&t #C>h ՉJ* (O #"ciR'q*8[s)p7 )"w!٬WxFx.i10sntڨj EՓTA�74dm c5u /-5WV^Х Cq+u=h}p!m!)~/-ρt@65vӾzBČ̕ }O+i1זt-Yh "5Tw7-'Qhu«+�:z_@Cj9@z,9,㰾.y= yR \4[pIR:�Hϝ0DC%5s%TsmcxMy47:ڄ:ӄ:W&Ģsl#i&#X\c�e9լ88JT#TlrR 7rM`Q�8 ΰ3ȞA%kL� 缆=F #CmG�X v|6jz*E`'$$&\b5EG!<K.%.ҋybp[{[N-# _rV++˅˛iZ$JZ�+[(e4 H3p0n_Ĉs5D۔I4�.$so10Z %ƴ)ׁpHO!! $d$!t&!S:5Ш{n9Wp viȕ[%ʭG8Y"Hʎh6pv*6SxjTzsFeRJiO)DaM)4C DNH8fJR.8Aѩ ґ9"H�5"rBR  @QAaS|4.2SG҃p\I%u HRơ*,A]:!MN SPuՀۈ\L򥁡A !$nE3�3&]@9Zh=5 Q -%Fd%>k75D5ާ s\gN 88�3'@Б=si+4Z˫ZM)5כN0]Zڵ.h5\7NN^멶Տ:xm[mv2۱7N~rV|1'kwUo<خ6<J~&DƛO6$lt'vUfl̞P6Sv\Fi 07s4'$kǑ g:EEmD ]2Vm4peo:]yOU("lɿP'l]-6hKaA@5�^@L50u9 ҨG)Mٲs dĪ yZh>`B%sMһ' |6BT#B2j̺!�Cl`\X%~ ":VoӚd. ΂WQz8$A&Y:sJpK 9'їK`0\hr=kD�Ɯ@JӇl XrИ lDS\aTJQ f .{H]w8�, <u$�ZI`}ٴX;EwSs"Tk@�C"j'd.oYO-16o2"nE+IB ]OwDZ3"Da<&ORxr(7k#� )۱]+F: /r-斒q)hVp~Q"ދ.�߸~P= M+0 *f]h`ym9B S 6L_:�ڠ TOq89ŢCiڄ}sXXXYLI$Qܸ�6F hMf aɪO̟[сs�mwdm\IR.k￑*J3Ϟ(:qHW�=#!9m0#e3bgp#$]�r"}܅2T;'(19)#.5~֒96"XP�#rQ 6Df.vAr�Q)-9!]Z9 !gq8ɐ ux3:yn3vw=B0@ֻЋfs1EuhܿapNӚ )`xo}pmy8ڠ)i<Sk� boR4 [s'7"p%R%uQq֮sm'vN৒ O9BLݓg: ݍk64!4 5tu.w!s"D21/]-;BJx'8 Is"WCLy SH^pjvwSÞ#/+d.㤮^%uaqg.*U<Ww ]JHURZE)U M<)"46 T1kκ*J2-8(Ҽ GIR$4IBě9W;EHV+nv5 7-OCfw" FLLͧϪ]pk"qw:�&]#x3+byGp0cq!0 % GpymI)(D!8&tBpASBUӃWN #,pTJ��[nHZs& E'iNc\qO�r�&:WD! RŢWHNh$ ^*F*Y($m!r/BXKyBGj"�Hu�$3iĈ]^ךLkLnwA-Ab|_ y&dWR|f0 .[5SH54lˍU^syca|kR"R�!ܛHğDm 6�^4Д&괉Wey3R4-hmɕ%oִ C"ӸVk/28c"uiLtX'qTԈR \ bҪZA#-z`[>J+1ܜiUp� q]Qρ9̀\xWSD�r~2s@Er%q4uUaOq եͨ;A<cjq`BQEUDPI#ԕ ]64%6Ȟu-ȷ�8/9$g^ֳ|M9@P\;7�[u}xrHHĻʦ<$f|O!='" s$ɸs\[uwS3 qiq  %H�1I$ٳf5MA4 c;v��N$ 7L'1 BݔҼns;,7\@fcYRW <P.Lac�5~.�HMDUukH(NDo.JMMQQ:\ׂD8b17n(IA+icp;` PCH]>R'�b耒+;ĂƋPDV`@'0P^pnǹ7n܉KK%V$ة*lуyM0K4Ad(� )4nk4Bzf./]vmω]!)Z.4`8kV RR-n[2mf%h~ ֆϝ"c뱗Lv.Y5XM*# uQҹ7hJ$$8'МpPS<A'@t% E<y JfIE&5Ceo4hǿz rJo)p JVG܌y'sRO*^щ&xRG Hybȴs4%Xrܺ= J- fl: kH#mw"K9ln`T"u&耐 n8@�l:BPHqi8:A!4L) @&[!A\O,Λ3T RcI\7(]r @`tWA2+2C?ZFӲAŽ2vB:nqs]ND:, -9Z86hNF n]2tMӥA_i7tfOTZ&̸:[,9et$]09MR#�j<5Z10 ffqJ#imFMqxֹJNdmғWjѩJJZpϏ8=yRj݃*V`sfNftAE0e.K(j[<gDhKH ({[Qx4H[N˙u'1'.9YvO%٩[s*}i*u*O3y7Z3QԓdsNu7\[Ll% ^mgt*ULIz#I\R-0u%#Ԑ%ɂ[DZB< hI0!)YuՀe@͠5=RY0}KgAvOH QwrvODޏ evU3֣A=#rj^:<)6." C'ã|txTra$ ip w4)yh)yh vRPN. 6pŽ"IcIA::2Al009ҝ(;'sM0tl.fW;$ԴI[8a4.;S]JR:&*<^퍜05� %Z:2 @%q|txSNȐer]DN,gЇ�o&`_p�`) sG1Ik_y$mȼ4hs<ļZϊ�8rMZK%IAq¼IIJֹ1yy6 K@hr+4S)dio~y]MI"J `se*$a#ML:ȋܕ9l ǜ c?:-aZ'Pw])42Io)d лb"ۨ(cnu^@&aTe;:q04КhJppN =s4N 56Z$T2?tF! :47S^jBočTSe1e4$}f0;̹E)wPHV$)n;9hvI4^s晀¨m?swtl7^' qysNGDOsI{)n_Q,{6խ'?]Cd}jOB䝃0s^{f|c$cC[hKrY(hyM/1<?Nsx=iv\a+DezONiplx[Ewc wywGZrjGH'`{{]W GthYfkä'�bZ 抃KLb*& ~ڼh p!׃)Qlj Ps#@8Nc|>x# ̀` ͦ:kv1& J` Rpp2` pN�*fɃrLjɯ $ *mS�@" D\�p/iW=WEʘ3.@8o] & Qtz%9;q]HS׮J$*pV,76!?#`:W<x6W_/=^asYK% /Mp*jTt4^T깕Q*H{"O:l.<Y~;c`fdi&بQyniĶU&WC`@޺کkӯlPp)f$sA+<]ƞ+LUXΥs<E8fʥF\: {llHA^Ȳ Z&Yw{{yzȫ:y'N\08!''KۉkvGF=m&358."4J8$]ldRYI"f'’>OdtOIe?BYg%!k8_Y(;#"Z(.AOLqa a:>B dvĞ]Oй-ZKNt<iɬ,luT] ]jo=H+pWQ:lGѬݻsh$E8DLλ66hBLyD,ȫ5O /Bl0'vfsTށٔӹ tPj�O},/] L$Yuz tմfḾŻ=%.3rpĜ3h%Ø\\|cp~Tp`ev,4K k.`g2%rI*3=: &'Д  $uvGui䁣r 6I:ilRC9{7֬䬦H P&MFÁ"jy̭TEUWHYi4K"q-y-NY7ֽrڕ؛ae<kR[Ҧ٨A kÔֻ-Nɖ, r8s1G+5Jdc +rfH}RjgvA١cjvKN3o9l5۪8,7λvadJ>!:zFB!�B!�H P8,85`,s<aΤD +[wsvFiɝ>P:Lia#*U.N&v&w2PT�t.&ŗx e<]jN' 1n#RLJyaր5j0s($ )Z셆Kƕd =8ޚ i4]"8Q )Ȑ\ HNtkF9ӒJ%U3 L0TIb:5R@s.{.K͘e:/(IuyHST1gʍiea%qeF.LTƣ\W]VXF\G{nEOHQqm)dZ dMTL+IׄJ[h.7�$S+mC le,\)|gSw&ɛVi* 6,I&I.>תE.QҚwXu}}IPU[BZ2ί7n` a95nuUVٖSN)cmN̊l#\g2A\Y c4ʥ6$!w+pglht[Zu==RjҏUH*iD l4ԹU'Ը%Sν&qѳ<ZLNQ'VZLKA^6[4 6I2u@9*юhMkAoPwYnǴ!tL@!oAkt' D8@4fCNAٸact.V7vss wMsF$@Is'n9tv1pRoqxu8Sn䙫, fG ISRn'D3+=əwY'@\]=7r:WIRM!:(NMJppN B 4)tXI4wj;f+IҼJJϱI:O1QTfSxu"Hnףc~詎u cp9:�ĕm4cb٦julpӲT6f93&ʚYHfDrHc0yBӭ-`WۓS9ʜEx:oKkkLcjUjMx=)is%K1SIyqܚT0cE�\}ҧxÒkF8nN1)BvG `o|޺cCXZ0䫱\.!�B!�B!�B!�BέVxBqi|O(铹~]SN-T»9p-pqBsjdd)@o-Qvi({5kNCpwJm)taygYZȂ8Eg-H{nƄƽ7r޵7Kr|iK*5̺J-2.*M:T|S/,Kq] t)ִd"cΉ;[Hę:' L֤ã .rfWR,֤?RSMQ: *$/t݂h|]$q$hKE%Wg(ƠqV7d8xHjRvz.Hr.\!ĘNOUbX9dm%WAIRZI: ;q,X橇8g \ZgRȸȺ;;�t.2j-%r}BDD)siԸuAŵ DD*#`. ImJ/)V% -"PW2]wȜNPuȟQ%tۓv'8@ Zv +>&zѕ2ޥ"G9hȍj-g` ]ĸliH_tHgH2oQ@9pϺ"C.Λ]* �sD�8)¦9Nff\mg]0lT j)b i rycIĜPDBt(˨7]08zV -΃0:K$oy 3cv5p�t790PR$JtT$JH*D8*Tԡ�9uIw4bsG*duk:s+caAF 0rgSQNiF;H&Jɛ'=w[%mC#@3Rƣ>+tL�ǘ5WyTH6r{RTgUU-XD`!Cϕ:JLYUqI&AliKw,fn~3Х)6͑רna%M{[=LdtrPKsݿi:Oub\i.$t ViSda/ֻ5cCX@ ȬD]NkGa]"܂�!@�!@�!@�!@�!@� D:j""wlĺ1 'Ru` :BDpUi"QힷBX6 ±߸hsHs\JK ]=l69Z2$nz9m/@; C)EL5K;*-NQҝb.> xpW= O}uT:R2ul@yB\@a̖AA4�:Q)Jfuѭ.7A\! RSI*$$.+`)5ĜSE˄spN6& ê@S{ #!VU\R`I'YHr3< RMiؘ\mTJ9U~ɹ^PIt縮rAT,dNeG&]-qQislT5G%qsT $IL6,h J W(Ÿ r:T3cow^Wh1sRI8 ;e5HNSzL+=«8xQֆ]f<ۺ%Jiݫ$sLݤ6vD Tᱍ\:)5Ԏ-fAذ]ޥֳC1vMHt7n;E<I ܙ'өEdrB OƜ5Z8]FOXZ:P Sxxe`rK[!7iS; kԏ0*vU|9-8LmCwM7U:�R*9e˸IٳKTC "7F\,p]vZKsLJ:pMi%L Yvc6u<89 Me9Q np o9WybE}JmSKݹM�CךE܅FM9Q ]@ZA`qM-5*mhQ6FE-83T8BE՚ך]b%XɘjF j7(d{B}Zp38U9^DAʻS.h :˝ySX^̞F#jg<a~**TbC/p\vѩvJW͝?<q(ɍCk*v�L}W?,hZqkM]w903=qp.֝NkGR ٵ .Ĭ! B� B� B� B� B� B� B�@p  )_M.APl>'Sucj4ZEU"L0!Ƒm` <5-TZ5̪˯iT M¦Mڔ̆�2w/=G̬F c*8_x qe(Aٳ]j\P48| TP`ӯW3Eǂ{:d7KA !,ʥVu)e%Ttܑ�D{*o& %v�S JzI\:o̚ie9CRgtB;/@iTdHޞd\bzA^4N4pO2r9m1zS^ .ok ,; :V59vX\Hc\ *̟*i+p<RjyE*sKXLSh.+w1R+e5E@,\NYpE'N+g'n)'n+2@&)$Bfԗ1Xx+rz!qҦ )K!sfWR\ TB95_  keV:RmlJ뷲,<,<ff]9lJЬ̥{& `ܫ0#oe@xf5[Mgm8;ucqsTj+7�pLn MawSn` "Sg1Ҁ#T̹7 q<i�];!vht' XkPu{[A=#rzM3zx�`#tm*1tyWˆd]K#w9T=�;=I'7{x!ʎ)X<~ғ`g񈃲LU<�R+}T{D;=n|utU.jSfm,baٴaۺ7dvG_ZosdKohvhVAn\ ppvAصxJAǶ`KuQS=fjCP_1fN5RΣOrNI^uǥKn+{jNBT{ւ('nz &NGfv:ݐePaԻdL7Mfibz0KC nJ &96 (4S`&SFMV~Jz'm֭WQZ9 q)ӰSߒdySB3שk:ۖ 7&cp βޕҞGR3NVAw9:jpD_qs3'oW:SR05 8sG2Ff!]"B�!�B�!�B�!�B�!�B�!�B�!�B�!�B�!� 3M J95Ae$ Ҫ1l6]lѹx<hXNN̦|ѬhhspQ̫WNznhӨtnzƑWe91t{cd ĖaouJ}�\]Š]jo-]s.;]m5cGxS p:5 0Ъre*RΦfy00ȸ6 ֹtypN럹:s.sNuթDp1g)JwlӢ.lʂZoѝ:OQ)'B'J�8J&5.0W'2H=:.sp)D6W Ja%L-k�nÄ&GXK\b&lS5Ѩ3ĸu5a,oּ <Z!xŧr2 3Buj>s'=׮Y>u5q 'Ih%`^tIqR."TDAOB#\MD݁M%uتsORsrJ05K)c8*l9IkeD3LIx-s9ұ UH]#-[jc)=E&J d[2R%āaunFgYs৸wu)R:5_ϺHin:|W LWpNmd"ۉBH$ .܎R=JIޭpsi*V:\h@&kq I%i'̶OQ]6͞J5ٶyJcꎍ�,;kų[}HL%<. 'UaIt%|N+4.&7Rl oDA\I51­V^;ŔF5'Bo|( ב딛r 7]L <n'-/);!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�.VKO*e8o]jl:NU^OT_20cSD0l4mm=hNrjyU;EU}c +RlwbeOm<MeDa5{58bcBrmlNVnjN.pGZ*ƛSLǧH(HQT9[UsTW&-"RvƑ(ʦa9CQݣ̞+6!Jxe)CX,0to]Xo].<)zg2OIܤ eF+kSwֻ("KN z-i6J- `Y\E,pxn䤳DJiksa!k @ dfpxJ_S81)Xh\;hlu8%4y[lBG}#8'iN,i,cWmρ4.v|^W,$9mfE:Q=%к'O+}+; _Y' 8VZ6 CLql!-j{rz ̸dFaF`w,S:Mŀwu�hw9 { [pL#"8{һ›m#,w;FAtXf:SVZkzMңկRzuR-ٶUY9 n4Z=(qΗM2W Y=zu$@rz,wٝi'}Zµ&ov:} WܹXZү$y L5ds7uӢ<(Nظ㙷X<,=Ҙ쒱3^67Ts EqW*9n$.*2Y%KZHhStl jq:PǜO%`op(7;>翾MPoM6S[57&\v#ҏkO4ЋNg1r0yK4dtBqz䍾VӪ\ K�8޴CbOB[Ssghkm6;WTpj9 )lr;@zRZy'0!gl C2~=J)1 `Aq)6zg@.JKoeg%� <W8fݝ�0; sĻr$ !@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@Z<TKO8D QN)]5rgq=Y2V[k^�ª QbFjѧ&BYd\<`UWQ;5,t8=mJ#bŇM+풙pgCTUCao ʲf2(V8<v<}*jQyY95R* $bD"'JI\{) 5Pл-tYA\CLԺ)xn &\4݉ƺH7#  -L&I(h|+;v$H5qD $mƦ(7-3$] , $mâ8� aΆrW)•G`-Ȗ4roZ:cUq+rG:Bܒơ0EħI@N2yӘ~ISQkiBO:좻w<u.XJܑv;T:,76neG$LDiГR_ZH;f @y'&JRA8.I(:Z<jMsT&F܂Imz.ُ.1N SUSVh%F~P #Pf-I;gcqRؤM9kkc[?[$t(&Is ۵S-PC긍�sS@LHj:jU;&F] |M 7uUTҥ Od̃=NoEŔrCN0w>*ř<p*] SPRn_^t9G֡Kgd7OP+OZ@tNRcY/<J̩e/mYJiӼ6r/Lr8{5. mG?P6=(ڴt>� STY5lG!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@2Ʋ٨8\ kUߵ"ff:µ\Х3c5i2ŚJJŕ"0Bd 4{GujPݓe&HMjf9;M;#{lTcɥrS TsN-4RΊ,qŇ\#˙jָqrđ52C3$ڵZ]Fn\E\ \c : !$$).@G*H(ruƺ |v˴dK µ'`#8<Һ7'q"K0 Bl m xN=+C9w@DӰac<S' C߾'R%vИ줋oUƗ_VCa&߾$̹pHKMPI$&ga7 gEH'@09$0Qdf�馣F<#8�l%9Mg#HD (; sFZѨK-ВF *UchhuzG4D+i1-clJ{^{keǔ QC&6 8ZR7e*4q.B̚ w;Re.Tk51zt+U) N]6'n s6{(ҧc[<D"8M! B�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�(9_c(e[^t rNv.SeywcfXZUOPʛjô8@p!A¬;"yP^�&EF*VLeEw^Uvșt2Iv2 p+C,gd)7 u+ ,4^x88rƩAO4̭2Ѡ_6S)a&3|U p+4Qͭ3jjyu$!@YMH$`4;CUa0JD!"�$KnD(:߀n7G]Ù!$oHə5\�H $B�Lɻepi!ɻrWȮ^dI5zlJ4UV"z(Ȍ (ʝf r0dܮOA~XGˇ+Wc)2CZ0�@J2JE3MWUV4(fE!Z,B� B� B�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@� o b2Z)ŗe`Ɠ5MSg+3tgfhc)h<= 5PYGJ W=髉ZjD}eAc|vQJ͓Tn]ς،La-&eTo x]3};)0x*_Fbui ;,ۡQ*>KH֝4 䝊/6X`Sb2ӻ[=kN唯u7Q*5de4KfCBsa":KsrLj9HJD $B�!+Z`.:�'�"E2b.h`SU7ת 9%EO \Λ[HJjE|ȕ\冫)cX, Řlĩ; O?ԃ8Rov'"ɠ<G+)yi]E`!S*!�B!�B!�B!!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@1֣? Mr =P!q:j֑\o~W3 B95fZe?'gfOρ\!snwoNjeHH7Ϩy@.'cۋ7+.SyhEgc{ Ik hh�IPWu7{oL.!�B!�B!�B!�B!�B?!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@ endstream endobj 248 0 obj <</BitsPerComponent 8/ColorSpace 249 0 R/Filter/DCTDecode/Height 359/Intent/RelativeColorimetric/Length 22354/Name/X/Subtype/Image/Type/XObject/Width 841>>stream �JFIF��Ig���Adobe�d������!!##!,##!.333.!>BBBB>DDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD��5�gI�"������������� ����������� � w�����!1AQ"2Raq3r#bBCDS $%&'()*456789:EFGHIJTUVWXYZcdefghijstuvwxyz��8W�������!1AQ"aqB #$%&'()*23456789:CDEFGHIJRSTUVWXYZbcdefghijrstuvwxyz� ���?���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������m � &D5ȳ<s_.MmV^cgdEd|ehErW8K7/&}Ęd=>(G)wRK@` N <yzV KNmr<%C%Ft.xrQ_Ygc1w#ASׇK5Qfu!O/loduFZՙƮ'v(qTEN^xIeT־Z]l5GFT']AntbE)(-s p :rS^aʀ������������������������������������������������������������������������������������������������������������������������������������Z0yNsGӳ)no6\n:x{K(%d%8v1BO(�N������������)r\Z5'XItI� N朞ASfE'[[\Fftɼ;=)itJ.VGͯ-%Z5L.XW GEO*!V*tt8ȼ!:Z:e_X U(铡%o b� ����������������������������������������������������������������������������������������������������������������������e2uZ`]PMЎTWdŒeUGϏܶi/R[;,* {$W%j3ZxNZ}HԭT)9z~udc{vkȺB4U|5ɴ(qcy9䔖m3+Jݳ>) nuS^D\g[UsQNeȯ=k{n~!P]BPˑ_ c=+/ td~es<}k = TJ<5jC" o5H΍vO 3<W"xEK UMQ?5M{SRo.? F2+^f G\XS]\n@nE$S(p ٲ* 1rO>&1obsSKɩ.V.լTq=B>:j\k\KghU澧cr~+*r/:9ԍfd5+Ilg]1X]#z P\r:j{;*wwl%VQSgT_t2aUS/[&*p2})cG(,SRtGC((qMPղZiQ\W){FqRRWMb,Ȩe+,WkISri<6tl|&க~m6javс'hTZ2΋OcGRi����������������������������������������������������������������������������������������������������������εz9<sSSe^#w i#J8,QDZV=RSZ[vEnS̎Jx>Q9SίQbа8ت+3@H19*,ym{IQi])Jo:Mɽ-8NoUǪRZ-}*m!(I!8]V܅Q+zAnB:�8%P/XF["(*|TtL9.T8H>OEJ>ʛӁR.xh^F*V[^kFKeJ]ipX99\įCR]"<:S 3(QRC'?1Ы1o%lӘki�d>(pj9.,ωSQƔT4)wCÐvYD;է^xQ}D|%:rϧ' -qveZxhʴiVmXFk\Zq*yá𗜮ɼ1VS<x.r9DsIk΋*J";CT\K[DjtT]e9?[/ +O:%J1qM=)7P$pzcvMQ'Cuc;uiՂ9)EAR:tEJVi! KiZ\D*O(jjQʣxK}Bw8eJ6Q^򬉹QhlIk\|/SΆkzj<rZu㌠mֶ2pRWWLXVUk\/� Sˡ [m\ib.�������������������������������������������������������������������������������������?����������ib��"WEN|>Uἢ㓭<m3Xc%}NYdί5 L2XyWT28f.<V*従۔I(UGvEQY"@Ołxu&W}i듻IkĐNg`U$\z[QGL5PQ<;z+ (%}cBPv՗s;S^>ܑcVm.UۉԙD3<{˧| Q{[9k6AɷU T}HVߘ(LG9.A�P:8 t[ FXI'ΎRi [٢ZѾ\NVi٫2(FjI$ISn܏#ʜ%c5cGӜMN$�\dR<z. Z TWXdY&][%yK\g%ǒ/d'L4ahӭڊ= =S#TΥO+[Nk>R]iކQK)}'uhkl Cr3Lruw Q\ҹtKMI)E5t(*2vdnRI: e=Cq9PI1H~d?F;ڱyҌp*IoGsh׊:*B黧֞Ƶ2^ r+Ƭ^t~rjCUNk-C[+L|#kAoɦ* p� ��������������������������������������������������������������������������������t.<նr7r/UH%;g&vW;6ݖׁT/h=s-5dY[oR^ VԒ͡tIl.Eƕr͖CbqT ]ګQͨ.C(ѺZ_ueڑU/Ge*j7yɩ|U=Nz}mՒˡ¶3S}/L>+NꓚKJV~9O]oRVBUm=Wl!V8d𧓭]:A|)-8YWUgFxGCGťooYVUSVr9)p|&n!7}OѧKTg?rv]G)Uuj唧ؚ# OK3Aiߓп UI{l8d;R̰ c߮>JT찻0~\O)zԅr:>&.5Gwd['RqfQY[ YzՐF1j\H<u9ʫV,wnMb℞$%C {u)͹=ںΎ;) 8ntvTuˆvoE8�)Ӏ-P8N�(�XPN*J]r 5L<ir=iBPv,$I]pVFS%q[5,FR&uR%v5! o*.oX ' j5MwAL-ރlek^tB$jYKЪ%떩L5o R*kqʢ2{٭*hN׌(%_'t' a5‹u(T, {5ڶ+PR(' ѩ{8yj'dt2Y{R8N;9W#;R2Y#ÃʞS-OWYvI6ԯs?1vʧZ&1e2دqry]ZkOZgbIKM6h��������������������������������������������������������������������������������ײ3*tV|/0T+U_+OW5Ml1"TTIQTכ+mw\Xm􆒱45RU/R8_GƟˮOJ5ODhWcw:BN6]UmYIlR8+.e?.cT(CbA&N t8%,:]Z4םɊ>/&9O̳PK Y1ڞKt.\nu!^>)SS}m6rSg.G'n ⼖<czsMF/S֝g3W\B쓖EՕ]h'ƨGՠeL*:GV#P[3e7NlQF&>�'R2vCe ru&Y.&">S%(1iGm㢶-#N1Ru_8dv7#v*wcaFrc QpaJ1a,dkmG6Iy2ʲhts8' HTx%Jx._6i M]5͟..QGk5x-],bnG[k1~0炳NYq*ϟ6=7*z&H' ^\NO�j۾_`9QNńU}91F/?d8݄EJh_N}|;3%nkTްӄjCȾ{uN1i0"CYM{ɳQ �tXXV*c[N#ȭ)vzddLᾆ1.d{Ly3̝GfnNB0M4v$RfHEZ+C6zXI5ɲ, 딻Vh*.%p8WU^exrZ]8׌ՊSJӃ4Ja5 5'%Jͯ URKZkt^HUS^to,ɳy} Ƥ%tFigY\%b S!A[KRoGC".Y D3 _.�����������������������������������������������������������������U'WR0xi ? 0W5=\\!y> [S",SJB~+#}=rQ}@1^r6t# _pey㾺Sb^-JqEpH<+Y: O{)b%b]pis/6s k,%(|!R|9J\Ѡ9$ @QcNrMKUpiW8{ӀN^yfhН_URy%vG-k6q:ƍY+kmkRhCuc.7ʕJϏT͏R@?D&bXJ#}BuY:tIGlrhu,UTF ?o1t>4*8{"3nEO&8'j?oeuPr)5yGq~j4Fm%*RwJϗ\܂ho'5W{RY{B8;pӣzRڕ!OyȮzS*JJvr9lIXbnM!:ϮHk#k)Uإ K)([3mb%]75)Noe6L"OHTuuMI?1oKojH[)!}*QΒzҖ8G4$9B TXm=)LT:KcYV-K'څSB/g%a8pמXԖyJϬ#"%)pl]2dXL`3KjZ|Hb:tM -h�� re-0L՚Z (T,v8' rJ5dϧ|(cA.)`diEM_E8>uN5c.t 5Rt'S˚^Zl1[P% <ܢ'l$mZܝe[(鎾ngSU#kI2ZSqjsyѴrJ{,l` Ri2> QOr6_'\*Lw֩,KjRXhѱA]kGlw/3�*�������������������������������������������������jrxg֚v-l)ЎBFY|ܠ^몚Ǯ:q*O)|Y<~.vlg,%KSY%8.'njjyƟ=\e- .o 䴕Scj|qVJg`o It1=5R9}r{9^Sd Q{$2x7ܰ!e~rM*ޔmmz^,Ƭv\V BK&K_ N9OZR[rF柜vlPZzM]# 5(V"wKFPj5x:kJz9R_8v9#:Ǫ_ :a<֞ Q�i\rZW::8z)ӓxR+Oaރ^gy29^Hz786Iv kJ,,ЙRF~ث&=ۭ7aU%€XDƲK7V3C⼔'V*\F�w-QoʚK(WKEv7OM:@uc"vzb6SWלOOACR y5OA)|JNN|fZ�&1&͖Ї�61k=LbRZ"2Z@iQ.ڥ"a~kc C$p*:{ Ƈ[׎YhKZ{Y3Tb%ME+)6okb:u*JI*Jy=BKmS+NMޗh]g^'TWqa Ə[Qe9:̚M4I/jJԄԛ>A$=gmYxS3^ȔZc*'{Ǭ-Yu|YBɧu4GCd6Ǭrgu8WFR\E$*ɧuY<d[B 9PօtvLn} F-&kZ�d%tpK7GUzjS:EsԼ$%:<tMJN8gEjֹJ_f\Y H9賣Z:jSr8_seM9O'[RΏJ֋!ڇYLpڹTnͭI[9֦@j(ܛ ^diR2jjJҏzQ -VKzN'Z$.@AnUj=5*Bq4W.��:D������������������������������������������&J9֯ )^򜰄#~qY<<'xXsbrdB<SP^i89sR+խM5%bF lHKpȭ{K(bΦҡ]};1FUC%ug,#'EbȮ^ʱ<i%:spcv2urΤlK}'0EF[,,/QGJ[c7RDK;FC 5׭-ëy֓I9pb8 ﱗغp yL?`�=(VҮ75 x\pkVkD l${:i<(G q֕Ύ+tqڛiY6+=px\*JGC$gҬ'C:]#2xnSdliLZ{hYFK(}�v58 9Szػ&ǠpIΊ2"2Sz�èK@mxät!8żVe1ϸq&~(m�.lg{csoo.$`։u\EIT=#!h:*OiMupv!*Q0r^.ȁZ.sM;֦icg''Y696ھG\GCrTQKHVITA&)6xIv+P*ԚJJDUi:jҨԈIκ$tC Q\^ۀlz(bmt\mQH1 ǝx+D1\Ӄۨ�[\m}>8�Տ9ƭ xKa`'m=~50NTw{ס|TOJVjb3M<ʗV-H4׮,TXGl/Ic~)NP\Fn<}s*Z&UeY<J)=̴V5骐lz%zӚ+!r~P5خwxLfJ}`�XR���������������������������������������Juu߃i~q'RudPviQ[eZ)҅(AY^m-ld*^@4#Iɹ՗ -zznifBԒ*!N)կ>(+عH2QeZZ/j4'qDKUGu˯F{̝c9Y+LSOҒVPYz-U_+I77&<%m^ʹ/[dBF*RLѣҲ@: �� : *9h]:�1< Wa)P>ʜ׉"E`ҵ9iz.p Rqy5薧0 kSfG+j{+gOLJ2WM;E="J)ך?Ӭr$[luS=h\*V|$XC˅[FJK9[QÈ($1[@|CϾtMm�crHlX09p$,.Vp5kLeۜ&4ΜcWy`Y8 I*M_: <+,dܔw~;(5SޒFUV%8Fo5;$2;eȐtUUJЂ;үJPP8'qM8fԍ6)E1c{_KrJ-_-),7f%A1(ք-8PkRv7M>'xksCkhڒM$;2)9ɱɛXT=d#FQq;N鹧+1X9I]:xcN4**[Qymh$ӲOo1RV|!ӓ)78jS9Ө<3jkhI#E9jgUczsVc]Ck@ t9 MV:pU{I&1<Nʎ,֌VيDpO vx?3�fh$ϡ:XKl  t7::R*rWסhҒWG RL\yHTVӡZOCMw<!ݒ]iNPkkwUXvqLdv+26GZPka(N!WnYZ\W w.TdO� �������������������������������6쒻o�w9e-ƓqE\iezo4J|#ͦNZK$WKb愄#N*J1J"QԕWd)T~.y5ʙdG#F;*x$rFr$=EJ`9y鎲)dɤ֟ʞ2oduw.VBܰГm.WdX!KmC XƱ5&51bbvvMo#۲ŝ#BOaJQUdExP׈ɍQ�MYp%M=2vb\>cLԩ,^)pzGo6sUV-N�ss;-gQDTw8�96sQf:*qx=�65[!'A)|rt %ɉ&fD#\Àg9F%�ؙe\^atWN_*nb8K?)rv FBs:,"C\6GFATҍ֡iRWc| *' VSgHN*I2Vz}J9#euD"nR4g{(L Z{i>uX07I&Uqt 2 yuxjԨ" T++'ɕXjt˯ (D]'"ŝ"t tMm7-8Ek9+sq(t`vKy56v$SJԅgЂ@܆(٨GI$)icc \3q*$@8+3N/4i[ ( ~p: i<Sө�RJ QŮwNJ:{ֶj:,^2+sOGӢ, j:5gR>vr:F:hֆtzVt!NU*hwdb4<��������������������������ZhΤ0\'.P@!J.svKݱSv]f;Ɠ9rjNjJ7//`tZ+O;a+ʗ|}J:F+Ke6Wz%<:Y*zȹ=^VUS(W+}\Ȱ8f猵l\(BfaךREC5R\k9FV˕X9#5Qrv;C&Q 6FRv;&Q"JbddE QQVp]W:gAμ8s.7q.%k}V:} bɥ}_/᭷pu5[<-OW>,aeg7eZfO#đν`,xթpzf=BlrG)IYhzI]F=Ck-omqBn)]j:qc6M)$y�T"9YhzǨJ[㢩YfːǨuu~P%4Ր=B=@gfc.c$SZ=Lz=D=ψ_!o tѩwl{iӍPW cznvbZkXUQ*Va'LUS b,kqqk[=<)mޤ:j+TRAn#&NpN--,CX{ISZ-eX�EJcUZI#T!-qN1ya% i)&d7:[RKR%+te}(b X~SƭVc}pQJཚ/ȭ3rgX4Y&Zm9RN&_PBc Y UQB\B,7PY([EN9,v7wcBzeU$`WI]!լ6Gwj:tZ^")E#lt$VMIEYj8Ђvc:Dcvpݶ*AIYjHs)%"X']G�3\QΜ2Q1�jwX 8tbO %^qpIk� #!SѮ,2Q:8?3ko ;~*nmxˡB9Z֧kƲ -(F7QyԲw:6~f g7VA� �������������������9U)y'sܪ2E򥯙argT7]YWVvU_<gtFSܧ.匟rhW(EGZXB:zYFW[)x0KEĕt`qPW2 BZ3Iԓwm6ԯ1 UcmX1X1k`tk:uM'f GnvL3Ñi%FUE%".;80V9N8`ŋlQqK<O"KBFY7K}#%k~x6zsth&=ohB.}-xU#Fm%c8׋N)IvMS-q+B^K)IX/1ɓaS(O+"Tߴ/Y,%LA <N,fs7V}߆ɥQINqVwzYW(֬pJLD7uN�\ JO:@qDLF6ۻ$N|[>->c#}gU)@:G9J,4!EPEP8*D(|8C+p8ҹYg^J!FS%e򒬭Xʚ,Fp\dE^e9Nh. '9f99* hm^ T 'ڀT*R5#/D:u*ZgXTLᦄR qI] IV)Yଭ缞c}hp"AKԳ^ ;2VK=)x-VJx'%|Solr W$rEậJΙ5mڒV.S^)2ׂE pňUPRZS-!%8-eL:eMbrLVv(KC!edKRZ"Yz |2VU2Q+pJY ItjnDŽa v(nR7`v#i,53⦃dVH;zI@5 MǼ*MI Ӱ:\QL:�22z# #ZЍw�Ғbj�DI�"lyRyٲ`(ߝhcnzZ i٭a-Ϊͪ?YъI[pԫQd6kGHVS< $:8X0%:Q N.w\Y&Q¤X9jRH(f>E8=O4(�;HZ!yM:wp2)-}8Mg/SX�JEIyM(<ĂΗe'Cb>TnONoz TRE"jzXo%.H2zm~[ZxC&2pvKbч)ﱒc}Z[ ΖK,5b0:5„ޭG2yoW Pw;jExpcXՄ9_*#Kp^o:9#Խv,2vtz'x*SRdG<ޝشN0бbG(R> n'(^m%DF8SW HP'%)|G9zܹ81C7:\\EĕlW,<#jQo|[r{~iC7!TqjI*gX̀VzZ%N/~Rs\pWp[yˋ'U�u-n<"3GڦӤNv[ԑuѓS| =% ?׃ RWiݥ=IM37IUe#{v>.OZilBJ6oCD[n؎B!꼳l:*؈~Έ4ۼ#qc!BWSVYS* UxʜXYeN,zĥǨ_FǩE;3%=Ltrڍ6=D17+ŏPo(b]\Ӓ'4̅U Fױ֊4gluμTv_>t;.6})ϕy *nh谔%YZ_ )A6پwJ3:wmj3V:ENghE*iV|;2N r_tgҽ)3ﭩk JjR,HSF\[y"1)h֜eNث,qIr5qV]Nᱶ=cRt:s̈́}=W̓wV;`J\fAܼtRIZnSv؞c,rQ7jHo1 J+URȶQ.WwxX9gm]gIzX_F vβ}`ӇhUT:v򎾎BDd2)n\2ʬwLD91roMŽFYosgS a7}}8(QwtZW8f8�ue iCwR {Gn-i"IR>cG] 8&8FY^q/:bp\b=kvFNJĴxۥ#8tU% 8.[B a3A)J\)72q$mzcѬk:(J\srwiƄzIqpWNNڜq8ƌ!k8kiit N1ӧ`MO'{Ўj.Z7h/\d6san^原U%DI N52rGmN1wX*U <)]X$|R͎߅8GT:zVLV5yrk ytV5%#۵8mZH\:yvoz/LݻFB+~sle&nj*nl� 'w8kg0ɼ-"=\u@$ ^�?8cϾ@I6wHЧv*F֖%.Iz&ټ' Ө'vWՈAO?s\g_((sYM,\{= \ŵ]\%M%$6L:&>0{$Ұ W6'b8i=88뭮㪋3Ssi%6}m"knfr|.OU)j|/MπTYg3gf+QR+۠㛯t3l,8EiNWht(^@^<RJJ cji>{%URN%p9NlEN[vvo%ai>N-ѓMSKw[ߒAc'VtnOLչ*mpqqdJv}A)'NS*Ww�Ŗ|-wŠbMOsrco\JR̳>t>,=;Ǚ+Ιruw$,RmE\XN+*ߑ6e''kӚj?\*Se+v*j]OJrJx)/nLXnqir$p9{(wrmx wJy)QwDRIenM&QߨG5Stܢ& :;\>dJqauU;Igrt\FJ9ק$H7.X2#*X)(2R-%tJ㔖|wDɚܪf)9a&UqN6%p$B);{XJJYƝ>sgHHδ]:SNjRmt;d+XVkbNEVk!҄䜠k+fg-64kmykοTM JSwN:BM;)+ӢΉ[T;*HF89 p.v!u,5iZgˁ)8BU͋$Um)Jim%{Y;>] Ԕe9J5kЄ#9nsi;MS5+c)Ԝo+.gkuHlj9a9a21Pz0gvU r^28ɼ(dA7NSxtͩs:8II;=,ma>< n(wSksYlJgDѕ!<ƝqzܒΖNHMX,j8J�868friX̎#$8E]XJKky(OCTUj9'xN'ZψI"b;qnrU`juPJ׉CG- ᭀ#ia`W;lXDzNpBJmGb^1<_=&JҶpZz8𣝢%EhbE򊙫~ep%U˩S;ɠRS]oCfiŒqg9gD[l0%s$kt)<-$c6S̍$)iw"Y/щJ:[ u&lm(g| q 0:1�J9-Xh|ٜb"|"йvSW8Lj�M\^vh?)S9JQs)LlѪ%kk:@RGpVJr8ROc,pzKIRZػv%t Utt94p'1�h8*A@ࢍhppG� 8ECELM- Rb9 C#ބeDtu VеDbQm_YP[ѩJu2yHMRŻ$vT!SȒY֔Ԣ,H)f,& Tatq;#*a9P3ͤj=ɝc8ڝmm$ɠ!/%,흅oң&AMSq9Ҝs ,c)E])ѝ䞆.ER\):ԣ+IYsa2ӂW.W[QpjYѶ9jo]ť:kUΦƒB)I;Rk^ЁRdڞ'UE]c}̇s\m!NQ>3StAN2yKQƊnI3g;A0i&%JW(U-xc:SQ^Zo3:*D(M7avjiҬbyF1"ԄSY9P&FQ]"jtUEx9ƽ7,g]:1:rM @h `8Da-ѲcÔlT^2}f'=-؍$ua7v/PfA<^sܣiRI$U]/0R^ocu (yTஶ5¤vw\LKKe6yM:qG]]ыsqҠd*ve+ݑ'i-J1/KLF1Z-,*S/gi2 rueɠe¥R弤EYE{ZVX]GkE(8',c7hh,W9wlAl8JH)]jKX8J睾;y�)Ύ֬E͎S F)͎:6rMY Y8] ]p;DsӔ}1rRjg^�v�8~|vqN Hry҄Zxj8KhPIHҥ�S�AP>%sg2X5V/]HI|k:<]@p::C EA@࢈*D ( >0ԥڂԴ\am{rZۆ3[LqZeXz9z?2`vˆ�pr@pz!Ɂ=FP5%kΦvrfmnMʪd7G+>fiQ26u z1Wĝxf[;2'},t+(,hsKotP[Ddխ`'aRGD&sLMӿgZk4jW\Hl:U>e;zN1qqQ{vj!9s{ ]΍TyZ.qsߤǝrw96ݓVHpeKNsXr㬁sj6vͽtN94VtNTjjn=BN< ֩i<e RL )Ð.8;S~re#6|ġd"SЎp&jVͧ fӨ'[8m2k{lXvVжAjΔ/::nN2KjJWrDIד+-odAhK֓JYp_5){;"-UE- EBux ~UsY;ZQ.B\%*}G&Z KzmϴzRZ\ҷys<Wô'3GҁU)9TŒj^trOצzBMic+ݎnZZ]1I* ZURnhq3o6wYZKO6EȌpVTKXet5NsG-K掕+դJWSM̮rkeF5v웿;8˥7rJ'G%J *իԧo2U:u穻%;%2B.~[88ax"Lπ5_[8Nnno8-,-˥IOd T;,[}Xڊu񛌯I/%t8(lmfG{▽' 'dTYΧW>Fs�;ak#\$9&g;!=!9 DsCC楚;?,HW0M4fҵeASW8|)90A#0~3q9gkdvCۡp&$gӥ)s")3)2"q G1Q2"D&rcC".dNPȋ91͈fɍvj5�LhPLhPLh[!ld-�RpRPm)%iAd�P�d*Q|mN�ھ-,SM\۸Zp!jDTS C$9$p詒)޽W:m#97 \Ql*tuᜳZ:toWbrnrӌ%|l ⮣L/**ʵ8EZdҟ:"qJ93qy?1_&T)$jC$VpYЊSs utKX*2RtM'1g 8mkAO!ΜjSOsUXcag%Rnʒݭkau|n2r|87v %u鵠L#. du\qn$uimd"R/NGIpbEV%8L"^>nîr"dL.#v@p哥g-w zη8%letւJ ^,*swװ _B9t3soYӔs^f(Zۣ"-W Qخq|%'m {V1;!-Knm×}vpNG:i=ʝŶ3,9:N1G:o1-=u,1To]{ӚIigE%}/sjjJk&mlї㭒`P8-Owg N7iN9g�z'Eɒr~v*ƺz>㬒أΦc'H�Z0�˔pٽpg${[4՘0ZX'XʊEr,#@�QhgS]CX,+tGYh| $ԩ8椒= : s9 :t(yBG$΂D.z: sB磇$t瞅@(.z(.zsg fr979 Fg!s�@ppn@pmŸC..p1𛃺8!H4KYO󄲇%FRBTwUbʽOqɝ gzL-ee\[JGKqvIf"|H;ƧFS:ąVNM;S9Ҋ=+#Mͺ-w'}Sj<(BM®њh򚩝'{5AYѓI[%] 9I7f$TPAsvN9Jkz^xƛh+V'4rO|qj% 4Wpd�{ċqn W}·cc4u<jEruBB:RtHM;BdZ/i-#T+9lJdaiM7 WIMIYÑXOA>}7`ols`'nuO㳆 %šJy%v[YޝѤnu'3$s)]ZX累1&AW+Ȯ埘Pt:78ӈCnkr\{g ;%lF-;׉MmNd7h2ģVo(pq5"PwgJ'*!/!wYY< ^;-UV5GG^'QɶޑItݦߠk7` MuR 9^Ox*F,Zrw<t.TB{!YvSP g s�2CiG3"e >B9 v@ZthzDRk@t认,@Ӣ1C9s:Iզl3XRgd[3d̑S fs̐(LfH\Lfl͐N ǀ.k(sXų(1lbYf(rbY�Yf(pK1lS`8�K10B8 10:pQ@P8( tXF[{&&gQF'!*8gR6bD9x:\å!I֓yWt Ƽ %(^.A%?vJR178pMK[S>up\".pԱ[�|%8W-+' ]\VY$q\'#VLEb&#�Wy&8+<GJ1U>|jw$0:CY!>q"4/}VqIٜDA#Ѻrs>c=/gULux -jΧYlbW=i$-$Z9s-TZNgv5Ԏ˴z{N̜)Йu><qԉk Tw.E|>wԆwԉk9$v+'CžK K$6W%ΎJDN&et''c9/uQ<SB: <+8*cp4 DQ� F(#FD gN#C ` endstream endobj 249 0 obj [/ICCBased 246 0 R] endobj 250 0 obj <</Filter/FlateDecode/Length 343>>stream H\͊0y9m҂ŶaXwƱ+1D{w)]XA ?2Tv=,oS֎]UD4UBA&XKCn%DЍCA 5zk!Ds٪ie|N!k},P1=z2*49QrS > /$ݬg@l QH%Gsow;f->z/g>sY4iqf)' 8v0[-1cLja#>}8K=|IIZ{{Tů`�\ endstream endobj 251 0 obj <</Ascent 882/CapHeight 674/CharSet(/space/comma/hyphen/semicolon/A/B/I/M/R/S/U/a/c/d/e/h/i/k/l/m/n/o/r/s/t/v/y)/Descent -250/Flags 96/FontBBox[-103 -250 1165 882]/FontFamily(Myriad Pro Light)/FontFile3 253 0 R/FontName/KGORFR+MyriadPro-SemiboldIt/FontStretch/Normal/FontWeight 700/ItalicAngle -11/StemV 116/Type/FontDescriptor/XHeight 487>> endobj 252 0 obj <</Filter/FlateDecode/Length 356>>stream H\n0 yCBh+qhb:@l\u"Q>'_Deu\7= I \;'oƋy\;<.SX-XWYeT߼$cYB4AFKڦMs,-17B G!3Bi|7A r\Ȼ1<.9aNbM)7cMFlǼ#Θ3d{*SFF)1բ3\E*ȧ(b?̌5SM4{A+fEb"߫IW!4YG<^,į�� endstream endobj 253 0 obj <</Filter/FlateDecode/Length 2365/Subtype/Type1C>>stream H|T{TI!l!* f0u*VnApy.,*H $ XAE,O "XP|V{5"uWz~x3ܹ{c quNVE}Tq 1Qps9Z9Xӳ"$1l eNy`Un p<-xgkd-_~([P #vK*>N%OP/WjJHDe*(>N %j,.a? 9^)s+#0 0l&Űǜ0l5Qy/0,xM0o2K8 $Ap𱅫E?yD,N֖[-;,ߋ9"EϺ(o| $5|^H:h0-Љ3@Mri -1:&A/l@sadW;T Z(tFGq O=#AN)6DoRD7bY�^!7 2@ 4D$2L& h4O˲ %sHܠ܈R^&cK[n*LZ ۪W3'Eg'Owܗ< U*5ZRݺ\riGS1qEpf= m<RSݼK@2y)}qyDMvN2+F!1'-~ B;Df"6YcT sQ$CP>ר׉2ɬ;[- xjp|_lKWtxI2xioӍN]un"*> %ģu?Ž![6J;R]dw]rSD`Mk2 ^\,2Z>{{DreGsDNuēګ̾YF2V{-b=H)g$Jkk10zH&Nx% iHV֫AYs^rJEԹӯ QQW9 vc9y<&LXR.gn@ƫǧљQ鳵`-aar"]ǥ pfwj5yUo!~pM4ր l|CUS�} _T2HDh r}fO݋6HR CxV(T5'\a?TAY>C+VaX3@,h:jw()oa/`Q_V֚CIÛ3 Z= 7 bnع Rk{ڹz341yj)hrE5Qw,CR;2POA(WoQ4>[$T Yq~>Ƀ<W r trdخ�fSdQ}/<],LS$O_# -As'=\-l�"YgKg+Yr %9jqn X ,(ʜ-P4$6U@U垬׺n VH]N !)Z0 0nK1ϜnfUce" 2xmfqA*h~GMEߢŻRSR#9<@�BC '*im}tJ ] oJn\(lH2h`A.^;8y!1S~* xENU%AR֊.gFdG{HM3b6WF/} Df6 < W $@\fH;S!H=yT2ز{g(꧂Aⳉ }^LԄ�kH75UGkf;a4 K~!_`pe'L~uz +<ږF^F&;v 3@i{ XI55OEɌrCb? =`/**#GVYI76HGK%gwKğGdcA@j,ۦ*Ho PRS st#I\Drï@*x]&͑V:~βK',=;#ܧ}d[K .v.Z(s}_ JhzA`mF}G[ GsIcyPۛ?Mݭ-9Y%%%VN<3>:ؔgxMRI5Li6w:L?O9yDvtܴi*@I@]O[4?S=<|ߛD~D�!n endstream endobj 254 0 obj <</Filter/FlateDecode/Length 353>>stream H\͊0yCښ Bk[`+1D{w)]؀/dfGTҵCo*e:`­ubHۚq052}0I.RDoBhM.j)tF<LR׺a{;N+<dY- 6jwxr]h6"K9 6 rʜ)nĊYj=3dW8J3kxϼ'f 4(֬H:1g 36$ӬYfaMz4ѤGs-jѬAu&?IƭY{8yuZE`� endstream endobj 255 0 obj <</Ascent 897/CapHeight 674/CharSet(/space/hyphen/colon/E/F/N/P/S/T/U/a/c/e/f/i/l/m/n/o/p/r/s/t/y)/Descent -250/Flags 32/FontBBox[-92 -250 1256 897]/FontFamily(Myriad Pro)/FontFile3 257 0 R/FontName/KGORFR+MyriadPro-Bold/FontStretch/Normal/FontWeight 700/ItalicAngle 0/StemV 152/Type/FontDescriptor/XHeight 489>> endobj 256 0 obj <</Filter/FlateDecode/Length 341>>stream H\͊0y9bk5R<8v5hdJ6 g&QY*8C4޽A⭷jCۛq 34NE\/ӌCeQ9Df؎W\ͷ{{WY!v-Qƽ6B6UK~^688w"ƌ-N1{Co_ m'vwUsvK$|"Qy&C5YLJ{=s"0K]`29^K1Ks/}>0%h֬YzEg-cmY,3Kߌf3c"|LGI{ZQxa7e~�n endstream endobj 257 0 obj <</Filter/FlateDecode/Length 2018/Subtype/Type1C>>stream H|TkPWffz B+4MpZ{} qQA�F@\PE"*A- 1٨f)cYf=MXMj֭u=w=$!!Ht YS <W25ӞGJri=j'fs C0τ/caCWrԴ<qq#htIZ1rgn6+W\yZ)FLsmVo6n1 ;O z9lMzv׻\1QkSqDV#5ڬD}Kټ"vhFB$>MA7A,%6*$ &P9iClJl$cRFF6l|l2l&Ⱦ'rP+)j+Fi ^0,KSJ jM0I!]aaZWZ2DXvZBM#5 Yc4Ph{ ?<jeb4{Tp͔)9Eii͈`Fٙz&mӷ,ܑLJ<YdAp.{'#3E_i5@{w56T,T 7ECfA0BqAA $(i~/ήOa>>|**{HjWƏ_%`< <dc.X*pO&]^W'}{r^KZKkM716&飼sn+Gntް�=OD3 Nt3'V0-Q+9d9RyHדJH^ښ1LB b0aX�f ,Mi`˜)?jD۟X4'Dne-բ`ӻ?9u) γ}Nw,Q\94}۔C(W�D!F`o~~s KbТl,Чp;Zo~Pa^aJx<]y*viC쉲 s!#a2ݑ<_,ߛObPLz셅~#!kWCDEShlu7T&%mCLس{o Mh4S�r|싍N߁].3+KGVi䙉:S0 ;m7Nےw8N7tPɼx ]`OLY\lmp[Z S[(n(hmu3N<˜} |ہc]č:AʆZ iik; K al?1Ԭ>`a[E]6p+fJ*W IިA?EV3%RS&szr<w jàJXeyqkr5" K/"\y AxK|n'jdCҾ;nGe)a3~).G(*[NxLT;GfS [Xs}`S+4QҺ U06!8TShRS_?y;PUՀw(W25EeBnKxjb:P 6ZE'vdYcߍ?r `cv$񘪭|+EsVLSSD?mlޭW rHb1^AN>wc/8ePS\G6?~C- b/8qK529"aLIYT:kbgrUyeiE)_CYܒ==ٵC# ]6ߺ)+.M-˙]IvrW2׀b,2\oͭ}(Ϊ ٯ|玷ebo+ס ܝuv|5%M{f{❥l? �1 endstream endobj xref 0 1 0000000000 65535 f 4 1 0000457678 00000 n 41 1 0000457998 00000 n 53 1 0000458108 00000 n 127 1 0000458178 00000 n 134 1 0000458564 00000 n 164 1 0000459006 00000 n 169 1 0000459454 00000 n 178 1 0000459854 00000 n 210 48 0000460298 00000 n 0000460363 00000 n 0000460870 00000 n 0000461179 00000 n 0000461584 00000 n 0000462109 00000 n 0000462526 00000 n 0000462952 00000 n 0000466481 00000 n 0000472403 00000 n 0000478159 00000 n 0000478572 00000 n 0000479469 00000 n 0000479861 00000 n 0000492515 00000 n 0000492629 00000 n 0000492743 00000 n 0000492780 00000 n 0000492876 00000 n 0000492903 00000 n 0000493317 00000 n 0000495968 00000 n 0000496005 00000 n 0000496101 00000 n 0000496128 00000 n 0000496542 00000 n 0000496581 00000 n 0000533507 00000 n 0000533889 00000 n 0000534247 00000 n 0000537162 00000 n 0000537610 00000 n 0000538110 00000 n 0000542086 00000 n 0000542198 00000 n 0000542312 00000 n 0000542349 00000 n 0000545018 00000 n 0000568304 00000 n 0000590849 00000 n 0000590886 00000 n 0000591299 00000 n 0000591658 00000 n 0000592084 00000 n 0000594535 00000 n 0000594958 00000 n 0000595288 00000 n 0000595699 00000 n trailer <</Size 258/Root 210 0 R/Info 4 0 R/ID[<38F81177F239849D69C477E8E6C3BB41><1E176FDB5E6C4E6F93264F099BE4DA4D>]/Prev 453291>> startxref 597803 %%EOF �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/fusermount3.1�����������������������������������������������������������������������0000644�0001750�0001750�00000002530�15156613252�014644� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.TH FUSERMOUNT3 1 2011\-10\-23 2.8.6 "Filesystem in Userspace (FUSE)" .SH NAME \fBfusermount3\fR \- mount and unmount FUSE filesystems .SH SYNOPSIS \fBfusermount3\fR [\fIOPTIONS\fR] \fIMOUNTPOINT\fR .SH DESCRIPTION Filesystem in Userspace (FUSE) is a simple interface for userspace programs to export a virtual filesystem to the Linux kernel. It also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations. .PP \fBfusermount3\fR is a program to mount and unmount FUSE filesystems. It should be called directly only for unmounting FUSE file systems. To allow mounting and unmounting by unprivileged users, \fBfusermount3\fR needs to be installed set-uid root. .SH OPTIONS .IP "\-h" 4 print help. .IP "\-V" 4 print version. .IP "-o \fIOPTION\fR[,\fIOPTION\fR...]" 4 mount options. .IP "-u" 4 unmount. .IP "-q" 4 quiet. .IP "-z" 4 lazy unmount. .SH SEE ALSO \fImount\fR(8), \fImount.fuse3\fR(8), \fIfuse\fR(4), .SH HOMEPAGE More information about fusermount3 and the FUSE project can be found at <\fIhttp://fuse.sourceforge.net/\fR>. .SH AUTHORS .LP FUSE is currently maintained by Nikolaus Rath <Nikolaus@rath.org> .LP The original author of FUSE is Miklos Szeredi <\fImiklos@szeredi.hu\fR>. .LP This manual page was originally written by Daniel Baumann <\fIdaniel.baumann@progress\-technologies.net\fR>. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/kernel.txt��������������������������������������������������������������������������0000644�0001750�0001750�00000034724�15156613252�014323� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Definitions ~~~~~~~~~~~ Userspace filesystem: A filesystem in which data and metadata are provided by an ordinary userspace process. The filesystem can be accessed normally through the kernel interface. Filesystem daemon: The process(es) providing the data and metadata of the filesystem. Non-privileged mount (or user mount): A userspace filesystem mounted by a non-privileged (non-root) user. The filesystem daemon is running with the privileges of the mounting user. NOTE: this is not the same as mounts allowed with the "user" option in /etc/fstab, which is not discussed here. Filesystem connection: A connection between the filesystem daemon and the kernel. The connection exists until either the daemon dies, or the filesystem is umounted. Note that detaching (or lazy umounting) the filesystem does _not_ break the connection, in this case it will exist until the last reference to the filesystem is released. Mount owner: The user who does the mounting. User: The user who is performing filesystem operations. What is FUSE? ~~~~~~~~~~~~~ FUSE is a userspace filesystem framework. It consists of a kernel module (fuse.ko), a userspace library (libfuse.*) and a mount utility (fusermount3). One of the most important features of FUSE is allowing secure, non-privileged mounts. This opens up new possibilities for the use of filesystems. A good example is sshfs: a secure network filesystem using the sftp protocol. The userspace library and utilities are available from the FUSE homepage: https://github.com/libfuse/libfuse/ Filesystem type ~~~~~~~~~~~~~~~ The filesystem type given to mount(2) can be one of the following: 'fuse' This is the usual way to mount a FUSE filesystem. The first argument of the mount system call may contain an arbitrary string, which is not interpreted by the kernel. 'fuseblk' The filesystem is block device based. The first argument of the mount system call is interpreted as the name of the device. Mount options ~~~~~~~~~~~~~ See mount.fuse3(8). Control filesystem ~~~~~~~~~~~~~~~~~~ There's a control filesystem for FUSE, which can be mounted by: mount -t fusectl none /sys/fs/fuse/connections Mounting it under the '/sys/fs/fuse/connections' directory makes it backwards compatible with versions before 2.6.0. Under the fuse control filesystem each connection has a directory named by a unique number. For each connection the following files exist within this directory: 'waiting' The number of requests which are waiting to be transferred to userspace or being processed by the filesystem daemon. If there is no filesystem activity and 'waiting' is non-zero, then the filesystem is hung or deadlocked. 'abort' Writing anything into this file will abort the filesystem connection. This means that all waiting requests will be aborted an error returned for all aborted and new requests. Only the owner of the mount may read or write these files. Interrupting filesystem operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If a process issuing a FUSE filesystem request is interrupted, the following will happen: 1) If the request is not yet sent to userspace AND the signal is fatal (SIGKILL or unhandled fatal signal), then the request is dequeued and returns immediately. 2) If the request is not yet sent to userspace AND the signal is not fatal, then an 'interrupted' flag is set for the request. When the request has been successfully transferred to userspace and this flag is set, an INTERRUPT request is queued. 3) If the request is already sent to userspace, then an INTERRUPT request is queued. INTERRUPT requests take precedence over other requests, so the userspace filesystem will receive queued INTERRUPTs before any others. The userspace filesystem may ignore the INTERRUPT requests entirely, or may honor them by sending a reply to the _original_ request, with the error set to EINTR. It is also possible that there's a race between processing the original request and it's INTERRUPT request. There are two possibilities: 1) The INTERRUPT request is processed before the original request is processed 2) The INTERRUPT request is processed after the original request has been answered If the filesystem cannot find the original request, it should wait for some timeout and/or a number of new requests to arrive, after which it should reply to the INTERRUPT request with an EAGAIN error. In case 1) the INTERRUPT request will be requeued. In case 2) the INTERRUPT reply will be ignored. Aborting a filesystem connection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is possible to get into certain situations where the filesystem is not responding. Reasons for this may be: a) Broken userspace filesystem implementation b) Network connection down c) Accidental deadlock d) Malicious deadlock (For more on c) and d) see later sections) In either of these cases it may be useful to abort the connection to the filesystem. There are several ways to do this: - Kill the filesystem daemon. Works in case of a) and b) - Kill the filesystem daemon and all users of the filesystem. Works in all cases except some malicious deadlocks - Use forced umount (umount -f). Works in all cases but only if filesystem is still attached (it hasn't been lazy unmounted) - Abort filesystem through the FUSE control filesystem. Most powerful method, always works. How do non-privileged mounts work? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Since the mount() system call is a privileged operation, a helper program (fusermount3) is needed, which is installed setuid root. The implication of providing non-privileged mounts is that the mount owner must not be able to use this capability to compromise the system. Obvious requirements arising from this are: A) mount owner should not be able to get elevated privileges with the help of the mounted filesystem B) mount owner should not get illegitimate access to information from other users' and the super user's processes C) mount owner should not be able to induce undesired behavior in other users' or the super user's processes How are requirements fulfilled? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A) The mount owner could gain elevated privileges by either: 1) creating a filesystem containing a device file, then opening this device 2) creating a filesystem containing a suid or sgid application, then executing this application The solution is not to allow opening device files and ignore setuid and setgid bits when executing programs. To ensure this fusermount3 always adds "nosuid" and "nodev" to the mount options for non-privileged mounts. B) If another user is accessing files or directories in the filesystem, the filesystem daemon serving requests can record the exact sequence and timing of operations performed. This information is otherwise inaccessible to the mount owner, so this counts as an information leak. The solution to this problem will be presented in point 2) of C). C) There are several ways in which the mount owner can induce undesired behavior in other users' processes, such as: 1) mounting a filesystem over a file or directory which the mount owner could otherwise not be able to modify (or could only make limited modifications). This is solved in fusermount3, by checking the access permissions on the mountpoint and only allowing the mount if the mount owner can do unlimited modification (has write access to the mountpoint, and mountpoint is not a "sticky" directory) 2) Even if 1) is solved the mount owner can change the behavior of other users' processes. i) It can slow down or indefinitely delay the execution of a filesystem operation creating a DoS against the user or the whole system. For example a suid application locking a system file, and then accessing a file on the mount owner's filesystem could be stopped, and thus causing the system file to be locked forever. ii) It can present files or directories of unlimited length, or directory structures of unlimited depth, possibly causing a system process to eat up diskspace, memory or other resources, again causing DoS. The solution to this as well as B) is not to allow processes to access the filesystem, which could otherwise not be monitored or manipulated by the mount owner. Since if the mount owner can ptrace a process, it can do all of the above without using a FUSE mount, the same criteria as used in ptrace can be used to check if a process is allowed to access the filesystem or not. Note that the ptrace check is not strictly necessary to prevent B/2/i, it is enough to check if mount owner has enough privilege to send signal to the process accessing the filesystem, since SIGSTOP can be used to get a similar effect. I think these limitations are unacceptable? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If a sysadmin trusts the users enough, or can ensure through other measures, that system processes will never enter non-privileged mounts, it can relax the last limitation with a "user_allow_other" config option. If this config option is set, the mounting user can add the "allow_other" mount option which disables the check for other users' processes. Kernel - userspace interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following diagram shows how a filesystem operation (in this example unlink) is performed in FUSE. NOTE: everything in this description is greatly simplified | "rm /mnt/fuse/file" | FUSE filesystem daemon | | | | >sys_read() | | >fuse_dev_read() | | >request_wait() | | [sleep on fc->waitq] | | | >sys_unlink() | | >fuse_unlink() | | [get request from | | fc->unused_list] | | >request_send() | | [queue req on fc->pending] | | [wake up fc->waitq] | [woken up] | >request_wait_answer() | | [sleep on req->waitq] | | | <request_wait() | | [remove req from fc->pending] | | [copy req to read buffer] | | [add req to fc->processing] | | <fuse_dev_read() | | <sys_read() | | | | [perform unlink] | | | | >sys_write() | | >fuse_dev_write() | | [look up req in fc->processing] | | [remove from fc->processing] | | [copy write buffer to req] | [woken up] | [wake up req->waitq] | | <fuse_dev_write() | | <sys_write() | <request_wait_answer() | | <request_send() | | [add request to | | fc->unused_list] | | <fuse_unlink() | | <sys_unlink() | There are a couple of ways in which to deadlock a FUSE filesystem. Since we are talking about unprivileged userspace programs, something must be done about these. Scenario 1 - Simple deadlock ----------------------------- | "rm /mnt/fuse/file" | FUSE filesystem daemon | | | >sys_unlink("/mnt/fuse/file") | | [acquire inode semaphore | | for "file"] | | >fuse_unlink() | | [sleep on req->waitq] | | | <sys_read() | | >sys_unlink("/mnt/fuse/file") | | [acquire inode semaphore | | for "file"] | | *DEADLOCK* The solution for this is to allow the filesystem to be aborted. Scenario 2 - Tricky deadlock ---------------------------- This one needs a carefully crafted filesystem. It's a variation on the above, only the call back to the filesystem is not explicit, but is caused by a pagefault. | Kamikaze filesystem thread 1 | Kamikaze filesystem thread 2 | | | [fd = open("/mnt/fuse/file")] | [request served normally] | [mmap fd to 'addr'] | | [close fd] | [FLUSH triggers 'magic' flag] | [read a byte from addr] | | >do_page_fault() | | [find or create page] | | [lock page] | | >fuse_readpage() | | [queue READ request] | | [sleep on req->waitq] | | | [read request to buffer] | | [create reply header before addr] | | >sys_write(addr - headerlength) | | >fuse_dev_write() | | [look up req in fc->processing] | | [remove from fc->processing] | | [copy write buffer to req] | | >do_page_fault() | | [find or create page] | | [lock page] | | * DEADLOCK * Solution is basically the same as above. An additional problem is that while the write buffer is being copied to the request, the request must not be interrupted/aborted. This is because the destination address of the copy may not be valid after the request has returned. This is solved with doing the copy atomically, and allowing abort while the page(s) belonging to the write buffer are faulted with get_user_pages(). The 'req->locked' flag indicates when the copy is taking place, and abort is delayed until this flag is unset. ��������������������������������������������fuse-3.18.2/doc/libfuse-operations.txt��������������������������������������������������������������0000644�0001750�0001750�00000031735�15156613252�016654� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������List of libfuse operations with their in/out arguments, created with help of chatgpt. As of kernel 6.18 (protocol 7.45). The list was only partly human verified - use with care. 1. FUSE_LOOKUP (1) - in_args[0]: Variable (up to PATH_MAX for the file name) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_entry_out (typically 128 bytes) - out_args[1]: Not used - out_args[2]: Not used 2. FUSE_FORGET (2) - in_args[0]: Size of fuse_forget_in (typically 8 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 3. FUSE_GETATTR (3) - in_args[0]: Size of fuse_getattr_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_attr_out (typically 96 bytes) - out_args[1]: Not used - out_args[2]: Not used 4. FUSE_SETATTR (4) - in_args[0]: Size of fuse_setattr_in (typically 32 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_attr_out (typically 96 bytes) - out_args[1]: Not used - out_args[2]: Not used 5. FUSE_READLINK (5) - in_args[0]: Not used - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Variable (size of the link target path) - out_args[1]: Not used - out_args[2]: Not used 6. FUSE_SYMLINK (6) - in_args[0]: Variable (new link file name, up to PATH_MAX) - in_args[1]: Variable (target path for the symlink, up to PATH_MAX) - in_args[2]: Not used - out_args[0]: Size of fuse_entry_out (typically 128 bytes) - out_args[1]: Not used - out_args[2]: Not used 7. Unused (7) 8. FUSE_MKNOD (8) - in_args[0]: Size of fuse_mknod_in (24 bytes) - in_args[1]: Variable (file name, up to PATH_MAX) - in_args[2]: Not used - out_args[0]: Size of fuse_entry_out (typically 128 bytes) - out_args[1]: Not used - out_args[2]: Not used 9. FUSE_MKDIR (9) - in_args[0]: Size of fuse_mkdir_in (16 bytes) - in_args[1]: Variable (directory name, up to PATH_MAX) - in_args[2]: Not used - out_args[0]: Size of fuse_entry_out (typically 128 bytes) - out_args[1]: Not used - out_args[2]: Not used 10. FUSE_UNLINK (10) - in_args[0]: Variable (file name, up to PATH_MAX) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 11. FUSE_RMDIR (11) - in_args[0]: Variable (directory name, up to PATH_MAX) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 12. FUSE_RENAME (12) - in_args[0]: Size of fuse_rename_in (24 bytes) - in_args[1]: Variable (old path, up to PATH_MAX) - in_args[2]: Variable (new path, up to PATH_MAX) - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 13. FUSE_LINK (13) - in_args[0]: Size of fuse_link_in (16 bytes) - in_args[1]: Variable (new link path, up to PATH_MAX) - in_args[2]: Not used - out_args[0]: Size of fuse_entry_out (typically 128 bytes) - out_args[1]: Not used - out_args[2]: Not used 14. FUSE_OPEN (14) - in_args[0]: Size of fuse_open_in (8 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_open_out (24 bytes) - out_args[1]: Not used - out_args[2]: Not used 15. FUSE_READ (15) - in_args[0]: Size of fuse_read_in (32 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Variable (data read from the file) - out_args[1]: Not used - out_args[2]: Not used 16. FUSE_WRITE (16) - in_args[0]: Size of fuse_write_in (32 bytes) - in_args[1]: Variable (data to write) - in_args[2]: Not used - out_args[0]: Size of fuse_write_out (24 bytes) - out_args[1]: Not used - out_args[2]: Not used 17. FUSE_STATFS (17) - in_args[0]: Not used - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_statfs_out (typically 96 bytes) - out_args[1]: Not used - out_args[2]: Not used 18. FUSE_RELEASE (18) - in_args[0]: Size of fuse_release_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 19. Unused (19) 20. FUSE_FSYNC (20) - in_args[0]: Size of fuse_fsync_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 21. FUSE_SETXATTR (21) - in_args[0]: Size of fuse_setxattr_in (24 bytes) - in_args[1]: Variable (name of attribute, up to 255 bytes) - in_args[2]: Variable (value of attribute) - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used - When FUSE_SETXATTR_EXT is not set: Size of fuse_setxattr_in is 16 bytes (FUSE_COMPAT_SETXATTR_IN_SIZE). 22. FUSE_GETXATTR (22) - in_args[0]: Size of fuse_getxattr_in (24 bytes) - in_args[1]: Variable (name of attribute, up to 255 bytes) - in_args[2]: Not used - out_args[0]: Variable (value of the attribute) - out_args[1]: Not used - out_args[2]: Not used 23. FUSE_LISTXATTR (23) - in_args[0]: Size of fuse_getxattr_in (24 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Variable (list of attribute names, each up to 255 bytes) - out_args[1]: Not used - out_args[2]: Not used 24. FUSE_REMOVEXATTR (24) - in_args[0]: Variable (name of attribute, up to 255 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 25. FUSE_FLUSH (25) - in_args[0]: Size of fuse_flush_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 26. FUSE_INIT (26) - in_args[0]: Size of fuse_init_in (64 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_init_out (typically 80 bytes) - out_args[1]: Not used - out_args[2]: Not used - Flags: - FUSE_SECURITY_CTX - FUSE_CREATE_SUPP_GROUP - FUSE_SETXATTR_EXT - FUSE_COMPAT_SETXATTR_IN_SIZE - Additional flags may include FUSE_ASYNC_READ, FUSE_POSIX_LOCKS, FUSE_FILE_OPS, etc. 27. FUSE_OPENDIR (27) - in_args[0]: Size of fuse_open_in (8 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_open_out (24 bytes) - out_args[1]: Not used - out_args[2]: Not used 28. FUSE_READDIR (28) - in_args[0]: Size of fuse_read_in (32 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Variable (directory entries) - out_args[1]: Not used - out_args[2]: Not used 29. FUSE_RELEASEDIR (29) - in_args[0]: Size of fuse_release_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 30. FUSE_FSYNCDIR (30) - in_args[0]: Size of fuse_fsync_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 31. FUSE_GETLK (31) - in_args[0]: Size of fuse_lk_in (32 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_lk_out (typically 32 bytes) - out_args[1]: Not used - out_args[2]: Not used 32. FUSE_SETLK (32) - in_args[0]: Size of fuse_lk_in (32 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 33. FUSE_SETLKW (33) - in_args[0]: Size of fuse_lk_in (32 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 34. FUSE_ACCESS (34) - in_args[0]: Size of fuse_access_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 35. FUSE_CREATE (35) - in_args[0]: Size of fuse_create_in (16 bytes) - in_args[1]: Variable (file name, up to PATH_MAX) - in_args[2]: Not used - out_args[0]: Size of fuse_entry_out (typically 128 bytes) - out_args[1]: Size of fuse_open_out (24 bytes) - out_args[2]: Not used - When FUSE_SECURITY_CTX is set (fc->init_security): Additional security context included in in_args and/or out_args. - When FUSE_CREATE_SUPP_GROUP is set (fc->create_supp_group): Additional supplementary group information included in in_args. 36. FUSE_INTERRUPT (36) - in_args[0]: Size of fuse_interrupt_in (8 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 37. FUSE_BMAP (37) - in_args[0]: Size of fuse_bmap_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_bmap_out (16 bytes) - out_args[1]: Not used - out_args[2]: Not used 38. FUSE_DESTROY (38) - in_args[0]: Not used - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 39. FUSE_IOCTL (39) - in_args[0]: Size of fuse_ioctl_in (64 bytes) - in_args[1]: Variable (ioctl command data) - in_args[2]: Not used - out_args[0]: Size of fuse_ioctl_out (32 bytes) - out_args[1]: Variable (data returned by the ioctl command) - out_args[2]: Not used 40. FUSE_POLL (40) - in_args[0]: Size of fuse_poll_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_poll_out (16 bytes) - out_args[1]: Not used - out_args[2]: Not used 41. FUSE_NOTIFY_REPLY (41) - in_args[0]: Variable (reply data) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 42. FUSE_BATCH_FORGET (42) - in_args[0]: Variable (size depends on the number of forget requests) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 43. FUSE_FALLOCATE (43) - in_args[0]: Size of fuse_fallocate_in (24 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 44. FUSE_READDIRPLUS (44) - in_args[0]: Size of fuse_read_in (32 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Variable (directory entries) - out_args[1]: Not used - out_args[2]: Not used 45. FUSE_RENAME2 (45) - in_args[0]: Size of fuse_rename2_in (32 bytes) - in_args[1]: Variable (old path, up to PATH_MAX) - in_args[2]: Variable (new path, up to PATH_MAX) - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 46. FUSE_LSEEK (46) - in_args[0]: Size of fuse_lseek_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_lseek_out (16 bytes) - out_args[1]: Not used - out_args[2]: Not used 47. FUSE_COPY_FILE_RANGE (47) - in_args[0]: Size of fuse_copy_file_range_in (56 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_write_out (24 bytes) - out_args[1]: Not used - out_args[2]: Not used 48. FUSE_SETUPMAPPING (48) - in_args[0]: Size of fuse_setupmapping_in (32 bytes) - in_args[1]: Variable (data to map) - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 49. FUSE_REMOVEMAPPING (49) - in_args[0]: Variable (size depends on the number of mappings to remove) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 50. FUSE_SYNCFS (50) - in_args[0]: Size of fuse_syncfs_in (16 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Not used - out_args[1]: Not used - out_args[2]: Not used 51. FUSE_TMPFILE (51) - in_args[0]: Size of fuse_create_in (16 bytes) - in_args[1]: Variable (file name, up to PATH_MAX) - in_args[2]: Not used - out_args[0]: Size of fuse_entry_out (typically 128 bytes) - out_args[1]: Size of fuse_open_out (24 bytes) - out_args[2]: Not used - When FUSE_SECURITY_CTX is set (fc->init_security): Additional security context included in in_args and/or out_args. - When FUSE_CREATE_SUPP_GROUP is set (fc->create_supp_group): Additional supplementary group information included in in_args. 52. FUSE_STATX (52) - in_args[0]: Size of fuse_statx_in (32 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_statx_out (typically 256 bytes) - out_args[1]: Not used - out_args[2]: Not used 53. FUSE_COPY_FILE_RANGE_64 (53) - in_args[0]: Size of fuse_copy_file_range_in (56 bytes) - in_args[1]: Not used - in_args[2]: Not used - out_args[0]: Size of fuse_copy_file_range_out (8 bytes) - out_args[1]: Not used - out_args[2]: Not used �����������������������������������fuse-3.18.2/doc/mainpage.dox������������������������������������������������������������������������0000644�0001750�0001750�00000004147�15156613252�014573� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*! \mainpage libfuse API documentation FUSE (Filesystem in Userspace) is an interface for userspace programs to export a filesystem to the Linux kernel. The FUSE project consists of two components: the *fuse* kernel module (maintained in the regular kernel repositories) and the *libfuse* userspace library. libfuse provides the reference implementation for communicating with the FUSE kernel module. A FUSE file system is typically implemented as a standalone application that links with libfuse. libfuse provides functions to mount the file system, unmount it, read requests from the kernel, and send responses back. ## Getting started ## libfuse offers two APIs: a "high-level", synchronous API, and a "low-level" asynchronous API. In both cases, incoming requests from the kernel are passed to the main program using callbacks. When using the high-level API, the callbacks may work with file names and paths instead of inodes, and processing of a request finishes when the callback function returns. When using the low-level API, the callbacks must work with inodes and responses must be sent explicitly using a separate set of API functions. The high-level API that is primarily specified in fuse.h. The low-level API that is primarily documented in fuse_lowlevel.h. ## Examples ## FUSE comes with several examples in the <a href="files.html">examples</a> directory. A good starting point are hello.c (for the high-level API) and hello_ll.c (for the low-level API). ## FUSE internals ## The authoritative source of information about libfuse internals (including the protocol used for communication with the FUSE kernel module) is the source code. However, some people have kindly documented different aspects of FUSE in a more beginner friendly way. While this information is increasingly out of date, it still provides a good overview: - Bharat Vangoor et al have included an overview of the FUSE internals in a <a href="fast17-vangoor.pdf">paper evaluating FUSE performance</a>. - Some documentation of the kernel-userspace protocol is available on the <a href="https://github.com/libfuse/libfuse/wiki/">libfuse wiki</a>. */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/meson.build�������������������������������������������������������������������������0000644�0001750�0001750�00000000163�15156613252�014432� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if not platform.endswith('bsd') and platform != 'dragonfly' install_man('fusermount3.1', 'mount.fuse3.8') endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/mount.fuse3.8�����������������������������������������������������������������������0000644�0001750�0001750�00000031425�15156613252�014554� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.TH fuse "8" .SH NAME fuse \- configuration and mount options for FUSE file systems .SH DESCRIPTION FUSE (Filesystem in Userspace) is a simple interface for userspace programs to export a virtual filesystem to the Linux kernel. FUSE also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations. .SH DEFINITIONS .TP \fBFUSE\fP The in-kernel filesystem that forwards requests to a user-space process. .TP \fBfilesystem\fP The user-space process that responds to requests received from the kernel. .TP \fBlibfuse\fP The shared library that most (user-space) filesystems use to communicate with FUSE (the kernel filesystem). libfuse also provides the \fBfusermount3\fP (or \fBfusermount\fP if you have older version of libfuse) helper to allow non-privileged users to mount filesystems. .TP \fBfilesystem owner\fP The user that starts the filesystem and instructs the kernel to associate it with a particular mountpoint. The latter is typically done by the filesystem itself on start-up. When using libfuse, this is done by calling the \fBfusermount3\fP utility. .TP \fBclient\fP Any process that interacts with the mountpoint. .SH CONFIGURATION Some options regarding mount policy can be set in the file \fI/etc/fuse.conf\fP. Currently these options are: .TP \fBmount_max = NNN\fP Set the maximum number of FUSE mounts allowed to non-root users. The default is 1000. .TP \fBuser_allow_other\fP Allow non-root users to specify the \fBallow_other\fP or \fBallow_root\fP mount options (see below). .TP These limits are enforced by the \fBfusermount3\fP helper, so they can be avoided by filesystems that run as root. .SH OPTIONS Most of the generic mount options described in \fBmount\fP are supported (\fBro\fP, \fBrw\fP, \fBsuid\fP, \fBnosuid\fP, \fBdev\fP, \fBnodev\fP, \fBexec\fP, \fBnoexec\fP, \fBatime\fP, \fBnoatime\fP, \fBsync\fP, \fBasync\fP, \fBdirsync\fP). Filesystems are mounted with \fBnodev,nosuid\fP by default, which can only be overridden by a privileged user. .SS "General mount options:" These are FUSE specific mount options that can be specified for all filesystems: .TP \fBdefault_permissions\fP This option instructs the kernel to perform its own permission check instead of deferring all permission checking to the filesystem. The check by the kernel is done in addition to any permission checks by the filesystem, and both have to succeed for an operation to be allowed. The kernel performs a standard UNIX permission check (based on mode bits and ownership of the directory entry, and uid/gid of the client). This mount option is activated implicitly if the filesystem enables ACL support during the initial feature negotiation when opening the device fd. In this case, the kernel performs both ACL and standard unix permission checking. Filesystems that do not implement any permission checking should generally add this option internally. .TP \fBallow_other\fP This option overrides the security measure restricting file access to the filesystem owner, so that all users (including root) can access the files. .TP \fBrootmode=M\fP Specifies the file mode of the filesystem's root (in octal representation). .TP \fBblkdev\fP Mount a filesystem backed by a block device. This is a privileged option. The device must be specified with the \fBfsname=NAME\fP option. .TP \fBblksize=N\fP Set the block size for the filesystem. This option is only valid for 'fuseblk' type mounts. The default is 512. In most cases, this option should not be specified by the filesystem owner but set internally by the filesystem. .TP \fBmax_read=N\fP With this option the maximum size of read operations can be set. The default is infinite, but typically the kernel enforces its own limit in addition to this one. A value of zero corresponds to no limit. This option should not be specified by the filesystem owner. The correct (or optimum) value depends on the filesystem implementation and should thus be set by the filesystem internally. This mount option is deprecated in favor of direct negotiation over the device fd (as done for e.g. the maximum size of write operations). For the time being, libfuse-using filesystems that want to limit the read size must therefore use this mount option \fIand\fP set the same value again in the init() handler. .TP \fBfd=N\fP The file descriptor to use for communication between the userspace filesystem and the kernel. The file descriptor must have been obtained by opening the FUSE device (/dev/fuse). This option should not be specified by the filesystem owner. It is set by libfuse (or, if libfuse is not used, must be set by the filesystem itself). .TP \fBuser_id=N\fP \fBgroup_id=N\fP Specifies the numeric uid/gid of the mount owner. This option should not be specified by the filesystem owner. It is set by libfuse (or, if libfuse is not used, must be set by the filesystem itself). .TP \fBfsname=NAME\fP Sets the filesystem source (first field in \fI/etc/mtab\fP). The default is the name of the filesystem process. .TP \fBsubtype=TYPE\fP Sets the filesystem type (third field in \fI/etc/mtab\fP). The default is the name of the filesystem process. If the kernel supports it, \fI/etc/mtab\fP and \fI/proc/mounts\fP will show the filesystem type as \fBfuse.TYPE\fP If the kernel doesn't support subtypes, the source field will be \fBTYPE#NAME\fP, or if \fBfsname\fP option is not specified, just \fBTYPE\fP. .SS "libfuse-specific mount options:" These following options are not actually passed to the kernel but interpreted by libfuse. They can be specified for all filesystems that use libfuse: .TP \fBallow_root\fP This option is similar to \fBallow_other\fP but file access is limited to the filesystem owner and root. This option and \fBallow_other\fP are mutually exclusive. .TP \fBauto_unmount\fP This option enables automatic release of the mountpoint if filesystem terminates for any reason. Normally the filesystem is 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. .SS "High-level mount options:" These following options are not actually passed to the kernel but interpreted by libfuse. They can only be specified for filesystems that use the high-level libfuse API: .TP \fBkernel_cache\fP This option disables flushing the cache of the file contents on every \fBopen\fP(2). This should only be enabled on filesystems, where the file data is never changed externally (not through the mounted FUSE filesystem). Thus it is not suitable for network filesystems and other "intermediate" filesystems. \fBNOTE\fP: if this option is not specified (and neither \fBdirect_io\fP) data is still cached after the \fBopen\fP(2), so a \fBread\fP(2) system call will not always initiate a read operation. .TP \fBauto_cache\fP This option is an alternative to \fBkernel_cache\fP. Instead of unconditionally keeping cached data, the cached data is invalidated on \fBopen\fP(2) if the modification time or the size of the file has changed since it was last opened. .TP \fBumask=M fmask=M dmask=M\fP Override the permission bits set by the filesystem in \fIst_mode\fP. The resulting permission bits are the ones missing from the mask value, which is given in octal representation. \fBfmask\fP and \fBdmask\fP (respectively) may be used to control the permission bits of files and directories separately. umask is overridden by the individual fmask and dmask options. .TP \fBuid=N\fP Override the \fIst_uid\fP field set by the filesystem (N is numeric). .TP \fBgid=N\fP Override the \fIst_gid\fP field set by the filesystem (N is numeric). .TP \fBentry_timeout=T\fP The timeout in seconds for which name lookups will be cached. The default is 1.0 second. For all the timeout options, it is possible to give fractions of a second as well (e.g. \fBentry_timeout=2.8\fP) .TP \fBnegative_timeout=T\fP The timeout in seconds for which a negative lookup will be cached. This means, that if file did not exist (lookup returned \fBENOENT\fP), the lookup will only be redone after the timeout, and the file/directory will be assumed to not exist until then. The default is 0.0 second, meaning that caching negative lookups are disabled. .TP \fBattr_timeout=T\fP The timeout in seconds for which file/directory attributes are cached. The default is 1.0 second. .TP \fBac_attr_timeout=T\fP The timeout in seconds for which file attributes are cached for the purpose of checking if \fBauto_cache\fP should flush the file data on open. The default is the value of \fBattr_timeout\fP .TP \fBnoforget\fP .TP \fBremember=T\fP Normally, libfuse assigns inodes to paths only for as long as the kernel is aware of them. With this option inodes are instead assigned for at least \fBT\fP seconds (or, in the case of \fBnoforget\fP, the life-time of the filesystem). This will require more memory, but may be necessary when using applications that make use of inode numbers. .TP \fBmodules=M1[:M2...]\fP Add modules to the filesystem stack. Modules are pushed in the order they are specified, with the original filesystem being on the bottom of the stack. .SS "\fBmount.fuse3\fP options:" These options are interpreted by \fBmount.fuse3\fP and are thus only available when mounting a file system via \fBmount.fuse3\fP (such as when mounting via the generic \fBmount\fP(1) command or \fI/etc/fstab\fP). Supported options are: .TP \fBsetuid=USER\fP Switch to \fBUSER\fP and its primary group before launching the FUSE file system process. mount.fuse3 must be run as root or with \fBCAP_SETUID\fP and \fBCAP_SETGID\fP for this to work. .TP \fBdrop_privileges\fP Perform setup of the FUSE file descriptor and mounting the file system before launching the FUSE file system process. \fBmount.fuse3\fP requires privilege to do so, i.e. must be run as root or at least with \fBCAP_SYS_ADMIN\fP and \fBCAP_SETPCAP\fP. It will launch the file system process fully unprivileged, i.e. without \fBcapabilities\fP(7) and \fBprctl\fP(2) flags set up such that privileges can't be reacquired (e.g. via setuid or fscaps binaries). This reduces risk in the event of the FUSE file system process getting compromised by malicious file system data. .SH FUSE MODULES (STACKING) Modules are filesystem stacking support to high level API. Filesystem modules can be built into libfuse or loaded from shared object .SS "iconv" Perform file name character set conversion. Options are: .TP \fBfrom_code=CHARSET\fP Character set to convert from (see \fBiconv -l\fP for a list of possible values). Default is \fBUTF-8\fP. .TP \fBto_code=CHARSET\fP Character set to convert to. Default is determined by the current locale. .SS "subdir" Prepend a given directory to each path. Options are: .TP \fBsubdir=DIR\fP Directory to prepend to all paths. This option is \fImandatory\fP. .TP \fBrellinks\fP Transform absolute symlinks into relative .TP \fBnorellinks\fP Do not transform absolute symlinks into relative. This is the default. .SH SECURITY The fusermount3 program is installed set-user-gid to fuse. This is done to allow users from fuse group to mount their own filesystem implementations. There must however be some limitations, in order to prevent Bad User from doing nasty things. Currently those limitations are: .IP 1. The user can only mount on a mountpoint, for which it has write permission .IP 2. The mountpoint is not a sticky directory which isn't owned by the user (like \fI/tmp\fP usually is) .IP 3. No other user (including root) can access the contents of the mounted filesystem. .SH NOTE FUSE filesystems are unmounted using the \fBfusermount3\fP(1) command (\fBfusermount3 -u mountpoint\fP). .SH "AUTHORS" .LP FUSE is currently maintained by Nikolaus Rath <Nikolaus@rath.org> .LP The original author of FUSE is Miklos Szeredi <mszeredi@inf.bme.hu>. .LP This man page was originally written by Bastien Roucaries <roucaries.bastien+debian@gmail.com> for the Debian GNU/Linux distribution. .SH SEE ALSO .BR fusermount3 (1) .BR fusermount (1) .BR mount (8) .BR fuse (4) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�15156613431�013233� 5����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/tabs.css�����������������������������������������������������������������������0000644�0001750�0001750�00000024500�15156613442�014701� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.sm{position:relative;z-index:9999}.sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0)}.sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right}.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0}.sm ul{display:none}.sm li,.sm a{position:relative}.sm a{display:block}.sm a.disabled{cursor:not-allowed}.sm:after{content:"\00a0";display:block;height:0;font:0px/0 serif;clear:both;visibility:hidden;overflow:hidden}.sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.main-menu-btn{position:relative;display:inline-block;width:36px;height:36px;text-indent:36px;margin-left:8px;white-space:nowrap;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.main-menu-btn-icon,.main-menu-btn-icon:before,.main-menu-btn-icon:after{position:absolute;top:50%;left:2px;height:2px;width:24px;background:var(--nav-menu-button-color);-webkit-transition:all 0.25s;transition:all 0.25s}.main-menu-btn-icon:before{content:'';top:-7px;left:0}.main-menu-btn-icon:after{content:'';top:7px;left:0}#main-menu-state:checked~.main-menu-btn .main-menu-btn-icon{height:0}#main-menu-state:checked~.main-menu-btn .main-menu-btn-icon:before{top:0;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}#main-menu-state:checked~.main-menu-btn .main-menu-btn-icon:after{top:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}#main-menu-state{position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}#main-menu-state:not(:checked)~#main-menu{display:none}#main-menu-state:checked~#main-menu{display:block}@media (min-width: 768px){.main-menu-btn{position:absolute;top:-99999px}#main-menu-state:not(:checked)~#main-menu{display:block}}.sm-dox{background-image:var(--nav-gradient-image)}.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active{padding:0px 12px;padding-right:43px;font-family:var(--font-family-nav);font-size:13px;font-weight:bold;line-height:36px;text-decoration:none;text-shadow:var(--nav-text-normal-shadow);color:var(--nav-text-normal-color);outline:none}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a.current{color:#D23600}.sm-dox a.disabled{color:#bbb}.sm-dox a span.sub-arrow{position:absolute;top:50%;margin-top:-14px;left:auto;right:3px;width:28px;height:28px;overflow:hidden;font:bold 12px/28px monospace !important;text-align:center;text-shadow:none;background:var(--nav-menu-toggle-color);border-radius:5px}.sm-dox a span.sub-arrow:before{display:block;content:'+'}.sm-dox a.highlighted span.sub-arrow:before{display:block;content:'-'}.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a{border-radius:5px 5px 0 0}.sm-dox>li:last-child>a,.sm-dox>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul{border-radius:0 0 5px 5px}.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted{border-radius:0}.sm-dox ul{background:var(--nav-menu-background-color)}.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active{font-size:12px;border-left:8px solid transparent;line-height:36px;text-shadow:none;background-color:var(--nav-menu-background-color);background-image:none}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:0px 1px 1px #000}.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active{border-left:16px solid transparent}.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active{border-left:24px solid transparent}.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active{border-left:32px solid transparent}.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active{border-left:40px solid transparent}@media (min-width: 768px){.sm-dox ul{position:absolute;width:12em}.sm-dox li{float:left}.sm-dox.sm-rtl li{float:right}.sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li{float:none}.sm-dox a{white-space:nowrap}.sm-dox ul a,.sm-dox.sm-vertical a{white-space:normal}.sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a{white-space:nowrap}.sm-dox{padding:0 10px;background-image:var(--nav-gradient-image);line-height:36px}.sm-dox a span.sub-arrow{top:50%;margin-top:-2px;right:12px;width:0;height:0;border-width:4px;border-style:solid dashed dashed dashed;border-color:var(--nav-text-normal-color) transparent transparent transparent;background:transparent;border-radius:0}.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted{padding:0px 12px;background-image:var(--nav-separator-image);background-repeat:no-repeat;background-position:right;border-radius:0 !important}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a:hover span.sub-arrow{border-color:var(--nav-text-hover-color) transparent transparent transparent}.sm-dox a.has-submenu{padding-right:24px}.sm-dox li{border-top:0}.sm-dox>li>ul:before,.sm-dox>li>ul:after{content:'';position:absolute;top:-18px;left:30px;width:0;height:0;overflow:hidden;border-width:9px;border-style:dashed dashed solid dashed;border-color:transparent transparent #bbb transparent}.sm-dox>li>ul:after{top:-16px;left:31px;border-width:8px;border-color:transparent transparent var(--nav-menu-background-color) transparent}.sm-dox ul{border:1px solid #bbb;padding:5px 0;background:var(--nav-menu-background-color);border-radius:5px !important;box-shadow:0 5px 9px rgba(0,0,0,0.2)}.sm-dox ul a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-color:transparent transparent transparent var(--nav-menu-foreground-color);border-style:dashed dashed dashed solid}.sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted{color:var(--nav-menu-foreground-color);background-image:none;border:0 !important;color:var(--nav-menu-foreground-color);background-image:none}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent var(--nav-text-hover-color)}.sm-dox span.scroll-up,.sm-dox span.scroll-down{position:absolute;display:none;visibility:hidden;overflow:hidden;background:var(--nav-menu-background-color);height:36px}.sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover{background:#eee}.sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow{border-color:transparent transparent #D23600 transparent}.sm-dox span.scroll-down:hover span.scroll-down-arrow{border-color:#D23600 transparent transparent transparent}.sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow{position:absolute;top:0;left:50%;margin-left:-6px;width:0;height:0;overflow:hidden;border-width:6px;border-style:dashed dashed solid dashed;border-color:transparent transparent var(--nav-menu-foreground-color) transparent}.sm-dox span.scroll-down-arrow{top:8px;border-style:solid dashed dashed dashed;border-color:var(--nav-menu-foreground-color) transparent transparent transparent}.sm-dox.sm-rtl a.has-submenu{padding-right:12px;padding-left:24px}.sm-dox.sm-rtl a span.sub-arrow{right:auto;left:12px}.sm-dox.sm-rtl.sm-vertical a.has-submenu{padding:10px 20px}.sm-dox.sm-rtl.sm-vertical a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-rtl>li>ul:before{left:auto;right:30px}.sm-dox.sm-rtl>li>ul:after{left:auto;right:31px}.sm-dox.sm-rtl ul a.has-submenu{padding:10px 20px !important}.sm-dox.sm-rtl ul a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-vertical{padding:10px 0;border-radius:5px}.sm-dox.sm-vertical a{padding:10px 20px}.sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted{background:#fff}.sm-dox.sm-vertical a.disabled{background-image:var(--nav-gradient-image)}.sm-dox.sm-vertical a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-style:dashed dashed dashed solid;border-color:transparent transparent transparent #555}.sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after{display:none}.sm-dox.sm-vertical ul a{padding:10px 20px}.sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted{background:#eee}.sm-dox.sm-vertical ul a.disabled{background:var(--nav-menu-background-color)}} ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/jquery.js����������������������������������������������������������������������0000644�0001750�0001750�00000530766�15156613442�015133� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),j=function(e,t){return e===t&&(l=!0),0},D={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&D.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(j),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(j).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var D,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^([^.]*)(?:\.(.+)|)/;function we(){return!0}function Te(){return!1}function Ce(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ee(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ee(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Te;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Se(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n&&n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,we)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=be.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=be.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click",we),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?we:Te,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Te,isPropagationStopped:Te,isImmediatePropagationStopped:Te,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=we,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=we,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=we,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:!0},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Se(this,e,Ce),!1},trigger:function(){return Se(this,e),!0},_default:function(){return!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return Ee(this,e,t,n,r)},one:function(e,t,n,r){return Ee(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Te),this.each(function(){S.event.remove(this,e,n,t)})}});var ke=/<script|<style|<link/i,Ae=/checked\s*(?:[^=]|=\s*.checked.)/i,Ne=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function He(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&Ae.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),He(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),De)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,qe),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(Ne,""),u,l))}return n}function Oe(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Le(o[r],a[r]);else Le(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Oe(this,e,!0)},remove:function(e){return Oe(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return He(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||je(this,e).appendChild(e)})},prepend:function(){return He(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=je(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ke.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return He(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Pe=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Re=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},Me=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Ie=new RegExp(ne.join("|"),"i");function We(e,t,n){var r,i,o,a,s=e.style;return(n=n||Re(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Pe.test(a)&&Ie.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function Fe(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px;border-collapse:separate",t.style.cssText="border:1px solid",t.style.height="1px",n.style.height="9px",n.style.display="block",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=parseInt(r.height,10)+parseInt(r.borderTopWidth,10)+parseInt(r.borderBottomWidth,10)===t.offsetHeight,re.removeChild(e)),a}}))}();var Be=["Webkit","Moz","ms"],$e=E.createElement("div").style,_e={};function ze(e){var t=S.cssProps[e]||_e[e];return t||(e in $e?e:_e[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=Be.length;while(n--)if((e=Be[n]+t)in $e)return e}(e)||e)}var Ue=/^(none|table(?!-c[ea]).+)/,Xe=/^--/,Ve={position:"absolute",visibility:"hidden",display:"block"},Ge={letterSpacing:"0",fontWeight:"400"};function Ye(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Qe(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Je(e,t,n){var r=Re(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=We(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Pe.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Qe(e,t,n||(i?"border":"content"),o,r,a)+"px"}function Ke(e,t,n,r,i){return new Ke.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=We(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Xe.test(t),l=e.style;if(u||(t=ze(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Xe.test(t)||(t=ze(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=We(e,t,r)),"normal"===i&&t in Ge&&(i=Ge[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ue.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Je(e,u,n):Me(e,Ve,function(){return Je(e,u,n)})},set:function(e,t,n){var r,i=Re(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Qe(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Qe(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Ye(0,t,s)}}}),S.cssHooks.marginLeft=Fe(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(We(e,"marginLeft"))||e.getBoundingClientRect().left-Me(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Ye)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Re(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=Ke).prototype={constructor:Ke,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=Ke.propHooks[this.prop];return e&&e.get?e.get(this):Ke.propHooks._default.get(this)},run:function(e){var t,n=Ke.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Ke.propHooks._default.set(this),this}}).init.prototype=Ke.prototype,(Ke.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[ze(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=Ke.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=Ke.prototype.init,S.fx.step={};var Ze,et,tt,nt,rt=/^(?:toggle|show|hide)$/,it=/queueHooks$/;function ot(){et&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(ot):C.setTimeout(ot,S.fx.interval),S.fx.tick())}function at(){return C.setTimeout(function(){Ze=void 0}),Ze=Date.now()}function st(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ut(e,t,n){for(var r,i=(lt.tweeners[t]||[]).concat(lt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function lt(o,e,t){var n,a,r=0,i=lt.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=Ze||at(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:Ze||at(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=lt.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ut,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(lt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],lt.tweeners[n]=lt.tweeners[n]||[],lt.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],rt.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ut(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?lt.prefilters.unshift(e):lt.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=lt(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&it.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(st(r,!0),e,t,n)}}),S.each({slideDown:st("show"),slideUp:st("hide"),slideToggle:st("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(Ze=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),Ze=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){et||(et=!0,ot())},S.fx.stop=function(){et=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},tt=E.createElement("input"),nt=E.createElement("select").appendChild(E.createElement("option")),tt.type="checkbox",y.checkOn=""!==tt.value,y.optSelected=nt.selected,(tt=E.createElement("input")).value="t",tt.type="radio",y.radioValue="t"===tt.value;var ct,ft=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?ct:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),ct={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=ft[t]||S.find.attr;ft[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=ft[o],ft[o]=r,r=null!=a(e,t,n)?o:null,ft[o]=i),r}});var pt=/^(?:input|select|textarea|button)$/i,dt=/^(?:a|area)$/i;function ht(e){return(e.match(P)||[]).join(" ")}function gt(e){return e.getAttribute&&e.getAttribute("class")||""}function vt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):pt.test(e.nodeName)||dt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,gt(this)))});if((e=vt(t)).length)while(n=this[u++])if(i=gt(n),r=1===n.nodeType&&" "+ht(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=ht(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,gt(this)))});if(!arguments.length)return this.attr("class","");if((e=vt(t)).length)while(n=this[u++])if(i=gt(n),r=1===n.nodeType&&" "+ht(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=ht(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,gt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=vt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=gt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+ht(gt(n))+" ").indexOf(t))return!0;return!1}});var yt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(yt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:ht(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var mt=/^(?:focusinfocus|focusoutblur)$/,xt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!mt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,mt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,xt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,xt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var bt=C.location,wt={guid:Date.now()},Tt=/\?/;S.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){}return n=t&&t.getElementsByTagName("parsererror")[0],t&&!n||S.error("Invalid XML: "+(n?S.map(n.childNodes,function(e){return e.textContent}).join("\n"):e)),t};var Ct=/\[\]$/,Et=/\r?\n/g,St=/^(?:submit|button|image|reset|file)$/i,kt=/^(?:input|select|textarea|keygen)/i;function At(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||Ct.test(n)?i(n,t):At(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)At(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)At(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&kt.test(this.nodeName)&&!St.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(Et,"\r\n")}}):{name:t.name,value:n.replace(Et,"\r\n")}}).get()}});var Nt=/%20/g,jt=/#.*$/,Dt=/([?&])_=[^&]*/,qt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Lt=/^(?:GET|HEAD)$/,Ht=/^\/\//,Ot={},Pt={},Rt="*/".concat("*"),Mt=E.createElement("a");function It(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Wt(t,i,o,a){var s={},u=t===Pt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function Ft(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Mt.href=bt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:bt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(bt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Rt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Ft(Ft(e,S.ajaxSettings),t):Ft(S.ajaxSettings,e)},ajaxPrefilter:It(Ot),ajaxTransport:It(Pt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=qt.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||bt.href)+"").replace(Ht,bt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Mt.protocol+"//"+Mt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Wt(Ot,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Lt.test(v.type),f=v.url.replace(jt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(Nt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Tt.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Dt,"$1"),o=(Tt.test(f)?"&":"?")+"_="+wt.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+Rt+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Wt(Pt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&S.inArray("json",v.dataTypes)<0&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var Bt={0:200,1223:204},$t=S.ajaxSettings.xhr();y.cors=!!$t&&"withCredentials"in $t,y.ajax=$t=!!$t,S.ajaxTransport(function(i){var o,a;if(y.cors||$t&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(Bt[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=ht(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Xt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Xt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Vt=C.jQuery,Gt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Gt),e&&C.jQuery===S&&(C.jQuery=Vt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S}); /*! jQuery UI - v1.13.2 - 2022-08-01 * http://jqueryui.com * Includes: widget.js, position.js, data.js, disable-selection.js, focusable.js, form-reset-mixin.js, jquery-patch.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/resizable.js, widgets/mouse.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ !function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(y){"use strict";y.ui=y.ui||{};y.ui.version="1.13.2";var n,i=0,h=Array.prototype.hasOwnProperty,a=Array.prototype.slice;y.cleanData=(n=y.cleanData,function(t){for(var e,i,s=0;null!=(i=t[s]);s++)(e=y._data(i,"events"))&&e.remove&&y(i).triggerHandler("remove");n(t)}),y.widget=function(t,i,e){var s,n,o,h={},a=t.split(".")[0],r=a+"-"+(t=t.split(".")[1]);return e||(e=i,i=y.Widget),Array.isArray(e)&&(e=y.extend.apply(null,[{}].concat(e))),y.expr.pseudos[r.toLowerCase()]=function(t){return!!y.data(t,r)},y[a]=y[a]||{},s=y[a][t],n=y[a][t]=function(t,e){if(!this||!this._createWidget)return new n(t,e);arguments.length&&this._createWidget(t,e)},y.extend(n,s,{version:e.version,_proto:y.extend({},e),_childConstructors:[]}),(o=new i).options=y.widget.extend({},o.options),y.each(e,function(e,s){function n(){return i.prototype[e].apply(this,arguments)}function o(t){return i.prototype[e].apply(this,t)}h[e]="function"==typeof s?function(){var t,e=this._super,i=this._superApply;return this._super=n,this._superApply=o,t=s.apply(this,arguments),this._super=e,this._superApply=i,t}:s}),n.prototype=y.widget.extend(o,{widgetEventPrefix:s&&o.widgetEventPrefix||t},h,{constructor:n,namespace:a,widgetName:t,widgetFullName:r}),s?(y.each(s._childConstructors,function(t,e){var i=e.prototype;y.widget(i.namespace+"."+i.widgetName,n,e._proto)}),delete s._childConstructors):i._childConstructors.push(n),y.widget.bridge(t,n),n},y.widget.extend=function(t){for(var e,i,s=a.call(arguments,1),n=0,o=s.length;n<o;n++)for(e in s[n])i=s[n][e],h.call(s[n],e)&&void 0!==i&&(y.isPlainObject(i)?t[e]=y.isPlainObject(t[e])?y.widget.extend({},t[e],i):y.widget.extend({},i):t[e]=i);return t},y.widget.bridge=function(o,e){var h=e.prototype.widgetFullName||o;y.fn[o]=function(i){var t="string"==typeof i,s=a.call(arguments,1),n=this;return t?this.length||"instance"!==i?this.each(function(){var t,e=y.data(this,h);return"instance"===i?(n=e,!1):e?"function"!=typeof e[i]||"_"===i.charAt(0)?y.error("no such method '"+i+"' for "+o+" widget instance"):(t=e[i].apply(e,s))!==e&&void 0!==t?(n=t&&t.jquery?n.pushStack(t.get()):t,!1):void 0:y.error("cannot call methods on "+o+" prior to initialization; attempted to call method '"+i+"'")}):n=void 0:(s.length&&(i=y.widget.extend.apply(null,[i].concat(s))),this.each(function(){var t=y.data(this,h);t?(t.option(i||{}),t._init&&t._init()):y.data(this,h,new e(i,this))})),n}},y.Widget=function(){},y.Widget._childConstructors=[],y.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(t,e){e=y(e||this.defaultElement||this)[0],this.element=y(e),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=y(),this.hoverable=y(),this.focusable=y(),this.classesElementLookup={},e!==this&&(y.data(e,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===e&&this.destroy()}}),this.document=y(e.style?e.ownerDocument:e.document||e),this.window=y(this.document[0].defaultView||this.document[0].parentWindow)),this.options=y.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:y.noop,_create:y.noop,_init:y.noop,destroy:function(){var i=this;this._destroy(),y.each(this.classesElementLookup,function(t,e){i._removeClass(e,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:y.noop,widget:function(){return this.element},option:function(t,e){var i,s,n,o=t;if(0===arguments.length)return y.widget.extend({},this.options);if("string"==typeof t)if(o={},t=(i=t.split(".")).shift(),i.length){for(s=o[t]=y.widget.extend({},this.options[t]),n=0;n<i.length-1;n++)s[i[n]]=s[i[n]]||{},s=s[i[n]];if(t=i.pop(),1===arguments.length)return void 0===s[t]?null:s[t];s[t]=e}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=e}return this._setOptions(o),this},_setOptions:function(t){for(var e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(t){var e,i,s;for(e in t)s=this.classesElementLookup[e],t[e]!==this.options.classes[e]&&s&&s.length&&(i=y(s.get()),this._removeClass(s,e),i.addClass(this._classes({element:i,keys:e,classes:t,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(n){var o=[],h=this;function t(t,e){for(var i,s=0;s<t.length;s++)i=h.classesElementLookup[t[s]]||y(),i=n.add?(function(){var i=[];n.element.each(function(t,e){y.map(h.classesElementLookup,function(t){return t}).some(function(t){return t.is(e)})||i.push(e)}),h._on(y(i),{remove:"_untrackClassesElement"})}(),y(y.uniqueSort(i.get().concat(n.element.get())))):y(i.not(n.element).get()),h.classesElementLookup[t[s]]=i,o.push(t[s]),e&&n.classes[t[s]]&&o.push(n.classes[t[s]])}return(n=y.extend({element:this.element,classes:this.options.classes||{}},n)).keys&&t(n.keys.match(/\S+/g)||[],!0),n.extra&&t(n.extra.match(/\S+/g)||[]),o.join(" ")},_untrackClassesElement:function(i){var s=this;y.each(s.classesElementLookup,function(t,e){-1!==y.inArray(i.target,e)&&(s.classesElementLookup[t]=y(e.not(i.target).get()))}),this._off(y(i.target))},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){var n="string"==typeof t||null===t,i={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s="boolean"==typeof s?s:i};return i.element.toggleClass(this._classes(i),s),this},_on:function(n,o,t){var h,a=this;"boolean"!=typeof n&&(t=o,o=n,n=!1),t?(o=h=y(o),this.bindings=this.bindings.add(o)):(t=o,o=this.element,h=this.widget()),y.each(t,function(t,e){function i(){if(n||!0!==a.options.disabled&&!y(this).hasClass("ui-state-disabled"))return("string"==typeof e?a[e]:e).apply(a,arguments)}"string"!=typeof e&&(i.guid=e.guid=e.guid||i.guid||y.guid++);var s=t.match(/^([\w:-]*)\s*(.*)$/),t=s[1]+a.eventNamespace,s=s[2];s?h.on(t,s,i):o.on(t,i)})},_off:function(t,e){e=(e||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.off(e),this.bindings=y(this.bindings.not(t).get()),this.focusable=y(this.focusable.not(t).get()),this.hoverable=y(this.hoverable.not(t).get())},_delay:function(t,e){var i=this;return setTimeout(function(){return("string"==typeof t?i[t]:t).apply(i,arguments)},e||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){this._addClass(y(t.currentTarget),null,"ui-state-hover")},mouseleave:function(t){this._removeClass(y(t.currentTarget),null,"ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){this._addClass(y(t.currentTarget),null,"ui-state-focus")},focusout:function(t){this._removeClass(y(t.currentTarget),null,"ui-state-focus")}})},_trigger:function(t,e,i){var s,n,o=this.options[t];if(i=i||{},(e=y.Event(e)).type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),e.target=this.element[0],n=e.originalEvent)for(s in n)s in e||(e[s]=n[s]);return this.element.trigger(e,i),!("function"==typeof o&&!1===o.apply(this.element[0],[e].concat(i))||e.isDefaultPrevented())}},y.each({show:"fadeIn",hide:"fadeOut"},function(o,h){y.Widget.prototype["_"+o]=function(e,t,i){var s,n=(t="string"==typeof t?{effect:t}:t)?!0!==t&&"number"!=typeof t&&t.effect||h:o;"number"==typeof(t=t||{})?t={duration:t}:!0===t&&(t={}),s=!y.isEmptyObject(t),t.complete=i,t.delay&&e.delay(t.delay),s&&y.effects&&y.effects.effect[n]?e[o](t):n!==o&&e[n]?e[n](t.duration,t.easing,i):e.queue(function(t){y(this)[o](),i&&i.call(e[0]),t()})}});var s,x,D,o,r,l,u,p,W;y.widget;function E(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function H(t,e){return parseInt(y.css(t,e),10)||0}function S(t){return null!=t&&t===t.window}x=Math.max,D=Math.abs,o=/left|center|right/,r=/top|center|bottom/,l=/[\+\-]\d+(\.[\d]+)?%?/,u=/^\w+/,p=/%$/,W=y.fn.position,y.position={scrollbarWidth:function(){if(void 0!==s)return s;var t,e=y("<div style='display:block;position:absolute;width:200px;height:200px;overflow:hidden;'><div style='height:300px;width:auto;'></div></div>"),i=e.children()[0];return y("body").append(e),t=i.offsetWidth,e.css("overflow","scroll"),t===(i=i.offsetWidth)&&(i=e[0].clientWidth),e.remove(),s=t-i},getScrollInfo:function(t){var e=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),e="scroll"===e||"auto"===e&&t.width<t.element[0].scrollWidth;return{width:"scroll"===i||"auto"===i&&t.height<t.element[0].scrollHeight?y.position.scrollbarWidth():0,height:e?y.position.scrollbarWidth():0}},getWithinInfo:function(t){var e=y(t||window),i=S(e[0]),s=!!e[0]&&9===e[0].nodeType;return{element:e,isWindow:i,isDocument:s,offset:!i&&!s?y(t).offset():{left:0,top:0},scrollLeft:e.scrollLeft(),scrollTop:e.scrollTop(),width:e.outerWidth(),height:e.outerHeight()}}},y.fn.position=function(p){if(!p||!p.of)return W.apply(this,arguments);var d,c,f,g,m,t,_="string"==typeof(p=y.extend({},p)).of?y(document).find(p.of):y(p.of),w=y.position.getWithinInfo(p.within),v=y.position.getScrollInfo(w),b=(p.collision||"flip").split(" "),z={},e=9===(t=(e=_)[0]).nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:S(t)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:t.preventDefault?{width:0,height:0,offset:{top:t.pageY,left:t.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()};return _[0].preventDefault&&(p.at="left top"),c=e.width,f=e.height,m=y.extend({},g=e.offset),y.each(["my","at"],function(){var t,e,i=(p[this]||"").split(" ");(i=1===i.length?o.test(i[0])?i.concat(["center"]):r.test(i[0])?["center"].concat(i):["center","center"]:i)[0]=o.test(i[0])?i[0]:"center",i[1]=r.test(i[1])?i[1]:"center",t=l.exec(i[0]),e=l.exec(i[1]),z[this]=[t?t[0]:0,e?e[0]:0],p[this]=[u.exec(i[0])[0],u.exec(i[1])[0]]}),1===b.length&&(b[1]=b[0]),"right"===p.at[0]?m.left+=c:"center"===p.at[0]&&(m.left+=c/2),"bottom"===p.at[1]?m.top+=f:"center"===p.at[1]&&(m.top+=f/2),d=E(z.at,c,f),m.left+=d[0],m.top+=d[1],this.each(function(){var i,t,h=y(this),a=h.outerWidth(),r=h.outerHeight(),e=H(this,"marginLeft"),s=H(this,"marginTop"),n=a+e+H(this,"marginRight")+v.width,o=r+s+H(this,"marginBottom")+v.height,l=y.extend({},m),u=E(z.my,h.outerWidth(),h.outerHeight());"right"===p.my[0]?l.left-=a:"center"===p.my[0]&&(l.left-=a/2),"bottom"===p.my[1]?l.top-=r:"center"===p.my[1]&&(l.top-=r/2),l.left+=u[0],l.top+=u[1],i={marginLeft:e,marginTop:s},y.each(["left","top"],function(t,e){y.ui.position[b[t]]&&y.ui.position[b[t]][e](l,{targetWidth:c,targetHeight:f,elemWidth:a,elemHeight:r,collisionPosition:i,collisionWidth:n,collisionHeight:o,offset:[d[0]+u[0],d[1]+u[1]],my:p.my,at:p.at,within:w,elem:h})}),p.using&&(t=function(t){var e=g.left-l.left,i=e+c-a,s=g.top-l.top,n=s+f-r,o={target:{element:_,left:g.left,top:g.top,width:c,height:f},element:{element:h,left:l.left,top:l.top,width:a,height:r},horizontal:i<0?"left":0<e?"right":"center",vertical:n<0?"top":0<s?"bottom":"middle"};c<a&&D(e+i)<c&&(o.horizontal="center"),f<r&&D(s+n)<f&&(o.vertical="middle"),x(D(e),D(i))>x(D(s),D(n))?o.important="horizontal":o.important="vertical",p.using.call(this,t,o)}),h.offset(y.extend(l,{using:t}))})},y.ui.position={fit:{left:function(t,e){var i=e.within,s=i.isWindow?i.scrollLeft:i.offset.left,n=i.width,o=t.left-e.collisionPosition.marginLeft,h=s-o,a=o+e.collisionWidth-n-s;e.collisionWidth>n?0<h&&a<=0?(i=t.left+h+e.collisionWidth-n-s,t.left+=h-i):t.left=!(0<a&&h<=0)&&a<h?s+n-e.collisionWidth:s:0<h?t.left+=h:0<a?t.left-=a:t.left=x(t.left-o,t.left)},top:function(t,e){var i=e.within,s=i.isWindow?i.scrollTop:i.offset.top,n=e.within.height,o=t.top-e.collisionPosition.marginTop,h=s-o,a=o+e.collisionHeight-n-s;e.collisionHeight>n?0<h&&a<=0?(i=t.top+h+e.collisionHeight-n-s,t.top+=h-i):t.top=!(0<a&&h<=0)&&a<h?s+n-e.collisionHeight:s:0<h?t.top+=h:0<a?t.top-=a:t.top=x(t.top-o,t.top)}},flip:{left:function(t,e){var i=e.within,s=i.offset.left+i.scrollLeft,n=i.width,o=i.isWindow?i.scrollLeft:i.offset.left,h=t.left-e.collisionPosition.marginLeft,a=h-o,r=h+e.collisionWidth-n-o,l="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,i="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,h=-2*e.offset[0];a<0?((s=t.left+l+i+h+e.collisionWidth-n-s)<0||s<D(a))&&(t.left+=l+i+h):0<r&&(0<(o=t.left-e.collisionPosition.marginLeft+l+i+h-o)||D(o)<r)&&(t.left+=l+i+h)},top:function(t,e){var i=e.within,s=i.offset.top+i.scrollTop,n=i.height,o=i.isWindow?i.scrollTop:i.offset.top,h=t.top-e.collisionPosition.marginTop,a=h-o,r=h+e.collisionHeight-n-o,l="top"===e.my[1]?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,i="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,h=-2*e.offset[1];a<0?((s=t.top+l+i+h+e.collisionHeight-n-s)<0||s<D(a))&&(t.top+=l+i+h):0<r&&(0<(o=t.top-e.collisionPosition.marginTop+l+i+h-o)||D(o)<r)&&(t.top+=l+i+h)}},flipfit:{left:function(){y.ui.position.flip.left.apply(this,arguments),y.ui.position.fit.left.apply(this,arguments)},top:function(){y.ui.position.flip.top.apply(this,arguments),y.ui.position.fit.top.apply(this,arguments)}}};var t;y.ui.position,y.extend(y.expr.pseudos,{data:y.expr.createPseudo?y.expr.createPseudo(function(e){return function(t){return!!y.data(t,e)}}):function(t,e,i){return!!y.data(t,i[3])}}),y.fn.extend({disableSelection:(t="onselectstart"in document.createElement("div")?"selectstart":"mousedown",function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}),enableSelection:function(){return this.off(".ui-disableSelection")}});y.ui.focusable=function(t,e){var i,s,n,o,h=t.nodeName.toLowerCase();return"area"===h?(s=(i=t.parentNode).name,!(!t.href||!s||"map"!==i.nodeName.toLowerCase())&&(0<(s=y("img[usemap='#"+s+"']")).length&&s.is(":visible"))):(/^(input|select|textarea|button|object)$/.test(h)?(n=!t.disabled)&&(o=y(t).closest("fieldset")[0])&&(n=!o.disabled):n="a"===h&&t.href||e,n&&y(t).is(":visible")&&function(t){var e=t.css("visibility");for(;"inherit"===e;)t=t.parent(),e=t.css("visibility");return"visible"===e}(y(t)))},y.extend(y.expr.pseudos,{focusable:function(t){return y.ui.focusable(t,null!=y.attr(t,"tabindex"))}});var e,d;y.ui.focusable,y.fn._form=function(){return"string"==typeof this[0].form?this.closest("form"):y(this[0].form)},y.ui.formResetMixin={_formResetHandler:function(){var e=y(this);setTimeout(function(){var t=e.data("ui-form-reset-instances");y.each(t,function(){this.refresh()})})},_bindFormResetHandler:function(){var t;this.form=this.element._form(),this.form.length&&((t=this.form.data("ui-form-reset-instances")||[]).length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t))},_unbindFormResetHandler:function(){var t;this.form.length&&((t=this.form.data("ui-form-reset-instances")).splice(y.inArray(this,t),1),t.length?this.form.data("ui-form-reset-instances",t):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset"))}};y.expr.pseudos||(y.expr.pseudos=y.expr[":"]),y.uniqueSort||(y.uniqueSort=y.unique),y.escapeSelector||(e=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,d=function(t,e){return e?"\0"===t?"�":t.slice(0,-1)+"\\"+t.charCodeAt(t.length-1).toString(16)+" ":"\\"+t},y.escapeSelector=function(t){return(t+"").replace(e,d)}),y.fn.even&&y.fn.odd||y.fn.extend({even:function(){return this.filter(function(t){return t%2==0})},odd:function(){return this.filter(function(t){return t%2==1})}});y.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},y.fn.labels=function(){var t,e,i;return this.length?this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(e=this.eq(0).parents("label"),(t=this.attr("id"))&&(i=(i=this.eq(0).parents().last()).add((i.length?i:this).siblings()),t="label[for='"+y.escapeSelector(t)+"']",e=e.add(i.find(t).addBack(t))),this.pushStack(e)):this.pushStack([])},y.fn.scrollParent=function(t){var e=this.css("position"),i="absolute"===e,s=t?/(auto|scroll|hidden)/:/(auto|scroll)/,t=this.parents().filter(function(){var t=y(this);return(!i||"static"!==t.css("position"))&&s.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==e&&t.length?t:y(this[0].ownerDocument||document)},y.extend(y.expr.pseudos,{tabbable:function(t){var e=y.attr(t,"tabindex"),i=null!=e;return(!i||0<=e)&&y.ui.focusable(t,i)}}),y.fn.extend({uniqueId:(c=0,function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++c)})}),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&y(this).removeAttr("id")})}}),y.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var c,f=!1;y(document).on("mouseup",function(){f=!1});y.widget("ui.mouse",{version:"1.13.2",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(t){if(!0===y.data(t.target,e.widgetName+".preventClickEvent"))return y.removeData(t.target,e.widgetName+".preventClickEvent"),t.stopImmediatePropagation(),!1}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!f){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var e=this,i=1===t.which,s=!("string"!=typeof this.options.cancel||!t.target.nodeName)&&y(t.target).closest(this.options.cancel).length;return i&&!s&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){e.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=!1!==this._mouseStart(t),!this._mouseStarted)?(t.preventDefault(),!0):(!0===y.data(t.target,this.widgetName+".preventClickEvent")&&y.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return e._mouseMove(t)},this._mouseUpDelegate=function(t){return e._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),f=!0)):!0}},_mouseMove:function(t){if(this._mouseMoved){if(y.ui.ie&&(!document.documentMode||document.documentMode<9)&&!t.button)return this._mouseUp(t);if(!t.which)if(t.originalEvent.altKey||t.originalEvent.ctrlKey||t.originalEvent.metaKey||t.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(t)}return(t.which||t.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=!1!==this._mouseStart(this._mouseDownEvent,t),this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&y.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,f=!1,t.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),y.ui.plugin={add:function(t,e,i){var s,n=y.ui[t].prototype;for(s in i)n.plugins[s]=n.plugins[s]||[],n.plugins[s].push([e,i[s]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;n<o.length;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}};y.widget("ui.resizable",y.ui.mouse,{version:"1.13.2",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,classes:{"ui-resizable-se":"ui-icon ui-icon-gripsmall-diagonal-se"},containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseFloat(t)||0},_isNumber:function(t){return!isNaN(parseFloat(t))},_hasScroll:function(t,e){if("hidden"===y(t).css("overflow"))return!1;var i=e&&"left"===e?"scrollLeft":"scrollTop",e=!1;if(0<t[i])return!0;try{t[i]=1,e=0<t[i],t[i]=0}catch(t){}return e},_create:function(){var t,e=this.options,i=this;this._addClass("ui-resizable"),y.extend(this,{_aspectRatio:!!e.aspectRatio,aspectRatio:e.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:e.helper||e.ghost||e.animate?e.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(y("<div class='ui-wrapper'></div>").css({overflow:"hidden",position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,t={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(t),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(t),this._proportionallyResize()),this._setupHandles(),e.autoHide&&y(this.element).on("mouseenter",function(){e.disabled||(i._removeClass("ui-resizable-autohide"),i._handles.show())}).on("mouseleave",function(){e.disabled||i.resizing||(i._addClass("ui-resizable-autohide"),i._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy(),this._addedHandles.remove();function t(t){y(t).removeData("resizable").removeData("ui-resizable").off(".resizable")}var e;return this.elementIsWrapper&&(t(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),t(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;case"aspectRatio":this._aspectRatio=!!e}},_setupHandles:function(){var t,e,i,s,n,o=this.options,h=this;if(this.handles=o.handles||(y(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=y(),this._addedHandles=y(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),i=this.handles.split(","),this.handles={},e=0;e<i.length;e++)s="ui-resizable-"+(t=String.prototype.trim.call(i[e])),n=y("<div>"),this._addClass(n,"ui-resizable-handle "+s),n.css({zIndex:o.zIndex}),this.handles[t]=".ui-resizable-"+t,this.element.children(this.handles[t]).length||(this.element.append(n),this._addedHandles=this._addedHandles.add(n));this._renderAxis=function(t){var e,i,s;for(e in t=t||this.element,this.handles)this.handles[e].constructor===String?this.handles[e]=this.element.children(this.handles[e]).first().show():(this.handles[e].jquery||this.handles[e].nodeType)&&(this.handles[e]=y(this.handles[e]),this._on(this.handles[e],{mousedown:h._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(i=y(this.handles[e],this.element),s=/sw|ne|nw|se|n|s/.test(e)?i.outerHeight():i.outerWidth(),i=["padding",/ne|nw|n/.test(e)?"Top":/se|sw|s/.test(e)?"Bottom":/^e$/.test(e)?"Right":"Left"].join(""),t.css(i,s),this._proportionallyResize()),this._handles=this._handles.add(this.handles[e])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){h.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),h.axis=n&&n[1]?n[1]:"se")}),o.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._addedHandles.remove()},_mouseCapture:function(t){var e,i,s=!1;for(e in this.handles)(i=y(this.handles[e])[0])!==t.target&&!y.contains(i,t.target)||(s=!0);return!this.options.disabled&&s},_mouseStart:function(t){var e,i,s=this.options,n=this.element;return this.resizing=!0,this._renderProxy(),e=this._num(this.helper.css("left")),i=this._num(this.helper.css("top")),s.containment&&(e+=y(s.containment).scrollLeft()||0,i+=y(s.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:e,top:i},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:n.width(),height:n.height()},this.originalSize=this._helper?{width:n.outerWidth(),height:n.outerHeight()}:{width:n.width(),height:n.height()},this.sizeDiff={width:n.outerWidth()-n.width(),height:n.outerHeight()-n.height()},this.originalPosition={left:e,top:i},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof s.aspectRatio?s.aspectRatio:this.originalSize.width/this.originalSize.height||1,s=y(".ui-resizable-"+this.axis).css("cursor"),y("body").css("cursor","auto"===s?this.axis+"-resize":s),this._addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var e=this.originalMousePosition,i=this.axis,s=t.pageX-e.left||0,e=t.pageY-e.top||0,i=this._change[i];return this._updatePrevProperties(),i&&(e=i.apply(this,[t,s,e]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(e=this._updateRatio(e,t)),e=this._respectSize(e,t),this._updateCache(e),this._propagate("resize",t),e=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),y.isEmptyObject(e)||(this._updatePrevProperties(),this._trigger("resize",t,this.ui()),this._applyChanges())),!1},_mouseStop:function(t){this.resizing=!1;var e,i,s,n=this.options,o=this;return this._helper&&(s=(e=(i=this._proportionallyResizeElements).length&&/textarea/i.test(i[0].nodeName))&&this._hasScroll(i[0],"left")?0:o.sizeDiff.height,i=e?0:o.sizeDiff.width,e={width:o.helper.width()-i,height:o.helper.height()-s},i=parseFloat(o.element.css("left"))+(o.position.left-o.originalPosition.left)||null,s=parseFloat(o.element.css("top"))+(o.position.top-o.originalPosition.top)||null,n.animate||this.element.css(y.extend(e,{top:s,left:i})),o.helper.height(o.size.height),o.helper.width(o.size.width),this._helper&&!n.animate&&this._proportionallyResize()),y("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s=this.options,n={minWidth:this._isNumber(s.minWidth)?s.minWidth:0,maxWidth:this._isNumber(s.maxWidth)?s.maxWidth:1/0,minHeight:this._isNumber(s.minHeight)?s.minHeight:0,maxHeight:this._isNumber(s.maxHeight)?s.maxHeight:1/0};(this._aspectRatio||t)&&(e=n.minHeight*this.aspectRatio,i=n.minWidth/this.aspectRatio,s=n.maxHeight*this.aspectRatio,t=n.maxWidth/this.aspectRatio,e>n.minWidth&&(n.minWidth=e),i>n.minHeight&&(n.minHeight=i),s<n.maxWidth&&(n.maxWidth=s),t<n.maxHeight&&(n.maxHeight=t)),this._vBoundaries=n},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidth<t.width,n=this._isNumber(t.height)&&e.maxHeight&&e.maxHeight<t.height,o=this._isNumber(t.width)&&e.minWidth&&e.minWidth>t.width,h=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,a=this.originalPosition.left+this.originalSize.width,r=this.originalPosition.top+this.originalSize.height,l=/sw|nw|w/.test(i),i=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),h&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&l&&(t.left=a-e.minWidth),s&&l&&(t.left=a-e.maxWidth),h&&i&&(t.top=r-e.minHeight),n&&i&&(t.top=r-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];e<4;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;e<this._proportionallyResizeElements.length;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var t=this.element,e=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||y("<div></div>").css({overflow:"hidden"}),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++e.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize;return{left:this.originalPosition.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize;return{top:this.originalPosition.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(t,e,i){return y.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},sw:function(t,e,i){return y.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,e,i]))},ne:function(t,e,i){return y.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},nw:function(t,e,i){return y.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,e,i]))}},_propagate:function(t,e){y.ui.plugin.call(this,t,[e,this.ui()]),"resize"!==t&&this._trigger(t,e,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),y.ui.plugin.add("resizable","animate",{stop:function(e){var i=y(this).resizable("instance"),t=i.options,s=i._proportionallyResizeElements,n=s.length&&/textarea/i.test(s[0].nodeName),o=n&&i._hasScroll(s[0],"left")?0:i.sizeDiff.height,h=n?0:i.sizeDiff.width,n={width:i.size.width-h,height:i.size.height-o},h=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,o=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(y.extend(n,o&&h?{top:o,left:h}:{}),{duration:t.animateDuration,easing:t.animateEasing,step:function(){var t={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};s&&s.length&&y(s[0]).css({width:t.width,height:t.height}),i._updateCache(t),i._propagate("resize",e)}})}}),y.ui.plugin.add("resizable","containment",{start:function(){var i,s,n=y(this).resizable("instance"),t=n.options,e=n.element,o=t.containment,h=o instanceof y?o.get(0):/parent/.test(o)?e.parent().get(0):o;h&&(n.containerElement=y(h),/document/.test(o)||o===document?(n.containerOffset={left:0,top:0},n.containerPosition={left:0,top:0},n.parentData={element:y(document),left:0,top:0,width:y(document).width(),height:y(document).height()||document.body.parentNode.scrollHeight}):(i=y(h),s=[],y(["Top","Right","Left","Bottom"]).each(function(t,e){s[t]=n._num(i.css("padding"+e))}),n.containerOffset=i.offset(),n.containerPosition=i.position(),n.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},t=n.containerOffset,e=n.containerSize.height,o=n.containerSize.width,o=n._hasScroll(h,"left")?h.scrollWidth:o,e=n._hasScroll(h)?h.scrollHeight:e,n.parentData={element:h,left:t.left,top:t.top,width:o,height:e}))},resize:function(t){var e=y(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.position,o=e._aspectRatio||t.shiftKey,h={top:0,left:0},a=e.containerElement,t=!0;a[0]!==document&&/static/.test(a.css("position"))&&(h=s),n.left<(e._helper?s.left:0)&&(e.size.width=e.size.width+(e._helper?e.position.left-s.left:e.position.left-h.left),o&&(e.size.height=e.size.width/e.aspectRatio,t=!1),e.position.left=i.helper?s.left:0),n.top<(e._helper?s.top:0)&&(e.size.height=e.size.height+(e._helper?e.position.top-s.top:e.position.top),o&&(e.size.width=e.size.height*e.aspectRatio,t=!1),e.position.top=e._helper?s.top:0),i=e.containerElement.get(0)===e.element.parent().get(0),n=/relative|absolute/.test(e.containerElement.css("position")),i&&n?(e.offset.left=e.parentData.left+e.position.left,e.offset.top=e.parentData.top+e.position.top):(e.offset.left=e.element.offset().left,e.offset.top=e.element.offset().top),n=Math.abs(e.sizeDiff.width+(e._helper?e.offset.left-h.left:e.offset.left-s.left)),s=Math.abs(e.sizeDiff.height+(e._helper?e.offset.top-h.top:e.offset.top-s.top)),n+e.size.width>=e.parentData.width&&(e.size.width=e.parentData.width-n,o&&(e.size.height=e.size.width/e.aspectRatio,t=!1)),s+e.size.height>=e.parentData.height&&(e.size.height=e.parentData.height-s,o&&(e.size.width=e.size.height*e.aspectRatio,t=!1)),t||(e.position.left=e.prevPosition.left,e.position.top=e.prevPosition.top,e.size.width=e.prevSize.width,e.size.height=e.prevSize.height)},stop:function(){var t=y(this).resizable("instance"),e=t.options,i=t.containerOffset,s=t.containerPosition,n=t.containerElement,o=y(t.helper),h=o.offset(),a=o.outerWidth()-t.sizeDiff.width,o=o.outerHeight()-t.sizeDiff.height;t._helper&&!e.animate&&/relative/.test(n.css("position"))&&y(this).css({left:h.left-s.left-i.left,width:a,height:o}),t._helper&&!e.animate&&/static/.test(n.css("position"))&&y(this).css({left:h.left-s.left-i.left,width:a,height:o})}}),y.ui.plugin.add("resizable","alsoResize",{start:function(){var t=y(this).resizable("instance").options;y(t.alsoResize).each(function(){var t=y(this);t.data("ui-resizable-alsoresize",{width:parseFloat(t.width()),height:parseFloat(t.height()),left:parseFloat(t.css("left")),top:parseFloat(t.css("top"))})})},resize:function(t,i){var e=y(this).resizable("instance"),s=e.options,n=e.originalSize,o=e.originalPosition,h={height:e.size.height-n.height||0,width:e.size.width-n.width||0,top:e.position.top-o.top||0,left:e.position.left-o.left||0};y(s.alsoResize).each(function(){var t=y(this),s=y(this).data("ui-resizable-alsoresize"),n={},e=t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];y.each(e,function(t,e){var i=(s[e]||0)+(h[e]||0);i&&0<=i&&(n[e]=i||null)}),t.css(n)})},stop:function(){y(this).removeData("ui-resizable-alsoresize")}}),y.ui.plugin.add("resizable","ghost",{start:function(){var t=y(this).resizable("instance"),e=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:e.height,width:e.width,margin:0,left:0,top:0}),t._addClass(t.ghost,"ui-resizable-ghost"),!1!==y.uiBackCompat&&"string"==typeof t.options.ghost&&t.ghost.addClass(this.options.ghost),t.ghost.appendTo(t.helper)},resize:function(){var t=y(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=y(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),y.ui.plugin.add("resizable","grid",{resize:function(){var t,e=y(this).resizable("instance"),i=e.options,s=e.size,n=e.originalSize,o=e.originalPosition,h=e.axis,a="number"==typeof i.grid?[i.grid,i.grid]:i.grid,r=a[0]||1,l=a[1]||1,u=Math.round((s.width-n.width)/r)*r,p=Math.round((s.height-n.height)/l)*l,d=n.width+u,c=n.height+p,f=i.maxWidth&&i.maxWidth<d,g=i.maxHeight&&i.maxHeight<c,m=i.minWidth&&i.minWidth>d,s=i.minHeight&&i.minHeight>c;i.grid=a,m&&(d+=r),s&&(c+=l),f&&(d-=r),g&&(c-=l),/^(se|s|e)$/.test(h)?(e.size.width=d,e.size.height=c):/^(ne)$/.test(h)?(e.size.width=d,e.size.height=c,e.position.top=o.top-p):/^(sw)$/.test(h)?(e.size.width=d,e.size.height=c,e.position.left=o.left-u):((c-l<=0||d-r<=0)&&(t=e._getPaddingPlusBorderDimensions(this)),0<c-l?(e.size.height=c,e.position.top=o.top-p):(c=l-t.height,e.size.height=c,e.position.top=o.top+n.height-c),0<d-r?(e.size.width=d,e.position.left=o.left-u):(d=r-t.width,e.size.width=d,e.position.left=o.left+n.width-d))}});y.ui.resizable});/** * Copyright (c) 2007 Ariel Flesler - aflesler ○ gmail • com | https://github.com/flesler * Licensed under MIT * @author Ariel Flesler * @version 2.1.2 */ ;(function(f){"use strict";"function"===typeof define&&define.amd?define(["jquery"],f):"undefined"!==typeof module&&module.exports?module.exports=f(require("jquery")):f(jQuery)})(function($){"use strict";function n(a){return!a.nodeName||-1!==$.inArray(a.nodeName.toLowerCase(),["iframe","#document","html","body"])}function h(a){return $.isFunction(a)||$.isPlainObject(a)?a:{top:a,left:a}}var p=$.scrollTo=function(a,d,b){return $(window).scrollTo(a,d,b)};p.defaults={axis:"xy",duration:0,limit:!0};$.fn.scrollTo=function(a,d,b){"object"=== typeof d&&(b=d,d=0);"function"===typeof b&&(b={onAfter:b});"max"===a&&(a=9E9);b=$.extend({},p.defaults,b);d=d||b.duration;var u=b.queue&&1<b.axis.length;u&&(d/=2);b.offset=h(b.offset);b.over=h(b.over);return this.each(function(){function k(a){var k=$.extend({},b,{queue:!0,duration:d,complete:a&&function(){a.call(q,e,b)}});r.animate(f,k)}if(null!==a){var l=n(this),q=l?this.contentWindow||window:this,r=$(q),e=a,f={},t;switch(typeof e){case "number":case "string":if(/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(e)){e= h(e);break}e=l?$(e):$(e,q);case "object":if(e.length===0)return;if(e.is||e.style)t=(e=$(e)).offset()}var v=$.isFunction(b.offset)&&b.offset(q,e)||b.offset;$.each(b.axis.split(""),function(a,c){var d="x"===c?"Left":"Top",m=d.toLowerCase(),g="scroll"+d,h=r[g](),n=p.max(q,c);t?(f[g]=t[m]+(l?0:h-r.offset()[m]),b.margin&&(f[g]-=parseInt(e.css("margin"+d),10)||0,f[g]-=parseInt(e.css("border"+d+"Width"),10)||0),f[g]+=v[m]||0,b.over[m]&&(f[g]+=e["x"===c?"width":"height"]()*b.over[m])):(d=e[m],f[g]=d.slice&& "%"===d.slice(-1)?parseFloat(d)/100*n:d);b.limit&&/^\d+$/.test(f[g])&&(f[g]=0>=f[g]?0:Math.min(f[g],n));!a&&1<b.axis.length&&(h===f[g]?f={}:u&&(k(b.onAfterFirst),f={}))});k(b.onAfter)}})};p.max=function(a,d){var b="x"===d?"Width":"Height",h="scroll"+b;if(!n(a))return a[h]-$(a)[b.toLowerCase()]();var b="client"+b,k=a.ownerDocument||a.document,l=k.documentElement,k=k.body;return Math.max(l[h],k[h])-Math.min(l[b],k[b])};$.Tween.propHooks.scrollLeft=$.Tween.propHooks.scrollTop={get:function(a){return $(a.elem)[a.prop]()}, set:function(a){var d=this.get(a);if(a.options.interrupt&&a._last&&a._last!==d)return $(a.elem).stop();var b=Math.round(a.now);d!==b&&($(a.elem)[a.prop](b),a._last=this.get(a))}};return p}); /*! PowerTip v1.3.1 (2018-04-15) https://stevenbenner.github.io/jquery-powertip/ Copyright (c) 2018 Steven Benner (http://stevenbenner.com/). Released under MIT license. https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt */ (function(root,factory){if(typeof define==="function"&&define.amd){define(["jquery"],factory)}else if(typeof module==="object"&&module.exports){module.exports=factory(require("jquery"))}else{factory(root.jQuery)}})(this,function($){var $document=$(document),$window=$(window),$body=$("body");var DATA_DISPLAYCONTROLLER="displayController",DATA_HASACTIVEHOVER="hasActiveHover",DATA_FORCEDOPEN="forcedOpen",DATA_HASMOUSEMOVE="hasMouseMove",DATA_MOUSEONTOTIP="mouseOnToPopup",DATA_ORIGINALTITLE="originalTitle",DATA_POWERTIP="powertip",DATA_POWERTIPJQ="powertipjq",DATA_POWERTIPTARGET="powertiptarget",EVENT_NAMESPACE=".powertip",RAD2DEG=180/Math.PI,MOUSE_EVENTS=["click","dblclick","mousedown","mouseup","mousemove","mouseover","mouseout","mouseenter","mouseleave","contextmenu"];var session={tooltips:null,isTipOpen:false,isFixedTipOpen:false,isClosing:false,tipOpenImminent:false,activeHover:null,currentX:0,currentY:0,previousX:0,previousY:0,desyncTimeout:null,closeDelayTimeout:null,mouseTrackingActive:false,delayInProgress:false,windowWidth:0,windowHeight:0,scrollTop:0,scrollLeft:0};var Collision={none:0,top:1,bottom:2,left:4,right:8};$.fn.powerTip=function(opts,arg){var targetElements=this,options,tipController;if(!targetElements.length){return targetElements}if($.type(opts)==="string"&&$.powerTip[opts]){return $.powerTip[opts].call(targetElements,targetElements,arg)}options=$.extend({},$.fn.powerTip.defaults,opts);tipController=new TooltipController(options);initTracking();targetElements.each(function elementSetup(){var $this=$(this),dataPowertip=$this.data(DATA_POWERTIP),dataElem=$this.data(DATA_POWERTIPJQ),dataTarget=$this.data(DATA_POWERTIPTARGET),title=$this.attr("title");if(!dataPowertip&&!dataTarget&&!dataElem&&title){$this.data(DATA_POWERTIP,title);$this.data(DATA_ORIGINALTITLE,title);$this.removeAttr("title")}$this.data(DATA_DISPLAYCONTROLLER,new DisplayController($this,options,tipController))});if(!options.manual){$.each(options.openEvents,function(idx,evt){if($.inArray(evt,options.closeEvents)>-1){targetElements.on(evt+EVENT_NAMESPACE,function elementToggle(event){$.powerTip.toggle(this,event)})}else{targetElements.on(evt+EVENT_NAMESPACE,function elementOpen(event){$.powerTip.show(this,event)})}});$.each(options.closeEvents,function(idx,evt){if($.inArray(evt,options.openEvents)<0){targetElements.on(evt+EVENT_NAMESPACE,function elementClose(event){$.powerTip.hide(this,!isMouseEvent(event))})}});targetElements.on("keydown"+EVENT_NAMESPACE,function elementKeyDown(event){if(event.keyCode===27){$.powerTip.hide(this,true)}})}return targetElements};$.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:false,popupId:"powerTip",popupClass:null,intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:false,offset:10,mouseOnToPopup:false,manual:false,openEvents:["mouseenter","focus"],closeEvents:["mouseleave","blur"]};$.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:["e","ne","se","w","nw","sw","n","s","e"],s:["s","se","sw","n"],w:["w","nw","sw","e","ne","se","n","s","w"],nw:["nw","w","sw","n","s","se","nw"],ne:["ne","e","se","n","s","sw","ne"],sw:["sw","w","nw","s","n","ne","sw"],se:["se","e","ne","s","n","nw","se"],"nw-alt":["nw-alt","n","ne-alt","sw-alt","s","se-alt","w","e"],"ne-alt":["ne-alt","n","nw-alt","se-alt","s","sw-alt","e","w"],"sw-alt":["sw-alt","s","se-alt","nw-alt","n","ne-alt","w","e"],"se-alt":["se-alt","s","sw-alt","ne-alt","n","nw-alt","e","w"]};$.powerTip={show:function apiShowTip(element,event){if(isMouseEvent(event)){trackMouse(event);session.previousX=event.pageX;session.previousY=event.pageY;$(element).data(DATA_DISPLAYCONTROLLER).show()}else{$(element).first().data(DATA_DISPLAYCONTROLLER).show(true,true)}return element},reposition:function apiResetPosition(element){$(element).first().data(DATA_DISPLAYCONTROLLER).resetPosition();return element},hide:function apiCloseTip(element,immediate){var displayController;immediate=element?immediate:true;if(element){displayController=$(element).first().data(DATA_DISPLAYCONTROLLER)}else if(session.activeHover){displayController=session.activeHover.data(DATA_DISPLAYCONTROLLER)}if(displayController){displayController.hide(immediate)}return element},toggle:function apiToggle(element,event){if(session.activeHover&&session.activeHover.is(element)){$.powerTip.hide(element,!isMouseEvent(event))}else{$.powerTip.show(element,event)}return element}};$.powerTip.showTip=$.powerTip.show;$.powerTip.closeTip=$.powerTip.hide;function CSSCoordinates(){var me=this;me.top="auto";me.left="auto";me.right="auto";me.bottom="auto";me.set=function(property,value){if($.isNumeric(value)){me[property]=Math.round(value)}}}function DisplayController(element,options,tipController){var hoverTimer=null,myCloseDelay=null;function openTooltip(immediate,forceOpen){cancelTimer();if(!element.data(DATA_HASACTIVEHOVER)){if(!immediate){session.tipOpenImminent=true;hoverTimer=setTimeout(function intentDelay(){hoverTimer=null;checkForIntent()},options.intentPollInterval)}else{if(forceOpen){element.data(DATA_FORCEDOPEN,true)}closeAnyDelayed();tipController.showTip(element)}}else{cancelClose()}}function closeTooltip(disableDelay){if(myCloseDelay){myCloseDelay=session.closeDelayTimeout=clearTimeout(myCloseDelay);session.delayInProgress=false}cancelTimer();session.tipOpenImminent=false;if(element.data(DATA_HASACTIVEHOVER)){element.data(DATA_FORCEDOPEN,false);if(!disableDelay){session.delayInProgress=true;session.closeDelayTimeout=setTimeout(function closeDelay(){session.closeDelayTimeout=null;tipController.hideTip(element);session.delayInProgress=false;myCloseDelay=null},options.closeDelay);myCloseDelay=session.closeDelayTimeout}else{tipController.hideTip(element)}}}function checkForIntent(){var xDifference=Math.abs(session.previousX-session.currentX),yDifference=Math.abs(session.previousY-session.currentY),totalDifference=xDifference+yDifference;if(totalDifference<options.intentSensitivity){cancelClose();closeAnyDelayed();tipController.showTip(element)}else{session.previousX=session.currentX;session.previousY=session.currentY;openTooltip()}}function cancelTimer(stopClose){hoverTimer=clearTimeout(hoverTimer);if(session.closeDelayTimeout&&myCloseDelay===session.closeDelayTimeout||stopClose){cancelClose()}}function cancelClose(){session.closeDelayTimeout=clearTimeout(session.closeDelayTimeout);session.delayInProgress=false}function closeAnyDelayed(){if(session.delayInProgress&&session.activeHover&&!session.activeHover.is(element)){session.activeHover.data(DATA_DISPLAYCONTROLLER).hide(true)}}function repositionTooltip(){tipController.resetPosition(element)}this.show=openTooltip;this.hide=closeTooltip;this.cancel=cancelTimer;this.resetPosition=repositionTooltip}function PlacementCalculator(){function computePlacementCoords(element,placement,tipWidth,tipHeight,offset){var placementBase=placement.split("-")[0],coords=new CSSCoordinates,position;if(isSvgElement(element)){position=getSvgPlacement(element,placementBase)}else{position=getHtmlPlacement(element,placementBase)}switch(placement){case"n":coords.set("left",position.left-tipWidth/2);coords.set("bottom",session.windowHeight-position.top+offset);break;case"e":coords.set("left",position.left+offset);coords.set("top",position.top-tipHeight/2);break;case"s":coords.set("left",position.left-tipWidth/2);coords.set("top",position.top+offset);break;case"w":coords.set("top",position.top-tipHeight/2);coords.set("right",session.windowWidth-position.left+offset);break;case"nw":coords.set("bottom",session.windowHeight-position.top+offset);coords.set("right",session.windowWidth-position.left-20);break;case"nw-alt":coords.set("left",position.left);coords.set("bottom",session.windowHeight-position.top+offset);break;case"ne":coords.set("left",position.left-20);coords.set("bottom",session.windowHeight-position.top+offset);break;case"ne-alt":coords.set("bottom",session.windowHeight-position.top+offset);coords.set("right",session.windowWidth-position.left);break;case"sw":coords.set("top",position.top+offset);coords.set("right",session.windowWidth-position.left-20);break;case"sw-alt":coords.set("left",position.left);coords.set("top",position.top+offset);break;case"se":coords.set("left",position.left-20);coords.set("top",position.top+offset);break;case"se-alt":coords.set("top",position.top+offset);coords.set("right",session.windowWidth-position.left);break}return coords}function getHtmlPlacement(element,placement){var objectOffset=element.offset(),objectWidth=element.outerWidth(),objectHeight=element.outerHeight(),left,top;switch(placement){case"n":left=objectOffset.left+objectWidth/2;top=objectOffset.top;break;case"e":left=objectOffset.left+objectWidth;top=objectOffset.top+objectHeight/2;break;case"s":left=objectOffset.left+objectWidth/2;top=objectOffset.top+objectHeight;break;case"w":left=objectOffset.left;top=objectOffset.top+objectHeight/2;break;case"nw":left=objectOffset.left;top=objectOffset.top;break;case"ne":left=objectOffset.left+objectWidth;top=objectOffset.top;break;case"sw":left=objectOffset.left;top=objectOffset.top+objectHeight;break;case"se":left=objectOffset.left+objectWidth;top=objectOffset.top+objectHeight;break}return{top:top,left:left}}function getSvgPlacement(element,placement){var svgElement=element.closest("svg")[0],domElement=element[0],point=svgElement.createSVGPoint(),boundingBox=domElement.getBBox(),matrix=domElement.getScreenCTM(),halfWidth=boundingBox.width/2,halfHeight=boundingBox.height/2,placements=[],placementKeys=["nw","n","ne","e","se","s","sw","w"],coords,rotation,steps,x;function pushPlacement(){placements.push(point.matrixTransform(matrix))}point.x=boundingBox.x;point.y=boundingBox.y;pushPlacement();point.x+=halfWidth;pushPlacement();point.x+=halfWidth;pushPlacement();point.y+=halfHeight;pushPlacement();point.y+=halfHeight;pushPlacement();point.x-=halfWidth;pushPlacement();point.x-=halfWidth;pushPlacement();point.y-=halfHeight;pushPlacement();if(placements[0].y!==placements[1].y||placements[0].x!==placements[7].x){rotation=Math.atan2(matrix.b,matrix.a)*RAD2DEG;steps=Math.ceil((rotation%360-22.5)/45);if(steps<1){steps+=8}while(steps--){placementKeys.push(placementKeys.shift())}}for(x=0;x<placements.length;x++){if(placementKeys[x]===placement){coords=placements[x];break}}return{top:coords.y+session.scrollTop,left:coords.x+session.scrollLeft}}this.compute=computePlacementCoords}function TooltipController(options){var placementCalculator=new PlacementCalculator,tipElement=$("#"+options.popupId);if(tipElement.length===0){tipElement=$("<div/>",{id:options.popupId});if($body.length===0){$body=$("body")}$body.append(tipElement);session.tooltips=session.tooltips?session.tooltips.add(tipElement):tipElement}if(options.followMouse){if(!tipElement.data(DATA_HASMOUSEMOVE)){$document.on("mousemove"+EVENT_NAMESPACE,positionTipOnCursor);$window.on("scroll"+EVENT_NAMESPACE,positionTipOnCursor);tipElement.data(DATA_HASMOUSEMOVE,true)}}function beginShowTip(element){element.data(DATA_HASACTIVEHOVER,true);tipElement.queue(function queueTipInit(next){showTip(element);next()})}function showTip(element){var tipContent;if(!element.data(DATA_HASACTIVEHOVER)){return}if(session.isTipOpen){if(!session.isClosing){hideTip(session.activeHover)}tipElement.delay(100).queue(function queueTipAgain(next){showTip(element);next()});return}element.trigger("powerTipPreRender");tipContent=getTooltipContent(element);if(tipContent){tipElement.empty().append(tipContent)}else{return}element.trigger("powerTipRender");session.activeHover=element;session.isTipOpen=true;tipElement.data(DATA_MOUSEONTOTIP,options.mouseOnToPopup);tipElement.addClass(options.popupClass);if(!options.followMouse||element.data(DATA_FORCEDOPEN)){positionTipOnElement(element);session.isFixedTipOpen=true}else{positionTipOnCursor()}if(!element.data(DATA_FORCEDOPEN)&&!options.followMouse){$document.on("click"+EVENT_NAMESPACE,function documentClick(event){var target=event.target;if(target!==element[0]){if(options.mouseOnToPopup){if(target!==tipElement[0]&&!$.contains(tipElement[0],target)){$.powerTip.hide()}}else{$.powerTip.hide()}}})}if(options.mouseOnToPopup&&!options.manual){tipElement.on("mouseenter"+EVENT_NAMESPACE,function tipMouseEnter(){if(session.activeHover){session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel()}});tipElement.on("mouseleave"+EVENT_NAMESPACE,function tipMouseLeave(){if(session.activeHover){session.activeHover.data(DATA_DISPLAYCONTROLLER).hide()}})}tipElement.fadeIn(options.fadeInTime,function fadeInCallback(){if(!session.desyncTimeout){session.desyncTimeout=setInterval(closeDesyncedTip,500)}element.trigger("powerTipOpen")})}function hideTip(element){session.isClosing=true;session.isTipOpen=false;session.desyncTimeout=clearInterval(session.desyncTimeout);element.data(DATA_HASACTIVEHOVER,false);element.data(DATA_FORCEDOPEN,false);$document.off("click"+EVENT_NAMESPACE);tipElement.off(EVENT_NAMESPACE);tipElement.fadeOut(options.fadeOutTime,function fadeOutCallback(){var coords=new CSSCoordinates;session.activeHover=null;session.isClosing=false;session.isFixedTipOpen=false;tipElement.removeClass();coords.set("top",session.currentY+options.offset);coords.set("left",session.currentX+options.offset);tipElement.css(coords);element.trigger("powerTipClose")})}function positionTipOnCursor(){var tipWidth,tipHeight,coords,collisions,collisionCount;if(!session.isFixedTipOpen&&(session.isTipOpen||session.tipOpenImminent&&tipElement.data(DATA_HASMOUSEMOVE))){tipWidth=tipElement.outerWidth();tipHeight=tipElement.outerHeight();coords=new CSSCoordinates;coords.set("top",session.currentY+options.offset);coords.set("left",session.currentX+options.offset);collisions=getViewportCollisions(coords,tipWidth,tipHeight);if(collisions!==Collision.none){collisionCount=countFlags(collisions);if(collisionCount===1){if(collisions===Collision.right){coords.set("left",session.scrollLeft+session.windowWidth-tipWidth)}else if(collisions===Collision.bottom){coords.set("top",session.scrollTop+session.windowHeight-tipHeight)}}else{coords.set("left",session.currentX-tipWidth-options.offset);coords.set("top",session.currentY-tipHeight-options.offset)}}tipElement.css(coords)}}function positionTipOnElement(element){var priorityList,finalPlacement;if(options.smartPlacement||options.followMouse&&element.data(DATA_FORCEDOPEN)){priorityList=$.fn.powerTip.smartPlacementLists[options.placement];$.each(priorityList,function(idx,pos){var collisions=getViewportCollisions(placeTooltip(element,pos),tipElement.outerWidth(),tipElement.outerHeight());finalPlacement=pos;return collisions!==Collision.none})}else{placeTooltip(element,options.placement);finalPlacement=options.placement}tipElement.removeClass("w nw sw e ne se n s w se-alt sw-alt ne-alt nw-alt");tipElement.addClass(finalPlacement)}function placeTooltip(element,placement){var iterationCount=0,tipWidth,tipHeight,coords=new CSSCoordinates;coords.set("top",0);coords.set("left",0);tipElement.css(coords);do{tipWidth=tipElement.outerWidth();tipHeight=tipElement.outerHeight();coords=placementCalculator.compute(element,placement,tipWidth,tipHeight,options.offset);tipElement.css(coords)}while(++iterationCount<=5&&(tipWidth!==tipElement.outerWidth()||tipHeight!==tipElement.outerHeight()));return coords}function closeDesyncedTip(){var isDesynced=false,hasDesyncableCloseEvent=$.grep(["mouseleave","mouseout","blur","focusout"],function(eventType){return $.inArray(eventType,options.closeEvents)!==-1}).length>0;if(session.isTipOpen&&!session.isClosing&&!session.delayInProgress&&hasDesyncableCloseEvent){if(session.activeHover.data(DATA_HASACTIVEHOVER)===false||session.activeHover.is(":disabled")){isDesynced=true}else if(!isMouseOver(session.activeHover)&&!session.activeHover.is(":focus")&&!session.activeHover.data(DATA_FORCEDOPEN)){if(tipElement.data(DATA_MOUSEONTOTIP)){if(!isMouseOver(tipElement)){isDesynced=true}}else{isDesynced=true}}if(isDesynced){hideTip(session.activeHover)}}}this.showTip=beginShowTip;this.hideTip=hideTip;this.resetPosition=positionTipOnElement}function isSvgElement(element){return Boolean(window.SVGElement&&element[0]instanceof SVGElement)}function isMouseEvent(event){return Boolean(event&&$.inArray(event.type,MOUSE_EVENTS)>-1&&typeof event.pageX==="number")}function initTracking(){if(!session.mouseTrackingActive){session.mouseTrackingActive=true;getViewportDimensions();$(getViewportDimensions);$document.on("mousemove"+EVENT_NAMESPACE,trackMouse);$window.on("resize"+EVENT_NAMESPACE,trackResize);$window.on("scroll"+EVENT_NAMESPACE,trackScroll)}}function getViewportDimensions(){session.scrollLeft=$window.scrollLeft();session.scrollTop=$window.scrollTop();session.windowWidth=$window.width();session.windowHeight=$window.height()}function trackResize(){session.windowWidth=$window.width();session.windowHeight=$window.height()}function trackScroll(){var x=$window.scrollLeft(),y=$window.scrollTop();if(x!==session.scrollLeft){session.currentX+=x-session.scrollLeft;session.scrollLeft=x}if(y!==session.scrollTop){session.currentY+=y-session.scrollTop;session.scrollTop=y}}function trackMouse(event){session.currentX=event.pageX;session.currentY=event.pageY}function isMouseOver(element){var elementPosition=element.offset(),elementBox=element[0].getBoundingClientRect(),elementWidth=elementBox.right-elementBox.left,elementHeight=elementBox.bottom-elementBox.top;return session.currentX>=elementPosition.left&&session.currentX<=elementPosition.left+elementWidth&&session.currentY>=elementPosition.top&&session.currentY<=elementPosition.top+elementHeight}function getTooltipContent(element){var tipText=element.data(DATA_POWERTIP),tipObject=element.data(DATA_POWERTIPJQ),tipTarget=element.data(DATA_POWERTIPTARGET),targetElement,content;if(tipText){if($.isFunction(tipText)){tipText=tipText.call(element[0])}content=tipText}else if(tipObject){if($.isFunction(tipObject)){tipObject=tipObject.call(element[0])}if(tipObject.length>0){content=tipObject.clone(true,true)}}else if(tipTarget){targetElement=$("#"+tipTarget);if(targetElement.length>0){content=targetElement.html()}}return content}function getViewportCollisions(coords,elementWidth,elementHeight){var viewportTop=session.scrollTop,viewportLeft=session.scrollLeft,viewportBottom=viewportTop+session.windowHeight,viewportRight=viewportLeft+session.windowWidth,collisions=Collision.none;if(coords.top<viewportTop||Math.abs(coords.bottom-session.windowHeight)-elementHeight<viewportTop){collisions|=Collision.top}if(coords.top+elementHeight>viewportBottom||Math.abs(coords.bottom-session.windowHeight)>viewportBottom){collisions|=Collision.bottom}if(coords.left<viewportLeft||coords.right+elementWidth>viewportRight){collisions|=Collision.left}if(coords.left+elementWidth>viewportRight||coords.right<viewportLeft){collisions|=Collision.right}return collisions}function countFlags(value){var count=0;while(value){value&=value-1;count++}return count}return $.powerTip});/*! * jQuery UI Touch Punch 0.2.3 * * Copyright 2011–2014, Dave Furfero * Dual licensed under the MIT or GPL Version 2 licenses. * * Depends: * jquery.ui.widget.js * jquery.ui.mouse.js */ !function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);/*! SmartMenus jQuery Plugin - v1.1.0 - September 17, 2017 * http://www.smartmenus.org/ * Copyright Vasil Dinkov, Vadikom Web Ltd. http://vadikom.com; Licensed MIT */(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&"object"==typeof module.exports?module.exports=t(require("jquery")):t(jQuery)})(function($){function initMouseDetection(t){var e=".smartmenus_mouse";if(mouseDetectionEnabled||t)mouseDetectionEnabled&&t&&($(document).off(e),mouseDetectionEnabled=!1);else{var i=!0,s=null,o={mousemove:function(t){var e={x:t.pageX,y:t.pageY,timeStamp:(new Date).getTime()};if(s){var o=Math.abs(s.x-e.x),a=Math.abs(s.y-e.y);if((o>0||a>0)&&2>=o&&2>=a&&300>=e.timeStamp-s.timeStamp&&(mouse=!0,i)){var n=$(t.target).closest("a");n.is("a")&&$.each(menuTrees,function(){return $.contains(this.$root[0],n[0])?(this.itemEnter({currentTarget:n[0]}),!1):void 0}),i=!1}}s=e}};o[touchEvents?"touchstart":"pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut"]=function(t){isTouchEvent(t.originalEvent)&&(mouse=!1)},$(document).on(getEventsNS(o,e)),mouseDetectionEnabled=!0}}function isTouchEvent(t){return!/^(4|mouse)$/.test(t.pointerType)}function getEventsNS(t,e){e||(e="");var i={};for(var s in t)i[s.split(" ").join(e+" ")+e]=t[s];return i}var menuTrees=[],mouse=!1,touchEvents="ontouchstart"in window,mouseDetectionEnabled=!1,requestAnimationFrame=window.requestAnimationFrame||function(t){return setTimeout(t,1e3/60)},cancelAnimationFrame=window.cancelAnimationFrame||function(t){clearTimeout(t)},canAnimate=!!$.fn.animate;return $.SmartMenus=function(t,e){this.$root=$(t),this.opts=e,this.rootId="",this.accessIdPrefix="",this.$subArrow=null,this.activatedItems=[],this.visibleSubMenus=[],this.showTimeout=0,this.hideTimeout=0,this.scrollTimeout=0,this.clickActivated=!1,this.focusActivated=!1,this.zIndexInc=0,this.idInc=0,this.$firstLink=null,this.$firstSub=null,this.disabled=!1,this.$disableOverlay=null,this.$touchScrollingSub=null,this.cssTransforms3d="perspective"in t.style||"webkitPerspective"in t.style,this.wasCollapsible=!1,this.init()},$.extend($.SmartMenus,{hideAll:function(){$.each(menuTrees,function(){this.menuHideAll()})},destroy:function(){for(;menuTrees.length;)menuTrees[0].destroy();initMouseDetection(!0)},prototype:{init:function(t){var e=this;if(!t){menuTrees.push(this),this.rootId=((new Date).getTime()+Math.random()+"").replace(/\D/g,""),this.accessIdPrefix="sm-"+this.rootId+"-",this.$root.hasClass("sm-rtl")&&(this.opts.rightToLeftSubMenus=!0);var i=".smartmenus";this.$root.data("smartmenus",this).attr("data-smartmenus-id",this.rootId).dataSM("level",1).on(getEventsNS({"mouseover focusin":$.proxy(this.rootOver,this),"mouseout focusout":$.proxy(this.rootOut,this),keydown:$.proxy(this.rootKeyDown,this)},i)).on(getEventsNS({mouseenter:$.proxy(this.itemEnter,this),mouseleave:$.proxy(this.itemLeave,this),mousedown:$.proxy(this.itemDown,this),focus:$.proxy(this.itemFocus,this),blur:$.proxy(this.itemBlur,this),click:$.proxy(this.itemClick,this)},i),"a"),i+=this.rootId,this.opts.hideOnClick&&$(document).on(getEventsNS({touchstart:$.proxy(this.docTouchStart,this),touchmove:$.proxy(this.docTouchMove,this),touchend:$.proxy(this.docTouchEnd,this),click:$.proxy(this.docClick,this)},i)),$(window).on(getEventsNS({"resize orientationchange":$.proxy(this.winResize,this)},i)),this.opts.subIndicators&&(this.$subArrow=$("<span/>").addClass("sub-arrow"),this.opts.subIndicatorsText&&this.$subArrow.html(this.opts.subIndicatorsText)),initMouseDetection()}if(this.$firstSub=this.$root.find("ul").each(function(){e.menuInit($(this))}).eq(0),this.$firstLink=this.$root.find("a").eq(0),this.opts.markCurrentItem){var s=/(index|default)\.[^#\?\/]*/i,o=/#.*/,a=window.location.href.replace(s,""),n=a.replace(o,"");this.$root.find("a").each(function(){var t=this.href.replace(s,""),i=$(this);(t==a||t==n)&&(i.addClass("current"),e.opts.markCurrentTree&&i.parentsUntil("[data-smartmenus-id]","ul").each(function(){$(this).dataSM("parent-a").addClass("current")}))})}this.wasCollapsible=this.isCollapsible()},destroy:function(t){if(!t){var e=".smartmenus";this.$root.removeData("smartmenus").removeAttr("data-smartmenus-id").removeDataSM("level").off(e),e+=this.rootId,$(document).off(e),$(window).off(e),this.opts.subIndicators&&(this.$subArrow=null)}this.menuHideAll();var i=this;this.$root.find("ul").each(function(){var t=$(this);t.dataSM("scroll-arrows")&&t.dataSM("scroll-arrows").remove(),t.dataSM("shown-before")&&((i.opts.subMenusMinWidth||i.opts.subMenusMaxWidth)&&t.css({width:"",minWidth:"",maxWidth:""}).removeClass("sm-nowrap"),t.dataSM("scroll-arrows")&&t.dataSM("scroll-arrows").remove(),t.css({zIndex:"",top:"",left:"",marginLeft:"",marginTop:"",display:""})),0==(t.attr("id")||"").indexOf(i.accessIdPrefix)&&t.removeAttr("id")}).removeDataSM("in-mega").removeDataSM("shown-before").removeDataSM("scroll-arrows").removeDataSM("parent-a").removeDataSM("level").removeDataSM("beforefirstshowfired").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeAttr("aria-expanded"),this.$root.find("a.has-submenu").each(function(){var t=$(this);0==t.attr("id").indexOf(i.accessIdPrefix)&&t.removeAttr("id")}).removeClass("has-submenu").removeDataSM("sub").removeAttr("aria-haspopup").removeAttr("aria-controls").removeAttr("aria-expanded").closest("li").removeDataSM("sub"),this.opts.subIndicators&&this.$root.find("span.sub-arrow").remove(),this.opts.markCurrentItem&&this.$root.find("a.current").removeClass("current"),t||(this.$root=null,this.$firstLink=null,this.$firstSub=null,this.$disableOverlay&&(this.$disableOverlay.remove(),this.$disableOverlay=null),menuTrees.splice($.inArray(this,menuTrees),1))},disable:function(t){if(!this.disabled){if(this.menuHideAll(),!t&&!this.opts.isPopup&&this.$root.is(":visible")){var e=this.$root.offset();this.$disableOverlay=$('<div class="sm-jquery-disable-overlay"/>').css({position:"absolute",top:e.top,left:e.left,width:this.$root.outerWidth(),height:this.$root.outerHeight(),zIndex:this.getStartZIndex(!0),opacity:0}).appendTo(document.body)}this.disabled=!0}},docClick:function(t){return this.$touchScrollingSub?(this.$touchScrollingSub=null,void 0):((this.visibleSubMenus.length&&!$.contains(this.$root[0],t.target)||$(t.target).closest("a").length)&&this.menuHideAll(),void 0)},docTouchEnd:function(){if(this.lastTouch){if(!(!this.visibleSubMenus.length||void 0!==this.lastTouch.x2&&this.lastTouch.x1!=this.lastTouch.x2||void 0!==this.lastTouch.y2&&this.lastTouch.y1!=this.lastTouch.y2||this.lastTouch.target&&$.contains(this.$root[0],this.lastTouch.target))){this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0);var t=this;this.hideTimeout=setTimeout(function(){t.menuHideAll()},350)}this.lastTouch=null}},docTouchMove:function(t){if(this.lastTouch){var e=t.originalEvent.touches[0];this.lastTouch.x2=e.pageX,this.lastTouch.y2=e.pageY}},docTouchStart:function(t){var e=t.originalEvent.touches[0];this.lastTouch={x1:e.pageX,y1:e.pageY,target:e.target}},enable:function(){this.disabled&&(this.$disableOverlay&&(this.$disableOverlay.remove(),this.$disableOverlay=null),this.disabled=!1)},getClosestMenu:function(t){for(var e=$(t).closest("ul");e.dataSM("in-mega");)e=e.parent().closest("ul");return e[0]||null},getHeight:function(t){return this.getOffset(t,!0)},getOffset:function(t,e){var i;"none"==t.css("display")&&(i={position:t[0].style.position,visibility:t[0].style.visibility},t.css({position:"absolute",visibility:"hidden"}).show());var s=t[0].getBoundingClientRect&&t[0].getBoundingClientRect(),o=s&&(e?s.height||s.bottom-s.top:s.width||s.right-s.left);return o||0===o||(o=e?t[0].offsetHeight:t[0].offsetWidth),i&&t.hide().css(i),o},getStartZIndex:function(t){var e=parseInt(this[t?"$root":"$firstSub"].css("z-index"));return!t&&isNaN(e)&&(e=parseInt(this.$root.css("z-index"))),isNaN(e)?1:e},getTouchPoint:function(t){return t.touches&&t.touches[0]||t.changedTouches&&t.changedTouches[0]||t},getViewport:function(t){var e=t?"Height":"Width",i=document.documentElement["client"+e],s=window["inner"+e];return s&&(i=Math.min(i,s)),i},getViewportHeight:function(){return this.getViewport(!0)},getViewportWidth:function(){return this.getViewport()},getWidth:function(t){return this.getOffset(t)},handleEvents:function(){return!this.disabled&&this.isCSSOn()},handleItemEvents:function(t){return this.handleEvents()&&!this.isLinkInMegaMenu(t)},isCollapsible:function(){return"static"==this.$firstSub.css("position")},isCSSOn:function(){return"inline"!=this.$firstLink.css("display")},isFixed:function(){var t="fixed"==this.$root.css("position");return t||this.$root.parentsUntil("body").each(function(){return"fixed"==$(this).css("position")?(t=!0,!1):void 0}),t},isLinkInMegaMenu:function(t){return $(this.getClosestMenu(t[0])).hasClass("mega-menu")},isTouchMode:function(){return!mouse||this.opts.noMouseOver||this.isCollapsible()},itemActivate:function(t,e){var i=t.closest("ul"),s=i.dataSM("level");if(s>1&&(!this.activatedItems[s-2]||this.activatedItems[s-2][0]!=i.dataSM("parent-a")[0])){var o=this;$(i.parentsUntil("[data-smartmenus-id]","ul").get().reverse()).add(i).each(function(){o.itemActivate($(this).dataSM("parent-a"))})}if((!this.isCollapsible()||e)&&this.menuHideSubMenus(this.activatedItems[s-1]&&this.activatedItems[s-1][0]==t[0]?s:s-1),this.activatedItems[s-1]=t,this.$root.triggerHandler("activate.smapi",t[0])!==!1){var a=t.dataSM("sub");a&&(this.isTouchMode()||!this.opts.showOnClick||this.clickActivated)&&this.menuShow(a)}},itemBlur:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&this.$root.triggerHandler("blur.smapi",e[0])},itemClick:function(t){var e=$(t.currentTarget);if(this.handleItemEvents(e)){if(this.$touchScrollingSub&&this.$touchScrollingSub[0]==e.closest("ul")[0])return this.$touchScrollingSub=null,t.stopPropagation(),!1;if(this.$root.triggerHandler("click.smapi",e[0])===!1)return!1;var i=$(t.target).is(".sub-arrow"),s=e.dataSM("sub"),o=s?2==s.dataSM("level"):!1,a=this.isCollapsible(),n=/toggle$/.test(this.opts.collapsibleBehavior),r=/link$/.test(this.opts.collapsibleBehavior),h=/^accordion/.test(this.opts.collapsibleBehavior);if(s&&!s.is(":visible")){if((!r||!a||i)&&(this.opts.showOnClick&&o&&(this.clickActivated=!0),this.itemActivate(e,h),s.is(":visible")))return this.focusActivated=!0,!1}else if(a&&(n||i))return this.itemActivate(e,h),this.menuHide(s),n&&(this.focusActivated=!1),!1;return this.opts.showOnClick&&o||e.hasClass("disabled")||this.$root.triggerHandler("select.smapi",e[0])===!1?!1:void 0}},itemDown:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&e.dataSM("mousedown",!0)},itemEnter:function(t){var e=$(t.currentTarget);if(this.handleItemEvents(e)){if(!this.isTouchMode()){this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0);var i=this;this.showTimeout=setTimeout(function(){i.itemActivate(e)},this.opts.showOnClick&&1==e.closest("ul").dataSM("level")?1:this.opts.showTimeout)}this.$root.triggerHandler("mouseenter.smapi",e[0])}},itemFocus:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&(!this.focusActivated||this.isTouchMode()&&e.dataSM("mousedown")||this.activatedItems.length&&this.activatedItems[this.activatedItems.length-1][0]==e[0]||this.itemActivate(e,!0),this.$root.triggerHandler("focus.smapi",e[0]))},itemLeave:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&(this.isTouchMode()||(e[0].blur(),this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0)),e.removeDataSM("mousedown"),this.$root.triggerHandler("mouseleave.smapi",e[0]))},menuHide:function(t){if(this.$root.triggerHandler("beforehide.smapi",t[0])!==!1&&(canAnimate&&t.stop(!0,!0),"none"!=t.css("display"))){var e=function(){t.css("z-index","")};this.isCollapsible()?canAnimate&&this.opts.collapsibleHideFunction?this.opts.collapsibleHideFunction.call(this,t,e):t.hide(this.opts.collapsibleHideDuration,e):canAnimate&&this.opts.hideFunction?this.opts.hideFunction.call(this,t,e):t.hide(this.opts.hideDuration,e),t.dataSM("scroll")&&(this.menuScrollStop(t),t.css({"touch-action":"","-ms-touch-action":"","-webkit-transform":"",transform:""}).off(".smartmenus_scroll").removeDataSM("scroll").dataSM("scroll-arrows").hide()),t.dataSM("parent-a").removeClass("highlighted").attr("aria-expanded","false"),t.attr({"aria-expanded":"false","aria-hidden":"true"});var i=t.dataSM("level");this.activatedItems.splice(i-1,1),this.visibleSubMenus.splice($.inArray(t,this.visibleSubMenus),1),this.$root.triggerHandler("hide.smapi",t[0])}},menuHideAll:function(){this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0);for(var t=this.opts.isPopup?1:0,e=this.visibleSubMenus.length-1;e>=t;e--)this.menuHide(this.visibleSubMenus[e]);this.opts.isPopup&&(canAnimate&&this.$root.stop(!0,!0),this.$root.is(":visible")&&(canAnimate&&this.opts.hideFunction?this.opts.hideFunction.call(this,this.$root):this.$root.hide(this.opts.hideDuration))),this.activatedItems=[],this.visibleSubMenus=[],this.clickActivated=!1,this.focusActivated=!1,this.zIndexInc=0,this.$root.triggerHandler("hideAll.smapi")},menuHideSubMenus:function(t){for(var e=this.activatedItems.length-1;e>=t;e--){var i=this.activatedItems[e].dataSM("sub");i&&this.menuHide(i)}},menuInit:function(t){if(!t.dataSM("in-mega")){t.hasClass("mega-menu")&&t.find("ul").dataSM("in-mega",!0);for(var e=2,i=t[0];(i=i.parentNode.parentNode)!=this.$root[0];)e++;var s=t.prevAll("a").eq(-1);s.length||(s=t.prevAll().find("a").eq(-1)),s.addClass("has-submenu").dataSM("sub",t),t.dataSM("parent-a",s).dataSM("level",e).parent().dataSM("sub",t);var o=s.attr("id")||this.accessIdPrefix+ ++this.idInc,a=t.attr("id")||this.accessIdPrefix+ ++this.idInc;s.attr({id:o,"aria-haspopup":"true","aria-controls":a,"aria-expanded":"false"}),t.attr({id:a,role:"group","aria-hidden":"true","aria-labelledby":o,"aria-expanded":"false"}),this.opts.subIndicators&&s[this.opts.subIndicatorsPos](this.$subArrow.clone())}},menuPosition:function(t){var e,i,s=t.dataSM("parent-a"),o=s.closest("li"),a=o.parent(),n=t.dataSM("level"),r=this.getWidth(t),h=this.getHeight(t),u=s.offset(),l=u.left,c=u.top,d=this.getWidth(s),m=this.getHeight(s),p=$(window),f=p.scrollLeft(),v=p.scrollTop(),b=this.getViewportWidth(),S=this.getViewportHeight(),g=a.parent().is("[data-sm-horizontal-sub]")||2==n&&!a.hasClass("sm-vertical"),M=this.opts.rightToLeftSubMenus&&!o.is("[data-sm-reverse]")||!this.opts.rightToLeftSubMenus&&o.is("[data-sm-reverse]"),w=2==n?this.opts.mainMenuSubOffsetX:this.opts.subMenusSubOffsetX,T=2==n?this.opts.mainMenuSubOffsetY:this.opts.subMenusSubOffsetY;if(g?(e=M?d-r-w:w,i=this.opts.bottomToTopSubMenus?-h-T:m+T):(e=M?w-r:d-w,i=this.opts.bottomToTopSubMenus?m-T-h:T),this.opts.keepInViewport){var y=l+e,I=c+i;if(M&&f>y?e=g?f-y+e:d-w:!M&&y+r>f+b&&(e=g?f+b-r-y+e:w-r),g||(S>h&&I+h>v+S?i+=v+S-h-I:(h>=S||v>I)&&(i+=v-I)),g&&(I+h>v+S+.49||v>I)||!g&&h>S+.49){var x=this;t.dataSM("scroll-arrows")||t.dataSM("scroll-arrows",$([$('<span class="scroll-up"><span class="scroll-up-arrow"></span></span>')[0],$('<span class="scroll-down"><span class="scroll-down-arrow"></span></span>')[0]]).on({mouseenter:function(){t.dataSM("scroll").up=$(this).hasClass("scroll-up"),x.menuScroll(t)},mouseleave:function(e){x.menuScrollStop(t),x.menuScrollOut(t,e)},"mousewheel DOMMouseScroll":function(t){t.preventDefault()}}).insertAfter(t));var A=".smartmenus_scroll";if(t.dataSM("scroll",{y:this.cssTransforms3d?0:i-m,step:1,itemH:m,subH:h,arrowDownH:this.getHeight(t.dataSM("scroll-arrows").eq(1))}).on(getEventsNS({mouseover:function(e){x.menuScrollOver(t,e)},mouseout:function(e){x.menuScrollOut(t,e)},"mousewheel DOMMouseScroll":function(e){x.menuScrollMousewheel(t,e)}},A)).dataSM("scroll-arrows").css({top:"auto",left:"0",marginLeft:e+(parseInt(t.css("border-left-width"))||0),width:r-(parseInt(t.css("border-left-width"))||0)-(parseInt(t.css("border-right-width"))||0),zIndex:t.css("z-index")}).eq(g&&this.opts.bottomToTopSubMenus?0:1).show(),this.isFixed()){var C={};C[touchEvents?"touchstart touchmove touchend":"pointerdown pointermove pointerup MSPointerDown MSPointerMove MSPointerUp"]=function(e){x.menuScrollTouch(t,e)},t.css({"touch-action":"none","-ms-touch-action":"none"}).on(getEventsNS(C,A))}}}t.css({top:"auto",left:"0",marginLeft:e,marginTop:i-m})},menuScroll:function(t,e,i){var s,o=t.dataSM("scroll"),a=t.dataSM("scroll-arrows"),n=o.up?o.upEnd:o.downEnd;if(!e&&o.momentum){if(o.momentum*=.92,s=o.momentum,.5>s)return this.menuScrollStop(t),void 0}else s=i||(e||!this.opts.scrollAccelerate?this.opts.scrollStep:Math.floor(o.step));var r=t.dataSM("level");if(this.activatedItems[r-1]&&this.activatedItems[r-1].dataSM("sub")&&this.activatedItems[r-1].dataSM("sub").is(":visible")&&this.menuHideSubMenus(r-1),o.y=o.up&&o.y>=n||!o.up&&n>=o.y?o.y:Math.abs(n-o.y)>s?o.y+(o.up?s:-s):n,t.css(this.cssTransforms3d?{"-webkit-transform":"translate3d(0, "+o.y+"px, 0)",transform:"translate3d(0, "+o.y+"px, 0)"}:{marginTop:o.y}),mouse&&(o.up&&o.y>o.downEnd||!o.up&&o.y<o.upEnd)&&a.eq(o.up?1:0).show(),o.y==n)mouse&&a.eq(o.up?0:1).hide(),this.menuScrollStop(t);else if(!e){this.opts.scrollAccelerate&&o.step<this.opts.scrollStep&&(o.step+=.2);var h=this;this.scrollTimeout=requestAnimationFrame(function(){h.menuScroll(t)})}},menuScrollMousewheel:function(t,e){if(this.getClosestMenu(e.target)==t[0]){e=e.originalEvent;var i=(e.wheelDelta||-e.detail)>0;t.dataSM("scroll-arrows").eq(i?0:1).is(":visible")&&(t.dataSM("scroll").up=i,this.menuScroll(t,!0))}e.preventDefault()},menuScrollOut:function(t,e){mouse&&(/^scroll-(up|down)/.test((e.relatedTarget||"").className)||(t[0]==e.relatedTarget||$.contains(t[0],e.relatedTarget))&&this.getClosestMenu(e.relatedTarget)==t[0]||t.dataSM("scroll-arrows").css("visibility","hidden"))},menuScrollOver:function(t,e){if(mouse&&!/^scroll-(up|down)/.test(e.target.className)&&this.getClosestMenu(e.target)==t[0]){this.menuScrollRefreshData(t);var i=t.dataSM("scroll"),s=$(window).scrollTop()-t.dataSM("parent-a").offset().top-i.itemH;t.dataSM("scroll-arrows").eq(0).css("margin-top",s).end().eq(1).css("margin-top",s+this.getViewportHeight()-i.arrowDownH).end().css("visibility","visible")}},menuScrollRefreshData:function(t){var e=t.dataSM("scroll"),i=$(window).scrollTop()-t.dataSM("parent-a").offset().top-e.itemH;this.cssTransforms3d&&(i=-(parseFloat(t.css("margin-top"))-i)),$.extend(e,{upEnd:i,downEnd:i+this.getViewportHeight()-e.subH})},menuScrollStop:function(t){return this.scrollTimeout?(cancelAnimationFrame(this.scrollTimeout),this.scrollTimeout=0,t.dataSM("scroll").step=1,!0):void 0},menuScrollTouch:function(t,e){if(e=e.originalEvent,isTouchEvent(e)){var i=this.getTouchPoint(e);if(this.getClosestMenu(i.target)==t[0]){var s=t.dataSM("scroll");if(/(start|down)$/i.test(e.type))this.menuScrollStop(t)?(e.preventDefault(),this.$touchScrollingSub=t):this.$touchScrollingSub=null,this.menuScrollRefreshData(t),$.extend(s,{touchStartY:i.pageY,touchStartTime:e.timeStamp});else if(/move$/i.test(e.type)){var o=void 0!==s.touchY?s.touchY:s.touchStartY;if(void 0!==o&&o!=i.pageY){this.$touchScrollingSub=t;var a=i.pageY>o;void 0!==s.up&&s.up!=a&&$.extend(s,{touchStartY:i.pageY,touchStartTime:e.timeStamp}),$.extend(s,{up:a,touchY:i.pageY}),this.menuScroll(t,!0,Math.abs(i.pageY-o))}e.preventDefault()}else void 0!==s.touchY&&((s.momentum=15*Math.pow(Math.abs(i.pageY-s.touchStartY)/(e.timeStamp-s.touchStartTime),2))&&(this.menuScrollStop(t),this.menuScroll(t),e.preventDefault()),delete s.touchY)}}},menuShow:function(t){if((t.dataSM("beforefirstshowfired")||(t.dataSM("beforefirstshowfired",!0),this.$root.triggerHandler("beforefirstshow.smapi",t[0])!==!1))&&this.$root.triggerHandler("beforeshow.smapi",t[0])!==!1&&(t.dataSM("shown-before",!0),canAnimate&&t.stop(!0,!0),!t.is(":visible"))){var e=t.dataSM("parent-a"),i=this.isCollapsible();if((this.opts.keepHighlighted||i)&&e.addClass("highlighted"),i)t.removeClass("sm-nowrap").css({zIndex:"",width:"auto",minWidth:"",maxWidth:"",top:"",left:"",marginLeft:"",marginTop:""});else{if(t.css("z-index",this.zIndexInc=(this.zIndexInc||this.getStartZIndex())+1),(this.opts.subMenusMinWidth||this.opts.subMenusMaxWidth)&&(t.css({width:"auto",minWidth:"",maxWidth:""}).addClass("sm-nowrap"),this.opts.subMenusMinWidth&&t.css("min-width",this.opts.subMenusMinWidth),this.opts.subMenusMaxWidth)){var s=this.getWidth(t);t.css("max-width",this.opts.subMenusMaxWidth),s>this.getWidth(t)&&t.removeClass("sm-nowrap").css("width",this.opts.subMenusMaxWidth)}this.menuPosition(t)}var o=function(){t.css("overflow","")};i?canAnimate&&this.opts.collapsibleShowFunction?this.opts.collapsibleShowFunction.call(this,t,o):t.show(this.opts.collapsibleShowDuration,o):canAnimate&&this.opts.showFunction?this.opts.showFunction.call(this,t,o):t.show(this.opts.showDuration,o),e.attr("aria-expanded","true"),t.attr({"aria-expanded":"true","aria-hidden":"false"}),this.visibleSubMenus.push(t),this.$root.triggerHandler("show.smapi",t[0])}},popupHide:function(t){this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0);var e=this;this.hideTimeout=setTimeout(function(){e.menuHideAll()},t?1:this.opts.hideTimeout)},popupShow:function(t,e){if(!this.opts.isPopup)return alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.'),void 0;if(this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0),this.$root.dataSM("shown-before",!0),canAnimate&&this.$root.stop(!0,!0),!this.$root.is(":visible")){this.$root.css({left:t,top:e});var i=this,s=function(){i.$root.css("overflow","")};canAnimate&&this.opts.showFunction?this.opts.showFunction.call(this,this.$root,s):this.$root.show(this.opts.showDuration,s),this.visibleSubMenus[0]=this.$root}},refresh:function(){this.destroy(!0),this.init(!0)},rootKeyDown:function(t){if(this.handleEvents())switch(t.keyCode){case 27:var e=this.activatedItems[0];if(e){this.menuHideAll(),e[0].focus();var i=e.dataSM("sub");i&&this.menuHide(i)}break;case 32:var s=$(t.target);if(s.is("a")&&this.handleItemEvents(s)){var i=s.dataSM("sub");i&&!i.is(":visible")&&(this.itemClick({currentTarget:t.target}),t.preventDefault())}}},rootOut:function(t){if(this.handleEvents()&&!this.isTouchMode()&&t.target!=this.$root[0]&&(this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0),!this.opts.showOnClick||!this.opts.hideOnClick)){var e=this;this.hideTimeout=setTimeout(function(){e.menuHideAll()},this.opts.hideTimeout)}},rootOver:function(t){this.handleEvents()&&!this.isTouchMode()&&t.target!=this.$root[0]&&this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0)},winResize:function(t){if(this.handleEvents()){if(!("onorientationchange"in window)||"orientationchange"==t.type){var e=this.isCollapsible();this.wasCollapsible&&e||(this.activatedItems.length&&this.activatedItems[this.activatedItems.length-1][0].blur(),this.menuHideAll()),this.wasCollapsible=e}}else if(this.$disableOverlay){var i=this.$root.offset();this.$disableOverlay.css({top:i.top,left:i.left,width:this.$root.outerWidth(),height:this.$root.outerHeight()})}}}}),$.fn.dataSM=function(t,e){return e?this.data(t+"_smartmenus",e):this.data(t+"_smartmenus")},$.fn.removeDataSM=function(t){return this.removeData(t+"_smartmenus")},$.fn.smartmenus=function(options){if("string"==typeof options){var args=arguments,method=options;return Array.prototype.shift.call(args),this.each(function(){var t=$(this).data("smartmenus");t&&t[method]&&t[method].apply(t,args)})}return this.each(function(){var dataOpts=$(this).data("sm-options")||null;if(dataOpts)try{dataOpts=eval("("+dataOpts+")")}catch(e){dataOpts=null,alert('ERROR\n\nSmartMenus jQuery init:\nInvalid "data-sm-options" attribute value syntax.')}new $.SmartMenus(this,$.extend({},$.fn.smartmenus.defaults,options,dataOpts))})},$.fn.smartmenus.defaults={isPopup:!1,mainMenuSubOffsetX:0,mainMenuSubOffsetY:0,subMenusSubOffsetX:0,subMenusSubOffsetY:0,subMenusMinWidth:"10em",subMenusMaxWidth:"20em",subIndicators:!0,subIndicatorsPos:"append",subIndicatorsText:"",scrollStep:30,scrollAccelerate:!0,showTimeout:250,hideTimeout:500,showDuration:0,showFunction:null,hideDuration:0,hideFunction:function(t,e){t.fadeOut(200,e)},collapsibleShowDuration:0,collapsibleShowFunction:function(t,e){t.slideDown(200,e)},collapsibleHideDuration:0,collapsibleHideFunction:function(t,e){t.slideUp(200,e)},showOnClick:!1,hideOnClick:!0,noMouseOver:!1,keepInViewport:!0,keepHighlighted:!0,markCurrentItem:!1,markCurrentTree:!0,rightToLeftSubMenus:!1,bottomToTopSubMenus:!1,collapsibleBehavior:"default"},$});����������fuse-3.18.2/doc/html/menu.js������������������������������������������������������������������������0000644�0001750�0001750�00000013445�15156613442�014546� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* @licstart The following is the entire license notice for the JavaScript code in this file. The MIT License (MIT) Copyright (C) 1997-2020 by Dimitri van Heesch Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @licend The above is the entire license notice for the JavaScript code in this file */ function initMenu(relPath,searchEnabled,serverSide,searchPage,search) { function makeTree(data,relPath) { var result=''; if ('children' in data) { result+='<ul>'; for (var i in data.children) { var url; var link; link = data.children[i].url; if (link.substring(0,1)=='^') { url = link.substring(1); } else { url = relPath+link; } result+='<li><a href="'+url+'">'+ data.children[i].text+'</a>'+ makeTree(data.children[i],relPath)+'</li>'; } result+='</ul>'; } return result; } var searchBoxHtml; if (searchEnabled) { if (serverSide) { searchBoxHtml='<div id="MSearchBox" class="MSearchBoxInactive">'+ '<div class="left">'+ '<form id="FSearchBox" action="'+relPath+searchPage+ '" method="get"><span id="MSearchSelectExt"> </span>'+ '<input type="text" id="MSearchField" name="query" value="" placeholder="'+search+ '" size="20" accesskey="S" onfocus="searchBox.OnSearchFieldFocus(true)"'+ ' onblur="searchBox.OnSearchFieldFocus(false)"/>'+ '</form>'+ '</div>'+ '<div class="right"></div>'+ '</div>'; } else { searchBoxHtml='<div id="MSearchBox" class="MSearchBoxInactive">'+ '<span class="left">'+ '<span id="MSearchSelect" onmouseover="return searchBox.OnSearchSelectShow()"'+ ' onmouseout="return searchBox.OnSearchSelectHide()"> </span>'+ '<input type="text" id="MSearchField" value="" placeholder="'+search+ '" accesskey="S" onfocus="searchBox.OnSearchFieldFocus(true)" '+ 'onblur="searchBox.OnSearchFieldFocus(false)" '+ 'onkeyup="searchBox.OnSearchFieldChange(event)"/>'+ '</span>'+ '<span class="right"><a id="MSearchClose" '+ 'href="javascript:searchBox.CloseResultsWindow()">'+ '<img id="MSearchCloseImg" border="0" src="'+relPath+ 'search/close.svg" alt=""/></a>'+ '</span>'+ '</div>'; } } $('#main-nav').before('<div class="sm sm-dox"><input id="main-menu-state" type="checkbox"/>'+ '<label class="main-menu-btn" for="main-menu-state">'+ '<span class="main-menu-btn-icon"></span> '+ 'Toggle main menu visibility</label>'+ '<span id="searchBoxPos1" style="position:absolute;right:8px;top:8px;height:36px;"></span>'+ '</div>'); $('#main-nav').append(makeTree(menudata,relPath)); $('#main-nav').children(':first').addClass('sm sm-dox').attr('id','main-menu'); if (searchBoxHtml) { $('#main-menu').append('<li id="searchBoxPos2" style="float:right"></li>'); } var $mainMenuState = $('#main-menu-state'); var prevWidth = 0; if ($mainMenuState.length) { function initResizableIfExists() { if (typeof initResizable==='function') initResizable(); } // animate mobile menu $mainMenuState.change(function(e) { var $menu = $('#main-menu'); var options = { duration: 250, step: initResizableIfExists }; if (this.checked) { options['complete'] = function() { $menu.css('display', 'block') }; $menu.hide().slideDown(options); } else { options['complete'] = function() { $menu.css('display', 'none') }; $menu.show().slideUp(options); } }); // set default menu visibility function resetState() { var $menu = $('#main-menu'); var $mainMenuState = $('#main-menu-state'); var newWidth = $(window).outerWidth(); if (newWidth!=prevWidth) { if ($(window).outerWidth()<768) { $mainMenuState.prop('checked',false); $menu.hide(); $('#searchBoxPos1').html(searchBoxHtml); $('#searchBoxPos2').hide(); } else { $menu.show(); $('#searchBoxPos1').empty(); $('#searchBoxPos2').html(searchBoxHtml); $('#searchBoxPos2').show(); } if (typeof searchBox!=='undefined') { searchBox.CloseResultsWindow(); } prevWidth = newWidth; } } $(window).ready(function() { resetState(); initResizableIfExists(); }); $(window).resize(resetState); } $('#main-menu').smartmenus(); } /* @license-end */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/dynsections.js�����������������������������������������������������������������0000644�0001750�0001750�00000017566�15156613442�016154� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* @licstart The following is the entire license notice for the JavaScript code in this file. The MIT License (MIT) Copyright (C) 1997-2020 by Dimitri van Heesch Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @licend The above is the entire license notice for the JavaScript code in this file */ function toggleVisibility(linkObj) { var base = $(linkObj).attr('id'); var summary = $('#'+base+'-summary'); var content = $('#'+base+'-content'); var trigger = $('#'+base+'-trigger'); var src=$(trigger).attr('src'); if (content.is(':visible')===true) { content.hide(); summary.show(); $(linkObj).addClass('closed').removeClass('opened'); $(trigger).attr('src',src.substring(0,src.length-8)+'closed.png'); } else { content.show(); summary.hide(); $(linkObj).removeClass('closed').addClass('opened'); $(trigger).attr('src',src.substring(0,src.length-10)+'open.png'); } return false; } function updateStripes() { $('table.directory tr'). removeClass('even').filter(':visible:even').addClass('even'); $('table.directory tr'). removeClass('odd').filter(':visible:odd').addClass('odd'); } function toggleLevel(level) { $('table.directory tr').each(function() { var l = this.id.split('_').length-1; var i = $('#img'+this.id.substring(3)); var a = $('#arr'+this.id.substring(3)); if (l<level+1) { i.removeClass('iconfopen iconfclosed').addClass('iconfopen'); a.html('▼'); $(this).show(); } else if (l==level+1) { i.removeClass('iconfclosed iconfopen').addClass('iconfclosed'); a.html('►'); $(this).show(); } else { $(this).hide(); } }); updateStripes(); } function toggleFolder(id) { // the clicked row var currentRow = $('#row_'+id); // all rows after the clicked row var rows = currentRow.nextAll("tr"); var re = new RegExp('^row_'+id+'\\d+_$', "i"); //only one sub // only match elements AFTER this one (can't hide elements before) var childRows = rows.filter(function() { return this.id.match(re); }); // first row is visible we are HIDING if (childRows.filter(':first').is(':visible')===true) { // replace down arrow by right arrow for current row var currentRowSpans = currentRow.find("span"); currentRowSpans.filter(".iconfopen").removeClass("iconfopen").addClass("iconfclosed"); currentRowSpans.filter(".arrow").html('►'); rows.filter("[id^=row_"+id+"]").hide(); // hide all children } else { // we are SHOWING // replace right arrow by down arrow for current row var currentRowSpans = currentRow.find("span"); currentRowSpans.filter(".iconfclosed").removeClass("iconfclosed").addClass("iconfopen"); currentRowSpans.filter(".arrow").html('▼'); // replace down arrows by right arrows for child rows var childRowsSpans = childRows.find("span"); childRowsSpans.filter(".iconfopen").removeClass("iconfopen").addClass("iconfclosed"); childRowsSpans.filter(".arrow").html('►'); childRows.show(); //show all children } updateStripes(); } function toggleInherit(id) { var rows = $('tr.inherit.'+id); var img = $('tr.inherit_header.'+id+' img'); var src = $(img).attr('src'); if (rows.filter(':first').is(':visible')===true) { rows.css('display','none'); $(img).attr('src',src.substring(0,src.length-8)+'closed.png'); } else { rows.css('display','table-row'); // using show() causes jump in firefox $(img).attr('src',src.substring(0,src.length-10)+'open.png'); } } var opened=true; // in case HTML_COLORSTYLE is LIGHT or DARK the vars will be replaced, so we write them out explicitly and use double quotes var plusImg = [ "var(--fold-plus-image)", "var(--fold-plus-image-relpath)" ]; var minusImg = [ "var(--fold-minus-image)", "var(--fold-minus-image-relpath)" ]; // toggle all folding blocks function codefold_toggle_all(relPath) { if (opened) { $('#fold_all').css('background-image',plusImg[relPath]); $('div[id^=foldopen]').hide(); $('div[id^=foldclosed]').show(); } else { $('#fold_all').css('background-image',minusImg[relPath]); $('div[id^=foldopen]').show(); $('div[id^=foldclosed]').hide(); } opened=!opened; } // toggle single folding block function codefold_toggle(id) { $('#foldopen'+id).toggle(); $('#foldclosed'+id).toggle(); } function init_codefold(relPath) { $('span[class=lineno]').css( {'padding-right':'4px', 'margin-right':'2px', 'display':'inline-block', 'width':'54px', 'background':'linear-gradient(var(--fold-line-color),var(--fold-line-color)) no-repeat 46px/2px 100%' }); // add global toggle to first line $('span[class=lineno]:first').append('<span class="fold" id="fold_all" '+ 'onclick="javascript:codefold_toggle_all('+relPath+');" '+ 'style="background-image:'+minusImg[relPath]+';"></span>'); // add vertical lines to other rows $('span[class=lineno]').not(':eq(0)').append('<span class="fold"></span>'); // add toggle controls to lines with fold divs $('div[class=foldopen]').each(function() { // extract specific id to use var id = $(this).attr('id').replace('foldopen',''); // extract start and end foldable fragment attributes var start = $(this).attr('data-start'); var end = $(this).attr('data-end'); // replace normal fold span with controls for the first line of a foldable fragment $(this).find('span[class=fold]:first').replaceWith('<span class="fold" '+ 'onclick="javascript:codefold_toggle(\''+id+'\');" '+ 'style="background-image:'+minusImg[relPath]+';"></span>'); // append div for folded (closed) representation $(this).after('<div id="foldclosed'+id+'" class="foldclosed" style="display:none;"></div>'); // extract the first line from the "open" section to represent closed content var line = $(this).children().first().clone(); // remove any glow that might still be active on the original line $(line).removeClass('glow'); if (start) { // if line already ends with a start marker (e.g. trailing {), remove it $(line).html($(line).html().replace(new RegExp('\\s*'+start+'\\s*$','g'),'')); } // replace minus with plus symbol $(line).find('span[class=fold]').css('background-image',plusImg[relPath]); // append ellipsis $(line).append(' '+start+'<a href="javascript:codefold_toggle(\''+id+'\')">…</a>'+end); // insert constructed line into closed div $('#foldclosed'+id).html(line); }); } /* @license-end */ $(document).ready(function() { $('.code,.codeRef').each(function() { $(this).data('powertip',$('#a'+$(this).attr('href').replace(/.*\//,'').replace(/[^a-z_A-Z0-9]/g,'_')).html()); $.fn.powerTip.smartPlacementLists.s = [ 's', 'n', 'ne', 'se' ]; $(this).powerTip({ placement: 's', smartPlacement: true, mouseOnToPopup: true }); }); }); ������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/tab_a.png����������������������������������������������������������������������0000644�0001750�0001750�00000000216�15156613442�015010� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������$���[���UIDATxK 0C'o([Ž%x#٩ We# 3t I 3+E~\D9wM}Y_A4Y}����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/tab_b.png����������������������������������������������������������������������0000644�0001750�0001750�00000000251�15156613442�015010� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������$���[���pIDATxM EǻԸu`V0}:t]Ds䮂u|x>1&m8SxLU޲iEOsnxKN~j����IENDB`�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/tab_h.png����������������������������������������������������������������������0000644�0001750�0001750�00000000261�15156613442�015017� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������$���[���xIDATxM@~ΒEv"!d*rGq={SݧH uO^[_Xvyұ=VCff{R%_rug(?gh\i>|s����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/tab_s.png����������������������������������������������������������������������0000644�0001750�0001750�00000000270�15156613442�015032� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������$���[���IDATx݁ @�@ѣ?Q"%If6[HQ<]dr s?O=w'F -~rÍ[芭m֬ݯнF)Y% `n,9B!ь\<#����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/tab_ad.png���������������������������������������������������������������������0000644�0001750�0001750�00000000207�15156613442�015154� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������$���[���NIDATxa P" ޟ.!L/nEX2i̝^ rV}r>=>b�Ha5����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/tab_bd.png���������������������������������������������������������������������0000644�0001750�0001750�00000000255�15156613442�015160� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������$���[���tIDATx[ 0Л66)IZ_%)(.(z* u97d:.Lqwg?8LWxʐOЧVql` 1+S^Z ~dA .YB����IENDB`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/tab_hd.png���������������������������������������������������������������������0000644�0001750�0001750�00000000264�15156613442�015166� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������$���[���{IDATxK @D̀""I w1E.gABntX\?,oۺ5:=}`V5!0݇CD*Dm#JI24eVK����IENDB`��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/tab_sd.png���������������������������������������������������������������������0000644�0001750�0001750�00000000274�15156613442�015202� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������$���[���IDATx{ 0�/ir:'V3"t.3$?)P/LĴG_Nm2ڜQ>ٓ莤HYlKKH2C)rBM08?j HZ-����IENDB`������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/nav_h.png����������������������������������������������������������������������0000644�0001750�0001750�00000000142�15156613442�015033� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������ ���,@����)IDATxA �@BQۛТ) ) aܿoRl����IENDB`������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/nav_hd.png���������������������������������������������������������������������0000644�0001750�0001750�00000000162�15156613442�015201� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������ ���,@����9IDATxݻ Q ;r5 W v?P_E27jA v #����IENDB`��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/nav_f.png����������������������������������������������������������������������0000644�0001750�0001750�00000000231�15156613442�015030� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������8������`IDATxK Eі[BmkHprӼ.ꎤR6Z VIE5jliIJ0/u޿6sH �y����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/nav_fd.png���������������������������������������������������������������������0000644�0001750�0001750�00000000251�15156613442�015176� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������8������pIDATxM F ((jMM[73o@s ´K�̑ y=[>P\U/gdHȢ(zNC.??;y@AK����IENDB`�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/bc_s.png�����������������������������������������������������������������������0000644�0001750�0001750�00000001244�15156613442�014652� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������_ ��kIDATxkQϝ̤I&m&156*nąܸR,4 +H(Ub1J.(EmߏhJmKS'C(х & r3g(z&_9}՟�@mu ` h`ԯ �&~M4%3?h)\Yi>Jb @giވkg\轭EUv+?E"pB\Y&$vM+Dn)}:Xo 3گ'.f0u9Ljf6%3Gf#sm�(,k*ʒJ�Jˢou_~ �r]%%mnu]z�r5[ư�XeI<g�@I3:/<�JQ) t�(M\63Cgl!;4h=Z29wҴV;L3j&7a!I)$-�7tIVVbf8ȃB<﫵imn lߣD #5'4?쬲Mg|5GNdr|&"7+'�@ 5Gɬ^;㐖.3r"_R@oI$����IENDB`������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/bc_sd.png����������������������������������������������������������������������0000644�0001750�0001750�00000001173�15156613442�015017� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������_ ��BIDATxkQƿ;3$4<f&&ipڤ->b+J-XBP u#q7 ntV@ӂ- q#3? 7;gwXA~*gm,.7'oN<2 .#umx^Xzdis=1SryTY}ŧUKm߼ĘpHL穨`nbOMgVAr kAOopGOOE>PnN,=P<s٤ߏ/޻i=Cݙ*X�}P#~8yS}ɢ)�6D麑B-/#1�6_ S*"3Rc�G +Q �0,J_LK� ZHj|3{|䃝&ќ<FW$k&]s<Fn3EۿO#l[9l[PbQr Xi3cIkVԈ5,LhJ_!{@K[Mmڿ&%A7kXq;pɊ7{򧛤ϵ(ɷiwk$ v,;i  1~����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/doxygen.svg��������������������������������������������������������������������0000644�0001750�0001750�00000036145�15156613442�015444� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" viewBox="0 0 104 31" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a"> <stop stop-color="#5373B4" offset="0"/> <stop stop-color="#7C95C6" offset="1"/> </linearGradient> <linearGradient id="d" x1="31.474" x2="31.474" y1="24.821" y2="26.773" gradientUnits="userSpaceOnUse" xlink:href="#a"/> <linearGradient id="c" x1="31.474" x2="31.474" y1="24.821" y2="26.773" gradientTransform="matrix(.6816 0 0 1.0248 72.391 -.91809)" gradientUnits="userSpaceOnUse" xlink:href="#a"/> <linearGradient id="b" x1="56.295" x2="56.295" y1="24.622" y2="26.574" gradientUnits="userSpaceOnUse" xlink:href="#a"/> <linearGradient id="e" x1="49.067" x2="48.956" y1="19.719" y2="9.5227" gradientTransform="matrix(.97968 0 0 1.0207 -.25579 -.25579)" gradientUnits="userSpaceOnUse"> <stop stop-color="#C0CCE3" offset="0"/> <stop stop-color="#EEF1F7" offset="1"/> </linearGradient> <filter id="f" x="-.010676" y="-.045304" width="1.0214" height="1.0906" color-interpolation-filters="sRGB"> <feGaussianBlur stdDeviation="0.45293203"/> </filter> </defs> <g> <path transform="translate(-2.5759 -27.848)" d="m13.609 32.203v6.8633h-0.05078c-0.40533-0.66867-0.96254-1.1715-1.6719-1.5059-0.69244-0.35193-1.4282-0.52734-2.2051-0.52734-0.96267 0-1.807 0.2027-2.5332 0.60742-0.72622 0.38713-1.3344 0.90556-1.8242 1.5566-0.47289 0.65108-0.83456 1.4092-1.0879 2.2715-0.23644 0.84464-0.35547 1.7236-0.35547 2.6387 0 0.95022 0.11902 1.8643 0.35547 2.7441 0.25333 0.87983 0.615 1.6633 1.0879 2.3496 0.48978 0.66867 1.1065 1.2066 1.8496 1.6113 0.74311 0.38713 1.6044 0.58008 2.584 0.58008 0.86133 0 1.6311-0.15787 2.3066-0.47461 0.69244-0.33434 1.2497-0.87227 1.6719-1.6113h0.05078v1.7422h3.4199v-18.846zm12.875 4.8301c-1.0302 0-1.9596 0.17541-2.7871 0.52734-0.82756 0.33434-1.5358 0.81965-2.127 1.4531-0.59111 0.61588-1.0483 1.3721-1.3691 2.2695-0.32089 0.87983-0.48047 1.866-0.48047 2.957s0.15958 2.0752 0.48047 2.9551c0.32089 0.87983 0.77803 1.6361 1.3691 2.2695 0.59111 0.61588 1.2994 1.0914 2.127 1.4258 0.82756 0.33434 1.7569 0.50195 2.7871 0.50195 1.0302 0 1.9596-0.16762 2.7871-0.50195 0.84444-0.33434 1.5612-0.8099 2.1523-1.4258 0.59111-0.63348 1.0483-1.3897 1.3691-2.2695 0.32089-0.87983 0.48047-1.8641 0.48047-2.9551s-0.15958-2.0772-0.48047-2.957c-0.32089-0.89743-0.77803-1.6536-1.3691-2.2695-0.59111-0.63348-1.3079-1.1188-2.1523-1.4531-0.82756-0.35193-1.7569-0.52734-2.7871-0.52734zm41.715 0c-0.912 0-1.7223 0.18516-2.4316 0.55469-0.69244 0.36953-1.2752 0.87043-1.748 1.5039-0.47289 0.61588-0.83651 1.337-1.0898 2.1641-0.23645 0.80944-0.35352 1.6553-0.35352 2.5352 0 0.93262 0.10007 1.8214 0.30273 2.666 0.21956 0.82704 0.55767 1.556 1.0137 2.1895 0.456 0.61588 1.0387 1.109 1.748 1.4785 0.70933 0.35193 1.5536 0.5293 2.5332 0.5293 0.79378 0 1.5446-0.16762 2.2539-0.50195 0.72622-0.35193 1.2834-0.88986 1.6719-1.6113h0.05078v1.7949c0.01689 0.96782-0.21071 1.7689-0.68359 2.4023-0.456 0.63348-1.1898 0.95117-2.2031 0.95117-0.64178 0-1.2075-0.14228-1.6973-0.42383-0.48978-0.26395-0.81939-0.74731-0.98828-1.4512h-3.5723c0.05067 0.77425 0.25276 1.435 0.60742 1.9805 0.37156 0.56309 0.8287 1.0192 1.3691 1.3711 0.55733 0.35193 1.1656 0.60726 1.8242 0.76562 0.67556 0.17597 1.3328 0.26562 1.9746 0.26562 1.5031 0 2.7025-0.21245 3.5977-0.63477 0.89511-0.42232 1.5798-0.94076 2.0527-1.5566 0.47289-0.59829 0.777-1.2493 0.91211-1.9531 0.152-0.70386 0.22656-1.3295 0.22656-1.875v-12.775h-3.4199v1.8223h-0.05078c-0.43911-0.79185-0.98782-1.3551-1.6465-1.6895-0.64178-0.33434-1.3926-0.50195-2.2539-0.50195zm16.523 0c-0.99644 0-1.9088 0.18516-2.7363 0.55469-0.81067 0.36953-1.5124 0.88018-2.1035 1.5312-0.59111 0.63348-1.0463 1.3897-1.3672 2.2695s-0.48047 1.831-0.48047 2.8516c0 1.0558 0.15108 2.0225 0.45508 2.9023 0.32089 0.87983 0.76758 1.6361 1.3418 2.2695 0.57422 0.63348 1.276 1.1266 2.1035 1.4785 0.82756 0.33434 1.7569 0.50195 2.7871 0.50195 1.4862 0 2.7517-0.35277 3.7988-1.0566 1.0471-0.70387 1.8254-1.8733 2.332-3.5098h-3.168c-0.11822 0.42232-0.43934 0.82772-0.96289 1.2148-0.52355 0.36953-1.1468 0.55274-1.873 0.55273-1.0133 0-1.7916-0.27286-2.332-0.81836-0.54044-0.5455-0.83605-1.4245-0.88672-2.6387h9.4492c0.06756-1.0558-0.01551-2.0673-0.25195-3.0352-0.23644-0.96782-0.62557-1.8293-1.166-2.5859-0.52356-0.75666-1.1998-1.355-2.0273-1.7949-0.82756-0.45751-1.7974-0.6875-2.9121-0.6875zm16.189 0c-0.76 0-1.5023 0.18516-2.2285 0.55469-0.72622 0.35193-1.3174 0.92299-1.7734 1.7148h-0.07617v-1.9004h-3.4199v13.646h3.5977v-7.1523c0-1.3901 0.21909-2.3841 0.6582-2.9824 0.43911-0.61588 1.1494-0.92383 2.1289-0.92383 0.86133 0 1.4611 0.28066 1.7988 0.84375 0.33777 0.5455 0.50586 1.3816 0.50586 2.5078v7.707h3.5976v-8.3926c0-0.84464-0.0765-1.6106-0.22851-2.2969-0.13511-0.70387-0.37971-1.2925-0.73438-1.7676-0.35466-0.49271-0.84386-0.87277-1.4688-1.1367-0.608-0.28155-1.3948-0.42188-2.3574-0.42188zm-66.063 0.36914 4.3066 6.4668-4.7129 7.1797h4.0293l2.7363-4.3027 2.7344 4.3027h4.1055l-4.8398-7.2578 4.3066-6.3887h-3.9766l-2.2793 3.5645-2.3066-3.5645zm13.275 0 4.584 12.803c0.10133 0.26395 0.15234 0.54461 0.15234 0.84375 0 0.40472-0.11707 0.77504-0.35352 1.1094-0.21956 0.33434-0.56617 0.52729-1.0391 0.58008-0.35467 0.0176-0.70979 0.0098-1.0645-0.02539-0.35467-0.03519-0.70128-0.07028-1.0391-0.10547v3.0879c0.37156 0.03519 0.73518 0.06051 1.0898 0.07813 0.37156 0.03519 0.74368 0.05273 1.1152 0.05273 1.2329 0 2.1943-0.23778 2.8867-0.71289 0.69244-0.47511 1.2326-1.2664 1.6211-2.375l5.4727-15.336h-3.7246l-2.8613 9.3438h-0.05078l-2.9648-9.3438zm-37.48 2.4551c0.59111 0 1.0823 0.12279 1.4707 0.36914 0.38844 0.24635 0.6991 0.57184 0.93555 0.97656 0.25333 0.38713 0.43187 0.84515 0.5332 1.373 0.10133 0.5103 0.15234 1.0482 0.15234 1.6113 0 0.56309-0.05101 1.1069-0.15234 1.6348-0.10133 0.5279-0.27137 1.0035-0.50781 1.4258-0.23644 0.40472-0.5556 0.73021-0.96094 0.97656-0.38844 0.24635-0.87959 0.36914-1.4707 0.36914-0.55733 0-1.038-0.12279-1.4434-0.36914-0.38844-0.26395-0.71806-0.59723-0.98828-1.002-0.25333-0.42232-0.43842-0.89788-0.55664-1.4258s-0.17773-1.0561-0.17773-1.584c-1e-7 -0.56309 0.05101-1.0991 0.15234-1.6094 0.11822-0.5279 0.29481-0.99567 0.53125-1.4004 0.25333-0.40472 0.58295-0.73021 0.98828-0.97656 0.40533-0.24635 0.90303-0.36914 1.4941-0.36914zm15.84 0c0.608 0 1.1142 0.13253 1.5195 0.39648 0.42222 0.24635 0.75184 0.57184 0.98828 0.97656 0.25333 0.40472 0.42992 0.87054 0.53125 1.3984 0.10133 0.5279 0.15234 1.0658 0.15234 1.6113 0 0.5455-0.05101 1.0815-0.15234 1.6094-0.10134 0.5103-0.27792 0.97612-0.53125 1.3984-0.23644 0.40472-0.56606 0.73021-0.98828 0.97656-0.40533 0.24635-0.91153 0.36914-1.5195 0.36914-0.608 0-1.1142-0.12279-1.5195-0.36914s-0.73495-0.57184-0.98828-0.97656c-0.23644-0.42232-0.40648-0.88814-0.50781-1.3984-0.10133-0.5279-0.15234-1.0639-0.15234-1.6094 0-0.5455 0.05101-1.0834 0.15234-1.6113 0.10133-0.5279 0.27137-0.99371 0.50781-1.3984 0.25333-0.40472 0.58295-0.73021 0.98828-0.97656 0.40533-0.26395 0.91153-0.39648 1.5195-0.39648zm42.602 0c0.59111 0 1.0803 0.11499 1.4688 0.34375 0.38844 0.22876 0.70105 0.5367 0.9375 0.92383 0.23644 0.38713 0.40648 0.8354 0.50781 1.3457 0.10133 0.49271 0.15039 1.0209 0.15039 1.584 0 0.4927-0.06606 0.96827-0.20117 1.4258-0.11822 0.43992-0.30526 0.83557-0.55859 1.1875-0.25333 0.35193-0.57445 0.63259-0.96289 0.84375-0.38844 0.21116-0.83513 0.31836-1.3418 0.31836-0.55733 0-1.021-0.12474-1.3926-0.37109-0.37156-0.24635-0.67566-0.56209-0.91211-0.94922-0.21956-0.38713-0.38109-0.81786-0.48242-1.293-0.08444-0.49271-0.12695-0.98581-0.12695-1.4785 0-0.5103 0.05101-0.99366 0.15234-1.4512 0.11822-0.47511 0.29676-0.89025 0.5332-1.2422 0.25333-0.36953 0.55744-0.65993 0.91211-0.87109 0.37156-0.21116 0.80974-0.31641 1.3164-0.31641zm15.535 0c0.87822 0 1.529 0.24753 1.9512 0.74023 0.43911 0.49271 0.74322 1.2138 0.91211 2.1641h-5.8535c0.01689-0.26395 0.0679-0.5641 0.15234-0.89844 0.10133-0.33434 0.26287-0.65008 0.48242-0.94922 0.23644-0.29914 0.54055-0.54667 0.91211-0.74023 0.38845-0.21116 0.86914-0.31641 1.4434-0.31641z" filter="url(#f)" opacity=".3" stroke="#969696"/> <path d="m0.97202 24.161 43.605-0.0019 0.0508 3.3061-43.6 0.04174z" fill="url(#d)" stroke="#000" stroke-width=".5"/> <path d="m10.283 3.5547v6.8633h-0.05078c-0.40533-0.66867-0.96254-1.1715-1.6719-1.5059-0.69244-0.35193-1.4282-0.52734-2.2051-0.52734-0.96267 0-1.807 0.2027-2.5332 0.60742-0.72622 0.38713-1.3344 0.90556-1.8242 1.5566-0.47289 0.65108-0.83456 1.4092-1.0879 2.2715-0.23644 0.84464-0.35547 1.7236-0.35547 2.6387 0 0.95022 0.11902 1.8643 0.35547 2.7441 0.25333 0.87983 0.615 1.6633 1.0879 2.3496 0.48978 0.66867 1.1065 1.2066 1.8496 1.6113 0.74311 0.38713 1.6044 0.58008 2.584 0.58008 0.86133 0 1.6311-0.15787 2.3066-0.47461 0.69244-0.33434 1.2497-0.87227 1.6719-1.6113h0.05078v1.7422h3.4199v-18.846zm12.875 4.8301c-1.0302 0-1.9596 0.17541-2.7871 0.52734-0.82756 0.33434-1.5358 0.81965-2.127 1.4531-0.59111 0.61588-1.0483 1.3721-1.3691 2.2695-0.32089 0.87983-0.48047 1.866-0.48047 2.957s0.15958 2.0752 0.48047 2.9551c0.32089 0.87983 0.77803 1.6361 1.3691 2.2695 0.59111 0.61588 1.2994 1.0914 2.127 1.4258 0.82756 0.33434 1.7569 0.50195 2.7871 0.50195 1.0302 0 1.9596-0.16762 2.7871-0.50195 0.84444-0.33434 1.5612-0.8099 2.1523-1.4258 0.59111-0.63348 1.0483-1.3897 1.3691-2.2695 0.32089-0.87983 0.48047-1.8641 0.48047-2.9551s-0.15958-2.0772-0.48047-2.957c-0.32089-0.89743-0.77803-1.6536-1.3691-2.2695-0.59111-0.63348-1.3079-1.1188-2.1523-1.4531-0.82756-0.35193-1.7569-0.52734-2.7871-0.52734zm41.715 0c-0.912 0-1.7223 0.18516-2.4316 0.55469-0.69244 0.36953-1.2752 0.87043-1.748 1.5039-0.47289 0.61588-0.83651 1.337-1.0898 2.1641-0.23644 0.80944-0.35352 1.6553-0.35352 2.5352 0 0.93262 0.10007 1.8214 0.30273 2.666 0.21956 0.82704 0.55767 1.556 1.0137 2.1895 0.456 0.61588 1.0387 1.109 1.748 1.4785 0.70933 0.35193 1.5536 0.5293 2.5332 0.5293 0.79378 0 1.5446-0.16762 2.2539-0.50195 0.72622-0.35193 1.2834-0.88986 1.6719-1.6113h0.05078v1.7949c0.01689 0.96782-0.21071 1.7689-0.68359 2.4023-0.456 0.63348-1.1898 0.95117-2.2031 0.95117-0.64178 0-1.2075-0.14228-1.6973-0.42383-0.48978-0.26395-0.81939-0.74731-0.98828-1.4512h-3.5723c0.05067 0.77425 0.25276 1.435 0.60742 1.9805 0.37156 0.56309 0.8287 1.0192 1.3691 1.3711 0.55733 0.35193 1.1656 0.60726 1.8242 0.76562 0.67556 0.17597 1.3328 0.26562 1.9746 0.26562 1.5031 0 2.7025-0.21245 3.5977-0.63477 0.89511-0.42232 1.5798-0.94076 2.0527-1.5566 0.47289-0.59829 0.777-1.2493 0.91211-1.9531 0.152-0.70386 0.22656-1.3295 0.22656-1.875v-12.775h-3.4199v1.8223h-0.05078c-0.43911-0.79185-0.98782-1.3551-1.6465-1.6895-0.64178-0.33434-1.3926-0.50195-2.2539-0.50195zm16.523 0c-0.99644 0-1.9088 0.18516-2.7363 0.55469-0.81067 0.36953-1.5124 0.88017-2.1035 1.5312-0.59111 0.63348-1.0463 1.3897-1.3672 2.2695s-0.48047 1.831-0.48047 2.8516c0 1.0558 0.15108 2.0225 0.45508 2.9023 0.32089 0.87983 0.76758 1.6361 1.3418 2.2695 0.57422 0.63348 1.276 1.1266 2.1035 1.4785 0.82756 0.33434 1.7569 0.50195 2.7871 0.50195 1.4862 0 2.7517-0.35278 3.7988-1.0566 1.0471-0.70386 1.8254-1.8733 2.332-3.5098h-3.168c-0.11822 0.42232-0.43934 0.82772-0.96289 1.2148-0.52355 0.36953-1.1468 0.55274-1.873 0.55273-1.0133 0-1.7916-0.27286-2.332-0.81836-0.54044-0.5455-0.83605-1.4245-0.88672-2.6387h9.4492c0.06756-1.0558-0.01551-2.0673-0.25195-3.0352-0.23644-0.96782-0.62557-1.8293-1.166-2.5859-0.52356-0.75666-1.1998-1.355-2.0273-1.7949-0.82756-0.45751-1.7974-0.6875-2.9121-0.6875zm16.189 0c-0.76 0-1.5023 0.18516-2.2285 0.55469-0.72622 0.35193-1.3174 0.923-1.7734 1.7148h-0.07617v-1.9004h-3.4199v13.646h3.5977v-7.1523c0-1.3901 0.21909-2.3841 0.6582-2.9824 0.43911-0.61588 1.1494-0.92383 2.1289-0.92383 0.86133 0 1.461 0.28066 1.7988 0.84375 0.33778 0.5455 0.50586 1.3816 0.50586 2.5078v7.707h3.5977v-8.3926c0-0.84464-0.0765-1.6106-0.22852-2.2969-0.13511-0.70387-0.3797-1.2925-0.73437-1.7676-0.35466-0.49271-0.84386-0.87277-1.4688-1.1367-0.608-0.28155-1.3948-0.42188-2.3574-0.42188zm-66.062 0.36914 4.3066 6.4668-4.7129 7.1797h4.0293l2.7363-4.3027 2.7344 4.3027h4.1055l-4.8398-7.2578 4.3066-6.3887h-3.9766l-2.2793 3.5645-2.3066-3.5645zm13.275 0 4.584 12.803c0.10133 0.26395 0.15234 0.54461 0.15234 0.84375 0 0.40472-0.11707 0.77504-0.35352 1.1094-0.21956 0.33434-0.56617 0.52729-1.0391 0.58008-0.35467 0.0176-0.70979 0.0098-1.0645-0.02539-0.35467-0.03519-0.70128-0.07027-1.0391-0.10547v3.0879c0.37156 0.03519 0.73518 0.06052 1.0898 0.07813 0.37156 0.03519 0.74368 0.05273 1.1152 0.05273 1.2329 0 2.1943-0.23778 2.8867-0.71289 0.69244-0.47511 1.2326-1.2664 1.6211-2.375l5.4727-15.336h-3.7246l-2.8613 9.3437h-0.05078l-2.9648-9.3437zm-37.48 2.4551c0.59111 0 1.0823 0.12279 1.4707 0.36914s0.6991 0.57184 0.93555 0.97656c0.25333 0.38713 0.43187 0.84515 0.5332 1.373 0.10133 0.5103 0.15234 1.0482 0.15234 1.6113 0 0.56309-0.05101 1.1069-0.15234 1.6348-0.10133 0.5279-0.27137 1.0035-0.50781 1.4258-0.23644 0.40472-0.5556 0.73021-0.96094 0.97656-0.38844 0.24635-0.87959 0.36914-1.4707 0.36914-0.55733 0-1.038-0.12279-1.4434-0.36914-0.38844-0.26395-0.71806-0.59723-0.98828-1.002-0.25333-0.42232-0.43842-0.89788-0.55664-1.4258s-0.17773-1.0561-0.17773-1.584c-1e-7 -0.56309 0.05101-1.0991 0.15234-1.6094 0.11822-0.5279 0.29481-0.99567 0.53125-1.4004 0.25333-0.40472 0.58295-0.73021 0.98828-0.97656 0.40533-0.24635 0.90303-0.36914 1.4941-0.36914zm15.84 0c0.608 0 1.1142 0.13254 1.5195 0.39648 0.42222 0.24635 0.75184 0.57184 0.98828 0.97656 0.25333 0.40472 0.42992 0.87054 0.53125 1.3984 0.10133 0.5279 0.15234 1.0658 0.15234 1.6113 0 0.5455-0.05101 1.0815-0.15234 1.6094-0.10133 0.5103-0.27792 0.97612-0.53125 1.3984-0.23644 0.40472-0.56606 0.73021-0.98828 0.97656-0.40533 0.24635-0.91153 0.36914-1.5195 0.36914-0.608 0-1.1142-0.12279-1.5195-0.36914s-0.73495-0.57184-0.98828-0.97656c-0.23644-0.42232-0.40648-0.88813-0.50781-1.3984-0.10133-0.5279-0.15234-1.0639-0.15234-1.6094 0-0.5455 0.05101-1.0834 0.15234-1.6113 0.10133-0.5279 0.27137-0.99371 0.50781-1.3984 0.25333-0.40472 0.58295-0.73021 0.98828-0.97656 0.40533-0.26395 0.91153-0.39648 1.5195-0.39648zm42.602 0c0.59111 0 1.0803 0.11499 1.4688 0.34375 0.38844 0.22876 0.70106 0.5367 0.9375 0.92383 0.23644 0.38713 0.40648 0.8354 0.50781 1.3457 0.10133 0.49271 0.15039 1.0209 0.15039 1.584 0 0.49271-0.06606 0.96827-0.20117 1.4258-0.11822 0.43992-0.30526 0.83557-0.55859 1.1875-0.25333 0.35193-0.57445 0.63259-0.96289 0.84375-0.38844 0.21116-0.83513 0.31836-1.3418 0.31836-0.55733 0-1.021-0.12474-1.3926-0.37109-0.37156-0.24635-0.67566-0.56209-0.91211-0.94922-0.21956-0.38713-0.38109-0.81786-0.48242-1.293-0.08444-0.49271-0.12695-0.98581-0.12695-1.4785 0-0.5103 0.05101-0.99366 0.15234-1.4512 0.11822-0.47511 0.29676-0.89026 0.5332-1.2422 0.25333-0.36953 0.55744-0.65993 0.91211-0.87109 0.37156-0.21116 0.80974-0.31641 1.3164-0.31641zm15.535 0c0.87822 0 1.529 0.24753 1.9512 0.74024 0.43911 0.49271 0.74322 1.2138 0.91211 2.1641h-5.8535c0.01689-0.26395 0.0679-0.5641 0.15234-0.89844 0.10133-0.33434 0.26287-0.65008 0.48242-0.94922 0.23644-0.29914 0.54055-0.54667 0.91211-0.74023 0.38845-0.21116 0.86914-0.31641 1.4434-0.31641z" fill="url(#e)" stroke="#4665A2" stroke-width=".7"/> <path d="m52.988 27.291c0.99602-1.0359 1.3944-1.8725 1.7928-3.1076l3.8247-0.03984c0.3113 1.6096 0.82413 2.5137 1.6335 3.1474z" fill="url(#b)" stroke="#000" stroke-width=".5"/> <path d="m73.89 24.04 28.885-0.2011-0.12476 3.3879-31.033 0.16229c1.2621-1.0234 1.9665-2.2859 2.2724-3.3491z" fill="url(#c)" stroke="#000" stroke-width=".41788"/> </g> </svg> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/closed.png���������������������������������������������������������������������0000644�0001750�0001750�00000000204�15156613442�015210� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��� ��� ������KIDATxm @!Gk7-`&sts@k}�2 P%_N .:0Dk›x" ֛)x5����IENDB`��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/open.png�����������������������������������������������������������������������0000644�0001750�0001750�00000000173�15156613442�014705� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��� ��� ������BIDATx 0 ׬ՙ\39b!9{|I>$#ߴ8/z/>2[giU,/~\ 9ٸ����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/sync_on.png��������������������������������������������������������������������0000644�0001750�0001750�00000001515�15156613442�015415� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������w=��IDATx_HTY8i4-g6&kQ)!0URKڅ/PE>K-+K.YdEPaAZSܝ;3wgfsWK.Da'q_k DQCg 0Y:qZ�)~L0HV z-C%g68%wUϿ }? ?3 K@h� aaUe� s~2&&B*Alji*˨,�oƣT,d[�3-*> LɟfkҠw#*AEjKUy>&{8m5Ki jjD*Nigw7DmzK۾M!k?o_lX#~XӑR*EՂדE;6e"Q(=Ezæ5Kؼָ_ 1zBJ X96jL^7{J1i@%8'7M_\Q#Uy Wo �x8sv|Sn q_m >b[JX,4[T{Ratjjzz'ȶiIws KC^Y%6ꈺ]vhiWvh'̂|[^YrD=<D]s8�C?  t4J㇏Wɱ rcjMɘx| ENԉAZj #4g����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/sync_off.png�������������������������������������������������������������������0000644�0001750�0001750�00000001525�15156613442�015554� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������w=��IDATxKhTW1I&38MII3b$c I1V1-(T.* t!K[čf`l(l"Y6gT}sgܹ d{8?̝;u`:!FB?Űm�'y�>ѝlU_?]Y(N8f1qn-et�m 0}b%׌=0?1s08;_ W|%\Zð >舽lnp.a{ )t; b n652?�>Oдunm`׭ZWjC~>־0+ {{f�Mŕټ` ݛ%uA6,]kWu]7ihu1 l Ҷ<L;!j*KRW/ 3nMjٵ5N fU:�;=i*ҡ:>̺:\cxhRQt$ fd<4B[fd7=.M9//O a},j?.5ښm?X2#d p(?c!a1ޗةܾ7dK:)3],H+ku<|`LhC7e םt� H$^2%�l.aeÉ|�s }D^hz~Rá]|#�@חև[k<|(*ݹdtM:,]' X_n| /cfO����IENDB`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/nav_g.png����������������������������������������������������������������������0000644�0001750�0001750�00000000137�15156613442�015036� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������1���&IDATx1 ��OHf_ ->~M iMS�<����IENDB`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/plus.svg�����������������������������������������������������������������������0000644�0001750�0001750�00000001270�15156613442�014741� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="12px" height="12px" viewBox="0 0 105.83333 105.83333" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <g> <rect style="fill:#808080;stroke-width:0" width="105.83333" height="105.83334" x="4.2409692e-08" y="-1.2701158e-06" ry="0" /> <rect style="fill:#fcfcfc;stroke-width:0" width="79.375" height="79.375" x="13.229166" y="13.229166" /> <rect style="fill:#808080;stroke-width:0" width="52.916668" height="15.874998" x="26.458332" y="44.979168" /> <rect style="fill:#808080;stroke-width:0" width="15.874998" height="52.916668" x="44.979168" y="26.458332" /> </g> </svg> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/minus.svg����������������������������������������������������������������������0000644�0001750�0001750�00000001106�15156613442�015107� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="12px" height="12px" viewBox="0 0 105.83333 105.83333" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <g> <rect style="fill:#808080;stroke-width:0" width="105.83333" height="105.83334" x="4.2409692e-08" y="-1.2701158e-06" ry="0" /> <rect style="fill:#fcfcfc;stroke-width:0" width="79.375" height="79.375" x="13.229166" y="13.229166" /> <rect style="fill:#808080;stroke-width:0" width="52.916668" height="15.874998" x="26.458332" y="44.979168" /> </g> </svg> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/plusd.svg����������������������������������������������������������������������0000644�0001750�0001750�00000001270�15156613442�015105� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="12px" height="12px" viewBox="0 0 105.83333 105.83333" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <g> <rect style="fill:#808080;stroke-width:0" width="105.83333" height="105.83334" x="4.2409692e-08" y="-1.2701158e-06" ry="0" /> <rect style="fill:#000000;stroke-width:0" width="79.375" height="79.375" x="13.229166" y="13.229166" /> <rect style="fill:#808080;stroke-width:0" width="52.916668" height="15.874998" x="26.458332" y="44.979168" /> <rect style="fill:#808080;stroke-width:0" width="15.874998" height="52.916668" x="44.979168" y="26.458332" /> </g> </svg> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/minusd.svg���������������������������������������������������������������������0000644�0001750�0001750�00000001106�15156613442�015253� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="12px" height="12px" viewBox="0 0 105.83333 105.83333" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <g> <rect style="fill:#808080;stroke-width:0" width="105.83333" height="105.83334" x="4.2409692e-08" y="-1.2701158e-06" ry="0" /> <rect style="fill:#000000;stroke-width:0" width="79.375" height="79.375" x="13.229166" y="13.229166" /> <rect style="fill:#808080;stroke-width:0" width="52.916668" height="15.874998" x="26.458332" y="44.979168" /> </g> </svg> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/doxygen.css��������������������������������������������������������������������0000644�0001750�0001750�00000131077�15156613442�015435� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* The standard CSS for doxygen 1.9.8*/ html { /* page base colors */ --page-background-color: white; --page-foreground-color: black; --page-link-color: #3D578C; --page-visited-link-color: #4665A2; /* index */ --index-odd-item-bg-color: #F8F9FC; --index-even-item-bg-color: white; --index-header-color: black; --index-separator-color: #A0A0A0; /* header */ --header-background-color: #F9FAFC; --header-separator-color: #C4CFE5; --header-gradient-image: url('nav_h.png'); --group-header-separator-color: #879ECB; --group-header-color: #354C7B; --inherit-header-color: gray; --footer-foreground-color: #2A3D61; --footer-logo-width: 104px; --citation-label-color: #334975; --glow-color: cyan; --title-background-color: white; --title-separator-color: #5373B4; --directory-separator-color: #9CAFD4; --separator-color: #4A6AAA; --blockquote-background-color: #F7F8FB; --blockquote-border-color: #9CAFD4; --scrollbar-thumb-color: #9CAFD4; --scrollbar-background-color: #F9FAFC; --icon-background-color: #728DC1; --icon-foreground-color: white; --icon-doc-image: url('doc.svg'); --icon-folder-open-image: url('folderopen.svg'); --icon-folder-closed-image: url('folderclosed.svg'); /* brief member declaration list */ --memdecl-background-color: #F9FAFC; --memdecl-separator-color: #DEE4F0; --memdecl-foreground-color: #555; --memdecl-template-color: #4665A2; /* detailed member list */ --memdef-border-color: #A8B8D9; --memdef-title-background-color: #E2E8F2; --memdef-title-gradient-image: url('nav_f.png'); --memdef-proto-background-color: #DFE5F1; --memdef-proto-text-color: #253555; --memdef-proto-text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); --memdef-doc-background-color: white; --memdef-param-name-color: #602020; --memdef-template-color: #4665A2; /* tables */ --table-cell-border-color: #2D4068; --table-header-background-color: #374F7F; --table-header-foreground-color: #FFFFFF; /* labels */ --label-background-color: #728DC1; --label-left-top-border-color: #5373B4; --label-right-bottom-border-color: #C4CFE5; --label-foreground-color: white; /** navigation bar/tree/menu */ --nav-background-color: #F9FAFC; --nav-foreground-color: #364D7C; --nav-gradient-image: url('tab_b.png'); --nav-gradient-hover-image: url('tab_h.png'); --nav-gradient-active-image: url('tab_a.png'); --nav-gradient-active-image-parent: url("../tab_a.png"); --nav-separator-image: url('tab_s.png'); --nav-breadcrumb-image: url('bc_s.png'); --nav-breadcrumb-border-color: #C2CDE4; --nav-splitbar-image: url('splitbar.png'); --nav-font-size-level1: 13px; --nav-font-size-level2: 10px; --nav-font-size-level3: 9px; --nav-text-normal-color: #283A5D; --nav-text-hover-color: white; --nav-text-active-color: white; --nav-text-normal-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); --nav-text-hover-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); --nav-text-active-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); --nav-menu-button-color: #364D7C; --nav-menu-background-color: white; --nav-menu-foreground-color: #555555; --nav-menu-toggle-color: rgba(255, 255, 255, 0.5); --nav-arrow-color: #9CAFD4; --nav-arrow-selected-color: #9CAFD4; /* table of contents */ --toc-background-color: #F4F6FA; --toc-border-color: #D8DFEE; --toc-header-color: #4665A2; --toc-down-arrow-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='10px' width='5px' fill='grey'><text x='0' y='5' font-size='10'>&%238595;</text></svg>"); /** search field */ --search-background-color: white; --search-foreground-color: #909090; --search-magnification-image: url('mag.svg'); --search-magnification-select-image: url('mag_sel.svg'); --search-active-color: black; --search-filter-background-color: #F9FAFC; --search-filter-foreground-color: black; --search-filter-border-color: #90A5CE; --search-filter-highlight-text-color: white; --search-filter-highlight-bg-color: #3D578C; --search-results-foreground-color: #425E97; --search-results-background-color: #EEF1F7; --search-results-border-color: black; --search-box-shadow: inset 0.5px 0.5px 3px 0px #555; /** code fragments */ --code-keyword-color: #008000; --code-type-keyword-color: #604020; --code-flow-keyword-color: #E08000; --code-comment-color: #800000; --code-preprocessor-color: #806020; --code-string-literal-color: #002080; --code-char-literal-color: #008080; --code-xml-cdata-color: black; --code-vhdl-digit-color: #FF00FF; --code-vhdl-char-color: #000000; --code-vhdl-keyword-color: #700070; --code-vhdl-logic-color: #FF0000; --code-link-color: #4665A2; --code-external-link-color: #4665A2; --fragment-foreground-color: black; --fragment-background-color: #FBFCFD; --fragment-border-color: #C4CFE5; --fragment-lineno-border-color: #00FF00; --fragment-lineno-background-color: #E8E8E8; --fragment-lineno-foreground-color: black; --fragment-lineno-link-fg-color: #4665A2; --fragment-lineno-link-bg-color: #D8D8D8; --fragment-lineno-link-hover-fg-color: #4665A2; --fragment-lineno-link-hover-bg-color: #C8C8C8; --tooltip-foreground-color: black; --tooltip-background-color: white; --tooltip-border-color: gray; --tooltip-doc-color: grey; --tooltip-declaration-color: #006318; --tooltip-link-color: #4665A2; --tooltip-shadow: 1px 1px 7px gray; --fold-line-color: #808080; --fold-minus-image: url('minus.svg'); --fold-plus-image: url('plus.svg'); --fold-minus-image-relpath: url('../../minus.svg'); --fold-plus-image-relpath: url('../../plus.svg'); /** font-family */ --font-family-normal: Roboto,sans-serif; --font-family-monospace: 'JetBrains Mono',Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace,fixed; --font-family-nav: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; --font-family-title: Tahoma,Arial,sans-serif; --font-family-toc: Verdana,'DejaVu Sans',Geneva,sans-serif; --font-family-search: Arial,Verdana,sans-serif; --font-family-icon: Arial,Helvetica; --font-family-tooltip: Roboto,sans-serif; } @media (prefers-color-scheme: dark) { html:not(.dark-mode) { color-scheme: dark; /* page base colors */ --page-background-color: black; --page-foreground-color: #C9D1D9; --page-link-color: #90A5CE; --page-visited-link-color: #A3B4D7; /* index */ --index-odd-item-bg-color: #0B101A; --index-even-item-bg-color: black; --index-header-color: #C4CFE5; --index-separator-color: #334975; /* header */ --header-background-color: #070B11; --header-separator-color: #141C2E; --header-gradient-image: url('nav_hd.png'); --group-header-separator-color: #283A5D; --group-header-color: #90A5CE; --inherit-header-color: #A0A0A0; --footer-foreground-color: #5B7AB7; --footer-logo-width: 60px; --citation-label-color: #90A5CE; --glow-color: cyan; --title-background-color: #090D16; --title-separator-color: #354C79; --directory-separator-color: #283A5D; --separator-color: #283A5D; --blockquote-background-color: #101826; --blockquote-border-color: #283A5D; --scrollbar-thumb-color: #283A5D; --scrollbar-background-color: #070B11; --icon-background-color: #334975; --icon-foreground-color: #C4CFE5; --icon-doc-image: url('docd.svg'); --icon-folder-open-image: url('folderopend.svg'); --icon-folder-closed-image: url('folderclosedd.svg'); /* brief member declaration list */ --memdecl-background-color: #0B101A; --memdecl-separator-color: #2C3F65; --memdecl-foreground-color: #BBB; --memdecl-template-color: #7C95C6; /* detailed member list */ --memdef-border-color: #233250; --memdef-title-background-color: #1B2840; --memdef-title-gradient-image: url('nav_fd.png'); --memdef-proto-background-color: #19243A; --memdef-proto-text-color: #9DB0D4; --memdef-proto-text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.9); --memdef-doc-background-color: black; --memdef-param-name-color: #D28757; --memdef-template-color: #7C95C6; /* tables */ --table-cell-border-color: #283A5D; --table-header-background-color: #283A5D; --table-header-foreground-color: #C4CFE5; /* labels */ --label-background-color: #354C7B; --label-left-top-border-color: #4665A2; --label-right-bottom-border-color: #283A5D; --label-foreground-color: #CCCCCC; /** navigation bar/tree/menu */ --nav-background-color: #101826; --nav-foreground-color: #364D7C; --nav-gradient-image: url('tab_bd.png'); --nav-gradient-hover-image: url('tab_hd.png'); --nav-gradient-active-image: url('tab_ad.png'); --nav-gradient-active-image-parent: url("../tab_ad.png"); --nav-separator-image: url('tab_sd.png'); --nav-breadcrumb-image: url('bc_sd.png'); --nav-breadcrumb-border-color: #2A3D61; --nav-splitbar-image: url('splitbard.png'); --nav-font-size-level1: 13px; --nav-font-size-level2: 10px; --nav-font-size-level3: 9px; --nav-text-normal-color: #B6C4DF; --nav-text-hover-color: #DCE2EF; --nav-text-active-color: #DCE2EF; --nav-text-normal-shadow: 0px 1px 1px black; --nav-text-hover-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); --nav-text-active-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); --nav-menu-button-color: #B6C4DF; --nav-menu-background-color: #05070C; --nav-menu-foreground-color: #BBBBBB; --nav-menu-toggle-color: rgba(255, 255, 255, 0.2); --nav-arrow-color: #334975; --nav-arrow-selected-color: #90A5CE; /* table of contents */ --toc-background-color: #151E30; --toc-border-color: #202E4A; --toc-header-color: #A3B4D7; --toc-down-arrow-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='10px' width='5px'><text x='0' y='5' font-size='10' fill='grey'>&%238595;</text></svg>"); /** search field */ --search-background-color: black; --search-foreground-color: #C5C5C5; --search-magnification-image: url('mag_d.svg'); --search-magnification-select-image: url('mag_seld.svg'); --search-active-color: #C5C5C5; --search-filter-background-color: #101826; --search-filter-foreground-color: #90A5CE; --search-filter-border-color: #7C95C6; --search-filter-highlight-text-color: #BCC9E2; --search-filter-highlight-bg-color: #283A5D; --search-results-background-color: #101826; --search-results-foreground-color: #90A5CE; --search-results-border-color: #7C95C6; --search-box-shadow: inset 0.5px 0.5px 3px 0px #2F436C; /** code fragments */ --code-keyword-color: #CC99CD; --code-type-keyword-color: #AB99CD; --code-flow-keyword-color: #E08000; --code-comment-color: #717790; --code-preprocessor-color: #65CABE; --code-string-literal-color: #7EC699; --code-char-literal-color: #00E0F0; --code-xml-cdata-color: #C9D1D9; --code-vhdl-digit-color: #FF00FF; --code-vhdl-char-color: #C0C0C0; --code-vhdl-keyword-color: #CF53C9; --code-vhdl-logic-color: #FF0000; --code-link-color: #79C0FF; --code-external-link-color: #79C0FF; --fragment-foreground-color: #C9D1D9; --fragment-background-color: black; --fragment-border-color: #30363D; --fragment-lineno-border-color: #30363D; --fragment-lineno-background-color: black; --fragment-lineno-foreground-color: #6E7681; --fragment-lineno-link-fg-color: #6E7681; --fragment-lineno-link-bg-color: #303030; --fragment-lineno-link-hover-fg-color: #8E96A1; --fragment-lineno-link-hover-bg-color: #505050; --tooltip-foreground-color: #C9D1D9; --tooltip-background-color: #202020; --tooltip-border-color: #C9D1D9; --tooltip-doc-color: #D9E1E9; --tooltip-declaration-color: #20C348; --tooltip-link-color: #79C0FF; --tooltip-shadow: none; --fold-line-color: #808080; --fold-minus-image: url('minusd.svg'); --fold-plus-image: url('plusd.svg'); --fold-minus-image-relpath: url('../../minusd.svg'); --fold-plus-image-relpath: url('../../plusd.svg'); /** font-family */ --font-family-normal: Roboto,sans-serif; --font-family-monospace: 'JetBrains Mono',Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace,fixed; --font-family-nav: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; --font-family-title: Tahoma,Arial,sans-serif; --font-family-toc: Verdana,'DejaVu Sans',Geneva,sans-serif; --font-family-search: Arial,Verdana,sans-serif; --font-family-icon: Arial,Helvetica; --font-family-tooltip: Roboto,sans-serif; }} body { background-color: var(--page-background-color); color: var(--page-foreground-color); } body, table, div, p, dl { font-weight: 400; font-size: 14px; font-family: var(--font-family-normal); line-height: 22px; } /* @group Heading Levels */ .title { font-weight: 400; font-size: 14px; font-family: var(--font-family-normal); line-height: 28px; font-size: 150%; font-weight: bold; margin: 10px 2px; } h1.groupheader { font-size: 150%; } h2.groupheader { border-bottom: 1px solid var(--group-header-separator-color); color: var(--group-header-color); font-size: 150%; font-weight: normal; margin-top: 1.75em; padding-top: 8px; padding-bottom: 4px; width: 100%; } h3.groupheader { font-size: 100%; } h1, h2, h3, h4, h5, h6 { -webkit-transition: text-shadow 0.5s linear; -moz-transition: text-shadow 0.5s linear; -ms-transition: text-shadow 0.5s linear; -o-transition: text-shadow 0.5s linear; transition: text-shadow 0.5s linear; margin-right: 15px; } h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { text-shadow: 0 0 15px var(--glow-color); } dt { font-weight: bold; } p.startli, p.startdd { margin-top: 2px; } th p.starttd, th p.intertd, th p.endtd { font-size: 100%; font-weight: 700; } p.starttd { margin-top: 0px; } p.endli { margin-bottom: 0px; } p.enddd { margin-bottom: 4px; } p.endtd { margin-bottom: 2px; } p.interli { } p.interdd { } p.intertd { } /* @end */ caption { font-weight: bold; } span.legend { font-size: 70%; text-align: center; } h3.version { font-size: 90%; text-align: center; } div.navtab { padding-right: 15px; text-align: right; line-height: 110%; } div.navtab table { border-spacing: 0; } td.navtab { padding-right: 6px; padding-left: 6px; } td.navtabHL { background-image: var(--nav-gradient-active-image); background-repeat:repeat-x; padding-right: 6px; padding-left: 6px; } td.navtabHL a, td.navtabHL a:visited { color: var(--nav-text-hover-color); text-shadow: var(--nav-text-hover-shadow); } a.navtab { font-weight: bold; } div.qindex{ text-align: center; width: 100%; line-height: 140%; font-size: 130%; color: var(--index-separator-color); } #main-menu a:focus { outline: auto; z-index: 10; position: relative; } dt.alphachar{ font-size: 180%; font-weight: bold; } .alphachar a{ color: var(--index-header-color); } .alphachar a:hover, .alphachar a:visited{ text-decoration: none; } .classindex dl { padding: 25px; column-count:1 } .classindex dd { display:inline-block; margin-left: 50px; width: 90%; line-height: 1.15em; } .classindex dl.even { background-color: var(--index-even-item-bg-color); } .classindex dl.odd { background-color: var(--index-odd-item-bg-color); } @media(min-width: 1120px) { .classindex dl { column-count:2 } } @media(min-width: 1320px) { .classindex dl { column-count:3 } } /* @group Link Styling */ a { color: var(--page-link-color); font-weight: normal; text-decoration: none; } .contents a:visited { color: var(--page-visited-link-color); } a:hover { text-decoration: underline; } a.el { font-weight: bold; } a.elRef { } a.code, a.code:visited, a.line, a.line:visited { color: var(--code-link-color); } a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { color: var(--code-external-link-color); } a.code.hl_class { /* style for links to class names in code snippets */ } a.code.hl_struct { /* style for links to struct names in code snippets */ } a.code.hl_union { /* style for links to union names in code snippets */ } a.code.hl_interface { /* style for links to interface names in code snippets */ } a.code.hl_protocol { /* style for links to protocol names in code snippets */ } a.code.hl_category { /* style for links to category names in code snippets */ } a.code.hl_exception { /* style for links to exception names in code snippets */ } a.code.hl_service { /* style for links to service names in code snippets */ } a.code.hl_singleton { /* style for links to singleton names in code snippets */ } a.code.hl_concept { /* style for links to concept names in code snippets */ } a.code.hl_namespace { /* style for links to namespace names in code snippets */ } a.code.hl_package { /* style for links to package names in code snippets */ } a.code.hl_define { /* style for links to macro names in code snippets */ } a.code.hl_function { /* style for links to function names in code snippets */ } a.code.hl_variable { /* style for links to variable names in code snippets */ } a.code.hl_typedef { /* style for links to typedef names in code snippets */ } a.code.hl_enumvalue { /* style for links to enum value names in code snippets */ } a.code.hl_enumeration { /* style for links to enumeration names in code snippets */ } a.code.hl_signal { /* style for links to Qt signal names in code snippets */ } a.code.hl_slot { /* style for links to Qt slot names in code snippets */ } a.code.hl_friend { /* style for links to friend names in code snippets */ } a.code.hl_dcop { /* style for links to KDE3 DCOP names in code snippets */ } a.code.hl_property { /* style for links to property names in code snippets */ } a.code.hl_event { /* style for links to event names in code snippets */ } a.code.hl_sequence { /* style for links to sequence names in code snippets */ } a.code.hl_dictionary { /* style for links to dictionary names in code snippets */ } /* @end */ dl.el { margin-left: -1cm; } ul { overflow: visible; } ul.multicol { -moz-column-gap: 1em; -webkit-column-gap: 1em; column-gap: 1em; -moz-column-count: 3; -webkit-column-count: 3; column-count: 3; list-style-type: none; } #side-nav ul { overflow: visible; /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ } #main-nav ul { overflow: visible; /* reset ul rule for the navigation bar drop down lists */ } .fragment { text-align: left; direction: ltr; overflow-x: auto; /*Fixed: fragment lines overlap floating elements*/ overflow-y: hidden; } pre.fragment { border: 1px solid var(--fragment-border-color); background-color: var(--fragment-background-color); color: var(--fragment-foreground-color); padding: 4px 6px; margin: 4px 8px 4px 2px; overflow: auto; word-wrap: break-word; font-size: 9pt; line-height: 125%; font-family: var(--font-family-monospace); font-size: 105%; } div.fragment { padding: 0 0 1px 0; /*Fixed: last line underline overlap border*/ margin: 4px 8px 4px 2px; color: var(--fragment-foreground-color); background-color: var(--fragment-background-color); border: 1px solid var(--fragment-border-color); } div.line { font-family: var(--font-family-monospace); font-size: 13px; min-height: 13px; line-height: 1.2; text-wrap: unrestricted; white-space: -moz-pre-wrap; /* Moz */ white-space: -pre-wrap; /* Opera 4-6 */ white-space: -o-pre-wrap; /* Opera 7 */ white-space: pre-wrap; /* CSS3 */ word-wrap: break-word; /* IE 5.5+ */ text-indent: -53px; padding-left: 53px; padding-bottom: 0px; margin: 0px; -webkit-transition-property: background-color, box-shadow; -webkit-transition-duration: 0.5s; -moz-transition-property: background-color, box-shadow; -moz-transition-duration: 0.5s; -ms-transition-property: background-color, box-shadow; -ms-transition-duration: 0.5s; -o-transition-property: background-color, box-shadow; -o-transition-duration: 0.5s; transition-property: background-color, box-shadow; transition-duration: 0.5s; } div.line:after { content:"\000A"; white-space: pre; } div.line.glow { background-color: var(--glow-color); box-shadow: 0 0 10px var(--glow-color); } span.fold { margin-left: 5px; margin-right: 1px; margin-top: 0px; margin-bottom: 0px; padding: 0px; display: inline-block; width: 12px; height: 12px; background-repeat:no-repeat; background-position:center; } span.lineno { padding-right: 4px; margin-right: 9px; text-align: right; border-right: 2px solid var(--fragment-lineno-border-color); color: var(--fragment-lineno-foreground-color); background-color: var(--fragment-lineno-background-color); white-space: pre; } span.lineno a, span.lineno a:visited { color: var(--fragment-lineno-link-fg-color); background-color: var(--fragment-lineno-link-bg-color); } span.lineno a:hover { color: var(--fragment-lineno-link-hover-fg-color); background-color: var(--fragment-lineno-link-hover-bg-color); } .lineno { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } div.classindex ul { list-style: none; padding-left: 0; } div.classindex span.ai { display: inline-block; } div.groupHeader { margin-left: 16px; margin-top: 12px; font-weight: bold; } div.groupText { margin-left: 16px; font-style: italic; } body { color: var(--page-foreground-color); margin: 0; } div.contents { margin-top: 10px; margin-left: 12px; margin-right: 8px; } p.formulaDsp { text-align: center; } img.dark-mode-visible { display: none; } img.light-mode-visible { display: none; } img.formulaDsp { } img.formulaInl, img.inline { vertical-align: middle; } div.center { text-align: center; margin-top: 0px; margin-bottom: 0px; padding: 0px; } div.center img { border: 0px; } address.footer { text-align: right; padding-right: 12px; } img.footer { border: 0px; vertical-align: middle; width: var(--footer-logo-width); } .compoundTemplParams { color: var(--memdecl-template-color); font-size: 80%; line-height: 120%; } /* @group Code Colorization */ span.keyword { color: var(--code-keyword-color); } span.keywordtype { color: var(--code-type-keyword-color); } span.keywordflow { color: var(--code-flow-keyword-color); } span.comment { color: var(--code-comment-color); } span.preprocessor { color: var(--code-preprocessor-color); } span.stringliteral { color: var(--code-string-literal-color); } span.charliteral { color: var(--code-char-literal-color); } span.xmlcdata { color: var(--code-xml-cdata-color); } span.vhdldigit { color: var(--code-vhdl-digit-color); } span.vhdlchar { color: var(--code-vhdl-char-color); } span.vhdlkeyword { color: var(--code-vhdl-keyword-color); } span.vhdllogic { color: var(--code-vhdl-logic-color); } blockquote { background-color: var(--blockquote-background-color); border-left: 2px solid var(--blockquote-border-color); margin: 0 24px 0 4px; padding: 0 12px 0 16px; } /* @end */ td.tiny { font-size: 75%; } .dirtab { padding: 4px; border-collapse: collapse; border: 1px solid var(--table-cell-border-color); } th.dirtab { background-color: var(--table-header-background-color); color: var(--table-header-foreground-color); font-weight: bold; } hr { height: 0px; border: none; border-top: 1px solid var(--separator-color); } hr.footer { height: 1px; } /* @group Member Descriptions */ table.memberdecls { border-spacing: 0px; padding: 0px; } .memberdecls td, .fieldtable tr { -webkit-transition-property: background-color, box-shadow; -webkit-transition-duration: 0.5s; -moz-transition-property: background-color, box-shadow; -moz-transition-duration: 0.5s; -ms-transition-property: background-color, box-shadow; -ms-transition-duration: 0.5s; -o-transition-property: background-color, box-shadow; -o-transition-duration: 0.5s; transition-property: background-color, box-shadow; transition-duration: 0.5s; } .memberdecls td.glow, .fieldtable tr.glow { background-color: var(--glow-color); box-shadow: 0 0 15px var(--glow-color); } .mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams { background-color: var(--memdecl-background-color); border: none; margin: 4px; padding: 1px 0 0 8px; } .mdescLeft, .mdescRight { padding: 0px 8px 4px 8px; color: var(--memdecl-foreground-color); } .memSeparator { border-bottom: 1px solid var(--memdecl-separator-color); line-height: 1px; margin: 0px; padding: 0px; } .memItemLeft, .memTemplItemLeft { white-space: nowrap; } .memItemRight, .memTemplItemRight { width: 100%; } .memTemplParams { color: var(--memdecl-template-color); white-space: nowrap; font-size: 80%; } /* @end */ /* @group Member Details */ /* Styles for detailed member documentation */ .memtitle { padding: 8px; border-top: 1px solid var(--memdef-border-color); border-left: 1px solid var(--memdef-border-color); border-right: 1px solid var(--memdef-border-color); border-top-right-radius: 4px; border-top-left-radius: 4px; margin-bottom: -1px; background-image: var(--memdef-title-gradient-image); background-repeat: repeat-x; background-color: var(--memdef-title-background-color); line-height: 1.25; font-weight: 300; float:left; } .permalink { font-size: 65%; display: inline-block; vertical-align: middle; } .memtemplate { font-size: 80%; color: var(--memdef-template-color); font-weight: normal; margin-left: 9px; } .mempage { width: 100%; } .memitem { padding: 0; margin-bottom: 10px; margin-right: 5px; -webkit-transition: box-shadow 0.5s linear; -moz-transition: box-shadow 0.5s linear; -ms-transition: box-shadow 0.5s linear; -o-transition: box-shadow 0.5s linear; transition: box-shadow 0.5s linear; display: table !important; width: 100%; } .memitem.glow { box-shadow: 0 0 15px var(--glow-color); } .memname { font-weight: 400; margin-left: 6px; } .memname td { vertical-align: bottom; } .memproto, dl.reflist dt { border-top: 1px solid var(--memdef-border-color); border-left: 1px solid var(--memdef-border-color); border-right: 1px solid var(--memdef-border-color); padding: 6px 0px 6px 0px; color: var(--memdef-proto-text-color); font-weight: bold; text-shadow: var(--memdef-proto-text-shadow); background-color: var(--memdef-proto-background-color); box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); border-top-right-radius: 4px; } .overload { font-family: var(--font-family-monospace); font-size: 65%; } .memdoc, dl.reflist dd { border-bottom: 1px solid var(--memdef-border-color); border-left: 1px solid var(--memdef-border-color); border-right: 1px solid var(--memdef-border-color); padding: 6px 10px 2px 10px; border-top-width: 0; background-image:url('nav_g.png'); background-repeat:repeat-x; background-color: var(--memdef-doc-background-color); /* opera specific markup */ border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); /* firefox specific markup */ -moz-border-radius-bottomleft: 4px; -moz-border-radius-bottomright: 4px; -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; /* webkit specific markup */ -webkit-border-bottom-left-radius: 4px; -webkit-border-bottom-right-radius: 4px; -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); } dl.reflist dt { padding: 5px; } dl.reflist dd { margin: 0px 0px 10px 0px; padding: 5px; } .paramkey { text-align: right; } .paramtype { white-space: nowrap; } .paramname { color: var(--memdef-param-name-color); white-space: nowrap; } .paramname em { font-style: normal; } .paramname code { line-height: 14px; } .params, .retval, .exception, .tparams { margin-left: 0px; padding-left: 0px; } .params .paramname, .retval .paramname, .tparams .paramname, .exception .paramname { font-weight: bold; vertical-align: top; } .params .paramtype, .tparams .paramtype { font-style: italic; vertical-align: top; } .params .paramdir, .tparams .paramdir { font-family: var(--font-family-monospace); vertical-align: top; } table.mlabels { border-spacing: 0px; } td.mlabels-left { width: 100%; padding: 0px; } td.mlabels-right { vertical-align: bottom; padding: 0px; white-space: nowrap; } span.mlabels { margin-left: 8px; } span.mlabel { background-color: var(--label-background-color); border-top:1px solid var(--label-left-top-border-color); border-left:1px solid var(--label-left-top-border-color); border-right:1px solid var(--label-right-bottom-border-color); border-bottom:1px solid var(--label-right-bottom-border-color); text-shadow: none; color: var(--label-foreground-color); margin-right: 4px; padding: 2px 3px; border-radius: 3px; font-size: 7pt; white-space: nowrap; vertical-align: middle; } /* @end */ /* these are for tree view inside a (index) page */ div.directory { margin: 10px 0px; border-top: 1px solid var(--directory-separator-color); border-bottom: 1px solid var(--directory-separator-color); width: 100%; } .directory table { border-collapse:collapse; } .directory td { margin: 0px; padding: 0px; vertical-align: top; } .directory td.entry { white-space: nowrap; padding-right: 6px; padding-top: 3px; } .directory td.entry a { outline:none; } .directory td.entry a img { border: none; } .directory td.desc { width: 100%; padding-left: 6px; padding-right: 6px; padding-top: 3px; border-left: 1px solid rgba(0,0,0,0.05); } .directory tr.odd { padding-left: 6px; background-color: var(--index-odd-item-bg-color); } .directory tr.even { padding-left: 6px; background-color: var(--index-even-item-bg-color); } .directory img { vertical-align: -30%; } .directory .levels { white-space: nowrap; width: 100%; text-align: right; font-size: 9pt; } .directory .levels span { cursor: pointer; padding-left: 2px; padding-right: 2px; color: var(--page-link-color); } .arrow { color: var(--nav-arrow-color); -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer; font-size: 80%; display: inline-block; width: 16px; height: 22px; } .icon { font-family: var(--font-family-icon); line-height: normal; font-weight: bold; font-size: 12px; height: 14px; width: 16px; display: inline-block; background-color: var(--icon-background-color); color: var(--icon-foreground-color); text-align: center; border-radius: 4px; margin-left: 2px; margin-right: 2px; } .icona { width: 24px; height: 22px; display: inline-block; } .iconfopen { width: 24px; height: 18px; margin-bottom: 4px; background-image:var(--icon-folder-open-image); background-repeat: repeat-y; vertical-align:top; display: inline-block; } .iconfclosed { width: 24px; height: 18px; margin-bottom: 4px; background-image:var(--icon-folder-closed-image); background-repeat: repeat-y; vertical-align:top; display: inline-block; } .icondoc { width: 24px; height: 18px; margin-bottom: 4px; background-image:var(--icon-doc-image); background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; } /* @end */ div.dynheader { margin-top: 8px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } address { font-style: normal; color: var(--footer-foreground-color); } table.doxtable caption { caption-side: top; } table.doxtable { border-collapse:collapse; margin-top: 4px; margin-bottom: 4px; } table.doxtable td, table.doxtable th { border: 1px solid var(--table-cell-border-color); padding: 3px 7px 2px; } table.doxtable th { background-color: var(--table-header-background-color); color: var(--table-header-foreground-color); font-size: 110%; padding-bottom: 4px; padding-top: 5px; } table.fieldtable { margin-bottom: 10px; border: 1px solid var(--memdef-border-color); border-spacing: 0px; border-radius: 4px; box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); } .fieldtable td, .fieldtable th { padding: 3px 7px 2px; } .fieldtable td.fieldtype, .fieldtable td.fieldname { white-space: nowrap; border-right: 1px solid var(--memdef-border-color); border-bottom: 1px solid var(--memdef-border-color); vertical-align: top; } .fieldtable td.fieldname { padding-top: 3px; } .fieldtable td.fielddoc { border-bottom: 1px solid var(--memdef-border-color); } .fieldtable td.fielddoc p:first-child { margin-top: 0px; } .fieldtable td.fielddoc p:last-child { margin-bottom: 2px; } .fieldtable tr:last-child td { border-bottom: none; } .fieldtable th { background-image: var(--memdef-title-gradient-image); background-repeat:repeat-x; background-color: var(--memdef-title-background-color); font-size: 90%; color: var(--memdef-proto-text-color); padding-bottom: 4px; padding-top: 5px; text-align:left; font-weight: 400; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom: 1px solid var(--memdef-border-color); } .tabsearch { top: 0px; left: 10px; height: 36px; background-image: var(--nav-gradient-image); z-index: 101; overflow: hidden; font-size: 13px; } .navpath ul { font-size: 11px; background-image: var(--nav-gradient-image); background-repeat:repeat-x; background-position: 0 -5px; height:30px; line-height:30px; color:var(--nav-text-normal-color); border:solid 1px var(--nav-breadcrumb-border-color); overflow:hidden; margin:0px; padding:0px; } .navpath li { list-style-type:none; float:left; padding-left:10px; padding-right:15px; background-image:var(--nav-breadcrumb-image); background-repeat:no-repeat; background-position:right; color: var(--nav-foreground-color); } .navpath li.navelem a { height:32px; display:block; text-decoration: none; outline: none; color: var(--nav-text-normal-color); font-family: var(--font-family-nav); text-shadow: var(--nav-text-normal-shadow); text-decoration: none; } .navpath li.navelem a:hover { color: var(--nav-text-hover-color); text-shadow: var(--nav-text-hover-shadow); } .navpath li.footer { list-style-type:none; float:right; padding-left:10px; padding-right:15px; background-image:none; background-repeat:no-repeat; background-position:right; color: var(--footer-foreground-color); font-size: 8pt; } div.summary { float: right; font-size: 8pt; padding-right: 5px; width: 50%; text-align: right; } div.summary a { white-space: nowrap; } table.classindex { margin: 10px; white-space: nowrap; margin-left: 3%; margin-right: 3%; width: 94%; border: 0; border-spacing: 0; padding: 0; } div.ingroups { font-size: 8pt; width: 50%; text-align: left; } div.ingroups a { white-space: nowrap; } div.header { background-image: var(--header-gradient-image); background-repeat:repeat-x; background-color: var(--header-background-color); margin: 0px; border-bottom: 1px solid var(--header-separator-color); } div.headertitle { padding: 5px 5px 5px 10px; } .PageDocRTL-title div.headertitle { text-align: right; direction: rtl; } dl { padding: 0 0 0 0; } /* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */ dl.section { margin-left: 0px; padding-left: 0px; } dl.note { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #D0C000; } dl.warning, dl.attention { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #FF0000; } dl.pre, dl.post, dl.invariant { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #00D000; } dl.deprecated { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #505050; } dl.todo { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #00C0E0; } dl.test { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #3030E0; } dl.bug { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #C08050; } dl.section dd { margin-bottom: 6px; } #projectrow { height: 56px; } #projectlogo { text-align: center; vertical-align: bottom; border-collapse: separate; } #projectlogo img { border: 0px none; } #projectalign { vertical-align: middle; padding-left: 0.5em; } #projectname { font-size: 200%; font-family: var(--font-family-title); margin: 0px; padding: 2px 0px; } #projectbrief { font-size: 90%; font-family: var(--font-family-title); margin: 0px; padding: 0px; } #projectnumber { font-size: 50%; font-family: 50% var(--font-family-title); margin: 0px; padding: 0px; } #titlearea { padding: 0px; margin: 0px; width: 100%; border-bottom: 1px solid var(--title-separator-color); background-color: var(--title-background-color); } .image { text-align: center; } .dotgraph { text-align: center; } .mscgraph { text-align: center; } .plantumlgraph { text-align: center; } .diagraph { text-align: center; } .caption { font-weight: bold; } dl.citelist { margin-bottom:50px; } dl.citelist dt { color:var(--citation-label-color); float:left; font-weight:bold; margin-right:10px; padding:5px; text-align:right; width:52px; } dl.citelist dd { margin:2px 0 2px 72px; padding:5px 0; } div.toc { padding: 14px 25px; background-color: var(--toc-background-color); border: 1px solid var(--toc-border-color); border-radius: 7px 7px 7px 7px; float: right; height: auto; margin: 0 8px 10px 10px; width: 200px; } div.toc li { background: var(--toc-down-arrow-image) no-repeat scroll 0 5px transparent; font: 10px/1.2 var(--font-family-toc); margin-top: 5px; padding-left: 10px; padding-top: 2px; } div.toc h3 { font: bold 12px/1.2 var(--font-family-toc); color: var(--toc-header-color); border-bottom: 0 none; margin: 0; } div.toc ul { list-style: none outside none; border: medium none; padding: 0px; } div.toc li.level1 { margin-left: 0px; } div.toc li.level2 { margin-left: 15px; } div.toc li.level3 { margin-left: 15px; } div.toc li.level4 { margin-left: 15px; } span.emoji { /* font family used at the site: https://unicode.org/emoji/charts/full-emoji-list.html * font-family: "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort; */ } span.obfuscator { display: none; } .inherit_header { font-weight: bold; color: var(--inherit-header-color); cursor: pointer; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .inherit_header td { padding: 6px 0px 2px 5px; } .inherit { display: none; } tr.heading h2 { margin-top: 12px; margin-bottom: 4px; } /* tooltip related style info */ .ttc { position: absolute; display: none; } #powerTip { cursor: default; /*white-space: nowrap;*/ color: var(--tooltip-foreground-color); background-color: var(--tooltip-background-color); border: 1px solid var(--tooltip-border-color); border-radius: 4px 4px 4px 4px; box-shadow: var(--tooltip-shadow); display: none; font-size: smaller; max-width: 80%; opacity: 0.9; padding: 1ex 1em 1em; position: absolute; z-index: 2147483647; } #powerTip div.ttdoc { color: var(--tooltip-doc-color); font-style: italic; } #powerTip div.ttname a { font-weight: bold; } #powerTip a { color: var(--tooltip-link-color); } #powerTip div.ttname { font-weight: bold; } #powerTip div.ttdeci { color: var(--tooltip-declaration-color); } #powerTip div { margin: 0px; padding: 0px; font-size: 12px; font-family: var(--font-family-tooltip); line-height: 16px; } #powerTip:before, #powerTip:after { content: ""; position: absolute; margin: 0px; } #powerTip.n:after, #powerTip.n:before, #powerTip.s:after, #powerTip.s:before, #powerTip.w:after, #powerTip.w:before, #powerTip.e:after, #powerTip.e:before, #powerTip.ne:after, #powerTip.ne:before, #powerTip.se:after, #powerTip.se:before, #powerTip.nw:after, #powerTip.nw:before, #powerTip.sw:after, #powerTip.sw:before { border: solid transparent; content: " "; height: 0; width: 0; position: absolute; } #powerTip.n:after, #powerTip.s:after, #powerTip.w:after, #powerTip.e:after, #powerTip.nw:after, #powerTip.ne:after, #powerTip.sw:after, #powerTip.se:after { border-color: rgba(255, 255, 255, 0); } #powerTip.n:before, #powerTip.s:before, #powerTip.w:before, #powerTip.e:before, #powerTip.nw:before, #powerTip.ne:before, #powerTip.sw:before, #powerTip.se:before { border-color: rgba(128, 128, 128, 0); } #powerTip.n:after, #powerTip.n:before, #powerTip.ne:after, #powerTip.ne:before, #powerTip.nw:after, #powerTip.nw:before { top: 100%; } #powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { border-top-color: var(--tooltip-background-color); border-width: 10px; margin: 0px -10px; } #powerTip.n:before, #powerTip.ne:before, #powerTip.nw:before { border-top-color: var(--tooltip-border-color); border-width: 11px; margin: 0px -11px; } #powerTip.n:after, #powerTip.n:before { left: 50%; } #powerTip.nw:after, #powerTip.nw:before { right: 14px; } #powerTip.ne:after, #powerTip.ne:before { left: 14px; } #powerTip.s:after, #powerTip.s:before, #powerTip.se:after, #powerTip.se:before, #powerTip.sw:after, #powerTip.sw:before { bottom: 100%; } #powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { border-bottom-color: var(--tooltip-background-color); border-width: 10px; margin: 0px -10px; } #powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { border-bottom-color: var(--tooltip-border-color); border-width: 11px; margin: 0px -11px; } #powerTip.s:after, #powerTip.s:before { left: 50%; } #powerTip.sw:after, #powerTip.sw:before { right: 14px; } #powerTip.se:after, #powerTip.se:before { left: 14px; } #powerTip.e:after, #powerTip.e:before { left: 100%; } #powerTip.e:after { border-left-color: var(--tooltip-border-color); border-width: 10px; top: 50%; margin-top: -10px; } #powerTip.e:before { border-left-color: var(--tooltip-border-color); border-width: 11px; top: 50%; margin-top: -11px; } #powerTip.w:after, #powerTip.w:before { right: 100%; } #powerTip.w:after { border-right-color: var(--tooltip-border-color); border-width: 10px; top: 50%; margin-top: -10px; } #powerTip.w:before { border-right-color: var(--tooltip-border-color); border-width: 11px; top: 50%; margin-top: -11px; } @media print { #top { display: none; } #side-nav { display: none; } #nav-path { display: none; } body { overflow:visible; } h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } .summary { display: none; } .memitem { page-break-inside: avoid; } #doc-content { margin-left:0 !important; height:auto !important; width:auto !important; overflow:inherit; display:inline; } } /* @group Markdown */ table.markdownTable { border-collapse:collapse; margin-top: 4px; margin-bottom: 4px; } table.markdownTable td, table.markdownTable th { border: 1px solid var(--table-cell-border-color); padding: 3px 7px 2px; } table.markdownTable tr { } th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { background-color: var(--table-header-background-color); color: var(--table-header-foreground-color); font-size: 110%; padding-bottom: 4px; padding-top: 5px; } th.markdownTableHeadLeft, td.markdownTableBodyLeft { text-align: left } th.markdownTableHeadRight, td.markdownTableBodyRight { text-align: right } th.markdownTableHeadCenter, td.markdownTableBodyCenter { text-align: center } tt, code, kbd, samp { display: inline-block; } /* @end */ u { text-decoration: underline; } details>summary { list-style-type: none; } details > summary::-webkit-details-marker { display: none; } details>summary::before { content: "\25ba"; padding-right:4px; font-size: 80%; } details[open]>summary::before { content: "\25bc"; padding-right:4px; font-size: 80%; } body { scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-background-color); } ::-webkit-scrollbar { background-color: var(--scrollbar-background-color); height: 12px; width: 12px; } ::-webkit-scrollbar-thumb { border-radius: 6px; box-shadow: inset 0 0 12px 12px var(--scrollbar-thumb-color); border: solid 2px transparent; } ::-webkit-scrollbar-corner { background-color: var(--scrollbar-background-color); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/doc.svg������������������������������������������������������������������������0000644�0001750�0001750�00000002737�15156613442�014534� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" width="16" height="24" viewBox="0 0 80 60" id="doc" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"> <g style="fill:#4665A2"> <path d="m 14,-1.1445312 c -2.824372,0 -5.1445313,2.320159 -5.1445312,5.1445312 v 72 c 0,2.824372 2.3201592,5.144531 5.1445312,5.144531 h 52 c 2.824372,0 5.144531,-2.320159 5.144531,-5.144531 V 23.699219 a 1.1447968,1.1447968 0 0 0 -0.01563,-0.1875 C 70.977847,22.605363 70.406495,21.99048 70.007812,21.591797 L 48.208984,-0.20898438 C 47.606104,-0.81186474 46.804652,-1.1445313 46,-1.1445312 Z m 1.144531,6.2890624 H 42.855469 V 24 c 0,1.724372 1.420159,3.144531 3.144531,3.144531 H 64.855469 V 74.855469 H 15.144531 Z m 34,4.4179688 L 60.4375,20.855469 H 49.144531 Z"/> </g> <g style="fill:#D8DFEE;stroke-width:0"> <path d="M 3.0307167,13.993174 V 7.0307167 h 2.7576792 2.7576792 v 1.8826151 c 0,1.2578262 0.0099,1.9287572 0.029818,2.0216512 0.03884,0.181105 0.168631,0.348218 0.33827,0.43554 l 0.1355017,0.06975 1.9598092,0.0079 1.959809,0.0078 v 4.749829 4.749829 H 8 3.0307167 Z" transform="matrix(5,0,0,5,0,-30)" /> <path d="M 9.8293515,9.0581469 V 7.9456453 l 1.1058025,1.1055492 c 0.608191,0.6080521 1.105802,1.1086775 1.105802,1.1125015 0,0.0038 -0.497611,0.007 -1.105802,0.007 H 9.8293515 Z" transform="matrix(5,0,0,5,0,-30)" /> </g> </svg> ���������������������������������fuse-3.18.2/doc/html/docd.svg�����������������������������������������������������������������������0000644�0001750�0001750�00000002737�15156613442�014700� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" width="16" height="24" viewBox="0 0 80 60" id="doc" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"> <g style="fill:#C4CFE5"> <path d="m 14,-1.1445312 c -2.824372,0 -5.1445313,2.320159 -5.1445312,5.1445312 v 72 c 0,2.824372 2.3201592,5.144531 5.1445312,5.144531 h 52 c 2.824372,0 5.144531,-2.320159 5.144531,-5.144531 V 23.699219 a 1.1447968,1.1447968 0 0 0 -0.01563,-0.1875 C 70.977847,22.605363 70.406495,21.99048 70.007812,21.591797 L 48.208984,-0.20898438 C 47.606104,-0.81186474 46.804652,-1.1445313 46,-1.1445312 Z m 1.144531,6.2890624 H 42.855469 V 24 c 0,1.724372 1.420159,3.144531 3.144531,3.144531 H 64.855469 V 74.855469 H 15.144531 Z m 34,4.4179688 L 60.4375,20.855469 H 49.144531 Z"/> </g> <g style="fill:#4665A2;stroke-width:0"> <path d="M 3.0307167,13.993174 V 7.0307167 h 2.7576792 2.7576792 v 1.8826151 c 0,1.2578262 0.0099,1.9287572 0.029818,2.0216512 0.03884,0.181105 0.168631,0.348218 0.33827,0.43554 l 0.1355017,0.06975 1.9598092,0.0079 1.959809,0.0078 v 4.749829 4.749829 H 8 3.0307167 Z" transform="matrix(5,0,0,5,0,-30)" /> <path d="M 9.8293515,9.0581469 V 7.9456453 l 1.1058025,1.1055492 c 0.608191,0.6080521 1.105802,1.1086775 1.105802,1.1125015 0,0.0038 -0.497611,0.007 -1.105802,0.007 H 9.8293515 Z" transform="matrix(5,0,0,5,0,-30)" /> </g> </svg> ���������������������������������fuse-3.18.2/doc/html/folderopen.svg�����������������������������������������������������������������0000644�0001750�0001750�00000006305�15156613442�016117� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" width="16" height="24" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"> <g style="fill:#4665A2;"> <path d="M1,5.998l0,16.002c-0,1.326 0.527,2.598 1.464,3.536c0.938,0.937 2.21,1.464 3.536,1.464c5.322,0 14.678,-0 20,0c1.326,0 2.598,-0.527 3.536,-1.464c0.937,-0.938 1.464,-2.21 1.464,-3.536c0,-3.486 0,-8.514 0,-12c0,-1.326 -0.527,-2.598 -1.464,-3.536c-0.938,-0.937 -2.21,-1.464 -3.536,-1.464c-0,0 -10.586,0 -10.586,0c0,-0 -3.707,-3.707 -3.707,-3.707c-0.187,-0.188 -0.442,-0.293 -0.707,-0.293l-5.002,0c-2.76,0 -4.998,2.238 -4.998,4.998Zm28,14.415l-3.456,-5.925c-0.538,-0.921 -1.524,-1.488 -2.591,-1.488c-0,0 -12.905,0 -12.906,0c-1.067,0 -2.053,0.567 -2.591,1.488l-4.453,7.635c0.03,0.751 0.342,1.465 0.876,1.998c0.562,0.563 1.325,0.879 2.121,0.879l20,0c0.796,0 1.559,-0.316 2.121,-0.879c0.563,-0.562 0.879,-1.325 0.879,-2.121l0,-1.587Zm0,-3.969l0,-6.444c0,-0.796 -0.316,-1.559 -0.879,-2.121c-0.562,-0.563 -1.325,-0.879 -2.121,-0.879c-7.738,0 -11,0 -11,0c-0.265,0 -0.52,-0.105 -0.707,-0.293c-0,0 -3.707,-3.707 -3.707,-3.707c-0,0 -4.588,0 -4.588,0c-1.656,0 -2.998,1.342 -2.998,2.998l0,12.16l2.729,-4.677c0.896,-1.536 2.54,-2.481 4.318,-2.481c3.354,0 9.552,0 12.906,0c1.778,0 3.422,0.945 4.318,2.481l1.729,2.963Z" id="path2" /> </g> <g style="fill:#D8DFEE;stroke-width:0;"> <path d="M 5.3879408,24.913408 C 4.1598821,24.650818 3.1571088,23.558656 3.053503,22.370876 L 3.0312746,22.116041 5.2606813,18.293515 C 6.486855,16.191126 7.5598351,14.372696 7.6450818,14.25256 8.0043056,13.746312 8.5423079,13.363007 9.2104664,13.137285 l 0.2548351,-0.08609 6.9294785,-0.0097 c 6.805096,-0.0095 6.934944,-0.0084 7.234011,0.06267 0.695577,0.165199 1.290483,0.557253 1.714887,1.130141 0.08158,0.110125 0.938747,1.556711 1.90481,3.214634 l 1.756479,3.014406 -0.0186,0.971942 c -0.01387,0.724723 -0.03365,1.032131 -0.07778,1.208575 -0.242792,0.970733 -0.88732,1.735415 -1.772382,2.102793 -0.58835,0.244217 0.247209,0.227436 -11.161974,0.224159 -9.0281537,-0.0026 -10.3636023,-0.0098 -10.5862902,-0.05746 z" id="path199" /><path d="M 3.0126385,11.849829 3.0235061,5.5881684 3.1020974,5.2969283 C 3.3478146,4.3863605 3.93576,3.6757372 4.756668,3.2971229 5.3293315,3.0330025 5.1813272,3.0450949 8.0130385,3.0310668 l 2.5522875,-0.012644 1.918693,1.9107086 c 1.404146,1.3983023 1.964459,1.9332518 2.089351,1.9947704 l 0.170657,0.084062 5.897611,0.019367 c 5.553257,0.018236 5.910365,0.023213 6.116041,0.085231 1.102257,0.3323708 1.857042,1.1184422 2.154229,2.2435244 0.05645,0.2137228 0.06373,0.5643981 0.07519,3.6220748 0.0076,2.032169 -5.42e-4,3.370979 -0.02041,3.349261 -0.0182,-0.0199 -0.414296,-0.691472 -0.880217,-1.492382 -0.46592,-0.80091 -0.93093,-1.577954 -1.033354,-1.726764 -0.735716,-1.0689 -1.983568,-1.844244 -3.315972,-2.060353 -0.280375,-0.04548 -1.345158,-0.05334 -7.238708,-0.05347 -4.713933,-1.09e-4 -6.9931825,0.01221 -7.1717862,0.03874 -1.3002273,0.193134 -2.4770512,0.889916 -3.283628,1.944192 -0.1076466,0.140705 -0.8359664,1.353438 -1.6184885,2.694963 L 3.0017709,18.11149 Z" id="path201" /> </g> </svg> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/folderopend.svg����������������������������������������������������������������0000644�0001750�0001750�00000006216�15156613442�016264� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" width="16" height="24" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"> <g style="fill:#C4CFE5;"> <path d="M1,5.998l0,16.002c-0,1.326 0.527,2.598 1.464,3.536c0.938,0.937 2.21,1.464 3.536,1.464c5.322,0 14.678,-0 20,0c1.326,0 2.598,-0.527 3.536,-1.464c0.937,-0.938 1.464,-2.21 1.464,-3.536c0,-3.486 0,-8.514 0,-12c0,-1.326 -0.527,-2.598 -1.464,-3.536c-0.938,-0.937 -2.21,-1.464 -3.536,-1.464c-0,0 -10.586,0 -10.586,0c0,-0 -3.707,-3.707 -3.707,-3.707c-0.187,-0.188 -0.442,-0.293 -0.707,-0.293l-5.002,0c-2.76,0 -4.998,2.238 -4.998,4.998Zm28,14.415l-3.456,-5.925c-0.538,-0.921 -1.524,-1.488 -2.591,-1.488c-0,0 -12.905,0 -12.906,0c-1.067,0 -2.053,0.567 -2.591,1.488l-4.453,7.635c0.03,0.751 0.342,1.465 0.876,1.998c0.562,0.563 1.325,0.879 2.121,0.879l20,0c0.796,0 1.559,-0.316 2.121,-0.879c0.563,-0.562 0.879,-1.325 0.879,-2.121l0,-1.587Zm0,-3.969l0,-6.444c0,-0.796 -0.316,-1.559 -0.879,-2.121c-0.562,-0.563 -1.325,-0.879 -2.121,-0.879c-7.738,0 -11,0 -11,0c-0.265,0 -0.52,-0.105 -0.707,-0.293c-0,0 -3.707,-3.707 -3.707,-3.707c-0,0 -4.588,0 -4.588,0c-1.656,0 -2.998,1.342 -2.998,2.998l0,12.16l2.729,-4.677c0.896,-1.536 2.54,-2.481 4.318,-2.481c3.354,0 9.552,0 12.906,0c1.778,0 3.422,0.945 4.318,2.481l1.729,2.963Z"/> </g> <g style="fill:#4665A2;stroke-width:0;"> <path d="M 5.3879408,24.913408 C 4.1598821,24.650818 3.1571088,23.558656 3.053503,22.370876 L 3.0312746,22.116041 5.2606813,18.293515 C 6.486855,16.191126 7.5598351,14.372696 7.6450818,14.25256 8.0043056,13.746312 8.5423079,13.363007 9.2104664,13.137285 l 0.2548351,-0.08609 6.9294785,-0.0097 c 6.805096,-0.0095 6.934944,-0.0084 7.234011,0.06267 0.695577,0.165199 1.290483,0.557253 1.714887,1.130141 0.08158,0.110125 0.938747,1.556711 1.90481,3.214634 l 1.756479,3.014406 -0.0186,0.971942 c -0.01387,0.724723 -0.03365,1.032131 -0.07778,1.208575 -0.242792,0.970733 -0.88732,1.735415 -1.772382,2.102793 -0.58835,0.244217 0.247209,0.227436 -11.161974,0.224159 -9.0281537,-0.0026 -10.3636023,-0.0098 -10.5862902,-0.05746 z" /> <path d="M 3.0126385,11.849829 3.0235061,5.5881684 3.1020974,5.2969283 C 3.3478146,4.3863605 3.93576,3.6757372 4.756668,3.2971229 5.3293315,3.0330025 5.1813272,3.0450949 8.0130385,3.0310668 l 2.5522875,-0.012644 1.918693,1.9107086 c 1.404146,1.3983023 1.964459,1.9332518 2.089351,1.9947704 l 0.170657,0.084062 5.897611,0.019367 c 5.553257,0.018236 5.910365,0.023213 6.116041,0.085231 1.102257,0.3323708 1.857042,1.1184422 2.154229,2.2435244 0.05645,0.2137228 0.06373,0.5643981 0.07519,3.6220748 0.0076,2.032169 -5.42e-4,3.370979 -0.02041,3.349261 -0.0182,-0.0199 -0.414296,-0.691472 -0.880217,-1.492382 -0.46592,-0.80091 -0.93093,-1.577954 -1.033354,-1.726764 -0.735716,-1.0689 -1.983568,-1.844244 -3.315972,-2.060353 -0.280375,-0.04548 -1.345158,-0.05334 -7.238708,-0.05347 -4.713933,-1.09e-4 -6.9931825,0.01221 -7.1717862,0.03874 -1.3002273,0.193134 -2.4770512,0.889916 -3.283628,1.944192 -0.1076466,0.140705 -0.8359664,1.353438 -1.6184885,2.694963 L 3.0017709,18.11149 Z" /> </g> </svg> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/folderclosed.svg���������������������������������������������������������������0000644�0001750�0001750�00000003714�15156613442�016430� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" width="16" height="24" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"> <g style="fill:#4665A2;"> <path d="M1,5.998l-0,16.002c-0,1.326 0.527,2.598 1.464,3.536c0.938,0.937 2.21,1.464 3.536,1.464c5.322,0 14.678,-0 20,0c1.326,0 2.598,-0.527 3.536,-1.464c0.937,-0.938 1.464,-2.21 1.464,-3.536c0,-3.486 0,-8.514 0,-12c0,-1.326 -0.527,-2.598 -1.464,-3.536c-0.938,-0.937 -2.21,-1.464 -3.536,-1.464c-0,0 -10.586,0 -10.586,0c0,-0 -3.707,-3.707 -3.707,-3.707c-0.187,-0.188 -0.442,-0.293 -0.707,-0.293l-5.002,0c-2.76,0 -4.998,2.238 -4.998,4.998Zm2,-0l-0,16.002c-0,0.796 0.316,1.559 0.879,2.121c0.562,0.563 1.325,0.879 2.121,0.879l20,0c0.796,0 1.559,-0.316 2.121,-0.879c0.563,-0.562 0.879,-1.325 0.879,-2.121c0,-3.486 0,-8.514 0,-12c0,-0.796 -0.316,-1.559 -0.879,-2.121c-0.562,-0.563 -1.325,-0.879 -2.121,-0.879c-7.738,0 -11,0 -11,0c-0.265,0 -0.52,-0.105 -0.707,-0.293c-0,0 -3.707,-3.707 -3.707,-3.707c-0,0 -4.588,0 -4.588,0c-1.656,0 -2.998,1.342 -2.998,2.998Z"/> </g> <g style="fill:#D8DFEE;stroke-width:0;"> <path d="M 5.6063709,24.951908 C 4.3924646,24.775461 3.4197129,23.899792 3.1031586,22.698521 L 3.0216155,22.389078 V 13.997725 5.6063709 L 3.1037477,5.2982247 C 3.3956682,4.2029881 4.1802788,3.412126 5.2787258,3.105917 5.5646428,3.0262132 5.6154982,3.0244963 8.0611641,3.0119829 l 2.4911989,-0.012746 1.932009,1.9300342 c 1.344142,1.3427669 1.976319,1.9498819 2.07763,1.9952626 0.137456,0.061571 0.474218,0.066269 6.006826,0.083795 l 5.861206,0.018568 0.29124,0.081916 c 1.094895,0.3079569 1.890116,1.109428 2.175567,2.192667 l 0.08154,0.3094425 V 16 22.389078 l -0.08154,0.309443 c -0.28446,1.079482 -1.086411,1.888085 -2.175567,2.193614 l -0.29124,0.0817 -10.302616,0.0049 c -5.700217,0.0027 -10.4001945,-0.0093 -10.5210471,-0.02684 z"/> </g> </svg> ����������������������������������������������������fuse-3.18.2/doc/html/folderclosedd.svg��������������������������������������������������������������0000644�0001750�0001750�00000003714�15156613442�016574� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" width="16" height="24" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"> <g style="fill:#C4CFE5;"> <path d="M1,5.998l-0,16.002c-0,1.326 0.527,2.598 1.464,3.536c0.938,0.937 2.21,1.464 3.536,1.464c5.322,0 14.678,-0 20,0c1.326,0 2.598,-0.527 3.536,-1.464c0.937,-0.938 1.464,-2.21 1.464,-3.536c0,-3.486 0,-8.514 0,-12c0,-1.326 -0.527,-2.598 -1.464,-3.536c-0.938,-0.937 -2.21,-1.464 -3.536,-1.464c-0,0 -10.586,0 -10.586,0c0,-0 -3.707,-3.707 -3.707,-3.707c-0.187,-0.188 -0.442,-0.293 -0.707,-0.293l-5.002,0c-2.76,0 -4.998,2.238 -4.998,4.998Zm2,-0l-0,16.002c-0,0.796 0.316,1.559 0.879,2.121c0.562,0.563 1.325,0.879 2.121,0.879l20,0c0.796,0 1.559,-0.316 2.121,-0.879c0.563,-0.562 0.879,-1.325 0.879,-2.121c0,-3.486 0,-8.514 0,-12c0,-0.796 -0.316,-1.559 -0.879,-2.121c-0.562,-0.563 -1.325,-0.879 -2.121,-0.879c-7.738,0 -11,0 -11,0c-0.265,0 -0.52,-0.105 -0.707,-0.293c-0,0 -3.707,-3.707 -3.707,-3.707c-0,0 -4.588,0 -4.588,0c-1.656,0 -2.998,1.342 -2.998,2.998Z"/> </g> <g style="fill:#4665A2;stroke-width:0;"> <path d="M 5.6063709,24.951908 C 4.3924646,24.775461 3.4197129,23.899792 3.1031586,22.698521 L 3.0216155,22.389078 V 13.997725 5.6063709 L 3.1037477,5.2982247 C 3.3956682,4.2029881 4.1802788,3.412126 5.2787258,3.105917 5.5646428,3.0262132 5.6154982,3.0244963 8.0611641,3.0119829 l 2.4911989,-0.012746 1.932009,1.9300342 c 1.344142,1.3427669 1.976319,1.9498819 2.07763,1.9952626 0.137456,0.061571 0.474218,0.066269 6.006826,0.083795 l 5.861206,0.018568 0.29124,0.081916 c 1.094895,0.3079569 1.890116,1.109428 2.175567,2.192667 l 0.08154,0.3094425 V 16 22.389078 l -0.08154,0.309443 c -0.28446,1.079482 -1.086411,1.888085 -2.175567,2.193614 l -0.29124,0.0817 -10.302616,0.0049 c -5.700217,0.0027 -10.4001945,-0.0093 -10.5210471,-0.02684 z"/> </g> </svg> ����������������������������������������������������fuse-3.18.2/doc/html/splitbar.png�������������������������������������������������������������������0000644�0001750�0001750�00000000472�15156613442�015566� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������M��IDATxݡJCa( %4 bȘͶ3v^EL ,b;{Ï/aYկq:\�IIIIIIIIIIIIIIIIII-l揊_t/ϻYQVYivk_ۣI@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$C[V=[f����IENDB`������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/splitbard.png������������������������������������������������������������������0000644�0001750�0001750�00000000432�15156613442�015726� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������M���IDATx1jPFE$H3f B܀P܅ 6q_E=o^v'{/(ESa"LQ)0E(ESa"LQ)0E( r8޼~ׯ>_3gOp?/գ7W(ESa"LQ)0E(ESa"LQ)0E(E?'V+q.����IENDB`��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/fast17-vangoor.pdf�������������������������������������������������������������0000644�0001750�0001750�00002222165�15156613442�016520� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%PDF-1.5 % 2 0 obj <</Length 34769/Length1 1629/Length2 33845/Length3 532/Filter/FlateDecode>>stream xctem&vvlv*m۬۶m;Nvӧ}~1=qM\{/2"e:!{#Sq{;:&zFn,+ௐ LNŔnj5503`�"N.�JU%u*c0_OgKs;�7S{[S;׎ʦ� S)@D^ASJN@)! 03u2(Xd-MM�fN��v&LK`pv05fal`dk` 0w2s{? +!'u],\�*;O Cb;[UZSҿtaj] -..22X:;z_i:[ڙg�'SsC'Sg0 ߪ7tpW.Φ6f0LcmniϠHٙ-7quӿDPM`bj g7$e>(o!7r+G%ZF�{�ng,cc17?8WCu'#bBv agYD`fhSڙ:Xڙe_11?g7M/IJ.7Y{uYXM cd{�\l,@bC'Kj:S|_`MeC;tKGWS)ѿ33r12Kj&,zŠpu2xP[Ҫ-qVeS/yy筙LeKvn+WFy%7 Ͷvܖ<ш0q*&  q7r~1'y_�Wb6EhӮ{2'#UtLʋw֦.f%*xm$ 1m"l:[,MFUZ3608Iyc t('QPXKsZ>Q+8D󲣹QP$suv8xrJ$P$f-{9\ b#ia c@`=-p\ʋA4/s k|ꅾ :DXPp}cCЍ.} 4 WnL.ѭR{uN0aT^Qf:]JJm_Kra&4WXT;f1Q5y&RFQ'TwG#wj%b(10/A"L48/<ґx;1 ᎋXfmX;fZZU u=$J6Xx!pbwwi|J5N!-�gQVd٘ubiS,`zxBb9F d ^쫯|, 2pmn[i,x( )C[6onD.v`w•ǯM{n3:.`>aV+A5{oya!bJsqGv}78'doh(sc>F[ZdH�|==r/g}[0%tܸ,@$#e-ay e=]yCi&<)İ>*Z1m<x#5~W G6qUk߭S%^@@MR؞l3vajl/l8]S : qf\2VنS c]C3@J`&_D l"ѭ zM̳zNU,RM@֣;-rfxF\E~Xw@#" tA_&,1:d zXZ=oq6 t+G+5U!3^T�82Ƈ1U(X50ϭyh͆T%hygr!M݈?o, m\֯{3>Zm=%_[L]Bx`\ G1+qì$!`Vkd"@ٕ>Զ<O#2xq)uB]UqrnH)ރq"uGy4syv)>,DJ^C +E=̩`9p@( rvJ Fg3+wS0)Mg2K Ιt=By`)MuyU:t gԖDy 63g4-?mCb'1h3Hv !ut *ǛN,9Utlqp]: @z\\M'g@L$]UGu<}O;!ni7.yYTUla'2XD *1a\wUhM0Mt"Ұ@OOmLBł<Zh lq A�4X |m c,5a*)^.gNc24#| .ɆO}SxkLʟvȹx, ЄK3JA]/,}A"+*/b.b x0{uq۶$1QK?1a XC=ADċǘ$OᶤJ`VjgqM!n+flLتUV%O\EtN@Ŝ.~cEBFvb-WH=H1w'/78ejao >a=LVHɢ So'E; F ]:^K SJx|"伽b �A$MeJ y&>ɶQ9.;fsj @P띆é#AybUZ4hr\atI,}i|I% Djuha+߲,w:hwx wEGBHt$,'|S"\>EAnޔF nD{�".ۮ}�#cfg<H'TXӳ_iˍb箻WD5<WyLJLSqG,A;Tv1Fd@r#埐cykcޭaB#l s(g^oLt#\ AdX|Kya ǑU/)-k;0+{A]6#ۓ\WɀG*{6v$; s!u3#.Y9._VGvegSYYr%g`!TUTt(5nR$_Gs\nz}j@.1(sOKLOcAss*{hm\)O%/~5f߯9;bwQVZ +F( @s*VL$ٖ(C<B$YOL1b`5|v*znH"֍:E,5&2ϹE^R9;F+47idP'xNƶM>^W\I} (S~1Ld2R#К  aL:uڴ  =yT3*{ݸjp뵶l~?oz&yZqvQ0gB5 v@pJDCPqNu\uW(̬%}Zo(:T b6hb(M).c<.Ohh裗bp) A!`E]\-Km;z#OU#rg|9ĩ*gc)BWֳs|O72 U[-PvVާsm[].{&ߗLEe]1p e) 3sQ;wϣPWBCIIpE+ !tIE%!0sXDP[{LRGWл) %M}픂yyޤ͵9)UOˏaT><rl2$"Dsz8݋ȷG%CBj^agh=ϓvɟ9;MQY<H=VTePze"7vB /H~r=q`0)h. SS| 32X[% ;OP'o'(W!e A3N0~ ݒ_pAJ5ɠ3兰 y%(eaF@K2<IV!{M~ds!6ẢgPC7,AlzeMUCgVΚU.QOzu0FS{IX|ؑ})|!f/$p8_%͊,: C4(b#1K 0!nKRY6i+) D&"*hV 9}C%zT64�z+Q$qNUuZnY|b .eh“[JiOP(~5~P`CX&nrKfQ2�60v\W AQc*ãy1wSMj.c>T E?\ǖ.οIcD1>$\x|2U.z:7-pp?JTKcLbEˠX_T4%َQ2[Tg V㒾,q ɳi0 )<ւֿ'"rL \ 7^m9?]&zT/@.fp#dV 9v:( ˔x`2UY:j]'ԿA1e&uX>zWKPUnZ])5ךiV[#kM>?[4g">M0qF~Ytb:wU|-9ZpAQ~' i Q EȲz9o[B}�[R-OC+N05�zs֝E$0 7AgܞI1xB`Κя i:F_,\ta ޱK@ Z$c9]Rh 2w-ll06xr y9 ɉ,E}e}NafoðI=EҞo8?:鞀~8OOK\G }ڟy8#޶ ~yz{J猖[FBFuɏI loW+֮Mܚu'ׁJ 1R>`*Fv۩䛨?$Kk[˵L{ r.S81w 9T.z@Ĭz[}68ek-i8oD}w1-ւ-1 B8sY|q_ "=H/3fyșaKNĺ[TK6)ڧ,5pDW{K=�nU3!.|8K?z,j-ah:̡ wQ Ʉ[I9GZadᾸ̳cX~/@q.IWF#aǤXa.#!rn9_9,İI6_SCHV8s"ů Ճ *wԄ[zI]p&؅y|xD$CV_5͜ C%#˔d\샒pnMKJ(SOu.&zŃ|g)`waFt<?~  6RM׭'Tz~X?iBk۹Lc,ܪ>e!UhZ K԰[; ?w%{b`a@'7Dgv洴FP'iD}LLSiERAƎ+ptZHMJ(Gs*ă!8𹤘([qTܱ;9Xa<tcn7;%І=<c0jYM;>ˁ u(w ѲR|L;Z/v'g#q,V] ̃PzK%/ɹ^װYx  U;_!T&f*SvZz0_޿}-zbL_� w]₌ 0fikT;lqU65qK>a>fuPxcJѡĂbnN 9Vގ yXpdAvפ'52Qby^1KʟNh3BvL3דWf(E%9ۍ蜆t s.t}GV�p&b4;_A dA^ؔAmd?]ثC vDyH;TRArL%ቀנZPUY[:UW@ʠ%MÑgHSqq MJ4kkU~IBY4 kR'@hQk>eK- ۅx33죕>Нa6?m^N fH;JTE~?7e_ޏiK*~ X4gv]#~Q691tG4I;ٍshz=A旅$'G$`23uNt!3ιkM8<&M֤JzRp^O ~EEI jZ2O*zq1̳sudy;e1дO O[G<[344}O-! |͚"Zk`B7vJH={k9h gt;oK0wFIY ~(p2 Vp Ϡ$jS?bAYGy_#|qM $݌7]*7?yÂ./321ym>87"i=[ =whi)?iiWjt+VjܝY,AWę^whcM9իa2td/۞!V|PMV]-g{NPڛr@I).+шC$;!RpTD6'LKwY6X@Z-6d? p ~T`d .٦⍔~,uXMӯjUlnq�5vk>~[bX֋ <%8! k&%1g…ysH5mQy++,lS (<0+d@I8U2 ؒ)02.՛@Tя aKa$y P91gٚ0!g뵒bD;Zl9+;O.9+,i_:Wnb0PFM^x`twLGXyiS!h% އJL7u)-ҼkhJF'5dL9,Mlꬨy}@t_4d(^~ܬ7d?х8;(H6fgZ@G_ $IjD!rIվov8}k`>�ݾgՉ]3)<:7lugELAh -C3 )qhtǂ;na]1c}o#1:;Fs!>݉->4CBB9<t{ǫZnߊ,(K]e ׎?|1A,1UwBg2;WDx˨x.eɝHf/;0_e;4:w>C"l0pSK9{lPuiOY2.uZH]{o6 ]}.!Շ\X?>DR@dGרΝj0` (B#ѕ�hMYj"$"<: ]‚] yN81{LmyaIv ݡYy=Ҡc@>߈xk/Φ. gK8+D"PQH~}{ǎ&l FT1 [T2.W^ /A4W$ ڨD~V\7V!ϙ'y!ě7&gKe-Bn4FY _;'Ŝ{^TCF׊BZ\af0Ȝǁd'J5ǵd2S6[@n 51i}* BTC\%OR½ZL/)`6SIt;v"U'M$R^/x8J#�*(5aqeT/Q 8ooi@]q Z_KC1docTVfͰ@O OQ1vc`BMPȉL hlR1)yɳyF~4z^!ghD˶Lb#]}]g: ށ5o39!jAE7) ;}& XjY9+|lެihtz(rF38IM _ -}c;^>X.ܑ$?T\ZYyYya{?jA_pbx Cj*=P"ʄC1+WsJv6[e}G�j1z?ęjD7M'G+;5ʴI3s&FJ_րR*¹mG:0A7jgZP0T0Nӯ*R,d}B/X1v"}4w 9En"lz?i:5 ]x@6=cPY2jKmtџqx "U#�i ,<%ϒF\/|_+#>7QynDžh ŸXf"єO +4:ZgŪפr?!usa, 4/Oud%r} ń^܁I5M`Q+esOg4܈|Q'>$Qwe8|,k慌k>`B>x r0{s&"@:-M%A~'~t c9lF4[K' '#1oxlHa,1|(GiHHU~Z'Y/ |C}0` ƫZ w_uSoVߩ BUB~armJ-9ƴ-;VZlS0 U:.aL ֪ZDJ\Z<A7(L7 4uGh sY魡eTD{fIEUZA/ųV=|5%#wB˳Noh׈Ȅ5 vG&쎳AIH[(92[4>M|1HCDDMJGo{W|LMHܼ4 pl䂆v[wB.(IeِA}#d4Q脻�N$`j1Nǀ Kp;yL`W+c^yV4 w m"yć/Jx׳%C,dg dm+yVA^]iZ[YSc.zb?{)y\<LtzZ6^ "cPqN9"8H@dP88ts٥TWʭB%Tuc!$۸TUBqʚ�3#Z^=cf#}ь~/BryPrG[zbz}Š I$ڂ1Reȑ$xHc|"U.BQk۾YWqj`FC fzzŴ &AjuR_[פYts`3)9x48XM{Vw08 YpfFG&f~1Ĥ[Q=F5!�8V$:]aR#^vlO0dU�P %d{NPN2b٩K||i6)̺oIhG=p4G1a{D Ul Yq1@5(mQ1(恾n˃8uj:t"jiaO@\5d#ZUzl8 U߮\@m/4 @ mkLnk {? uLUPU㊹%0>LoO[ux; Mg[Ҹ/QJ hDJc>4 �MʭKcf;N/hA` TcОJX LwO~h bIa)N3viE ^\7%E6}兴=nSRR4Pדt?tE²4�J FX*&)k*qþ?H:q3x}/cTHCZR$|cc(U{pvJw,{v)ONTqʿa?Cj~F}gYR E+/R7ɣtjE]Bnedo_1?n8".v 3ؒY45M3i~wiνf'PL z'bSTWD/M_70_@kJE0Pl(0f$[Apƿ8 an4wceJJT@&!i0ZR[S @2)vZ\b/5>=<Zwc/2˨脍?9^J} y4}ܖjlK1$>q6ކbqz$ Kƶu CE? k#O=MVU2�̬Q_ -<e0Tj|zm<C>^'2˲Pf*oV!hYc.N4 S#>Dמ+ ZrA-?o@5bT|XW#j/.K#Hd&P{Ɲgy�a3WRpqŢ9"I/ u̼f$C4G#CZY-&u£AՑ. gM%&vj҇_$rU-Q&Ěs%Պ\!#u9fΑ&9Ձ`F`J*u9ߚ8T&H8h.\ )y-|l!4[ Kiw0ى@)kҷhHE\֚USD0 9uvd$E< %1m8 eUx1iݻd%(]Lr4a'l&WJ!#Wf"x#{HKDiZ(U)I :,4A\Kr c*°w+TVK093s`0g*0PBed tH.y|k9Rw.r3&AT+s)%"n\#FMݸލWTܐP=@"~Z/0?9 *0\«/PQ=!I*I AbxY>''j'je4{<moU3#"Gzwnjc;˴�Kt{3I S<`>(Q5Y&� (y2ūh mk /=nZ(+<F.î��Q?mɲW::z.vbzi(_tE=:Ez LsNW`.h?㇬W!M.-[Vf(BS_[YEL GȼWyv[g$c*Ò'@c~Kohw(!\�s(60j~j&>d7(})L1 l#|e0b(uah>PN^s~[>SkQ6;R!v!XbQF+4&ɭ2`= {m*-&PV7Z~ ;|Z zm`zbJHKsdRK57%'s?p 7-o,h&Ơb8q>Id4۟ y=:3d!0% <EO:*m=ycKfE}i849\)\i㜺Tq{ER l3~L = .z(]֝De#17rYRz^8||1Xʬ$սZ3gi< 0$ Nۗ*T^9zԵm]ڜ҃L`dH<X &n|<2 9&7h۝^ّ]{qNQV3ԧb;͟k@b{g; `0s~}| Yy[9s[DzF:?d\ oJZ[6ROP;iҀrg|Jv 9UfV揚Ofucw@cq/|Ӵ7H00ԯ. <3Ov*VZ([Γ_Xle4fUZ]]s7߀Bt.MQhG ZRhnsQ18-`j1) D <张o8$e]8.}ܯA+#w5lyM+CIenCڒo%פ?}=uiɴ{b`;S!#"!n:} DEr0VpzcB0߼̮{g<">瓶EN7Z;,=\"c$Gpx*{Em"f97fZ Pq*};g|, 口O+NHBPmK)&|i%?.nxNQ 3B."^кsZ{3m3<^zlb Y TX?g]Z0oVeOp ˺rn7–t˱p)%nm`nso%.Ea #DYoou滒cƐ2dsZa3v~"?rZ ;9i~4-u Éaa-*nksEE#пLwO2/D.Vss'53qqɖY&UID1n`z(y曨%bEEЦ3[ViB,͎"Sh{\=CDu kyid Q?8R9Fte˭X]Lg 獟*wG64A \mU`ad>D\]ElS#@aQ|*n#(hgƘ톾K/Gp ]bz25K8Ht5r Չ|z"2zC%Uyӱl.Ui}ʡcNF@R)ΙjƧ1/8(4g̑nڐepsYvf�[;C�Znؾ)| 3gJXc? ̃NM eU#T0w bL�۬9kS&DJvnr:!<‡ &-Ъ"4HxElJ@? ~Ɏ?E|4X|jfsfִxKhryj Afm}m3Ռߖbc7 06õ߮.1aD,GE[`T dҰt�md\EzHw=> A] ;C%x5_mjD 6Ĉ 2K^N.Tw_:* $n x# σ?=/s0OMi)E5!%Wۛ*.pXXd1F?Y5C}w&RvwLv~f!1p\h8:n[S@3Nv"Mvxhky;U~?ʰ|uꭶu})d_rRivr=e"*AݬDF! sE;;<&r2xrlRmEOȃE)bȓ[MA)b@F}"avCy&/09uL7H bҍo .49 ud Ψ27M/-]G7+e^6p<+٬Գb00clTUuFZBNw=%DS$Ag-c:l׊]xl~͂Oo&Ee0yk"8O}"LG+ >pcʿEIW&BԶU:3?W'L$ D?;Q$¢CN}hU `p 52$}# ޛQ6]ago$ ?"@/Y(e>.wPx+!` ' Ҧ�mY c&b|G(S׶̘#auⴑVaD%6  Ejqah#/dւLb|f r k7n0G.R/+$EF?7[ WJT<ŋ$>LSԛ@ZC PgZ>A0V2%,mf #!#l`kf ,6ٸiD= jo:{�<F9"E[}4LcrmdN ۖKWL>zz{!T�z= !aH|t7,T#pb LVKmHًDtu^Y?+1q#Q =?#' u]V}YHPy`@+Ɵ<o?F{v97wJ1j'-Fȵx0e|T5#Z-coִ2ˡJVMBi$&}G3{ZP#!:wp<3-u^I">nщ{pכ0i] Lq)_qVj?QxMQdm}D1W輲dJhl,C[Cb,yI`iVRAM$V[q #iU wxJE Aq<TۦG_yzt zC^zMJYV(VݝpX ,e{z7 /S"it^$"wH%`Ⱥ`l�<iс|c{wڨ*u%WXl=<1mQY [ݎo&6h!`'oLS[wU.oXsJ YJVL솁9&.b=sS"*qGz6c8(12TX9!2N.9{cه+&<8 kwST9erN!FL< R='[ԫϵZy_LNMV^&\V# mzusw,%yKc,4l<H&nfj[(Y#y@l@uC:a0PL)m\`Ylv4qU$Yb%Lfxon_ G00N, L}޽%ua'CYoͫ/-361Dƫ/j>hꝘ9`?P~(܏d7)QYˏč2#2 .?Y!]Ӈ Xf)`Nu:{& KlFRTlmJ&>`ɋ֙A6WF7џ_G쌦:Il;!y^Ui 41(-PPj~rQ?^a|cx�T!g[MgP 0VWrWqˠJM,q?mY?/T[ǽJU2A] S讨,< 2K@ѭ =6U1pFLgUkaYFl;nݞ W+k+dAfu T0f*0jؤ4HusV1>pŻ0GZ e|2\1sLG(B1WEyl * +J τYv3EtMbeU*O691N\P�+V1YO1ieKHM?4`ǘ�Y /SX^#go wzzHyp�7Wڼ$R75?"3nX�QBވ-E yYh`=H D>BؼKL_qhKvu-f-59ezf _ղ &vыuPCփ#)'NzlSTEYgrqbgw݅Rv mj�gQ +Ἆ) mmi/2% a}$Pa1fa[<zA]?GVlCnY$O#g`>#75!C�hsׂ5Sn?f)PmTMF4V[� @) sc!{XxBeCw=T;I+p I  YX5p`v/L@0Wm"zB.;Wxu)TT{G}c~ 8%)ɓ,KY]k̴q86}i ǁSS)%\?wIFnuzcX.S׮#9>I1p$wWe6[_e̼.$23M}L$SӮ"6b >ɦYw7H᥎|QdNS.r`Jw\Eix !۲ZfA).L3DaPc:)Q km+.ʮ阉&gZb.~3=^Di Zʄ"0tro++S}wc cWlvܻϤV/e‰ A=Kn7+@!!RB'Fm:!CB=�lZZƒt0:f @c,90-];~<*& H? U�I'OSv Aff`2U*jߨ]Fx(F#'F8 A; 0S3ȱ@'7?s|&TN\RfU mȯ<AعQr @[ 1#˗ }Vb}ϹƅBvtu֜QY Ϡ˟[&BR T;-<p1†`UszfrcOugE4 <hH*p9 B : b+d̦ vcZV;va2ơ~PMx;>+,,UT []a,Dv棺<ƈ3M(UfC߸Jl IkѨX]Rap|Պ-۠3y;rC\:@W͔lpWNM#M#[_K#+F%i{/&كEUAB>T^yv4eyBO~&Mr:EI[Q-3Y,ؾo /pN nh ?늺T?oj9}!j,Q29k^'vsQ^[Ӌ.dJzoUı>Z l�:΀eT<a@2 zk*"Z6IJX>=~ny.xL\6 (.y??='A욾. â43S]Cxۙ.{{)ow<t8)<�'ݠV8+SØBW|B݊\}c]8~o^f=1Y")#FX:5աz*`]r3*әHjBviЂAH{O0snj gMwVPrdv* ?Y|ryX#SfA;i#c~>*}eSG=vkd/Ʈ{ӔD}z�Կ(šW{[\q*?Ň4a6+B>SLJizpQ< T9)v<g*X6T Nn/CGJ)<5OuitH] M{6bOҘa/!& lQ t]ˬ ց|4?Ax֓p^ cmgalǚ`n Bт ,}Ŋ'B<Ya3${BܶS"XSE*AS$ژE},4V/O>r-# $MZH%;zaa%yj?+]4(.&<岯 (~D�q>Hduij|%T+QGvLew[mQ:uy][]@aŰ �n i(% .3( �-1ZGm.+XP3mKg?zR-z"V|_f\v*Bf69 ;{k覊1v+p',m4ߺ`M f)o0^<M,^$;2or$g K3@^>0A' zU-岙{IӮb(ԅ\mR9�Tz({\eޯ>W3XsV~g*[EʈzP}ũNH>*x4̚\2tuRX:\-b5 %׼"Оze}q6g3nEDqIM {yPB=lF2Ƒ_~GICVn8Y[;"zDdoaAq˭$g<nч"-.)dЭɺO3B,B$^#g^h @4$Dq]땹^>~`=fZ"HH;4k4cL t8LfYc@G/t;w&ib).gUXJ1!~2Q`^y EZd=뭵cQmj(,W 3w}QpTmLY۟2e-05t,bm+{S�(RA9t^1&Ng.`Gcη7Mӽ,j"�g46}?ɸx~_CPtNK݌H뱄K]mXn7{2NniУHUc%ry0(?=XR5nN9 _$LxqS횒A-I84�K6N[r^X!9Q|赚ʃR3@ ȇT2Va'(+?Lu.r7/(CPԏ=o_[]dOS3>o mA KR#9TPf7]-^TMߙ_*v;u.Z03E MƧ4}ڧ)~>v<ؗF3]AZ*m\6L1]]⮾tT7a"x4劌7�3b.d7ΏU/͵tؘl 8W'OVDgdнV9@B^C!yt):Vc-®&~d˺IzP\v14(D8`=u2Yi0ѫ2jX#=̄q Aq%yE9@\6餋犅̠Tȫ{_dЌPm bft2-Ma!SKH76 Ĭ-4,TK& �2Z_΋ CDL??M!7;o\o0Ldd Ny f )M8_2[Ȼ짐 34L1o! Pg{GD4L2>Ol1Oр?dTiGk2%SLX7P-hchB=F:bƓCeh8|cPwQD7 WC^޻x@`TQt}ЄKY!;jrKod0R.`4|JK5Oeikm{,;BU, Yl j|i h:xln8O3_Q Uʋv "^>O :7]=]S\5繍נp`,;vy[TZbU'Ӑ,+#1&6;- VTv^4HaQB1jzF˩rD { bM+TO|?AݗpZLJE* G�&6t4UnbfjtYOuQ6ٹp';\2ik�m(C?7d`T&  Nf9e{>X4xpxt-VHH`Il8  QJf'vc9YdpUE@YGa/,m:W% (5O|rkxb6E1OdЬa(Lǣ4Rʛ<͞s1c;Z$3i[Aޢ 7*΁Mς?k'0:_m"0@ڧM]N(̣Jџ[\7T|:C|n+uFgC"6swW0.ϹrR  q wBd;b­*~+D7*Z/C,'^' +[xaQn;|*S(3 dWVZB+ T2M=g"uت1NXVkƩN/ź\1HYҦB0mAcTzM D|n KH!E_<_"!RTe3xbtݸ V=x.)A6Ow(Û?/<&$ d/Ȯ[Y;oqy`K 1&=`NS {MC~1KF*^!\P^AkA ၴ,7{vRN*WݾVVUœ+ }npj1ts>3|v_6Ft-9V\{R#z7Rs5?@sTUI;7&WPs*E$7syvs0o}<gHmI3G%ʼB t5|;#)*!{ﮛSnSXz < K\~_h*2xdd�`2.ل*WGw :" :ϏJBb`?{šuvZqS&�\`ӻkfXbK,Xrׁ-RY~Z}-hGd*S4^;7ap|Cۓf.d;dkjkE}`a:ၮ I3PCmeU*Ovj_ ~;u0)cnKr9wxWOx(<~}a րRE贆.{T b1s%OR#J7~0{5m?">O# U|0R#8krw+z qfPR!i6-B!n|)׻zyG)km3 {뼞qra^8h⹿@nKWopU�EU'Pj2:vB {U{XfYqs&Uĩf{v(/cMݒt w*MQۻbܶYC4ϔ6@L윰)DئPÔU�Ycͅmg~ȧxsygBȔ�. Q̵<n0j{Y0a?:ƕ'J9 .KTxuOq[tؐYգ'G2aHKl"${yzF_۸]~DdsWOJn+g EA7{yU#, >+'&Յ�p¹}o�UlMhwU}C*svR8>MY6ζpo8g6qx 6feһLmz'\_z1$XB,<`N?⸣1:VO:M;ʼnǼ~=ɗ[<6əoF%Sc>€V砄g/0"lwx> {BwCkԣ>S$Vh0)[r 6)Io;'wz6%` P}V`mֹZuMj򚺺/U%ôFWF=>?e}P3In9ZG s.]1)M\я%>+TEw/gF%cDTL Dj>_SqW!%-thsN`|FĿr{ Tg|U!`5=mQΚko;^D3V'^70<W^GfFVG�ڑp5Gs$R:1*t%ʐVu.qC|%+E|qLU :d,O[Kof̣=CP,!#FXqxz\<3B/͉3vqRٶ-D@w0TR miBRW0x6/#YX! %yrR/-ִh`T-DB",G6V~{­0ݺŵ^%(,}W;~W*GjY [4Q&MSHB d)m4_>Vgs` ):q|x詮D[L~ ê}A==|kelХߎ/fؼ;hm'D?R.hP/ZJ-+ C1.jad/S͡V)pQ\V&@@c2$ᚠVc\7^غMjd]ڱV(,Ukug# X1f?w}F3N<k EЦ~ї$yA4dB31T0c )dQ*eKt%5BLcv*ﴔdB67;=K7]V>)7J2Q0$ VV7͝C1V'P@KzEE˅8rUBʁ %w _QHw/Y!<_ܡڑ//.)ZxX]}ئ7c 9?'hD/] lV[R\EQ3� ǁ>υ<¾ݤ.a%,nf K1䮏Bu=u*�:>$w8.,gؓ2.D^a?ˎx^8&GÝ[~871hHv@"\6A=r|UIp/^k)y8S- �Ȝo{$C>]l }i3N�YJށ|01b:Ɲ=?{捴8_W>$.leGqlE-]_(+#ĐFC mpa[Y~I2F;8)tKތ9c 4:_ ZfY9:?g\" ^8<7YJf*l|ZyGaS/dZU_4^G3? GҦI-֐:E~<,]ZUnM}I*e2E0fg[MuosS40b (Ѝ,iÈ0-�C&ԝ(@ q` F�U\jtNMĦrcQ]�2 z0S:/߆0UXiB<7(Z=vĎYmP R( V=` GQDv.Kfq=A0?ׅVVO1Τ5E9u5.2tu\3LpM1c+˩:'o4l=.@d5`]ZZc7dmԷ7nbJi,+{ٵLq+~ϫkZYߴ6KN-ʞW~ɆC~ j2Ɉi)E! wԍ CiVȺ?sr_uw$n3L;@VE `r~jIn|w: ` iK{%J�ø�0E}1:5^{b�TUv:C}VRYJlÿ́sJQ OEjz{Zx"lj58PE93vZhi:l=B2ceO2P0%M^nRc!&np]h\Evg uw/F##C[eE$dPVRJ4UD &KTt3˲IRj!u踯 = d?i@bDB'8?*UͰNPvYX�8jMb.P܆&wJL), LKϙ݅Py5yQEr׆ijر) CT)nױzY۵754f?lDB7z4)龻B�pF<쵻[r Fr j# C75&U |=z4 )NKCM/c<~Q8eZS`[8 BWj`hfK^ďz¡a DLou JPA-UJ`+�aEȵKln@3ָ,kH` [Ԡf"AoeytExxFz_<F;3:Wb(zON{+x`rQ9X  }Me|Ծ7݋8sJ*|a<M,iS_z37 kS$LN va'fRƴā߹2pXz>G{xQr?/b1lloJwf ihkYiCOG̾˭pn#6Q�! Z/~j,ĶB`WwzM>k̰@EdpV崙Xk*Zw6DH0Cw A^ zd$/_9SL]9!ifP(/Ɖ\(ƺbf@qO6TzVXX:ÊPNPm }(ʢQ,aIX^P9 ٢63n$Pwz>#B$nd3*]|rB64ъ(A=i>`DzvWc<,A̩cɰײwOigKp70P}6% ,k_c`ڽwֈxc4mmiH(VPL ^8gmy]J5tr qi$}2ućzZ csϭZ^XԦ;Tat|Ddx/5cA#zw5 #ɮsty�tsvmLf5}|xűfǹ.\$ 7א: )"uugxpfb(:kN/IXhL5Gf1-z9Y Vs mpCUOlzg{Q9SN8;u|Y/*@όgJ!3v-#c Eʖi:e%X9dm?%�#(18xtWm&)ڿSq{Ue{ZF冰Lz`߉mw½"[T>{! /MZ[.$r�oS5I\dH>'î/c{NUt?%8&!ݼޓrMcܼY(h@:|ߐ's韉ZT-xCޟ)eqozBi.cynu:c1Wn_  ; hûmD,T[:S$;VK2J<qͱ8[7ϷAP$iST�o޾f@f.8/c}AK]2e|3h("" i&I4qQ%V̫Ė`.QBlmXDڰ&*⼓,c|*8FvdP5EmWKH=r˷MF}8{@ jrǝ'6 @QJ#9(f e',l} >01Aewt6|MASHHn[eLh~;6LpDO60%'@ <`G/eIg^%NdmF=.Hw,yQ8+q/;4P)ABt„]ڪhhADh][`b-Vϳ^ݏLdԹHP )P k^r{_FQ"nyStĞ_` FhnWAD31S XGQwn#]R5hqTmB(ڑv|Q@9͋D9\:yV[-,K .2ӁaȱUPOU$8|aæ񮔉*/P:!nI0CxY3J!^m(7Q׀ N&= #LX"%qUi MctiR{'NuQ&ŽyV)§xge/nnwMI``r+"l 7nKiJS#Q:Ej,.}ѧ*5HL 2{jMgsirHvaq[y!nnkslLtV6494ZZىFKR+rĥf.e:R|>K^h^>ѫ=9pv#5ԟq=Kg}<k ]|%1JԮ,3zVա?F<;&0Yɪ(!vxb{ +9U >%HWbb)'*!r W1+Ja]Sa"ȼ!C ~ 7+a p;x�ݴ�#/#]^Ax݇We*J!OG;B @scħs6><| uig%/d܊{1kݡ ( a#K^ҜF%jx(lG+CH#ɑ%7.νw&~w+ )w"Ww:'5cc c'_G&5$E # $q;5=ψ?g;r{eLɹv_r� vD,g Kxyq[+T`~EF?8=jq.ofh@wKx 7UI-12-} !CiSъW3Q"_"Z:D#~zܔ:FfO(ՏHI>ǽ0ĭmd42T)` Uv,hSZF``]8M�Qҟ V,reopl`}ėYs,5L*c6! huW\ ?'N'?LVXRW tQ4n8enϡaf8AcP AY5<0ON6c >]Y8 4Ԡ8B Um,0fP1 aYi;:A_2 ٮFӕXmZ6iR؅p?AO_2ŜLflڀëbI>AE,S̏0)юs'ZDaz肹ygͽ!dr*Z<ݥ !Ջ<kZ$YPcZ2l@g;D69!@pnu.\vx4BJO3E:�ߑ5jdŤ9͘?g8K)tv(RŐ LH%IwU 3oƫ`\JM.n|{5;;al:hH4ٺka%a3! J9@w͂3i9Qb99QlvOj+sɳC٬IfI v6"?(nED{\i趢}/aƊj!s.N4bdckp9HfJft;A ?>`qWnZ B5\|l5$9)2\ 6d9J?$i^(:UnPđ;l-L92  =&=a�OeDa\6R} N"but4VPI\w'"DV6ԽOaWY*sb"jVP}$dQ3$Qa-EÆf#J9<}T4|e ?gxkN. rqʩKa2K?JЯv*1hS.:��^wAܑ"[T¦Xː#dUyȏn9^sA#lk,Q\iRb7^/h36ƴX}6Cci{(0"^ɄfɊ`f"4/!F԰D1#fO;-^yF5k)ūfW;s a%+P#@$yݤ=Qs:jU `B8vݭ U;(U0%1�ɀA2D74Rx ZA"#'HA kByeYV 41-TIG -s<q'狼ygWjA9�+xZ fW2kHJ砙{\Bb\qHOδS%aݗ[!f�D-"93=s(Zy % "Nn$DI}FO�i'opGf;~MUDte5&(p_+aY]%v$Z0yވ%>BR2TpeS0j'g2 o\4-sX^MR[D̬yÓs\8gFҔo_u=S}c FfI6wZiXLMz)>Y9K&{NO"sݹۮ уJ0+`p/P<"@u֝7Ǜb[ dۦ-F1חMh/85HWbPc#c&MuM)6 LũC\iH)Hq+֧YcUT1M�5>; vh/5i1 b{KE<\c{>zWcQ<}  dp&9Pig=ܭ-EN=ìJS:|!X:pǨ^suڏ^bu}Ѹ1̫6Bv-!2I:Fz?){@)Ƌ<bb|Ebx>DZ[C7Ues`̣b[z `s6nfr/y9R,&%3=phdyA%<w_:nn6Nj2bx\-KxMYȵfGVX}|EdְX/p 'i&-6W)ΝH9+{}!6͙֮AUM~VCZ<F_}ss,yGZpBIκEG %n}¡Z#)'j{(L̂ʓ{(g'32­7W]Ibگx�~|d]<P+\ۖ׭km-̑vOtؑO 9a+E٣~Mvr]~;An WE;YX~]lXCэה)|a$ I?IԘyWPT&W2o NnG8>uufOG<"[\?5/U ?$ =]?zĉQHh:#+Un!+VcMO| L)[*&i){C CCy$oYqGQ0L2\j$#"m1ј9A bw"`pe!h=par;95O3E<ʟOrQSo  sH�}f6Og'JDL>%}DkG}fM?SfD. /"K=bP,%.V;+.8Zoz!']=KDԣR(' }\|^~Z9ut*p\'=<,*{x{ I(#!QfZ -=qxs͡ѡG,? gk<B^w/L<(I .Kz?C`; ;e:oR9ݢ+g<!Ϡ}(Ȃ7gFϟw4ESS]ocȋ< [/{lK?¶83PbqDR �Ie_Ejߍ--CPddDV"a12bSWE8B#EwoU=]`gE.#fb <Ggݨ3fF9P>ĆŷKRGAXok.5|QlV.1ݿAd lΩ#;Ja E&:@شu G0䧖ek]챲;f&t#6B TIAiu)`Ŗ\]*hVYOB\W8<<FEDL7FCs$bBuQ߿nJ1A>· EĄ8&:^b0u8ָJƀO80MtyuMg}& 8#Ǿ+X+ XIѽHBȀkx x!YJu Ѝލ˚!sFmp>L88%nW Dғ,@XULQbͪ4->)OY1gm�?4Ԏd]\[A{ +bd0(m_~EgibK%9Ag@i[RS1hTJYHnx4O]ߚ,mpI_Dc=M˟8Q/&#N5H߿QsT6UWYV<;Sb]UEOL,g(ȋ8 ~)(Hmնp&)e5 jH޹dV%$sciDm@R2Vl˾>ܠ˙"GaƞeZBmIiPc7aΥ7b�?Vi ARb$cQr6њi䥑q Θ;0u%x&`[01> p r[YyK?Xj178ߑ泦BߖzLwAL5jf5"mC:!)> �6!FJ'q@M¦Ϙo$Y$) v4#%eq"u08[H\OgeɐRe_}Oxf5M~Llu./)]Ek\h)Vo uOLOɔBTVvJ$'HOÕ:3Qm "m=7+.Q=S08ȓ{w˒幨q}.؞,o;F̾4eN&TՖ!ifv; _pN6@"dre 7->_+hnZz22WJB0_=l5)6j&3]+ m soEHb-R9}2lLt~ssR~RYy"<P^nRp[d:3zbTm {=ea]%$EDe>h^io^QXLkȂv|nuNv|s%t"ijU=`3:< Jhn<1Z=8Adt!gbA#_Jpݰ8Eb[ADW ؀Y7MϪY:.1dRnm|YF ILK|׎V_JenJحIԏ/YPg &+&G܏ ><W0>śl?2Dkl1: ? q1&4!c{fDY)G\斅r\GI̚[!9RDX6DFλy~/Qmm"55ڻmU&7Ȋ&a$.OwXvUAOS!A\KR@LgG(xiʽmϋ8{!mݶX_/%´NY {БSB55MКgMO2 UhˀN ?i{Dw^47M"࠱qR: ~+"Z6 |"0&ҙ;L 鳋g Z1 ?y7ʋƅpuO1Q~ЎcZU_Lnw}w�NBYVqCgW6GfŢHYԉ9;2Cgq.`%{H9,[fTM@eSt@ڲ<T/~]err.]=9p/kϞlNCӢ %G>ht9ZMi?'V/7;Ӳ_L^R|P[EỏP*3('w';8mq"aU|D3U~뙂Jl^W#L,'|^n5_,k_'AP@[YOV>c3;s jC֫L ̂'|3]h>wb[Re/RT|T]A;�*>-涷s-Q9@W"OXBfH'3qEvGcf!Xtc,s yE:QJ}Z 4ɍ;Qx֗&YnB,W >c~jɊ3hh�o.ERk$r\n `";bX7 2fSnuu ԂU+jQ_ )Z1뎨yGJ jG~z=ed֞ˡId]ŵm!547^�qy+8v]8K]L�J'^Bܢ|Dăr.Ā Shf5kctH2 +,2wCoduab1*}Z zTMhF4Q>kګQn ya] ١{`Q ƅ{]]ѭ1:a6߁ݫksH{9]KB iN[r;_^L%r%ZgU>5omGPqQ/OLC<Y2\@3GY1\b94KQ�q5$fM{Y|#(NuJa']>!!MN~:|'2_ljV Jïsgg. �agoi�+^ԯm#$x!?F/E,I%϶ac/$P*F@5sWZN ")N:>%Ƴ՝02SS9GUIVyWHeeC@7d{h2̆<Ȟ{Ij$7N4Y~hN(;qxY©0KK4׷ @|zJ+Qb#x `Ɇܼ`c_<ҿ}\ƧCyбtԟk;L̘?�7}"B|"HBBJC endstream endobj 3 0 obj <</Type/FontDescriptor/Ascent 729/CapHeight 729/Descent -218/FontBBox[-174 -285 1001 953]/FontName/NimbusSanL-Regu/ItalicAngle 0/StemV 80/FontFile 2 0 R/Flags 32>> endobj 1 0 obj <</Type/Font/Subtype/Type1/BaseFont/NimbusSanL-Regu/Encoding/WinAnsiEncoding/FirstChar 32/LastChar 116/Widths[278 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 556 556 556 556 556 556 556 556 556 556 0 0 0 0 0 0 0 667 0 722 0 667 611 0 0 278 0 0 0 0 722 0 0 0 0 667 611 722 0 0 667 0 0 0 0 0 0 0 0 556 0 500 556 556 278 556 556 222 0 0 222 0 556 556 0 0 333 500 278]/FontDescriptor 3 0 R>> endobj 4 0 obj <</Producer(pdfTeX-1.40.17; modified using iText 5.5.9 2000-2015 iText Group NV \(AGPL-version\))/Creator(TeX)/CreationDate(D:20170131183532-05'00')/ModDate(D:20170201191245Z)/Trapped/False/PTEX.Fullbanner(This is pdfTeX, Version 3.14159265-2.6-1.40.17 \(TeX Live 2016\) kpathsea version 6.2.2)>> endobj 5 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 6 0 obj <</Length 152/Filter/FlateDecode>>stream xe @DSjyl*l҆xĽE,ga jz4ňc A(Z]rn^z-FZߩTv2ڤ?Ζ ؁۞!'33:{CF~2h#(OPћF '�uLCHkz4: endstream endobj 7 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 8 0 obj <</Length 153/Filter/FlateDecode>>stream xm @D)\SDBNH^roC,fyQC IB"`nڠf<uy978w 8YB3 wQzܩ4TrtOt=ŏT`1i4 endstream endobj 9 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 10 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.ʚy0x{V?-ѡh8|3C IB"`m]jGʖצ[[{x "$ymTgto_γ[/>yaxRkK!0٘4U endstream endobj 11 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 12 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.- ^OKt(93ߠgAS!ڶV66ܢgmXqk}JSۍse(]F|F {v=CNvbtf d򌠴B7N& }Ʉ%47 endstream endobj 13 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 14 0 obj <</Length 151/Filter/FlateDecode>>stream xe @})ܙR%rB7IKP)oP3`H! V4tmc5 V׺Y:.Vܩ4Tvumv_%ˈQ\Ϟ]^ķp7:d<#(U_q83!844 endstream endobj 15 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 16 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.y0x{V?-ѡh8|3C):C<h:fUQ *͗צ[ 8EBg$6w տ3*כRO},'OpO*UbS`bZ 4O endstream endobj 17 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 18 0 obj <</Length 151/Filter/FlateDecode>>stream xe @})$T`#6$P)o3(FHdPvD5֗s`zoĮ՝jEo7)ͮǙ9ٱbq237^\,BEoN)Ҽ P44F endstream endobj 19 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 20 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.j- ^OKt(9̠gAS(8F ImU (Nצ[z8 "$yTigTo_γ[/>z~pR)l< iM/Q4C endstream endobj 21 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 22 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.ʪy0x{V?-ѡh8|3C IB"`mB ci'R2Tsxˁ}Y$tp?zdp<͝*CΊMYO},'Op(:ŦdcZ %4= endstream endobj 23 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 24 0 obj <</Length 152/Filter/FlateDecode>>stream xm @})ѻR%rB7IKPpfAE! Em V׺Y:.Vܩ4T\mvٟ,#>+Gq={vCNvbz d򌠴b7n14Tԇk4 endstream endobj 25 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 26 0 obj <</Length 152/Filter/FlateDecode>>stream xeO @S̱.Xa%7*\7l~ZCq jz4ňc mk3Mu9ϳ[S&EX-#>#Gq{v=CNvbtf~t2<#(S_4EꙆP4@ endstream endobj 27 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 28 0 obj <</Length 152/Filter/FlateDecode>>stream xm1 0+nE:TpJ#t-<L1wq*zF )$ `nXZ\rn&\;;KsP[ET6[;xh ,( Qb%oP)LS"RiE/4 endstream endobj 29 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 30 0 obj <</Length 151/Filter/FlateDecode>>stream xeA @W̱.y0x{V?-ѡh8|3C)EL<h:fUeĵ.G=C<NnbtfY~2u<͝*CŦ,>yu'o8X'w)0 1}4I endstream endobj 31 0 obj <</Length 10/Filter/FlateDecode>>stream x+���| endstream endobj 32 0 obj <</Length 152/Filter/FlateDecode>>stream xmA @W̱. K nUV?-ѡ0 *zF,)$ &E>h82 T(cMP/ځaL28Nꗣ3P̳t]śZCq*01fe4 endstream endobj 33 0 obj <</Font<</F38 34 0 R/F39 35 0 R/F22 36 0 R/F40 37 0 R/F14 38 0 R>>/ProcSet[/PDF/Text]>> endobj 39 0 obj <</Type/Page/Contents[11 0 R 40 0 R 12 0 R]/Resources<</Font<</F38 34 0 R/F39 35 0 R/F22 36 0 R/F40 37 0 R/F14 38 0 R/Xi0 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 40 0 obj <</Length 4383/Filter/FlateDecode>>stream xڍ:vFw/݆1[שU^=Kyu�j'1U5{f6}}deCcyhME<7>ps?qaZ(Z:?nLlNkit<Nqx߰|T1-0WfLیP tQqp>z?nRA{ & 4R8UvGSʻ05f]ݮV&ɢ�6?<sIuR6MYt> \gQ<Se*LjwTHe6zev7lpcanXELr}N7qkӛ?XDP%_ZocQ*0\ت$ N6[mCxqro&p?C/ -Ў黢jHb0H#@ʡO xjcMuŀBPN0$h OeTrOxlc8{ƧS-Mн!/xaQ8I^JK4Ǔ۶�8$Nکr,Nv s3fF۟�| :D#J8c5/^SQ#TbvɔDiF@ǩ~mn\[bTsXؤy ;=4_ qOй(R癞W k4;}derQ}�hF}76f U3o=`[ /al<-VvyBcI{ZM%N;b<]K'�C1r[q!j@j)Nmұs[=͂?C=֭fԟZ^fʸge<TArls~`M hctXSKTDM#mg=3C[ DՌ%/\Xu.Qt쀓qS2vUo}r�jM] q.,,mhtB8!�ׂ`drҩ8a7,>-@<- PfFQqawz,s*qb\" 1KvX>E{|P%aT m 4ra)a|…s nk5/khBfUZcNbO2^} Ԩr )yIa įImDy<0Px {BJÍ1ij.? `QAĠ$$h\sn}<\OE4  `|pxztȽt(&.U&X@ĞY]Bj*Và Teq>4?`_}I B we7XFyXe}_!͂1ͦXRfX@ ܑd/ܼP97?\Y5yV4-Pߥo;[i? r�=bʡ.FqvbPڕ= *"v gV,gTn/"+=((UN<P8#. .xH@"O V {(;m;3ޔyplʡ.eƟ1WgeI`XECNAa+3UNvC,ϬS#3@>�=Q@bZS~D!6:똑} 7N t�cp{*ƑɡQ.G7s}[K/jXUG<i_n^REǠ$ 3( UG*j q9rClj(?b`w H"AܰI$A?-)n[h/h_'Yɂ*J\xJv{yz}όj;9*Z$8&X8}'TY$0QP502'fH@$~ăC /D^EU,_3d'*#1 $B%T\)|W,8zV1مssLiWRЊ Y`#Y <Yb r.Cvc%?9A2vJ|Kru;?~:vbbK}-? WtMwШ ;vɮ$Wn_߿a;ݯ*@}0G//]~Pl"MAsc?(hP�o`o##GͺnoOH)~ Xpc= @25w \eou}1]{f- 1:(ԣ"M\z*?~" ZKZ4W3D 1o$l`gBio3] ܲX[1a@M<Q\F/9@cffCTrOŪ^V.A ᭁ.A}=/dKji`3~aFp9-hB$T.@tm9|P z$lQ�M5WDtr�vD1tW]QH?-ªY6.JDXwx) O}{o b89,e]m-v9'paЇ8q&6X~\,cR<& ^c.b&fq,R/4gcԐ7<4{.(Uy7y)Peey>I V<aNC;呿dx>肪Aeʮ( S߱u܀7,�q#T2Hl4\ x7LWZ-$(*O%+.kvI>ClE$/i|t[@׮)dWNwdEjygG]39 WX'+,\ JI~1@>˜b>3l<ve!"FaƍbOCHFItL⬯9#S^{Sȉ�xjeRT%?5pqV`SX:wm3z#za;[d}{q%k+u1'`"q;a{@G~.&<^VΩKʦa1* di|1#JC/#-⊵MӊSjpE&DtKr&83SE'b~~\3d4?{e ҒٸpDPl%C#<4y%ŅđD-7IbW`x]&G hvE+'x¨:U>T›ɂ)X2vǭƎt fQՙKc8A0f| {MsboafukUFY?O8S$cjA֘aSnې+a7`#G s>#\bRсs:F-b ,r>)X89uaΉfο}%8o3)XAϜG 89`D&%sH/['->޴Fڒ-�Bw :LE*UxjUdP"oR䚐MN@WƘ8ˌa`K莅gJK*;!y3MWۥM,$;n%$Ғ* @L |�-,^}ͰVvF:*(4Pem𳤄o%S's:vLFkEoi'3tl1.jӌ]<17 YlOEƮ|'XH6bg nX=+P)sZD"\Bq@ ŹFְ0"(Rs)O:\[^:oBe@c.E=�tmB]ws@ M퇦/YIkޗ>/d^<x%˛Y'<}?˛Yj/DAfŅ{Dd#}e Oē+Sή_!l�ܗ\?ҫ(x[P L;7Xzb 1AN4`7K8Z.,(Ӑ3nǐ8 PcgjN*`HU# P\], Noj3~pǔٱM/!> %V6>~TJ3M_Íٽ˲8/ffוQ{?g?.v(7~vϒ.R#_WN endstream endobj 34 0 obj <</Type/Font/Subtype/Type1/BaseFont/DXHQIH+NimbusRomNo9L-Medi/FontDescriptor 42 0 R/FirstChar 2/LastChar 122/Widths 43 0 R/Encoding 44 0 R>> endobj 35 0 obj <</Type/Font/Subtype/Type1/BaseFont/NCLNVQ+NimbusRomNo9L-Regu/FontDescriptor 45 0 R/FirstChar 2/LastChar 151/Widths 46 0 R/Encoding 44 0 R>> endobj 36 0 obj <</Type/Font/Subtype/Type1/BaseFont/PXOHER+CMR8/FontDescriptor 47 0 R/FirstChar 49/LastChar 50/Widths 48 0 R>> endobj 37 0 obj <</Type/Font/Subtype/Type1/BaseFont/SUQKZJ+NimbusRomNo9L-ReguItal/FontDescriptor 49 0 R/FirstChar 2/LastChar 151/Widths 50 0 R/Encoding 44 0 R>> endobj 38 0 obj <</Type/Font/Subtype/Type1/BaseFont/DVCMRV+CMSY10/FontDescriptor 51 0 R/FirstChar 2/LastChar 2/Widths 52 0 R>> endobj 41 0 obj <</Type/Pages/Count 6/Parent 53 0 R/Kids[39 0 R 54 0 R 55 0 R 56 0 R 57 0 R 58 0 R]>> endobj 59 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/fuse.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 60 0 R/BBox[0 0 390 151]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 61 0 R>>/Font<</R10 62 0 R/R8 63 0 R>>>>/Length 1096/Filter/FlateDecode>>stream xWKo9 ϯm3qlv@=8$vkg;i$H]ɏDQ`r\}pFzT6U;~4d>95D�Q8Μ5AsV8kgX0&5hk gۨMmuc!HmgU0FF-YRz2_t#ɘѠq"oyr_W>\#" L͚kX]^:+J饦k�,+KM6%P $:[pPb룘yJ(уn>&D�\N)#] IqC魂*5 _}~T8$|8E #$'|<rx"1B u trk:5%_5J1*L,RRC(ۉΫ!BYf 2"4ܦDAeBʡy6cR*iNV~ 8sΑB:(пnP+&){:<tӉ+qp䔚͐#z/_ehb8Sz{FqY؛9lpy6&>H1h_߾ia`[CsYhg6OikhƂR BRIޏ(wVtJR(nFYSNCYSqWKL׿uع/>:tJC:u1\]slw <8<γur竤@Qp:!ƙ3@l%ǜ !o߶q= M8{n7۾=<<;Zف H s<?)ځ%wNe{8+�O'"`Y }5M|z7HJY\r\'d @!_78>qtًN{&[ID3W=Ͽ*ÓJXŖZ߯nq++*SsCqʪ~93ҪTY&eug_f~tpJ endstream endobj 64 0 obj <</Font<</F39 35 0 R/F38 34 0 R/F40 37 0 R/F57 65 0 R>>/XObject<</Im1 59 0 R>>/ProcSet[/PDF/Text]>> endobj 54 0 obj <</Type/Page/Contents[21 0 R 66 0 R 22 0 R]/Resources<</Font<</F39 35 0 R/F38 34 0 R/F40 37 0 R/F57 65 0 R/Xi1 1 0 R>>/XObject<</Im1 59 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 66 0 obj <</Length 5400/Filter/FlateDecode>>stream xڭ<rF VÁL3`=Om%>%7"! kmן�rHQKU0wv~zW~8YLgW73-gZDarGpR?ug8uQի{(<nJnfUrwoa6g" >W˲Y?\0O^qU=ND|9MɩWMm55?lU}MUoFWu_uZў(V_U}m.諦Fx,ؔMӮzQrRxԦl[gAy&Xmil7�XvS`ss{ <#N,IW Uw \q; |\)V]åYvp_T+3csO?Sh\o*J/%?" ltVצ9EW-pW`4b/YsǢlD@*KQEk-:r;BɡZG:�B @oew 쌠AW�%UEUob- I _8tmڲXr϶+aq2R}F 8X4K*;4E-e[!=tffZ}8N xd t)B>! G#=w)6[5ɝe|d6B`\SdX/d(AQ]E{͋f9,@l3/|a*s2<fhjOj;* `uS􋻲ʗjqg%? 3,Vۥof �%)"푲�o8X^/Ra-dz4 0GFN "ZD@Q۪'2f#Q9 ٰGj\F7enAF1, /e[bY3ɎJ3,uC,2@TtB:*, ׂ%Ff|`F @Pw CEQ3j%bM3hD6v g LgYtX?ϳ02NH]Ûjv;奢YWP尸V4G;?C-:0 c*p<=rA,m86 c %eVat@YB$Uzl?\QMf�8dE|4&&VQQOxU [cH \`:rK.rs4,5H3#q/Ԝ` !.<L�d�C.^Df:P,@Uk u2ײɌ7mMBeȥ+M6(8C&(?@"}7RQlG̓sz[5[%ݦ\T`qqeJ =w,IQ$`^[64,rۺekeM\.հJP;VDY>6p*ؤa@tG:h(H?,?D*vY|yE4jvh*<{ɐ#m2L?O(k3xC6}쿔Pp6:x, aDs38e,DLNNb۲F39{wўCsRhw\4_p϶$Ad0J8yfG>G/~v7Q(1HA\BÆ26V D= n|;W34~wH+Ԩ(�pGu:q"ø! 4G>n:2FbXLD:g:aY fk`K0AB; ZAsm({-01q!H'g5RkWM[`|7Dq- K{.1@tTTam4ٶq_i{w4 ^Pհ�ڲVRccsw0fkl'9n쵐C4t2mv* s_4ETgR˽W Je :٤b$psid0žQQi`iRavs�;1ÈͬI`)BEIE4 m9G+hĀ{& 5�'y9{D)X&Ca�X|2'' G"u<ڋmXCT;Dq'cEqsuo(jW0ȱow3J2 M^A"c|03d̯ 8!t�͹yI,1vn`5\ՠqhd>0JƷD 6OGֻYlHlcfBzlBпQ!8Q%sN}gxnx753u@Ǡ+|8(]WdhdG$W9IjL,>}$VDCCcy1o@*D{cP`nףB/^lj†( ܁h5ӱT7  R^퇮]&; pUt.ȢȆ0$ve1 0v^ N}yEݦ-M| Q)EHbiq_p_lm 偰]m.m(k  6)k&fa|e>*n]0q-xM"Y/Dzw-ZJسMN4L+L: :4WB\4x`Y4覥` w|�ӗO`$Cm4]C ǀY?vd<|BUe3&ҮFy>:YDinse"=,Pb쏿`0qgRh]>qϒHM#8Bnљ1ʤSh(~XPәgi.J&Ct+6ruUpay$D CLV߂'飡{<( Yhff4hIs=_?n qrro&zLߣ~(=�sZfY >P_wp R["(wt*8}2H ŗE_&ĭx7/a蝏(01�?bpL&i ?q~h!#Ջ8&K}`\B @(cH\ (IsgPRA@&vYgqFR_H> %a;0/r)Z`م#~|"O>y+zi�ly{,1 xv9 ǟB!mS5AK^,3͸I@37~Vǘ L(}@pțpiV8ɎFX5imaXw-YQ;ҷF(9Y3|p V' $�{yD>ZiTAAPH}7جs䫉6('£L] rQ6>~4&;Go1P-P.A%ƍmer:m?y?AWIyy(;.)>!G|`$;eQF 9Ȉ@y/ku�O^ڃyw)V/ O!'912,kj[K?U1HѠ`鈎P*<yNOݎv\i$@DB.o?c@c'`|S-Fw* Ka zUwO.ӝ6~w,]XI$d1@xq~x<vi"1B}q*Ld7z8A�7=&jE`~(Q[y\^9&Q}_<Μ21uDΦv]w;{ݾKM0JbW+ uˌ1v1t@<‘rq@<$̐$O|c;Cy }cx><RǾ)yq0to_*8S] wjM={$ p@"߄a MƬx[uɻPmo#;΅b bSޠd>0ƿ| Sq> 0g8ǽ$=u2ܘX@g G rX&PkA3a{g"F4Ԡp ۿ e&E':e`fJ)69^|~??yJ.oYqB$8qU5-"|+7!IޏarTNN_wtӱj9 Kgz?` LcSP05P4fRnG0>p!c}l#N3]0O$"&zRDs8d$%tcIǢユ?LMf)h|1c~x8X8w1"lmlnfԖ]ڬiҧ#w&�nXkƤF={Ljd,E0V+Gw?WKp*tdzrj:7Љ4e^U-r]}΃n5M.j_cI+f&w| D=$P3Nc4tC8|ƌ_jͣzu$⹋Ҳlu|q~("^"QC:T Rs>fϵf `.]X췀Q;FNjs`aBMu"t*T]+,$^ݕh&ݔ}Ia1_mf|?"L#6LRL1{ZK ;ٮJC'KyF7`/c?y"*cx IrzPmMJ1jHGq͹0 ÷TX҂2,]s^X2]F@_nL AK ?3'fjw&%ouWÛZRnО xvJa>~K);;fah{M_||ld0^## 8"@)䓻&[.{}L@Tu�g0JϘ&y|^ܱ<:ZxKjnۢޮ`؁:VKW]"]oxk%R {y#LHz_m(P"9GoXLxm;lx! $q@e5mA*Uᩤsa@Mleqi.<-Xښ)7ANq92dA-'Jq2 YQؘ)4CUMlKf.'O7B endstream endobj 65 0 obj <</Type/Font/Subtype/Type1/BaseFont/LEWTPW+NimbusMonL-Regu/FontDescriptor 67 0 R/FirstChar 40/LastChar 121/Widths 68 0 R/Encoding 44 0 R>> endobj 60 0 obj <</Producer(GPL Ghostscript 9.19)/CreationDate(D:20170131180939-05'00')/ModDate(D:20170131180939-05'00')/Title(figures/fuse.fig)/Creator(fig2dev Version 3.2 Patchlevel 5d)/Author(Bharath@Bharaths-MacBook-Pro.local \(Bharath Kumar Reddy\))>> endobj 61 0 obj <</Type/ExtGState/OPM 1>> endobj 62 0 obj <</BaseFont/ZRCVAK+Times-Roman/FontDescriptor 69 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 0 0 0 0 0 564 0 278 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 722 0 0 0 611 556 0 0 0 0 722 0 0 0 722 0 722 0 556 0 722 722 0 0 0 0 0 0 0 0 0 0 444 500 444 500 444 333 0 500 278 0 500 278 778 500 500 500 0 333 389 278 500 500 0 0 500]/Encoding 70 0 R/Subtype/Type1>> endobj 63 0 obj <</BaseFont/THIFVF+Times-Bold/FontDescriptor 71 0 R/Type/Font/FirstChar 75/LastChar 115/Widths[778 0 0 0 0 0 0 0 0 0 722 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 444 0 0 0 0 0 0 278 0 556 0 0 0 444 389]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 69 0 obj <</Type/FontDescriptor/FontName/ZRCVAK+Times-Roman/FontBBox[0 -218 775 683]/Flags 4/Ascent 683/CapHeight 683/Descent -218/ItalicAngle 0/StemV 116/MissingWidth 250/CharSet(/A/E/F/K/O/Q/S/U/V/a/b/c/d/e/f/h/i/k/l/m/minus/n/o/p/r/s/slash/space/t/u/v/y)/FontFile3 72 0 R>> endobj 70 0 obj <</Type/Encoding/BaseEncoding/WinAnsiEncoding/Differences[45/minus]>> endobj 71 0 obj <</Type/FontDescriptor/FontName/THIFVF+Times-Bold/FontBBox[0 -19 761 662]/Flags 131104/Ascent 662/CapHeight 662/Descent -19/ItalicAngle 0/StemV 114/MissingWidth 250/XHeight 463/CharSet(/K/U/e/l/n/r/s)/FontFile3 73 0 R>> endobj 72 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 3413>>stream x}WyTS׺?1pV(y؄j:lNjq" (�!s S28V^kK]۷am;뮻zk$$g~P (PPS>8K 00/p͵DA\p~y(> ή⨒Қ|qˉqɿ[vݿlٴykDv?W"vT"V_rJJsDٕO78PWYUW(%*)SB\Y]sxC'oA*:D5TH%Q;:*JvS{&jz^SۨT/,š^dBP9} NU fр6 ܀Qsᖅ8zoYܳx6h[A̒g`s\.Eg.a8gVMhZ%K%8W]V^ږR[RQ.�%+Ð3pŻgKqN5Z`򽮖3^.zWHr*YZ#nfU.B\~ȼH4z!7d|)h2;N4!1CZ 5}̖>Nw<lZ" BPY›@<}JX2% }:q뮀})/|#,|,JyXN4! B%.h8^={J|fEBК R ؍6dGMsX8PZ oobPB:``#> +_('+ ^i60o�VUk$r1z]ڦd.K8PQm=t#^8'Z(8 QJET0A0~Rbk]M2:ĕ;zɮ8mrnɾ3U,QeT?ΔTvc =vY>YQ|B2 ߔ*"YȫB6/ 4:Vsg:e_-5*jWpgL jZ[k!-ҘVqZqQcߤ U@E%){󇞈29,&6'jwn. qIG{' nArq [!ЛuCۊ DRuV"i4z!Lo8zPYY&D\yGB<3{ i,_4S}a)آA�*)"U1OdL] c:$V) gwQx7=Kr 5 IK*l׺]GN93t94/ r!̤5כwYgjͨ Zx&NmR7̈́u\9GT׻ƅ8)]w#bݧ<`fd {El/;SY(@,[17GNM&Y۫GϢS-%k*֩TҺ*KΎ -xc|/O/d2ލT* )dy5 g}Q %Ω2><޶ӧk;.A|P֋ :F*�Fc߈psAOy2?|:sot,yia%_T(MF !8\%oEp�˚SIÓ[ԹEAML<݉f,%~Yje mcO�nҺ\XҳtIuRj4X5}mHd"yyD[E`DR^8X>vg ^uEȶu^.yeVM=yqn\쑯U6U6ڥTG=R󲅩%n. ]}ןzo}Ǹ-4OiТBcmK56llBkd:!HHVgb!Ya445Sd Wi2 _gpř/.8IS,b0[v^h { I{}GN%Tt&pvn 8sS'|xwD%[Uqby\v'J޻rp:ǿe= /g/'˜Oֲ)@5#*V'זK$te L0=$y߬bgq0d}naV]V֢9E{{/Ҍ2aYF] y+ ߩQ~DNtOp0`#{_q"#;9qaC߳ J6d/ D^ gy?L^VD#lwsӼ14!9Wt9ݻ@hձ#2Oz'O"cG&.j߆9fVG27/Ѯ>8}926tUy]_/Mь5Ο`VWW[uN=-89फ़=Bkq:|֐m|ev-7< dcBUnhG+]}00W*:+:;ŝ"V*|xB>?/ ]ijꩢCjiI-fzƕ\2,wOɱ+UzT thLw C#u~?JM W\GW pŲ[7x8_rmKMWl/=1�GYgn7kZZ@""7i.Ia9&G`l@JT[T(/8L9*Q!Ft|0$56V?-qp:ΝK{G]mz .ȊKӣv(t[}=`p|ic]'q$(9t']R,Rv1fp2?p9ljNcX^,ˀ!Q-a~YJc Y}熑™N#5Yjd!vHEvJۂ an3g:Mz$lx?ܶMeNdjvl=]c'!8ޙ4x )lcIn<Ҳ7AUF| 7ގM-;(VЛ2Pr[4ϞR$y@ΞZi,]H>^HM|CӃUIfs XOdA_N .zh׋{, Y=j8P�I endstream endobj 73 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 937>>stream xeR]LSg>e|ߴ%M$f&H76 ]QdU TN[)?вwzZʿH7:݈L3f[̖xt;lYv}'y_RP4Mo4[L\6f}GJK m%j3!>&>.L)iOb[nwh߫:rlgA&ޫ=4q:v\X[du-9hՖ>M5f2UUN*]T SPvAeQv]B* 1u,05U1[qRuA ?1Ϫ}Xnh<$zy#$ `kAw>B3&1i#^uzd9Hځ8\VˢB[7ӽQ_{fițDp691wzykD�_u3˟c;9ihzsS_5 :]nGޠ+ƚC{J.zi̻c#XN^~ޢi$ٱHEЅ)J$ܺ=bBn10=!΍w&4>2Q1(3AQg\`=>zI= E2ÁǾp␫DzL[  3mWVvyt$k q%/:^ԙ<n@:2z{aF]s4"^jKݷ{f/s Mv}3O^|GbZFW$eCmB_ mB%e4& \D :U@ޭԑw ^GOŐT8aI,~=|^@ 7{!Srw#"8ҏ=N:/Eq7`F%c٘B AZGPXPmU Ec%L endstream endobj 74 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/queues.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 75 0 R/BBox[0 0 825 521]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 76 0 R>>/Font<</R10 77 0 R/R12 78 0 R/R8 79 0 R>>>>/Length 2744/Filter/FlateDecode>>stream xZKsWɕqrJ$]9|PQkJ H9)|=]`eGK|hs|P{^\~P#!w7 1GƏ2qJH[VZ[\1 )`Ka,3Rb9 A'$N> o*v`͙%[BzAES3z_a^lX\&$)V0p\nKgmNyrz᱒B1'L1"K QۘtD�I*hR*Tmڐւ(ʈ +<"/>("`PDR9Ʃʱ^`QkY’ c|:G/K*o<$Q6ZW"Q>h[!ST^82�k'4Ȁ*i%[$bAT,Y,`J3?%u^O_s9{ԇaG C*3=rpT=qE!я8YYrd(a*Z"SO'M&jRUA*APTTM"Su?!_9$nӒ]O5aMa1Q&i"< bm|"fmEHp U BL1ZۋY"T? 7.\r6u!󙘾Bl@x&rWmuαI א:BmܫpSet[`ѣƞ?g󁆹 EtD JTM/ )D=QA:"*P#b'V �phzLLJNo. UbSU^-=jT̫`ЎWAx5*B՜Tj mTE*5Vy|Ty]8y3{tŘΌsz2125$|S!Aٜlʓ졬(Z?w"dNؖH'w\E_K˪{\L]=1Ū"<W4š<8Ey:77!zLӅ DOB5->ԑ3zRX [1Hm^\z x b6e`^^ƥ.̊u+kv,xL/Cv-,c2Fx >tp ,h4=Ie{<<mHT"1?\C:2@aZ Έҙ8/Ar 1҂$ƕ;!}i|'`T.Lh1kHSy(UpiVU ˊftN_wCF*gXy&cd <cpHMʑzRw"%5-ʪA~SLlCt[pwt37.hʖtz^:]R {ؖ~|(a4ʔ9  C8 o2Bl+.eh~< yaZZff$"BzPƚL@W Kz]GAX!̒-c|W)A%OA0+rM}DK?tzʙkE0cR74zJ+}ϊAY_U<I 2yEՁ*WE~!ġ+B*V`*4qMXS +~ۇ<g;LFHzYNՊP(4D }&Oj¨}j r@gwMX}_O\=͵qi嘕X"٥+&,\V;œBZwkGsOr4s^\LaWO|8`J"8W8>`񽡐(a;ܡ/.-(q-٥2DqcNt_cz&݇oSI{Fj wK Ew}. 6|{VL3㻏{" lKμ5=;7~xy?>$8'%˗k+%#(//Y6Jǎh}=M8ڻ@6C#QˬϽqzSq>`G"Ga|LOA҈mh;YGu7!z93&fw,ϱ~}]17uR#uX٤<vm'76k^{uf˂I_}ڠ^ Wt>ono;%*w}{߾;t=f?X)x~0g>ݗ~|]´ U_de}u9̠\@wߦ/ۯQK|if%>FE6ȇ(>O-%D-<D-Q!!.C5-d- n,Hu F:ݹbزaEυVbeC$y!'SPI%MMfr'_6.];fRK4\ҬASo"SPZ5y"@TN@h${4̹m:@󎶸adGw lһ 5ˏkmZ7|&O F/$]CjVQYCS禷:/e-'*F:{yjŎ (2gb^~OS_}iW3UW*~y\-Pu~ S*& endstream endobj 80 0 obj <</Font<</F39 35 0 R/F57 65 0 R/F40 37 0 R/F38 34 0 R>>/XObject<</Im2 74 0 R>>/ProcSet[/PDF/Text]>> endobj 55 0 obj <</Type/Page/Contents[13 0 R 81 0 R 14 0 R]/Resources<</Font<</F39 35 0 R/F57 65 0 R/F40 37 0 R/F38 34 0 R/Xi2 1 0 R>>/XObject<</Im2 74 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 81 0 obj <</Length 5191/Filter/FlateDecode>>stream x[IsFW0bS`A KIfݶEl@B؜_?oKlLR#RHd&r}⫿L~GyT_*b^~ޕMU]]]JwK5=M=$=ĥXqo*U}[5׿,eT;sw^kKC5 uKr7R~Υ$laF2Tuu{w6ޠv1L6^ ?2np(S'=ǎյr#=EY_:qWb`ri4Xі(IDNvo#D5Sm`NEv~D`>YqrO^(,3Įh$`JǺ |PIMgjW buڇ:K*znBxfr+sw%]ӕvg+1Y+?q~CoȨCtUv00]uۢ<_Zy~/9#2�櫲RT<|<0C e;rR9%Nx`/:+Ǭv�+>Xi䙵vAɍtW2W` D''u(,IȃdUkHM @8e&=BPEYM] /#ZC-/ˮ>DQZEdm*}e~:@/оRN#IEI]ݎ$[I$r"=XLQW^tƣJAmxq!Ltebtb<n/saDZ/ڡ)FgPUT'"!kSZ}r#q7D߇?l$+j@ha+]WFifZ$ju+z9s=k�4ԒVx亮2/԰F:OtAE~Re[ SJ]b=A|O֏g/v'ŞB@st<׮HA&-NiICBMI$3f$j'/1%FOZ=c`;{HX 1&H%?,7'dw#3֡*냐Z+YܪƲj8w7$(ts*3оB@�d--~_!WJYP]LGWϿWhD&ϮJGy /.sp(̷%g߾rK" tg(:~"WbK#dlx#(3jX*D4J>SfIciC`8 VpJ�(NgX%<Q9ʣ)*GHB+'/]HgӉ8T>ܯ5^X�gPݥԝv6%ur^.a5*}ցEAv]ZMB{ӇXac<Є**#\,.hcWRA"/90f[O`C瀣259%<ă$*#tpD,Kx0kmMXR ^) sf-9MLXѬ1 U.-&DvU~X `v+sB%*;2*N+;ƮL+u> -w�= Ӎq/..:qVٟ u ~ʾ>E4X5a㙖玗PŠ0A E?rMxȎygRLwc1>Jiji$-BF(.J5,|l:x<x|+c�. =3? Y6 'W(\M-v^<Ry^T+溇oAHkא(g7=^t!o^@ ;< ] Ԗl.P۠`*Af7'!:|#T=X C`GPV_ $J_%N0< >@Wrg`dru! *Tz ȥrr_5]?H-687ЖqF)ޟl b'ҸQ@Lg87/Y Y*L}nW>1U*TSDXӖ[ O*p1\XMb.f`%ǞZ tH17z| XK}M*,;"\P�ýш y[M$pZPɷ|xUuՠGxl𢙠F=w/- ,Mm|!{sjIj5b,xx/P5Tzm3fo55z<itdϼY>9l(Ґ3Zb)˽i~܋waAP,P ٰ# OL;GR̸o܈t* NkGf�&ɃqK3/Ue`׃2oQ'45rG !Lb!"l4,LZC_"<Tqo- r8A%�tdCؼK4G%}sJ�.Yl 9aNB{?7, )ͳ9#HdkxܛC�Eu7k4}}W["| !Gf[#b'u<^fFX`df-^8pyta,eyf=G\TRY~CQg}!ˡlYV4-s/Dpn&VH 3U -l-,}:HYt=>>fκ p^Xu?:i3Z�N| _ra`פ Sv48g 6=\zS؁70 Bz\c}xas7bxhp� }-]5>VX*I QPǛŷQ&`qQLW=bgyQ&T~Wt <~ *<gD1hK[ᬯak7ցLPQC;9B5sPx7z*9dK=UO7Z^yY3Ɜ4T\>Sq˭c҆\�W/:p6[58uZaj EP@KswBXQ<CGDcQ ~0, B4^s]Y3'rf}p@Ԃzd)J)Ψ~#x�z9EN}psιj &ZOڬh*I:Z'S5~�_Aڍ6�07줳`S4C9 5IBTS`uWеSF+ZyQV(AˏP# ܏=3~#ࣕw*›h|""DD]=?SF\+e'4t*gJH vcfsnD9$ ʹS�P@NSP_a|+B+L kY|>*ن͝1x߂*-( {^a !-,8U#K^+~ajjM< dؿ=Je`hI أ>-e)/Q�n̈́�6Ю%53B֗GІSs$QMҤ{LM&`y[7o.l6o�ySw.$3{u5bxc)Tg4$B,AυxAmOAy2؍h}b<!©}Q9o^ut!/R#X2H=S" ;m( jAբ-XC#ѷ(ȏ Q`kLlXpbɇ3+Y㋎+~JEfsEQ :m+{rKw/hY0,Ũ>8. yU%N Cj$K/_ .Y{#s2B,|n|\[tq }ԳQǘ<)'7åv-f` ,f`S̓<09@:JfxIpg3ҽ0q�g|Vxx=|Ei1Wh}y\qdl3Ղq{WaJX.`eB GbJ+Fo{Rr`АU|ÎwJQZ~J6a:Ox7o.k dPl7ҁ�j,i TZCv`t<2SyCi" YdJ P<I^d& %$eO~r$dMb"I_OIR:<OZl6Lp2=$XlC6Pveqpmy0^}T]OD7j>l$Z^#U,o*Wv80OPc��>y| XD$,HA[,j6?F8ݪu"g*{ '(1� 6&2\lKl"ab]EYlmR2}-hpN]/l}Oy;B}JBI�.f ֨U d0i~q B_|c=%J܄)& Ùc["SQ?•-A{0a�lŵLR�ۤs)`Mt"<TẃOR K'ZO E IgHXS#3' ӥXۅR� &5Yt_(ޑMAO=Z±ەn;rQ9b^_E~ Kx??|_sNgTG-7LXF[vsqB/1Dzڳ=o1 4E'Pa߬os_ 9z*#A RbI,6;ח|_&)$V[*^냯\S,UrPo_ .37k M `Bj|b), c s C_TY/by28+!9^37hԓC'CtWTy endstream endobj 75 0 obj <</Producer(GPL Ghostscript 9.19)/CreationDate(D:20170131180940-05'00')/ModDate(D:20170131180940-05'00')/Title(figures/queues.fig)/Creator(fig2dev Version 3.2 Patchlevel 5d)/Author(Bharath@Bharaths-MacBook-Pro.local \(Bharath Kumar Reddy\))>> endobj 76 0 obj <</Type/ExtGState/OPM 1>> endobj 77 0 obj <</BaseFont/IJUPVV+Times-Bold/FontDescriptor 82 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 0 0 0 0 250 0 0 278 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 722 667 722 722 667 611 0 0 389 0 778 667 0 722 0 611 0 722 556 0 722 0 0 0 0 0 0 0 0 0 0 0 500 0 444 556 444 0 500 556 278 0 556 278 833 556 500 556 0 444 389 333 556 500 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 78 0 obj <</BaseFont/UMLCRX+Times-Italic/FontDescriptor 83 0 R/Type/Font/FirstChar 70/LastChar 117/Widths[611 0 0 333 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 444 0 500 0 0 0 0 0 0 500 500 500 0 389 389 278 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 79 0 obj <</BaseFont/YJRYKM+Times-Roman/FontDescriptor 84 0 R/Type/Font/FirstChar 32/LastChar 84/Widths[250 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 722 0 0 0 0 0 0 0 0 0 0 0 611]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 82 0 obj <</Type/FontDescriptor/FontName/IJUPVV+Times-Bold/FontBBox[-21 -202 806 678]/Flags 32/Ascent 678/CapHeight 678/Descent -202/ItalicAngle 0/StemV 120/MissingWidth 250/XHeight 463/CharSet(/A/B/C/D/E/F/I/K/L/N/P/R/S/U/a/c/comma/d/e/g/h/i/k/l/m/n/o/p/r/s/slash/space/t/u/v/y)/FontFile3 85 0 R>> endobj 83 0 obj <</Type/FontDescriptor/FontName/UMLCRX+Times-Italic/FontBBox[-72 -209 629 662]/Flags 32/Ascent 662/CapHeight 662/Descent -209/ItalicAngle 0/StemV 94/MissingWidth 250/XHeight 448/CharSet(/F/I/e/g/n/o/p/r/s/t/u)/FontFile3 86 0 R>> endobj 84 0 obj <</Type/FontDescriptor/FontName/YJRYKM+Times-Roman/FontBBox[0 0 702 662]/Flags 65568/Ascent 662/CapHeight 662/Descent 0/ItalicAngle 0/StemV 105/MissingWidth 250/CharSet(/H/T/space)/FontFile3 87 0 R>> endobj 85 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 3609>>stream xuWyTS׺?1sTL(Mx8:80 `&B� 0(" (uXmުKk}>]wVI} < cAa@a`(s=AϛBC7x/&^7ރA3 &h[f<G"K.'rxo EK@.Ȑ32f$ s lܖ=(gX;/.8DW.<vHl?ADO,$!0 ećQb;D v]nb5XCkPb/1I0ƒH&01Ll3�kC:ԅSvzW bOثk=p;c| 4u{(d􉞤jqssAG|`{sM2RVXZ ze% <}? '4+8΅M6 }Kf%}rPg(4@ :N$SU4IP^QUUhv4!iI}D{|=t? .k\΍> nǐ9$K䧸RvQ7гg~oV]Fx芘uQa&2ȲHz%$a G?9a;ǧj8ot-_JlTb.`r:f-] 0} ܫMRYd~E-y]p+ss$GQQ&df({8mx@4lPRh @ 幕Ǫ EEh Fp> )Hf^f< ooߧ <ժd~7 1„"U O(F)^.69\!v̖^~ 71-LE c<FR>n7) QԄ1҃z ܖ꾇v"북u`죴ԨQ՞㻵ࢿf`-f{slRTDdbvK3S2y ĢZy$&((#{k+G B4G&ntVF;%V8vrLph@WU5ek[~\jWTdxq3*!ר O(P`-ɦ!3Ο7O揑5M꟝j;u %GBbLԝo ]<GNuN]ՂI*] p2s. %E*PfQUnP[?8 sbvF{Bk[YUK%zDy㛇%YnwikH<Z^KKC t$wܐ (1& =! u=㉫*fi+%#voN/13j�FG$䅒!BdZTDE(@! --䚑?v_ &*(N֍ ܺ|I>treQ c##D߉%v1͉LMw  } a UKEMXaT*djAeRf)0T4ƂhAkw20C/8A9L_ |:+ɯ"/z$0E|l{KOWXUjn;㼾ښDSSɒsbӥ 7sʐ}S^Ցf0\YWY"K"]wZ'6i j, rՊ:-&ҙ0 'Jt""Gs1yGPtk'zV?BdD9h|ZU:zϓǧn&+uL$%$0y�'ڄ7]7|]R[nPNeҙJLS ҋ��/>%[?I^~DQH|V & ʁ؅Q}iWz:<GsәS"_U*pWQޥg'!I/L�C/ߏ7Gmu:o�5om黾Zx @e<w\>v5&\0}%I0"V1B;ɸh<38ߓ?uFPm:.VO:-kT\F58 z])Nuwz̎' qp' 'kkQ$Nv \UIt<BHŋYMQ1|H$IpA@67x<@FS]r Vџâ3iD3-AgB nQ5i }#oaLXw={?1<>s6WV=M O;?_RsmYk j"U2 -?uhryi.PnEwƴ. }E~1!9'mĹh)5"gߑӤ}(�G@BҲ™p݋ぁ�#/İΠ0v_Tck1pP?ݽMdVhD(RۉԨC9KD +j:eeᦤ+)ǏOzx<qw֮ ,LO7F$#yI!{ۙr8~ NV%1Fx+*]>ךZk[;lg9Lh=)Tj 2ϝ*mU#WHl7{(D ,u&ތgq B<~v~j=Y8 #\}.CU�ZMzKS',-PEr^ r|=7N(J<58(s(gdv_NY' davN]Ɣ+K[Ȃݰ\;  ,X̖Z/$2OSUVa+.u>n_Ùctт?u 7{˒O`jLm0I:YPb.-/5y } (XBv&*"wSV9(K SЃlqF> bj,ʝdؤA[nZ);\  2/41bGN1-<M]{ 3E]SrZFHqzsw >(x4|VgQIvG_&7$M%I'ǟEawr 2sLxRnH$/㦰%>jf3P\v Ӽxj'V6n[lz[EKo6J`R*ۻf[Smfhnץ*e{dCS]Wzpb?C۹𜺟0v4-(:)0[1̭jՂԄ>Ff:j:`oX80[Dy;M&SdY,Uf  [ endstream endobj 86 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 1605>>stream xeTiP[}BۢQ,ՎҴ$&񸩱.b1Hb�I.}z-# $a}!ıcq?$G_Nt~sΜs>q8rU;KU 2~/>Ã,.dY˲{H}i u:ɚ/W׭{#ySmkb6JӬ7ԵzTii^씔V 24ͭZ~äX1Varۅ`X!Ųұ0W$MppN==k@=h}Y SZ)(+Z@H :G^C`*hlǎ"fğUamsDCNh;xN߱Ay|.|rfO�X ]N|l.ڠ[?$00b5IOrF>Gsgtd݋3 !^GcWo:Jlw Ύ֕7TTZx[9u;䩫g4|&qsok~^>D%`yI[0Y,j/aBhFwG#0e~6.TOo^�%1(@>. 4ԱH8>OE K :7A/mM^Czݽacؕ�b,>81ё4fM YSSSd9졶Zޭ]"cF*H6Xv5-;ۣ59(>;®`ts2}W.49%vu$! 8Du.<$/:FթwQwGo:y+YO4# cl>lI/EdItpG"O[5=ñ`oà;"*'፹{'U]%Ӧq%#_B/̸6 pK'ձbgɓ{uDTTgиusnM{,~Qpwr`o9`%w30&] n>izwo-}; FlP?@1!KߧrVEa͖ $?%9Y0U9E{4>=TE:D}�}^O `wv˙ ` ;vL1 ?T ǧ =bZ|q_8X}(Ư|1!c¯hW!nֱ hs6@\VĆT1u9FE6x$:ˬf60.ݿpb &4pYءPIy~)<HR(w+_pQfO{繻RMWIiWhJ[ \Έ`K Dt_8Փ ,;3M8S-PEnTu`lH<0xh@(_V)Iva끐G60HE) ==, C endstream endobj 87 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 382>>stream xcd`ab`dd M- MU~H3a!3k7s7ˆrB{w``fdtu/,L(Q A($Ud\R3ԀԜԼܤb ~ > A9E2 ! ,,减}=gľ0~zeIg2kjm�ʰCe~\:$yWUh;adL{7[O#be݌+?&dѹjoFlX`kmǺmwsrehrv>jg=n&F{K'� 'M��|6( endstream endobj 88 0 obj <</Font<</F39 35 0 R/F57 65 0 R/F38 34 0 R/F40 37 0 R/F8 89 0 R/F11 90 0 R>>/ProcSet[/PDF/Text]>> endobj 56 0 obj <</Type/Page/Contents[19 0 R 91 0 R 20 0 R]/Resources<</Font<</F39 35 0 R/F57 65 0 R/F38 34 0 R/F40 37 0 R/F8 89 0 R/F11 90 0 R/Xi3 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 91 0 obj <</Length 5104/Filter/FlateDecode>>stream xڥ[IȑWgsB.@#y4=Vyx6Vk~ĖXXYu!\c""vϯy;S슸t{89ĉqc4}<|z껩={2USǻ=|}SNE ÿ JEP^gE4vw:_K$�Cy5Ϫ-MG pzC$i˱M1/hMCsѵ0e_!W粨<Ԕcu䂺'/k$_sQJsQ^KDUb% x<5p憛9* SIT~ߊK+?ǎ$˱/Lܥp YuSmo)),1e'7,{o:uE K{8]MA'Ǖ}6CDx7s8iA/LсOc+XA kI͏B5,ud0 ԗǩl@dçFi黺V% YGM}qHS/H*mKj8t;cV-�:쫡9R W| گJNhZ{hЂD!c / 7lë_^)(Mvj"ֶإEgy;\^}9M>SNE5UV2kSW6^ު L,ײ//H*VS*TzBW rDb (:dj4O՝Nte nj"#+Bƚ]+e0"1c]VοPX?.ݝJ_'<f5;IM[X5##<ƫR4ޡspz\da>c N<~'Y~.GNJc5_ 痦j/@D۵HF k<׳_+|C\$gL|j{HxUde[Œ^uхr㕾V{:`s `a!vr:ܾ48/h&cckFl\?eY'fgs'y}]+r6ùkB)()UԓKRgڮ6'N@7f*9IT%'Z( 4T22??_F<cQ•FLn=4BlgNn瑩?~ TG&/|KDTߍcS /ZPT `ZXM 'Rոfh~.iS`yFPIY ;~GS3zW ir\6TtZ=3*: r`3q⬟f U^{al*=WnFzQh6be<u<]j`D: #$#Ȭ@F/8F.v(FvH<D8 CwEq78j\1I=zM#vQj* Cߵ< H]G9 p5-v45\ =o (b8]@OF~U6gVt2^9gٍ.8oh: qIqg ,l@Vx$Œ6_J~4Ykh,ǒe@ciH@&+a,0rƵ(>]^M6�OQ 콅y4/D[y.]\O>"ÒGߑp(4- 1nHH!Kg_ÉX: fF勺 "{nV8;Ts=; \X.RdT?T�]4O $_)&ʆ=0x*2Xp,u2e@]o7YF2ͽd,T7)*+:R),g^4RtЯKa-N>0ڳ42�mۊ HṰ ±J&J'45eu{ٟ`L@/Ý#Ҋe Ckv\WW,w@"bzi>Af?\\AY pZ NW$d*S=rsoP9�&C__G&NΣ'$3p~d 6.WT)1 0�plG >ʁ;8ukqU=\N_8FvF`#4,S/H0qV HIHrƍ񡼛jX%Ki,dz3QΐW1^ˬgMJgk\-o3$~e)r[Hl51u+p̣AY^WJ@|a {JtB`TROGq1gcxg@0U!F-h,R{BG*@ɜ*Ar`ͮ$g^y)ԁfA\V(1*4?x\_%h#p ;3Ch1i�L:2ob `�lO[MH +Y,\Ip=^㔗6#8{oq |Jı�IQ b{k$`5IYc-Hj8ġ:2;PNlBP_s, ؕёaۯwuX ȺETNcw) AOs;pf$pW^dJaӍK*ۗ r\) dMā4=!)b"6LAB߀\>I(SA/]L(>:)DLze+>vpW*qжX0؊#424nҒʙVzd!`>G`Gk}AV9g4e.ǰH!3ސ`QS?%IF 2zr B%xR6kx'/{mZº`]y|"ܵ, &o"/eYTqƱQSc8.sЁ8f+$�0&1or.yH<V.` PqjӐ@B;I !I_@*` APBԄ[ pRAmJ1Z,|X), W QMj ^`Gx/Ryk`pKna /35ŸդZdΗ�jS�3eߛ@Gw ';JBL.6(:b GyBc䋿^aҐк+x4)0%)_v1͔/W42o@f�h4i;~OQpL~jgyPMuC:&`̣~38^r6), U`Pe>�c-h3I!F/a}nyҷي4VjPMԼD?3 ~?C]D@T'ZZ*7ET=@X/yq%8&#y | )wDPV(CC_ fhR<wLc<[_֤r`p73nVfmoew3;BchAvޓ`%g5I7"K$WKR�ȋC it˖CJF΄Zb!S V+lKd}@''iaUM["Ŧ+PX)Ezֳ6KD1�nru$(K8c\&[[EE b)]?+~2k9 W/xl^ߺ@fIn-92ٰ|N8:|\̓ym|NDR0"Ƹ9\P I"a?/]3*fT<T\/sb;}�i@y %$Ru3E ?ɂ�&gD3a78H }t򅡹36)}4wY2hHP_i"x WS&;RMPo f|c?!?bͬpj7E8)QpPkEtp[-+1o+ER$ 1:A;݉K<ݫvߨܣK�C. \0-+4T*i<u?,#_WnUaS|Wa金_8@AӐq#Hu geUSNhAVnR_̘g,y},'5btՓCIҳH B nǬ ;LDyŒߎB];OO9 4H3{P>a9GmNioqI?dO�ރ t!Z�tvN].WMP"i#)8tJ' B'l0Ԋu~PEEkwٞ Ƞ5ݮ-Hjg`/8Dԡ:r)bG t>$MG8Ik|"NJ :tTUO·H(Ҽߨ*cN5ST,YhHA,R@| q2qv^]or)<xHYko|ܶ@/nb&NSG& t`%sus}’ 5B,8Vo<d"gOH;30{V6Q(ED$n T}V\Fl.+Rr^J^YDRzX!zӬI o4 VH{.]83$xopzI!I'x<)~o,} {ON?۰(|k [?27h|rux*  n,Ε^<f`ǎ/2ҿrc͎ ]%>ֿw{ds2;N'BJ]7NbbK,k;+AHX3KyŌcn- O PZB`pe/l/4}O_V]6եK[AS9U= 8Y}Y!<6gl`׀9k:Xz|s(x1 endstream endobj 89 0 obj <</Type/Font/Subtype/Type1/BaseFont/LRICUV+CMR10/FontDescriptor 92 0 R/FirstChar 40/LastChar 50/Widths 93 0 R>> endobj 90 0 obj <</Type/Font/Subtype/Type1/BaseFont/NIWRQN+CMMI10/FontDescriptor 94 0 R/FirstChar 78/LastChar 117/Widths 95 0 R>> endobj 96 0 obj <</Font<</F39 35 0 R/F38 34 0 R/F57 65 0 R/F8 89 0 R/F10 97 0 R/F7 98 0 R/F11 90 0 R/F40 37 0 R>>/ProcSet[/PDF/Text]>> endobj 57 0 obj <</Type/Page/Contents[5 0 R 99 0 R 6 0 R]/Resources<</Font<</F39 35 0 R/F38 34 0 R/F57 65 0 R/F8 89 0 R/F10 97 0 R/F7 98 0 R/F11 90 0 R/F40 37 0 R/Xi4 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 99 0 obj <</Length 4919/Filter/FlateDecode>>stream xڭ[K䶑WmS|{o)O+|t`Y]TWe>4j/3GZGlD� /31CW~oԦOLJ>d& ({x~=ǽ-ݵOjdW/6NzkJktڪoM"?=kߗ hY{c"Id,(`&i(>_Y6f 4{9Hs'(X^<̋D76hhCiQ4{6euEIf[O=&Nݙ!&a,=A>dO)ܷ 9 ĺthޚ$b{S}xÍ]sį)#EjpfQ$wo~fIñ )6OW|XGikyhڮԷ)S95}]$4rl:'ݍ>g}zՇftv;q*˺}<۵H\,ǥz;:P0kb`^K3q= }|ևrI tNA~_/۱ff73+]N"֐"O'H"'Χ;ȰDGvx= ̃Igzl>VldH!,V%A$QjFKlY,g1gn2bBb^H=JLX Y;~:m {\VLZt{*ܴ&SD}ŝGڇFlh$|fc*:&ma6Z; Sj$@'vj b/^wI?7mf�=J|b{{5FkE2&j96Yj%i{ @AHNM)k9 !�2@hΐ3=u99`h̴Z^9+#g*p±u-D'zP 9?忟;,Kt[ D1J]s_G᮪gVM@*(<2d(|G.mQ.: 0hV}wu/w[w>dnA7 1N`GjZ.:aP {df!߭63wJmUa/i!MV!]%5y/!hw<txz2n׮~AP`Ӣ{1Lyt[C7oS4-#V @[>sxoX_`Xxlw[AZD6'^ܨn h|Krr\̝d~8ud>ܼ̌4dWalaEb9H 3 ~GV1RXj逕ʪƗk/V\!Y8 F^:~phQB~h:BK Ð=l4) h{z]_\hC6!iQoIZM �wqI:bbׇfFtkj8"l!E>{lpCH9B AȢ9+vZv)Fz2[oY&La(d!+ngbWjkhZ8c!¥?JR bS9 MYL@57ɕ= ˗Zg'CŸ}sir72KʪyJ+F2]n<j:0鹔%Wćv 0Fߗo"-_�Ќ%}hCLjEj H+chmK^c&@7 2H5$ ;! 9ytiZ̟?q!4HE *u>%\1c^Ҹ"SefAd쐶R.O=(#Ypь'�mm4( L6i0CJ.129$i!"Vc&3^b s_L9U[}&K6i˖\f)&ςn nӘĉڇN{A'j"T!ƨg-X-R⮑T5bIԧ / hi)D2Rm7͊sV9HivEq!̀Zkfp✍~tv^c̞S4yd_9nbUĂ{368 "6-}X :>jhWcҗY&)t6]{Al/[ 0[A|D$WX}]olzx!ژP:Xj3Zģ4sMplӍ9;34P#" c7jj0f7aǥOH qN߸l~W]Da#/+jlnCo$)ڶchr~W%RE&(rhb$)[;f:EPb%:$dv7*H"R\ RR~t0̭+X$=4c# NF1Im  0/uDRޣVIV0m]L,t8ɥY۸K0ECjN oZĥWxb,dGxrLeu u$([F{NL7#=˃ӕJ,Q_Ak‹V1W/M`(_IrM&&,n*5ٲE5"ӥ?O1Jz3F>>'?q#mĄeq$y9!2< P''<(ꓥZ̲ݚ7ճl 1uӸwDR>zH:ot õgb9B$Z?~Psih9 tOϵ�jh\kUPyF?ǜ97f|88ԝ3 yVa|G)ծ4t>> VkGÌX9`P9yy f0cVa+ggm:DǾHTٯnR'dxfHޟWJp>[[##+b0[Q{7rz6/ѣ̅#>Lys8Z8"u& RNR]&23 D0)d jx<w/Ҙs�bNn>; U+{F� <  Z% ,.u9L=}U(u$*RW^L^H =et �& |/@`;Y㯯 ?wӸf %,2JF$C5q0 /,) B;WYI^l}(S\PxpJ|/lDpY~ReèQ~ @"2!Rdh*EΌñjvs zI ȵI>T~f3'#Ĩ: +*8% NEHB2yop~@_ch~? 3 Hyb4Zߤ7ib2F-ktpm3wM;p*|6T_FRӄ;f&IvKXkJzA< gx!q)aQ#]dp!jCw]3p3ĉׯB!8̎3h[z,)�6Uu#Q1TR1@T$uٷ�:R<Zl?p܅ݺGKjy]8ف}uC TzLn_NHS#ZrtoAdn(R˃E"a_Q]]h%^^~[rwS&魣\R@r;RY;Aѿ**n$[;kQ{m+<Vq!x^y~7wd{Y~w-0ctſ㩫:ҩwi` BAba*�Nb$xcE"4H#4t*P'mwWro|6-+PlTR0 Y, ~+19R$Z2f0M0?*U %Hc݌ <DY|DZY?֎~Se #HBR?R|Qtb?AQ5<_^CJ7J�0~~r%N](]>ђ֥t*B\$,ty\+**2, h\V N8%zF"KT^_ ~6H\DA.. QR$=0)83,eZ.MDjkkuRMos2 ]zÅA_qn1ǃ3܊cXWeF Fx{ R+pĎ%qʋwF~N-b܆GvLn Dnp*wn 0|SWoRT͔)_4v+e6ȭ Z~UhRq.omR1W,ksXn"%ܓ=ya`hx$*~į rCgNSisRT{Ds=u^\ &SrUwS\^f$tmqu Gz& *:M:N~/ψpN|,+[w%[ួ7>r=eIFmslCuvB&lMn+E$9;Mdi~A\I1@L֍s %ޜ^&�BDAbR_nKN$%o d)!UKSyԁ;ރ(FI'duBIWb$Y><2f3/ ejwZ({Kks.pWL[Y#ބ\uHzf]n,KJǮ1W٘`Ş endstream endobj 97 0 obj <</Type/Font/Subtype/Type1/BaseFont/GINCXO+CMMI7/FontDescriptor 100 0 R/FirstChar 78/LastChar 78/Widths 101 0 R>> endobj 98 0 obj <</Type/Font/Subtype/Type1/BaseFont/IPECWW+CMR7/FontDescriptor 102 0 R/FirstChar 43/LastChar 51/Widths 103 0 R>> endobj 104 0 obj <</Font<</F39 35 0 R/F57 65 0 R/F38 34 0 R/F70 105 0 R/F40 37 0 R/F72 106 0 R>>/ProcSet[/PDF/Text]>> endobj 58 0 obj <</Type/Page/Contents[29 0 R 107 0 R 30 0 R]/Resources<</Font<</F39 35 0 R/F57 65 0 R/F38 34 0 R/F70 105 0 R/F40 37 0 R/F72 106 0 R/Xi5 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 107 0 obj <</Length 5193/Filter/FlateDecode>>stream xڭ;ۖ۸ Yvbq 8n;dֳn${<~(t73T_WAv/"X�q)ԽJnm<g߾V& D&#7FDa~!2sj+~tt}eB)`:?gBiB&.>|6{& Unia#,͠UmnooPQhdc[78ٰc!~'#:w>ੳ-=45SmN7[!yGq[nNU_n{wfoaI Z2vK:UYW"PvI^0m^.34G}]]DI$&ތ$$i*\H4(ܛTa&Fz~h`^"ؽ%Ym, sle$-2/^Gq\*ͯX/̻֒̓<&LrFqxBJ�_4m-&Jaee~JP?@:'|\+=b `N}C,ɭ"ywrl"3@RooWFMKq#:# \JomadHy{xdsˠvC~0R"&jmNw y�2@Q=\AI}AuDp,[قd =7T|kP628EpSa wA>j ʺuĤЁiT^uoM?j K*x,hC`ok÷oyTW~Kj7偓QV۪W-]W@m H*UH|HhQ@@6{cgOvk&Y61vw%OFM,�t}T8jnFH4n{noHQ֘�:z?:((c 6 z,I[BߡF=]@cUF:}f;Zq)2R�"7>J%HQ E4eys*_FJE܂J l%u[diz;w +?(9 /M%e ƈV~$,=bQ6vD{q - KhV1VLN۷0 T<xРG:x0a<?zr$[20O;O;\8L%fx)jh=' s43elX5^�ڪK6G �iL8%bފR%%xIMqOWPhFGG'J M�ԓd(6 _x2>vdHjH؄<' xR/d$ׅ6aGɝk֘Tӕ2;%ٗ˭LOh&F\�5A0[Wm :>[.#Ws?u&$#K&e s@0lX`/YZƐD$)iuSjgUK$>ߕ6h rZRc$ R g%y5 { 2'ڈf@3sm*4k9BM-r$on^u[j=9FO{7]c?s^ud7y"zFt>uwX/W% nmx$J>~;^':o2t H5c^݁tN!BE2Xӡ*goc|wWM9NGw&1~rѢ}͑J`) G#>GKHqϢTqUO58o~<>[/9?0]YC x:~d`'V-aʂP[W,UK8nf <E:f%_tH)1H \ǝBol~wE2{(#CT W8y2NLњ�Nb� bߥ8ycdd!l \/ĐA<H�̈́d~,79+=p+K?1Yzg}+3o}_ִ$ "Ap^�]ZkagB鯼9 %aH-q�?24JYc$dy[>jXu27/(@"BxӑXh Y3l4&KrH3:D2 �c<4'C[G^tk] ڙ@>C @ohVU1L!_Qi8 أBѫ[C %>!>bK%\>8'n(<t>4QԕNB %9u/!9?+!`''ܕwd3u_a9_9=GVa9E ҮD0IJ7v4XrEl\@C޳}a%#FйYDx1PaLfA%֥fmL!,�Re) |s5IfiS g6;ztSo{ox;}ʺۡYbƅHǐc?-Q-TM󠋺~d,\2T[z4HU`3z{2qgbb-w7` "ضoEPE*DQ&QmMX5\(Y6_6Fb)w�l`}$ [ޮ@\R$<3+4DFubZ;_ o,kiex%v kOH[9?-Ɇq}T܁z;çe@"I`7ӑc,Q&yTݸF -9-`2dD$*RN_cZr�Ep`K$Ga$&z 955D9 0�x5)ѣ@ja?4ccXE_fDxgC8\ J eHATqYN`c8zc2a[^8i&bEV6WYWC3XY,S`Rb`eyx0v̝hk$ Y{>Ն vEUvߤ̻SQ! $J%$o/ݷ-��n9Դ3쀗F(6(-V%0ϋ_n;s>|瞳tT* Y1?(c9ڌidK.d,yp8/D.HKYT/B N C A୰e 5Ǿ<`8:(L^QƳ޷)L 6P[t{4pBvV S3wf$/L8 du[r-�B(RF^t^Ov_(Y9W*`Lk%ЦoGGEy�U9Q9{PY ]XcO-<e"m=jD3~x`w?a_@r5Չ|\8ϊ"3n+9b<">Cg=ΎI.`<7-+K+))~]ĉݾkZb?`c>h;נ@tMXY#`l6r|U>668J޴Ww_8uy^\搷caas$af@ͮV='uXv6j:DY9c[C :!ϣѭCWo\.HUbsh"w9!cRaȓ.91g ;'T W軁TSQ !f`qRS~[_% Se$ˠsQ [pFѝt]GNT䓣.=}�fm".y~42Lѓ|`FgFTMu(8e<kPk3rx^=N<#z|R~ِ&NJY�%WGϱV{L-QsFEFF\zB3Q;{zG-a]b8{P9q^v�d%`kA[,o!?AY`6E.DfV)TJ {SjUq<40}WE}\i wR~\Z)&c ;{@d:5o35֋Bsgkp^\,Y'6z?h@w3 sa~:Glpsυ3{ֹL#UQ ,Z~9A(׆C1(lN{XL.3ؽyslvN8^dSRS5Qf5>SKhaH G%N`nt#kރt.Kބ¿%E2 ')yՖ7,X O=6.B&[,͢l ǹ/j_`)g: 0"xun#nڧA=l:V ͩֈñE!_*Ε{sM7XY0m><x. 7VnVY<[Ld(|HizGy[7 _? ._fWoG_'btZdgte=~QiW<˄eF+U0 SdZ:%5տQk/Y6ȓg,K&zp0VR>SE֘03t({'(u%)C\3!T|Q~AzF_8Ul2q_3@g)|'/(4ZW.O.N2eak* &sX 5] .X5U'5j8zOhAvH-`X2OT`)[:mlGf�L_BnMxSk&BZc9\.UITF}MXrBu k:ix.46S*S[CCM}D w൲sM]=}!4UVv˕u5C~6r׾b<5΋};.M̆?�3 ݗW8WXigR$I(TqGC[Zf<y,yٿ�a endstream endobj 105 0 obj <</Type/Font/Subtype/Type1/BaseFont/ZELEMN+NimbusMonL-ReguObli/FontDescriptor 108 0 R/FirstChar 45/LastChar 119/Widths 109 0 R/Encoding 44 0 R>> endobj 106 0 obj <</Type/Font/Subtype/Type1/BaseFont/HQFFHA+NimbusRomNo9L-MediItal/FontDescriptor 110 0 R/FirstChar 48/LastChar 118/Widths 111 0 R/Encoding 44 0 R>> endobj 112 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-reads/diff-ops-reads-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 113 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 114 0 R>>/Font<</R8 115 0 R>>>>/Length 8953/Filter/FlateDecode>>stream x\Mdqݿ_Kдc),K F` L(}ɺ6oGeee<Y{?|o}To{q'~O[v_<}|_}^|qq~G[{|o^}<þ)o=Gc<8p2sܧ>Շܻc;0n]Kn_īj!}j|ޡIOB?}?az?h}nO z c{ů|Z_o`Tÿ>~ cBr>‡ Ǘ%RX/?ˏeKW[[/?[yů>S-9{yЉ_t3G31|˘^.v h 潷2 lv߾ӛo_}o+wZO!77}g^nt^ooxzabm2 3^_|sdoE3vijҽo/_{9RĔtI񗼪WѯRۗF!:k"s+֣ٽO볯Ooxols)d+fn_y3G<Z[g_j9ʾ}sbHR\0�L!u.λ{y _ jlJX*VDc\Ec ,iߛU,>4:-8zx 1p*Wǩ/8fм>5AS$BoŻ'{R~3uc2O8sM>lGJ0(yce+'ǽ=jZKuL|FDwjOXK BL9:^Iw=[l#M0Ƭo?ŘM1]vVpA\lW;C()0$5B0S1j."# K}P<A绶PL],i*su1@՚CH 1c@"7a�O &aYS{ar3k oKl$]h35k0*fա̹i 64k@dq[æCcI1 /I5l-8{9H7jV8{Aϸ>7 iRZ{VX'NRud%;=* mI47]P!{ri91F'ݦ⪁,سڬ@{ P}Cbǻ(WzGN T6 1cVܞ6Lt MƷ ,CiGC9W `?L0\*C7GM.qqQgښy�T2@ ft˴YI l9mjJaAbŚլ2+1:% \D1@ޘs13;or\GDIn;07X9 vo0ݙ][!c(| orIPn{enS379Pe<™>yenJk)mm ԿM_M&3cueHq6%_*;cW:ml* b|7öM.'{emb36PW 6<n7{c/kӠXWgm`4p.Yr7KM.4 .ݬ6;@06P|bmk:el`26ol w Xnl p5zw6Py0W9'gk�~gۚ +bw'rl[3a-gHjnc3mlalBQ.]f mllalBK�Ɋ9c&M36SECqK {%yR%.z= ]eP!_?b`@t{@[`*y\-OC4 Ȧah0Nf4)W- TPy$;,`.4i]N4]QJЁ١z}4∋36~hӦHȋVK :^D Y~p# 66&=9lMliԦhЁVS{Nd^G^32fM[(yCD^`Ւ/ LɗgoV�!_֓oj/O:-2%.Ý|!ܽ| op'_&CnQ˱v Ͷ -?9T:t""H:/o?셀sc#o9:.=8z~>a;k9;#>l5do[ Q kAHjC4#M|SWl>BOg4hVqSIԖl1B#`u?bAf5ԧJ'wmGLMOg?ANBoL@hko4�`"#:AJL"ZBt8@+}wPݥ" -UCr/ych(T<{J RU,^*Zsv_ R T/U*_X\LxR0O fRը-CwTfRF*{ _*O<ss1&ŽwÙ< cLl8M0, kϘL%8fbj,&n^Ϳ@hLeh ]�14$6#fQE6/D;5f0UX! d3T 樰5Ņ@1b3c$|R a#̑a[ iw ;2ՆTE&[DvW!/ L0Q3  L[Nl@**6$E7v,¥˄P^"s|0qᶱh1oaR6<QU b nbF<uQM2IDɫޑCi;U:TwXӻD`ڽFz)ͭw4wEH`zSHޑ7rzG׽!h䴝j\.2NNH`N >F[hRzNhꈱX Y!]Fw9ɳ:cJQz3MywH]\pzڌ vlS �ގM(^2L ϱͱ}9& toK MkW@׀DJ K MۉP ͏ CW܀7#'@�1ZVo T6k @K;, ^pv(V]h/n ` /Lj9B`T<T6 iNBGHR#JᘤKOuHɸE""0oF)FhHcKMbt5= tbQ Tem#yh3G> ;LT2G@DuQ57qņhĬEN ڗŷ9׾kZ8# F =o0|N*Mq77x>mk߀%}&}:.Wc1Li5־yG +洯C-{N:7ViJi_k_~*^:{J xA}>i_}0N2:vGOyy' 3 ipPZq&W5|Q6�ݒQ |= vӯ"aK ߲jĞ$-aC/!U$@o`Fأ5r>eEQbT8[2jGMؕZ27288<BL[yeb&gc?Wtq+qC=b!N2tgzC1CfG2!FM 0NacbWc�W͖mq l֔rͰ4b98fhsč<M8 z4G1 l C3poeA),ZC,Vc� qSҌȸ=*[eڤ31M& pva@BfTW6g҅C < IFYP6oD3*b5xy Wbc Q'C^YA8Q|RQZ3Xr7a"V(B~BʰM 0):BrFPPQ+ŀW lߢ9V ,'Q<c4m.Hm,s@,e8�<~fCfN$ߌ!f4[(ؘE\@^t,=`Dze6)dIMb|X9?.vN(+^vc ayZF-iZq =[JQ/LwU1y~&(| R{0> +kb7r\!#/[t4BɱcUtNAvJ-̤CWy^l's\ -UE:3iV86lxFH!ϩS6u'L]6pd[?3rasd 9_X+#%-[[Y;XoO_$nmsX/@s_=UB[Qi)T͜)(dFL,'PJ2 ABS^!䟆�3=k"Vi"WHjJi2A+ 59WAw .*9 4Y ut8j?%9Y= Ή_ Ϊ/ ,\aw4Xi~དྷj 6N{@ qU/PX-/Ќ/P`5 Dʊe/~I}H16~f lHY5_RӍ_ꝣ/ C6~A<Us 획_ L|ԣv/SuC'~vDN/ҚԆB_"5~I G9DJkq_Rc=p%RZ,: <AF=KdIn\%400-i%RZTn>K/ M_"B@,ˉ_3?Kj'~ @/?񋸄~I5v`y܉_h7~%!K`6~Id8D &n4~16;K*E `c{E+ O=4�/b%_�}Hi/)DJE'~I̫r'~?Kʥk'~ NȚ_&_RTF_:b/[DJ SOO)/}ٮ%Rb 1lU_"$/0-E7~ApH _cM/Cpm!MS?K _:)0$X4_"* u%R`'~A?5d#%2HD54_lSV|6~C_69)-=n(_"qc *O" AֿǗء}[<!~< l+x"=a(Dn8>5xK#4 Sl�M, oiĝ)H0~LVSa0Ů݄i"a23Y0M$L'Li":<]89MZ)8>69y^XXx010UASt=L&L!yor0USs),7aHwO't}bawt=ПՄ)& SOEOCg gagh_UPk+V໵Apt}*I!>Bխ}o+A־O, ^{=>h9zѾ\;#WQ<]}Gt}b]tz$N*}zL>՗\$Q⴯&A}NH,UOö\<])kst=h$}MXJ_ #Yjgz0 }tfRח8@gV(uW<s00Ь2mz]?)ůR!}R|6EWewBd-lH0a*VcX-Ҋj۹1zBMsr̜VՌQξSUful0ۛ#b uod2oQ ݑvзVF9fpaP3n@@sՍrH˾C") +D@;|ս9u|ss?zc0Cr=;Rx =ma%.5ѭ%e䬣=2.[2ِuj\NݛScqոzfef,"Sfv3/^&0н5>Tuo&98 G{LF{̙ .` mge^Q`;+:*cO|˕M]lHp*UT⿪םZh]v.^[)][ε5К5xmM澸&מZٹxmM&smMfҚ.̥!i?[l/,&MmYlȊYu[ ,2O Mr#ZisvU!jsZK eR7,%#7dIy2" /TYa6\=l a{td# Jl-ʲf2|t抻 i(F[x(lV/ci[?.P&n/giҶ+dS.P ϻ 2sO?hj $鵼lNۃ h2oQʶ0CA&s@;HY5rǫKvwRfW/{rk=9EA-9l]9IA:{e%R͌U.ث�yT53ڟEU탣֗v[~R#'etmI]-?�}[~4zеޛ-?mY=-?I K[>)vi/q-X kƴb--V^Y.|[~6̵|rҖ_.mzҖiڥ-�_i-U%zEO|ª|i˧ի|zi˧-Xq\Yc.m�ܤ-8+_ ˥--yi!|tiI#-NEs'1~ig8.DҘS.(o),|&o̷\.LZNߘBm-nmW3--?1|{-Un÷+ -_w+m C<//cwdq؈T7{7kV_Yq;QXf¥;Qc&_cKTmXv-EE;D[*\w,9cMtK?}wl;^?X wjeD=ޙ-xǏ/-D<))|<9f) L=J,ӁC@JC@Nn<9G@07nɭ9]{5G@9*Pe"$!;덀`1`\t$~D@w-`kW\߼߼g;އߜzYaՎuR[WacЎxO\o)WQ͵Zt[lG u[ ut8[Ywsi\+ԭ4ŲDe5.k,^'uՅ| ~{ls>X wx>AA0s ݊yk*^o%\`6IRW/o]ݳV%񸩬*ڵ1 7Dhݬ~N>0o&JD&#QnA)5LUiuxjB:F}S�ga[kߕx;3/;@#Ld!/H!_rB[\^Ax3P%gy(%%YsI �=<_s*(pB^OlCûpI]џpDy4uJ33|E8S>gs}9#]I f?Z爕uɏ+LUyQ9D<0J;VK;}%VL ]N;}%VM6΋zkO0۰ϋR뵠yQoQ }`[#~|a*Cx֘1}D*r>΋RϋzkOiuvpӋKsGoM?΢;{h>/?9qf4YW}^jE>�qD;,KF|!|G?ga rQ8v}^T#8DL>1J!\'r}b늂SW #9>1cr<ΝTlaM_'FJ+WNdQʮpK8*˲>/c~J<h2U۸=<ٸ?|cx\i q#3Y}|AQYY=/x<;K88_ra9]Ra4�q)H2\S]"xiL].ykޱ]!w&;G!>K-|D͏;N2wC]2.d/zB枺 .dKېeݚulck[]ԦYVW{}(@1L0p-/˶ұOH}& u +tօ}_grq endstream endobj 116 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 117 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�JU endstream endobj 118 0 obj <</Font<</F38 34 0 R/F57 65 0 R/F70 105 0 R/F39 35 0 R/F40 37 0 R/F72 106 0 R/F14 38 0 R>>/XObject<</Fm1 116 0 R>>/ProcSet[/PDF/Text]>> endobj 119 0 obj <</Type/Page/Contents[25 0 R 120 0 R 26 0 R]/Resources<</Font<</F38 34 0 R/F57 65 0 R/F70 105 0 R/F39 35 0 R/F40 37 0 R/F72 106 0 R/F14 38 0 R/Xi6 1 0 R>>/XObject<</Fm1 116 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 120 0 obj <</Length 4667/Filter/FlateDecode>>stream x\[sF~`U*Pfgm!$V_gΖ`.==__gh6ٻWl'Y*f)b_l X޴ZTb!WyRʼnY72eq$jvy=KX0$ǹ].gD>Kui>6l.4~,ٯң'I4? <0 xSM?JX''[e ylݟW+3)1q]=O4vl?cNyڿHZq3մZ%̔ka<7,a2ndGXS˖?Ʃ lؖuW`'*]75 ۸oV%jY]i'k72><͍;| >~i6gBG _dҜtcw%/~\d~�R�tx # ݀)`;xA^P E(4t@gsXXsPcoGr'sd r #r8 S < r4@4Q"TgFg7EܬL}=@3hO]KD`Ǡ~*!衷L_&'A=�69 ϛ~\[`kS(/ؕv�<5G]m KDC$ 9u 'SB3^EA@Q+@yѧx=G1TD96m�0 P �w2;&gpQ>d*CBDU,Vi|^%\s�~yy I2dvL½Ϡ=Y&~2!_0DRJX6_"_eSG8}O{T豮V+̹Q5p3)۩ՊϲjEi*4%`2g&IϫHQ2 MI�OAO= 05RGHD|D<$>dXuT`9P6^γ4< O6jL84D<Dž_iP>f6 ̳Isq(ĺ\d*CB/%)CY,uy[6a"ƹnvFgz ;_ֆʵޅ`16rz2C< sg<y kII<y%qCvj478fpOr'gǤ/5<V'eDY꾼zϛ{>�tW29ycM} 4'Bh(߷qt3f#˳\Dŕ@I0QC]8f"VTMgePqi3Fe{U}CնnhLhsO*�5!,b^]YgK[?5R8P}y&X]T&a못Gq1s`5ˈ/ӪryOäA$w%H'CEeu =ۢWC,-J׭Ò"zSχq R514U-[KL IC,6ڊ\lu[:whٮv|;9 @r  .iY_),.Sid^ aé\v-㩏]tL먥aMכbR&һ^گmuSW&6@&ZZu_x*&oTez0Ks\ZQiM|N{vw/ %}Zuh;{08MԸN!c4*y(4&VJD?SH(`zo9iheMLU!(ݶ5Zz6/d@Gh-k-Cr_(MXO8C渲54sr&AQ'crzRΤk]Gcʢ5%zŦFm %`, y[VJZHTi4# :1b^yG"v)KQ;1=1ߑN-V4f<w-no<S  oA8TF8rp0 # ^eyvjvx'~Pߙfp_"d--.gQە\vȤ`�'i,`1�{-nh+Y@kq z#WK a3f+=rq6%es}Xov zl{rѫ , 60!`+ 9XFe t&8`4w8s51]LIRҁ*` UZ'26fiJܺ$I݈" Ӗ}Wlomxė(I�وIx rIގuSeS-<<0M惷l8RU[YTt"Bv]'E!7d +g'=^6,z;vXӞSUTy"pv$:L>avd`𾩌l<|rϳ@\x7V#PNiGL k0 XTvժÛ"]ġYr=zdyMH3v\F``۴nnMjYء A|{XHA/x[\Y"ܭ[4·˯Wc56�R[UY90od~)?,Dꮴ z罾ijv+WUktN|&s [Uiuϒ8a ݣ1:%jh Ǹ6Ђ45BL%T|U�~U�'6 Yz6~YuM0Ҙey� '!ƃa!1˓#N2p.‚e߅&)u_"\T+0ߤ) 4lv̆Q\hWQmDa^\}28Cd{.0$9t̋lUЀ*Kxj)'dttW56I:@TYv<kEd$0'k602Vvs֠0cwuzhvD0pX[3�pMbzj.~KIh%jTC07uQy A+A*ϝY_Y3O fAc.h-hq{ Y&8{Ck;<p|)DqaPi{i`W2\/[Śl^ Os7r?#,BxcJEdMu}U2ȸK&y=Ac3Q]_^C^MP (E ֎Q^L*)?hbmcc[,\XУql(<} ĽvX {kU^wqV&;K \a=dӜX`EḲx[ Xk0]cIj>텢!nMKB=$kܬ~hB)d!݁ )Tm�w3o ;%gN!v4@rĶǀX$%8Dn2!^cUJ25ߜU-Ean,¸ }m#`J۷SptƍЧ`ތI iqyhlPPCnIdRD.uݱaLڏ5%(I76!9 ~0ss #hmQ8*3F"WÄEoyv04<i0-eL)J~`*\,5 5 6A~q9d{IMAce�&z+ߟMO#.5zoGG14OfXWZH63DQpݨmw VMI`~kW?r.wFh?|p^\Nczua7<N+ܩ ř=5^̝LpRq}FzEYJ~d9};@6ʗcbz$lzٛvaDj ҋEL&PSs)v.$ 澩ԨzO/F 7KsesIog¥$UsMX=*y< .zoƐd1ˋ~W5Y)mm9l:Je.kr7^bƚ#?IG~]Ʃw^x*XGǒ/ݕ(\p?-aO@aÇ!4ۚ4bi_f")48GTpjU82'3�NZ!Zc?>G'}_ �UfSo(Z"g8%a(+u WM ̮QixL}GO!Ý*0bGbT wb)aI"Ic4hs#D:'#ǚt4|& ի0jYWnmeՎŶ\Z]QYq`) E L wul_Yc^n| endstream endobj 121 0 obj <</Type/Pages/Count 6/Parent 53 0 R/Kids[119 0 R 122 0 R 123 0 R 124 0 R 125 0 R 126 0 R]>> endobj 117 0 obj <</XObject<</Im3 112 0 R>>/ProcSet[/PDF]>> endobj 113 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 20:51:22 2016)/ModDate(D:20160927205204-04'00')/Title(diff-ops-reads.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 114 0 obj <</Type/ExtGState/OPM 1>> endobj 115 0 obj <</BaseFont/XTUCJG+Times-Roman/FontDescriptor 127 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 0 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 0 722 611 556 722 722 333 0 722 611 0 722 722 556 0 667 556 611 722 0 0 0 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 127 0 obj <</Type/FontDescriptor/FontName/XTUCJG+Times-Roman/FontBBox[0 -218 775 688]/Flags 32/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 116/MissingWidth 500/XHeight 461/CharSet(/A/D/E/F/G/H/I/K/L/N/O/P/R/S/T/U/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 128 0 R>> endobj 128 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5365>>stream xmWPg^YHn$FHIpq .M3S :B"Q?(H4ӻq&qlI0r-7Hշoyy^f�cXK<#bD1A Jj Pl3g5R^ U6ep}cXy'q\ZBDXxܽZ~ÿٲk.?;#bRDQln{'saiqA!!o^AѢ(#qq7޲e#DDLpr|'Ļ]EaA a[Ɗ'I<t,.iBNNGxxFqزut[0l vsܰ70wl-恭<7S[6vۀ9aӘ3vی {ێ\w2a1 f0;b6&ž^c5-.гװ쟬8$G7k1sE/^X%K=v[[^y+_Zo/+Ymkww}$7R9ֳ}X^Fl<1*إrQȜ8ZLKi#ZiVW B4pt@ RW!H ǚju֋)<Ů֐%N Q>UrlB OL: x8Tohc{B$BbO+`icǏ ڂi*t>Dja5bB*DqXh7k P�m7 brzq=؛"_I< x B154no<Ӷ2j5YeS΅[r$xk0 睭+*܂wD6Z7ે|# Tp,8Ȗǣا8ql0 2J]ͯ3lyB Y/ZpH'�yy}DU,i'[+JG\<;շ E;=sʲ6|U~4N)[ Q{IsLY} l>B43רpOw* &Y <՜r|LC&c8˺1cK{I1a5@qOb.0Mgt$, Ζ XssLZJJ 9^V8́q,\^Y)9:r!:zg Յ[i{MSl*,|YzW=A5G px%sIYBP-ȬDҞbMr>-;UpH N%tN�WNC } }ߡƁ}E*𐀱 SP#ӿKXS|uDm2)m.O]6{. WqT m^^6guՔ&'ůvp<#&D>ѕA "9TruEtux.e*fBMŜ]rO.%"#q<0{[-7Z"60�R = 'Qiw G2J@ N9 `pt|QIOX: adn$M 0Mw_zZ2s=.��. `�n+Nj kتB-iY/a)E<0SЪE~JZrs s wgK+EEJT 2B؃^̝4]F3pl E +*&uJIm 8uZk?P c]\) g⸻zF^e` s/Y\UŰ~v)>W_)q ΒUq@D]jsV_̩{.<6GR{Yqϐ8kedAg&rg%#0 <QUT% 2Kg5媾>N5ڌ}Ԗ7DʕY2@$iJV13 O$RLEyy:anY^)Qs) m@ %#@$q =Ww嶀W]\ҷwtӗfzVEge(@/ hu¥|{uMm%y5 $'xh NK?+lÈeOMu\5grVT@Η;C{wK06jS0 68*i(N:^_t-U/#_@�_naS^V=#W]ڋ. 5{Qq')ɓd4G@Rt<z^L3WTuPEx(vmbQb^hVK2+AtUrQ]c2>%Hc9bNZ$ٖ|n6TU7=8ͬlj t % rraIr6 cGvI =M=[)07-7 W@52O)6AMuW8xZ^.D&~pq)?//*S .j8G,|5hpXi8"+*ժu, =_W gӫg4,U 65HmdCHʻUUE ٔ;*ېa>GR/*Mb^K3i)Q6 jΫN̍p*އmdQ#?08pͱ7Һ xnNhL(Lp3E M 3xv6 ŕ;d]a:=6knq/C/\?b_zٹ.0N댺vu#@HwF4!))<lgk[# f,?=kA/t'BkJ[+׫k�`m h:W0>@TJ'ƵV聧�OxZ}/ĸf`:Y8H65gZ@㭶c/k>7ݲFE WCKSS4?TOh*ʬәʉ3Ty~AҼySlaY>f Ȟ}x*K頓YqP&̊U-EKu׳<k"ni`LBDy$j`j0 12t^Cb�@sV@ڹdE( nWAmn̓ Kb y1$ԆA2 /L-gf:#)*L-a}F+YԳO4?Zy| bZSi勏 twpy>s~4i(7Is@f<yߠlh *�qBpVa6kʙAG2.)I,O477[biG~UޙlP_4l[EyМHf'TRsWF  vz@+ׇ+EeH?:$q[P8s1 +iWm'ɺ{5+!_>׌ k~IX5*ڕRAς:Zu9wT*GFh/W2_z \PրL3wq`Vcst4>,_rp.^rr0 hw 삜~0dJDc ܀hcez5|<1\7UD.5@9'"ȁ&C.W{gt~ Aju 3*c:.ՊcG�tcHZ{GLڣ"tYO:ˆb865(Vih+Kg<N.YtTܥHYԷtRo=kk kz 0/^+ K,OJ3dy ܫ@Vz؁r|@M  *;$/,[kq6Q^q7 x|ALa0vJv#| �хxB>zw1i֦=# n" V;Z8p9q\5׻.%`+ēSCx(~ǎa iZĂl81WjhՕɢs8$g29AL rp~`<xJ5<fٔ&š4\:MSӻyz' DEȢuYv?v {7*DGGI`N5[6sC[}~N =qPALT<%| M}#‘qp\NmMr|׵ O ;C.aaLXv4q}&#zCM򾡢 -ιq h7ycH˅Z=c]ԍ,8 G 꺿RY)tD\Bj\~VQNi[V'xJu1rڃʕ 'S͗/ ndR{ZTD-PVì ؔqnC>犸��|%Nо�.?)R"V[zKNhZrS͗K-&1 5&cujmruEj4j+>sppaXf-6cO?OiNjG?G|tkb{}co/+9芷d_I01~EN>n gϾgoF'ɭa`@wYw +DP=@YYX-gL&Uu_ކ6ÅDE+w!v Dl{!7, R3TPE =oTߪk֘W4oC;!f_7.LH3_L|uLu}~MWv?w=Iz ~U~fLt>'ufw-M] \ömt.pˁc:1%&ɍiU)Wc >t". OL$%/@@ҒLM8T:Dy۲%K^.Ya<. endstream endobj 129 0 obj <</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R>>/ProcSet[/PDF/Text]>> endobj 122 0 obj <</Type/Page/Contents[15 0 R 130 0 R 16 0 R]/Resources<</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/Xi7 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 130 0 obj <</Length 13311/Filter/FlateDecode>>stream xŝoݸϧSS<1}7N6r3UIj/dʣFwjslhM؝FnyCo?l`.hͿ[<4v~<|z} Fͥt#T2B_L¸ҘQ:Dg!]b ۴Q6ŖM&5뽡"nؾLEKњ2N"5ݥE_ 1*RGQ4%5PES OHb0)q'a2[o\&o}2^KOe%oÇ�/*/_XMJ(QR5̚Qj Xu.?~ꝛ.S7Ck~|~uͫ?>>|?@׻䠪Ȣls*k 37#>oCK -_tT@ym* @F趂)n˗1w~|@SXXÔ4[4jejpa?2wo͕tϝy{̅ї!_՗~BKD(^kv x:nOko7/ e}\/ Oe=??vQlۗ=F7f1tuRT_jn_߾߾}~܊WC:k 1fH0葉0W6Dj;tu^_Ezbp۪뵹:Sb Ztvqx(F #=�#_jF"_F"_nFӢ@\F"_+F@F"_F@FJWw~Wx/Heۿ6ǽdzH\fa6C%#OYy JzMQ<ڊ&f( ̊ U=İgY*&P#VhEkak9/ՠGÇvC{ g@dS|wz>ΐ] )_I)PH(#O-<RŎrJ&RIb2~)Y/1mcX)g$piFK8ƘLJl| F b&*p' qR5ZJ |OQ2(@;%**pN `@*T)d|93 \L,cw&Yy 8dyH0PhOdkR!z-c;Ѝ&KdrF,36B^٨ ,GXY(#eTFۥ<JrBWbk 9r�<ư\SNHʤ-F6\?ޱw9o\/;:&%_npRNQɗT'ܪ@տnVUBI+2P|+;pA\|*Wv2v&Wy8\yH0PhO6X*OelXFK`rF ,3�c߲i<Gh+2 yi<,!am@ذ5<BrBXbg<4XmcX)#eF&6`?޸IK !H@mRA%_R ,*PNI�K \hS`) wJ�,2XZ_ښ \L,lA9!#_tn??7$Oȯ)C~ǩ0\?zyZNU+c8# 0daQ S&�=,aQ:)'_HzRGTmcX)#`jjwc8G9:(J@b*e )$-efIɠhY Z;%@^rh/#в.>TyOi{w|T<Gߊ=*Q<z'3CG & _oqڱk2aqck[&BώMTۄQK dvyg>wt[x$]\ו.n6&\L޽&p?:#ك叀W$UUO AR@ꯔ *2pdPT)PJ?wJUU*˝J TSTɗqÁX|*W6屽~+>e}}<, (qDzl'4w0%v3t/Wި%/ n=Fh+3abWF2 e51,S(9=ư|SNĪڋ.w1g7MrFX$gU,L h 32FEˠ8S?JgU,?%PSɗ1sPǙ 59M󰅙?c*y\@֯"RL<95c׵~\;`^yu]ā6nba1llv̳/N�87lNIr_^?w/#51ŗCP|)Qz,K ܤ(_JTϝ/*pNIK TSR #xg://TK_a=oâGvZ9nL ġ ;,=h]zȊ=>ZWQCV 036r`e&LwnQGVlX;|{)nM\N ^�gփ9 .oc v.G ΖGKNJ׀V&L|MZPﱢ/5 oe_@hOiE<OkuӖlqzHsi" U60I&r:I{HOLC+Ighr7PM13ۼYmFzt >| J|8'VeP3J-V:5Cc'+)Pj@VHCLkG2lX-Z*d0`hZaWX(^.v^?\vRGXZ:9Z|K% aK-2(Ο\|KO @@T'�ver2V/]޴66K-�^quKM4TXu]39Y+JHWt vS.Z^pajㆷe h:)h+*W§v{E>Rh lc3U-Xw)$pW⨱v]aeI] LEvc[ vG . l!H@mRAb_) *PNI. \DS%wJ�2vu"v-_P: 55B XDCUŮhfrg+IpWj]¡C.Z^p vb:)h+*bW';_ TB�vEuq&]Ղe`Bv%.u]aeI]Ivc; F o, o-@mJo.= o/@ o0EJo1@Jo2&tص|Auj})˱&v+]\]\7kmt Pcr\4t$@..X~.[E^p 5u zʩɎܷ; PศN9+r0.qmro;ïTR$T.mc-kq]1ǵ>{^tF:F@RX$NU,I 辴 '2FˠpR JNU,Ŝ?%P#S8ɗp\Ք|52V)/Y#(EJ뿢 Hf(q+GnpرrL e0P5Ph qx);1jsjO=h4ZbAϫ*ڡu�#bvȱ 짚0в/ :Yr(A�V ؗ,ڍWݕlTX(�_)&8nb3WE;wp& QwUb*PTŗ; %B- TSPP�j dĝ@-ZMi{Z˗TGQ7sBf@8:**v.jʝYSBi]‘d3kU VAWe- ǗC,"j% rEȑj@VH (]6GZ] DPH`g~,Zuy+u7lPcv*f,qӋGd, M!H@RA2_n! *PNI`, \@S%wJ2f&m.c/X+Sx b,0gZYtR^P 5UU!+5\ZB:蚐] Y~Qd rdYZFȊ]A#_c. Xy4_4&U#,*EQDWt%a] T,m+ k 2A JJ5JfXJ'J]Id@uOI+@W7jt5m͂&]*AWgЌ9oa>}~uw@lD]X&<w M7Vh3qui :۰V)bX[\ppo]#w1ڮզ̸0PFdSX( [TR4�5P@Dobg ,-+tآnmTm]'zZYȳfx@ A#_$> TNK /w0Jmp$#_.)|@Ց;%|{&tw9tL Ip/{°j!&tzvM 0 U �8Э@pnؾ|4wVAoU=Uo3WϣG]X9t5!Ms-݊SMh \=4;([`I-@vlm,͐]Eox`8k}[YSzlG17VGOC4W(1ISQ*HAє@*) 4WK'wJ2P]N _F)TE.M-_P~MR 1TbR U:V]dՊ8Һ8#uMS&VAW)ty/<ézʣ89d cZ!U�'5 M,$PHT8ƞjcL!qI}>nbZp& 7Rb*PgTHŗ%B* TSO�R dĝ@*T>ݰj,Xm LU7^L߇;xn8MȢMr�P6kvE3;UV5d7a!sb� [KL ʰ g]&W1]+c_[K~̈́^$}Zj2vBZC-ie'#k_. SK1`;C,G'mi1U@~pKΏ@b_ꇄ"p!DP׀J9s25*ʜ 7TdmЅw/$8zxZwpKqh.=Is3l~3leb؝$)J^WF`B6$W�]$*W.\9Z  9_Ǧ`MK-u%L9xﰼr_F<ẁrd&ra:뇁g`??N#Ї*D_ WrJ|-R2(�@-;%*pN @@) ˸,ouEA,rV,rfU,5Y|PhtbXj-Y|PS5bY)Yj_,,5-cYҚE QPjSY%5,bUjf1eE(Y\fq, \SkWY)5)#7b(+f[prּWL9�q_5`'8h;pm?j W]PW S|0yrcgh^<<2 V�j?Gf'7e~دbe?%P2ϓ42?-`ͤa=\t}[znbk6`YP#Lְgj5HcX: )9(,ְRd�E@QbOp[DZ u ?6avB XEQj.Ǫ>wL) $,Sq]zeYj3ZUU;7} ԋE Uz`@ʯO嗆}[9Rh e19:ҀՂB*r%.[:3u]a]IYخ?RWYΈ^wtb*p% aKu?J[|P2(@%;%*pN @@N)˘˯/ }Amj3_)u]z!ܵ wUz*wE3{B0 *xšnW)-u5+:WUz^kW-XtR͞е5'W=MlFX2hR�aNk^hdR�Ȯz[트q$\pG'K7[b$*PTP%{ %. TS2s@.) %wJw d}-&EZ:wpb]j!CθK7t旧׶yz}Pc#jI\k]bE^oJ QntUA/{h] [U" N5m` 6�qQ,?r.C6"} w]aBp !�q!諰tE6r(:<V"g||[˞ǖ||ሞV || ʞ'I7[K>( *>|chw\XɠJp>fRF <, qD.]GE3 ήs"ڮmT1|ne ZSԋoNģ�QOFvXszXBB9!kST6ɗ,z37y15.cO#>rJ '}ܤmcL]'䏀$LUO A@j *2pdP`)P?wJ\UJ˝�L TSɗ0ztf._}>3 ?`f~}<, qDzg4s$8q&vS*00sfbg�3<f:3OF%&.dA0|er>fFhRGw2O`@k}'@6rBdֵ s1d^$9(/;!HL f)$de6Eɠ SU;%�Pq /#@ĉx9Y]\P2;ߖ o݄_2 }d͞<h5zY A+g(;ً)d}lT̄<[ 'd6ݓ/%X(g$`fbp>rJgbK73 8s1g$LxA Ar&_n$g TFK 9/7*Jřs$p&_)@;%|3Hntr򅪜z̙&g~8|!gnlt^gܩ\ۤ2ɟ<h`zyfNXו1jxwl# V6*kbƢw|Ջr>Feד$XQ)'$kZ=-lÊP9!]mcKΤ#R">WP$U% 1h@ /[Jq$08_)@;%ࢺucp_lkg%-_~17Rvi =8{>FLfX!xc`!*0�y cg`=.dalT䄉<V|c}O`@@I|Jg+y-Ԕs8ۦh{ v0:}]xA A2%_n$S TK /(JŔs$0%_)@;%|)_n$L|wk0-SϟSơ'+bC G|nf2s0U!z*c؁RSI=R Sbc=Mדm1 L0dʂ=VL Lc3HyL\K6[oU1K4$LaKdq0?'`?^jC0W=1 R*HA@*) 0W+wJ�&2PN $_ vrgڠD_Aibl10 1 Fm2T4@m2ݴI:ɵ1Z;14IPmckuv(S&Vء<.B;M2O9:J  4cNq^i'd t]cyrX^?~-K|X TO ADj ,2pd`)Q?wJf;%,%2PNI�K�c{1pX._ c\5!CW;ujx^&cǪȞ7Ո=X VUq+_X<Ű`daQ V.bX=,a�QI7=B%rBJta �I[11z;LPMiтӢdfeBA2@{Z|KeP,PM?%*ϟ`f )f<{bBUn@&5|(qd j}.‡&GKmUqA+_(wM|ڮ{6D%}a�Lr&Li3{e\M W}gE}O>ͽ'>NGk b'*p' yR5VJɓ|5Q2(@5;%'*p}N @S)<  <|iQz4] n( ?e~>Hh<ۧFs�:Mکȁ~ό \uj42F?ʓm潲Qhdb)lPNG KlXgZ|%G9!-yj`TSIX>5S,#1 |C`)Pz+K ܛ(X Tϝ�|rR U*�Xe<4nTvӥYM<,qw* }N%F;Y]#q+_6+/n\G'{n%+*EXa("@%FM'_GQF`ʗWRÿ�)v1,ڔ2UӚdQs9n6tٹԈ-#, r.*OAҥ@XZI|KePt)P?%.*:˟K (T)tٶ.ߧʖpi&߄ˣlu|خGY#HV?Azи"`;;??UcWPRZpg&2 6QT9;kCX!jTR{I7S8q38dO7R-|GncHvΈCPGF#|xC A2_^rC,PZ<de $)+ TÝ|sXY UK�+e58+Y5f1UE QR*p"(Y\fQ*JjSYUjwJPb5;%fqeŝ\2n5"Yd"G(YlK"k[R!W5VA,f2ȚW5?%P*şfe@OR2ž~SE˗l}Ō f(qcR{ 8?5}|)8T8q=u(481&At)7֘}J4Va`;\ Ct̷Mr(ubCO FSxo -_!a(a\n<;zEB+OT*"< + SYӦo-~Qה&CPH@c"_^4CW;:1R*HL]Aa@) W+8wJ�2PN `"_FOnW3l�Ư~цM@ 0(h\ j > .3TX'D 02at\nl"atU"A"lmO31A3H):E"Lhx0 rws-b~� ~9<ȡΩ- </PӍlÐAr�"Axwp%P"p]k@Bxpk%T"f3)E􅚏Z]suEE?WU'-n2#> Nq^ΰ2C!؍m<.ЕV=wOhq`/ZOhľ,Υpz\hktʈ} ha%\ac2^? k" @|?K0uEpwt|su^?ޏ"-`݂Hkb w*ʂ^ ) dH W+Pk<wJJdzɝ}젷뉾1fpB e7g"^P㐫n{Ӻͪ$4gDM&lsu͝(eIw>]>hV 4L?B7m+ $ĺ{TB!q-1MК N#e�#vmG1^o˵[b C t 鐯ub*PTtȗ%C 3S萯Wp� dȝC@A|꾨 af >(xmQ| ѣ.-x͚G) _oWoLu*0Ec_vEHm&)m&l3M᡽ܮQK~ YKºH7�rPk 7ֿ@EЛpO'r^b|ٌCPK@/_Ų! ɗbYZC+PVA/_i T@OI _W %J|2:K0H/Sԙz&x(qj̻D= zX] ލ%|/)q+_Ԁ_[GSwU2wW) ՗/&a]]y-ҕwxS9Uw3xsyŸ?m#иW4$U1S*H]A@j7) W+<wJ�w2PN ._\5f#, \!JjSY%5,JEIb5QP*PN YLfq, TSkSƭf ~&Y5bYtbXj9]*f*Ŗ,ZY*fj[RS, Y)Yj[FV蜟=clnW#g'bO8 P㈓CEP7ϏOڸWO,f{|1A@&dZOsͪa�lmrU.zʆԋT6]h}2uק q!wUZv�r&w 􂽀ՙ»'s 6smݞx?~<S "! !H<@-RA)_nw O*PcNIS \ֹSx*J$wJ�O2mzŧ,_Pi )<6QCٍC4o?~*O!}9Û$H3@pu6dV]x3AI/}K#jW)W~U|&HVeN&{njMȊSLdL$@>L~Mޱ%A )9WL.LG?dG1#^ȶ0!(FG/ !HFݝdD g)$#eEɠQ F;%<r/#0J})v׆'MJ 2(Ȫe2(溠΀fI0b" Mu"-~fdsU<~IPZc*!>}dF}Fj& h%ԵC IXWO�D D�j<0r ^<$ M̃Y&by)M|hUC@%_^hCDW$Q TL /<JEp$%_)@;%@{eJr<$(.?쫷?~N'_>\پ_?}yo?՗o_>/ǧ_~~<"vehUO-K1w>ϒԸO}mW_>ޟ;q^3 \* VtmZW}{=_z;9Zۏ{xck _-?Wo?_oW~yOqӏ?u^}:)a?#}u~I/pگx:׉Q_"N_??_{ȷmyK|0׏_>?Ywl<GO浟("^f)KC?-Ó)' x}'}B}a䐼dm%ۭ30:<ȟrMڛQ?"?_5v]:?tu c3} \\]ߦ[7mY|<:߯Ʀ/_~9-6w+-/r_sn^[~aS� endstream endobj 131 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-writes/diff-ops-writes-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 132 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 133 0 R>>/Font<</R8 134 0 R>>>>/Length 9243/Filter/FlateDecode>>stream x\M$q_1G)7dْe#,$jEGJ {gfgfy;n�Uʪ_n=7|n}Tn}J~哾}v[R}~gOy y߾;͟V 5ۛݎ<ZzOq/a>?x?ιDz?<u?+r?{ E/4r_%=<_})E?(E-^BQq.%ZkD'ޏF_9c *9~z{2'0 amկ7QZXӰ?~i4~ћym1&w7]ӈ9 }#lEIH~It/$~6rB~ ~| Ra9?~Yuׇ?G?M).'yFŻMC* |u ?n?M;a|o~__G#98i06^ϗc~ǧ FVqZoo>gu;gX=$@>7SOeܳ d>\bӠ{+❉{s_c<~7!}tm֏O󳯾?ްo.3t)=z z<n_}$n}+p" y@y]a[L(!J}ɑ!k> 1̉{+hx ;q{lJ8!6N&y;AC1%U ؈FxbU~Z+'=e.mhڑޅ=fs __H=u o5(p  nQ5rSw d}mk] GST!-Q{5X.P!~߽aXZL!펍A#k>�ۨXcPNcq?+ QtpXxCË @1ީ$3SGCRӘ8zp$~ ~G; |5;4n5*V'eK+?uPڝ]9lVށ=CstH>PmL ' ЦSi1JӪk)sl8BB$S9�:Tq+t,`2!y:1*ġu ˓UPf43ђhU.4IECzEOE-$bIX)gcxx8h(Þ`&ɔ'vEI̐L !?R^,+A2 uqr&c ѥ-Ǜ*Th He'lQ 5=$&h5g4@R5GESI0D1ZRFKYBT�b3m `#1 ?76`q"#7ZY1Ue,ԅ~rLx&}4 k `15 ])rp9j63h\Y(&qӘ^d{D7#Zo 89BZ؉pW> jaCgY98 <Պi0:4ZўiB pV+a;fΚт :9߳3ZS�vF+$ $g2@-20 Y9P ohj!Bfu,§*ajvf lQ47fBI2EB\]r٢�iB ZS[-x7 )n|048}YV 젙V-Gg]yjQGV ,jQIZXY]Vb/fق6{fclAi(we Gufby8 \fX2f f(xSle!Hg(h�/bq8eV sĂ1#",ڋ8K_{e"glk C5s,f_G8Y<Х\0{Ì,R;)@a肳D3c] ,Ǎf{ ,'=!D E"IdEN`#G7L'`"jrZsDh0P@sʡ缸3Iyw2013wXI^ܙssgzkg2^v L:wv/v=0w( 0e9whtLG>w0.;żrIwi H803{_߿8qwx<b=/88πqFpR-e{�0@*(xn~#a biU!8"T<[#ΆA0Ld3F24m38b-t18Z &%CPZ ۅD8` '[JӀhẀL{sXσs5 lg.0 5!aN?{z]� *FνyQ:pcKT0&KTѶ҉(ED)Eu VUh/*E&\:b|U/Q=!xQ f/QAkw29Q jKTa Ë hsxQ\ Mfmx_ g}}ѐYƁ+6Jm&<� N2ȓ'!3Wb~H&~< p"D{Q#f:Ar at6TxCŹR9֐AXܔf`%*a`8P|s_}OC$f24SÀ`*$ϰ`+4*qO1PB1PaJfTJ`/j n#8E` _>l L] .0|Ҁ@2T93o3$*Gs鍱eb?)>`�/}{a>pK8ؼ>0R>pB,}H8xx^N; >$f>@>�#P"N3- v=fڊ';}8h*="qE0Cd^qӰy}a?®K_꾧 y-F>;e^5õ~8ӻ8mb|n_B\ƒLWe JQq g݇& n44nL@lKHp 0m x[a@޲:ؔR}!4FS~a*A@_m+&0b(x7‰ }fӕa|'(I�[!*Q\y9Ai&+1Z1B9X*[qNAak 'q*^ã.E/(<JKPg\(]bGXLJntT̵{AQIvAQGY8f t!KP sfʰv)킊riKP )''(upY:G'/a_?`yΏ!g/vE[)u(*^-@ZR.3a*4`rHQ8{q~+ye!#L9Ntt+f� 9+oɻNZAGwO*߄'sHPUÒi%L&sL 9&3=%c̞c'|Pc ʟfuRB,Phc,8#r#stK=da9œ3f>Dhb9}tCCiK0diKzSzcC$^1L3>LE4)>jNI^({}~]CٲP>NxР8}TKUe \Np2 ʥޫ;} aiʭsMx rN6}y.o|&W~*xJNC*C+\Ehҡ[S5P} /Y2%%Aу8 > <VE 9c􍧏`6[cJHcRgHF0; Es}fh G s p8+*<kI`E m(,jQiDk&9+Fl.(s`Ul THx֣85+XP4 wIzgQM5+aY|C ~(]?+V Ϣ302ġAȰ `ICY<lVBNGUY3ʐq,C}*$DTI~ ơb,a 9!8 yuԀrh+Y@{M_E6a!A1˵ʣƹEQ9lJ6+[!cXm2d-f*ìUm/VWWC̈` Te$g=c^%ȬK,wvL)]߸B.LA3Y4'6$DUSвgQPY"4?X ɲ:gQPD*,*:xQwi[61}'Y⢪,5�UYTjhV|T@*ڞ a"7_Gjٳ@1�0l eWL–]J=V~b1m VԪ�#1*(bN*j#̰)&CL&Z .ur⡫1mp`=3tIa.Nr U3}GB`pʓa $lW~LYZ,r K bV<j+KϪ*(*+GS̊I `o+C,�N>%"\yd<1}Xuϕg`VިzUT^`EPC"U}XE^Aq?1),/b1AKz0ZVb {\`,/LabO͆%giՊE3PT9CI0rƃ-_}'F*XH[fa$`D<1ST}aR뤒N[YR#$˲0rҾ0R4P # BnH%;F'FF26{a0�Hn_ GC a$iR"1cT[ 0h #H^pJ2Iua$` Ma4FPa]FJ�ZH ֓H0tֽ0Rc #%ТH{)Ƴsa<FW"a`F“V0 H4|a5za H օkhiH Kh #ŬH54H1n0H/.,2bF,a} #%20RdaFɮj %AsNPBIM] PR YJ$xlԩ%<,7yP^UJ-:Q* %.-BI1J*-`*pҀp'Ner8i@KD:qR&94%\ș8UNN"hI' pп,I#D.<JB,`JƩ0@R@$u( y!l%!IID�w UmV/ŒI;O[ Ã$!Rc88$TArڂHE Љ"5(U'D"CVUOO=9eDEwnU 01eDjlĨ "a}ʢ;ԎcV'D&a"UjtX`W Rmq'D̔Ewc8+ +C$"D"f"a\ jŸ"a-JuWjclER[`Yp&ђpÎWxrKWQڤE 쫬S>^lLre_eD<Ү2 NmJ v5~3U˸Үb t=XZ|͕w?xJsiW+ #FJ .kT4Mv7tam9i+*(\HC;z]ͻZQ.6 ]yWyiE@+ g/dyn3 .McYԄDmKb5 7MF 1>ȥ r TKLx r HgVAN!xMH"]7_Spe =&$ =&$I0MH̕¸ 2 " ,M "CǦ M^Ud® UUWW&c29b |2zT(z\�kEf3V.U.J)|<!>!T9C!!"V>{+\/a"r+"\HE)ˇy^>_|+"I{mˇ�m/Uˇ^>D(~֞|r[0o|?^Ї<Ӈ^>L ^>L ڶZabSy/&|n|nClĀv+BaӇ4>�({+B^>L ӷa [01ʇjĜV>LnV>;[017'@2 (D,ְ|ouwe^=vx$VcZ1�E={eO7L̴wo({6a޽Zy&ݺmv{6)+{6ֽ]B}ƎX'> v紑s_ḟj#l4M' Sd] ,#)VaAy2Qf%7# I֚؂}"xZ#8́dQ<#CAr[{ ijCڔ0 $)L +ͥp >,hF+x1lRXe<`<44l./NX_wxMfpV1ىtq6a4$L4E2\,-zJڲIycbux(ڱɬe6tSM6Qy6i87Q%,gi3 _EMTO Eʱ_w{l"{DqE?&w.XStv9m_jMʅKM[]�[Wd%WWt5+/]qEKZۿ1f؂^5~53.k>\zzدnlW75_H ܮn0 bmW7tHدn [oدn oapu]� \vuCW7 zsut>\0uW7c`_�_݀8?\w|!]݀0?<\puC`@n`j!3`W7`Gn3ѷn?=\gFq]~uCuuH[W70دnHuW70zدnHuW704دnHg^W7W7dgغ_ݐ-un b߮nȖwW7Vnzbwu+ t ,JW7jW7Tܬ%}|H Z_5~`3gVC-٣j=B4�y:B`tj&IQ5B#dކr0Fhvu(]#4�Ab <]#t Mu#`zt�/\sr^u]?fʆ]+v )Ɏm:H2VUF]2#WT]-iHDIEB+뼩8FIZ&ϩxePfe^zpa e67d ,_mI^3$17_߿}R{bpj�O>l/.գgCh /׬M궳؂tkoĮ[A:7YM3Hj5mє"g5]z(Jge#5lzGA|A_}]?|+ ?=-tO9';\KeƫuN7L{7Rv9kBfrO59V2ԑLs uKF6ߡH֒.U9!>,֑B9s#?Z 4|^ "]Ej珬?;/8] ښz&֧K7X9lSfҀF˚! bntjo]Y9٦&bk ?QSw=_0d|>-x =ʿwsB.l<Br!r!KwEH٥ "޵\Efh=Q{Go{{/zxGV{F)f:]=u! |ѭ=[ŕ?`~tGcwlp mNe/&hNU%J-g0[2r/~) +9W!ȒX5#Ʀnńd*H<)?2Xˑy _16{Jy#UPHę3:)u3=DX+L3"BB`y]]%Π?Te=p̫9?V7FոkSTłEӺɕ$Y#G1C"kw2f`z)V [-r,6 ѷ 9Ĝ3hd˾: TcƊ3rw~orCCXw Sz?/![rV�ƔҟYRs:zl.DY fѕ؋h[B7^ōceyGFW &+/]dmRnYzXK,cvaNwZo%qb!6O>f@~Y`u1Efx`Y]3jк׬.qʙ/K\a.񄢻K7yj]]"uեDyW祳]]"$ `~//Ο=qXbg>b_obKdb]l麄]la'=bwqb|[lK ^l ?b8 endstream endobj 135 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 136 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�J\ endstream endobj 137 0 obj <</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/F57 65 0 R/F14 38 0 R/F70 105 0 R>>/XObject<</Fm2 135 0 R>>/ProcSet[/PDF/Text]>> endobj 123 0 obj <</Type/Page/Contents[17 0 R 138 0 R 18 0 R]/Resources<</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/F57 65 0 R/F14 38 0 R/F70 105 0 R/Xi8 1 0 R>>/XObject<</Fm2 135 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 138 0 obj <</Length 4556/Filter/FlateDecode>>stream xڥْ}U)*�ΓeI$Ju`K$J$6_wVJU=}(YV껛Doo^3LgSIݭ~>T.U3i1F?V!]90Rg8vkG2Fm#$ѧ*^vGX-,숖=V2_;0|"My\s9Uzc8 (ֺ8͕&[o~؊vSo:<}?p,ta(벯P5UWՎXzݥci3U <ۓZ@pێ]_4W ?~HK/TT6x5`\M%k?g8QUٗJF5*@+nP?c\ٔǾUx٫D ܏<檦_wնU@Vﻟ>jQ<U/(zv[{p9*C=ȕ4;ϩg!'n{Az4�}h|1ʁ OTwP)#ЃkE@c|칟I/YbjZr :Dy `BN<z$z ῇ-1o?!>ng^ 5VF+>DͨI܁0*T^弹TSV}ҿ´U 3 Q<6qLcW$x-bkΏp|]lhŪ!`"i8 VboP! #731n5ڄoPǎ~g0Ό"ڕCW m l\KOJ#;ȉhNqW"yfDz<xlY ѡ)e36pȩ$.>X٨CWnBZx'G;6e`۞j;nKPÙIz=;Odէ δ(u_S]Yoj ֱI20&]JM;0pnft6^GqN7@v�e /&9XUK׸<oa^ @B^VgHy=_⟱xvM,N�OSZՂwmIŽ3({noOB6.rGs*2@pGv3F6A|PxE38)&|۞M)� 91$�{B$ 26LN.N�kZA]O2ģ)Ĺ-&c| -y~4 j6h| !COk_,8HB�7D)"3zyoi,TOG;;^Gz&Wƌgt dVy"]<_~KV;Mhy"/�:>újUni&ù+x@Dн#W>XC(O0[,j ikV_?As#Iu~zCFJԔ]DNժ(K+EUxߗ6x %X魛-va?sM ɈRI6 id)m-#kZ?Ta<:О)/ySO7wHtt�yK"?m!౾]{Bn�N߲]v57jتsK Aʀ;gh)Ms˨\AlLs:j<a@=')fQĄFXňC ]wcy/?b6Cж1ft¾m{w\[ 棔S#f*tT'+08KFsn;2:v*$5Q݊,#H`CAr/i]Ǹuq9.W/Rq8;MWG=L{'];CQ!7v}P:7G?)s1%A6PJU pP_ޭU]-G6۪J:4ۺQXb:g7Gu9�WAnfO{W{7o ñ&_77,k7!W1ssGu=wwcSX^XB43y96a4W=.M$ "V{?1 QOC+En.;8g݈sI/D鑄tBlo80 ={L*dl_]5ʥy"ːYky7ts.Bg3Lglsn:9VCo6~2O!`?"uZ=]J2_t~^+D-,4ł4gly0} ~]({p4G(΁K(  I]8�}f"S*ZTf# 0E/usdI%0AAw[0&@U$fj ]ߞ@9W=y!Ʒ7UTw`- לQSI5 MJ Jn@1'0A7K6H4<-Br^x/,+K JL>>3Mq{RJ#.n %3uR85q1äpgIa_'q^Gڝ8hSʘFx!K gb;JA%` vIqJ' A D*]@<2qLs/9U ˰/OdxHyyR)blH5Hw( 1\B&Gq1$"VUX.xc:]u09KLP}O[>B3p1Ǵ3 B͆2Pnr/\-,`R2Ɉ({m6ݢ/d}�ڄSF/}bq] k%-va? X)F #1f|ͣ&]oRx'ќ~ҸR͸DаRt%9ŹUKʂ ŽBX!0~ DP&?_p  #|01C{�Bu nkvpbѓ K6rӮscugn{l*U�T~_7M<g3*b !6 J&cC ?3. ˧:fy#? %e@՚d((1It�7$v t 7w_(Tn~ i 2maKL7K $�Xk̪K$0<*նêDY+%bt@l=ZDat_s%bf/w=2f.NKŔՊtFZd15&?$_JSsY `O=s; XcU)�hz�'Ca`\C3>AlZE*…^/[މO XYz 9 3I E^֥A n.2`|06d[rɉ-Nz+4!j>Os+;f+y6casyN-FWXYY!u fU%XnAVyRa۱etX>i4`JޝM{76~lJ_A2G0 @GdV.8Jopwax_I@5Gߴa�QMsQB dC0F|/&5OE5f|gɏh?ČG7?JLl1+Ր6~UN?M̗>w$}xw381G`3ċ@Ț,^Q u6_K_?U@,TcB^z)C]HP­`bK\QR*�,-Ίa=NuO\Bz*69;͵e7ŏo :JK)CAxD2 YGr ܿz`J`ӿď=u>Ĭ›k7Dr[ڑ7l;KP:&ݩ@IҀvr_%38Op *u"�K*mV̗}}eVbu?\UiByǣf{4EQ F~z&ʷ, [߆wX}܎7 X^'lqoiW =Ɂ!aEc6ocsycU*v:[M/L7)(// ;+kz~7AFϊSt)#[|Ȼm< Yqxvms+\<Lė rfo<b뮲cm:u`f:ɧcTo1kWm* pJH< JO^0S\>sw| %Fp0p[~Y}C~[4%S6 ޡ鿍p<g&ǵPzU endstream endobj 136 0 obj <</XObject<</Im4 131 0 R>>/ProcSet[/PDF]>> endobj 132 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 21:55:29 2016)/ModDate(D:20160927215541-04'00')/Title(diff-ops-writes.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 133 0 obj <</Type/ExtGState/OPM 1>> endobj 134 0 obj <</BaseFont/KPWIKZ+Times-Roman/FontDescriptor 139 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 0 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 0 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 139 0 obj <</Type/FontDescriptor/FontName/KPWIKZ+Times-Roman/FontBBox[0 -218 932 688]/Flags 32/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 139/MissingWidth 500/XHeight 461/CharSet(/A/C/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 140 0 R>> endobj 140 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5716>>stream xmX Tڞ23nTSڄZ+nj] "E$$$k $,Bd_qj܊q'x{=99|.f5cX3<£+EQAі,z6@8;s\3 6aUvȆ^<~ jfalK=$ą%}pe˖?Jb,[̼IFb 휘##ÏمFJb킂yE #vGĈ>pn̟ã&MFn_hb. M W0 [-[#ng݉="9/x@k{ACQ^׬]# |b0G[ab >abRl;sV`3ہvb.l51#lsfc6Q\b<l6>bYaB1+54e͔rvV|N3>Wd 94uԢyN{8yz ٌfް>d[5f5jFeszNs7~y{ҷ,`=&ָ:҅=DyMdOESSybk$B`�N@޳,F <'U{bXLL(4C9t0.BQv*\"\+ޙ Yh'IV@E~*@B(> oaj"xpit.SB+ Wz&S2BibBv$�R8ote>P&Cqk9ֈ.YU�:[ZnFw&HHA-0Nt ]oU8$E 9 1E2]0 p1sF)L1_a>H!>bhm'ùCs 5zezqRם$K ^Ƿ_%f1Ufc'8IxzRJj2H|YoOa"y'\ iY{ 4p9)iเNȯ�doTsIǺ-qd*Uj<r3Jw\-(I`"�^Jk:],H5WQ >#jxp7 grC"=PRCʃKRMmA$bnu:ğ\^ ݯ6fW0et.W'MCTI0 e -B6y=|t�DArPYNxA/I$/(PS#A$%"nϙ?|ynt6?&Я&J ,0j٥�$^�B@BY9)SR0Jp>FV<g8f+&`9DC.D,;ka&([KϣKr�y$%@ Me1pDKߙ U)B^dܚd Np|Cdy&*\ J#?y0b R7Jd߷^h }_>wAؒТHl.$AcQ<UM^iR؜.-*.(4M-^:P) IneqbbAOܽ{'>ޖ\"y"H}t"VqLAz\+cu)ٵ + UGn =z] n6\n _S+DMj8m{Nb ޗUU6%@}DDqB@m?t6]MZ\ C9xX"O*/1a*y^rN�F�\)ְYubh<bH⺜t&-=MzdܗJM&c&wΣ +CuZҤ8*Bd `} nQM#Fc !'hI#j#R%pU&K_"&x^B^uvS'aawq(VIǟ2pe�~V)q=L|HJ�Q\ILdp^>Er'Q!i] >u)>cj/=[γ8nedAɐ, lUnzێ)dLgˌ2%@Uu}\)zT!d_�e4\%\[Wm)) Krlhj>O@ab<'.{]s^UI=O겶NZhUdFZRj\0>Av8drTY25H QhZo KK1 v'e '[?Hl$<@WsA<QNPY7l$r:BwzfEB&BOug& Wo2d1 ^B�- K҂DϠ߃omyd[o2Ӫkܓ35bu:R{?zEFbp;%dҰ` wEm:�˟2r TUW1F-^EL,Z ͗-i)9YRԈX^<PetTNgV*G9jC٧" d .(ylXAZPH$'ou\tA�W-|aiN#^) ^] EUj1^1;9'@ |BS:cCcDߥ&|n6 m~{zC 2;R</2`;9�bt{Mւ_Hz  &9帥@=jxGTlP5l3raR^ Q[Q:*@%JƮ-DsS.uO 2MJ)ISE| r>>?tSp@iJdkcvmk pUaԗ :-NY噆4$#WE9h>W. zz?ew`t $Yr@m A`ceI5 ꛪƸvj/Kᇏ8G﯇w@T铓5P'ү0ZNuC!ΆwA18+Z/ ] ;|h~qUrgC2V`@qy@ӁbgWZ4.!!,#Ua^a<3;Hv \Y2mn[66wVeJ@jCs6/-lW:Y|<"-hMq5L2~)5 ~>2<t}uW[گA3X: 8aZsU[4o`I̢H:ɪAm^ܼI}5'B3|4qږL$39YXl)mN. Ϗ 4=x"ӤԞdg |ݣ'͕-\?ܻ-QEyq%ZKr9)j631VB"<U,}_*Bݶ#ZZ~K]1 4K*Uae < u�_- K-GƗ)a~.$: loZZꍽ`):b=z6SbZsRZ��e!.4t\ ?e.-W$a˲-VAtC y23NUgG0,O[xu~UF�'r!*iZgy$7LbDscmM96ZFޞhQO+(e.w}DTJ{ňl1m- )ǣ"%A l?\ =!,Q&xr5.?oBab-+7g,|QÜ$VwχėO}q,ȋOCK,ev&R`S(-qP#ES:<nL\fN^YFh7ql[:`"ٰ,N_a s1oAڡyYyYYbU~: Sb6Ơhmlb<!rL 4<Y@mi"3OvKE9QǍiHQ`ⅎQUNT$%$bwF7r+pu1Me |0iY ax$Imfzv_[2�F@(Ԡ/-efEN왱8$Wd0UPTHүxBN)zsWb酔>D}I�YΐaΈ3yRE6}m !@8T[` se: ֪@^qPn&͋ST.<JiW7yǺK.7@$wp!<oqCƳYiib`g3 $ǪDh6مi59t#(u L)Mbr.VG[;Ԇ34URS=ܒ-&f$+ࡉẉ̐JK,׏Գ꾇-hv<@4$�MՊ8? ΂ዝp& h.?+=q!ح_; a ϗ3`+6̶9QVXZPW,`^]"XquBJ,MdU$7EN ӵYWlZߡ!#8:mc#yx% G"?e WnX#@;'g9__ ށ+P]v=$7 GUI{''%yZ3(ٔqY 0K|YlCmσS�PYh\ *J$?)&SO6~KG2K[�nMq“h#enPZy}) eOg+@A^K4,ITI#R1-d2 [5>$Boݔ,D#jt-O?p`Te wEӪ'cX'_iUnpu k|:._g,̬WA>9"4BA\ Wד(^qb(0%x^Ug&W'M,C wEK*}Fw[M/ylsPt133aMP?oNW ǻ[~0]>9p#`$|,xՅ#GlۍAf8t�\"O|v/`舻@fDPG[YM߰$RJx{Y�'*8Qhh>1.VE-%8N4e(>D -~W۩cXf/*6y]ؒI&s;>=~"*l7~&jyK ~ӿhn~!n ֎=[Ւb̥!̼nwp/r ur*ݔh5zzJ :VGԊۋE|n`EQȡu4A43^V͘aA~` endstream endobj 141 0 obj <</Font<</F38 34 0 R/F77 142 0 R/F48 143 0 R/F39 35 0 R/F40 37 0 R/F57 65 0 R/F7 98 0 R/F14 38 0 R/F70 105 0 R>>/ProcSet[/PDF/Text]>> endobj 124 0 obj <</Type/Page/Contents[9 0 R 144 0 R 10 0 R]/Resources<</Font<</F38 34 0 R/F77 142 0 R/F48 143 0 R/F39 35 0 R/F40 37 0 R/F57 65 0 R/F7 98 0 R/F14 38 0 R/F70 105 0 R/Xi9 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 144 0 obj <</Length 5605/Filter/FlateDecode>>stream xڽ\YsƖ~`Քk* h ybfd<@$$bL �Fsn,d,y8Ys`1ٷ/_/$Y523!g͋%9γ̔ұzMގeif fJƹ\_ݼZg,nf6Ma2Lnߣ]q_Wsh{w??vNcd75hC$In=Aq.o(kz\,.ʶ{|"a.Je8d78|)qda.E'9rx)fMg_B&q(ߦ{rV_feVOZʓXI=}Z?6uj/1_Y4=͍HG_un۔\}KW2r5u_߾һ+)K<XelN%u8TQ4{&>/1 Q{}v�o(N)%b]+Osxg\`L1Xa:TǙ|fMT&"$q.oW:5pD${TH#bCZ'$Ybߖ ]8'J.aFWiĠcƃByS?2"J˘8J XV]eQnyH`zv ;2g> v.kS&NO%F|4:N ^>m~٣T yHvG.>>d{YXfKd<*OrA<Au,Ey.c`33Yduw({(ɟK d<hggɣN^7J}B=K`Y>yBLz4WѶ+֧S1d$=c\dT%QLݧ;9i|==iF zd|tDajJo�`2xsuѕ"(*;Usq+! t@ݵ_maKI-?4dnj.\qmn-T,+Dvj29Ge_wՎϨ6CՓUy̹v曘C)9Jqq466q.ͩ3NyC\)r h+[|N{,(QcŶvo8sT3 +<I<~� RF?_~O%a!XX0`NPwSSPƉJ>ӯ_k+(I8m *zKu h+Z=H8bt<&XF˴#V[dI3)[7 *Vt/onICghg6ci7[@H,n{*7^UʽU^7]U+^u( 宬ҍP3խmi 7;[7L{hX6 I�"vK0ZtrwxK%\$Nm620êݞi,}p"lY}dXY,V\%Tʮ$h&ܩ_}&Q[aᇪ޿Ǣdi^-ɡǞ ތ97n׎UIp^wC@Mxi|C$ƝdeVվF=7tA I_'U`#R-oe*ꭣn۸v;H)nQq_V/KG؉ `ݪ^,zWlY{ >C_NOhoMRvܙ9|}d1~ 8K\&\~8-A]ؒLE1LG{v*ҭ(V%C2&K~d[#LNTGnVn X�fz4/X:aQ䱵'z b VEǃUD 矃 -,=,3b/jSH 4?rUd}\hm<8iYrQi H4 Xb K5uk[Wc`rl +<WFO|54p>XQ4m;[,YfW�$FA&4-US`?3i"}S,ˠ|©I.AnRs/%N*Ip7 6(pNEQflmc[^EQwxG�[&7ϙo,kKKl )ՎF E?Ğ|F"aV+byhIЮϏ (]_(QfE2Z=AO1#DShrH\V0{sN rUWXԬC #,lP0y!ł$Ɂpv_N3Sh p7# 4QZsчcA"[W5gLX9 = p܈Z4(e[Z Mɥ]eJ3;ݡE \}:Ď7sn? qk< |[߶v"Ać0C(uC�@weCKR c9vF*,*> 6lYuJ#&|")b{cC9l?�7*vEeX(LRG 9vnAX[r]$Z.=q=&]phNݣJB(mEXUS*jw̬7ON42Wб}с&rs)*οi1BbǧSv̹)Ȳ 󩍪 .:qpA1'κ;<Gl1V:X!/Bgc4NU<UTx JC[AHX<ް↋5h=N 訫աp(A:5Gg>] 'o)}ħPm%L�@WcEQ5o؀lGqfpni8%͠s`j7raU}F @_2ʷ>c0aoιM9TTu gY[- gա$y"n2l6ctK<K.tZH k^1TєQ.r[lVb=.lhsE#yl 8ݶy~=5 tT"Z&u){iHM#?;ө,,@0H<H$Pji" (Ak 'hC@)Žxؾr# 8kXk 5Vj]4L{xG-d8=c 6;X,}-A &=ݱX/a]Nl' KpdAɃ4!kjD޾{(Xo#\[OؙtO�VdBz B00=};0('>p�@H}|"G넗E'?5o?HHSpޜνH$ذ`ߠuSO)5yS1jx)X:N|A% M$~ 3s}dЂc4fOv9aZd2gP0�.#X a#r\Dv4knNzW vÈ=ae{ lkB)dp+Cm4zR31 _ kx][Wdk?kE@PA 1(Ub 2\rK2EL9K/(t|ʦm׭_C9]T<n9vP0\챽0$/W*}k4 @x]խi] $:$pұ5\TT_xK8?vY3M0%#$} sݔ'ޙwmrtsBJHa-<9H]'V;^g lVb߀’'cPZ3ۢrPN [#&&U+52m)!IL"cX*$LOBll"XGjF8jHdOxK@bJǰ(k�4@{%n R9RMx,2r`֧j%id*KP>pO`V.jQ'O0H{졬d*qeC>BV'n Ae4c=hU\dKBGK+C/=̠v 󆦲їb/7]%b5lK2vv]/3i:PLi:'b[WG7za%+gxE "-2RX˓ӻ $]$? (_q嗑34\Pρ:4>}!X֪ TI񂩚SSzQpC 繘12"< B.t X9.}: Ԙ$:i';m,� 2i Dops@ʮ'bd#HH0,1$`aTi^uWR8?dLS/"NtnYI0"X?;%<m=BV>1L%" 'z߮lf,@#Gߗ-ԃ{eF]z[ ]r1 a{7tQ31NmEOY~uպ썁DZ xQ`es|b;U 1"xm5 w"[G ؎0o;]P w$5c܎Ex~޹/k']Sx?U>ʼnБ=LKKSX B[Ȳ!Y4!GO%L6ҳ9s6I?2ŐoI>LЩysx+>לtf9>-ckz?\ } 8فi_+ǎUfJe$; F>)}xPp<8\E = V,8) @a9kՂX,L  4|ޅ5GKJUح|ր˽?81g#͹-݀`xe?+bLQA8=3A0Q'-V%Meݭ8,Tuᛩ0F4RP]XolVIlm0 I.@o;J{X�- w: %s\Mҧw*W\ |iWH{Vko)g) 'u;c v Yf/F I"BT3qeQg+f˚0?QdRHa�.q/a>`)IW1A],3p x SzIJʫ%a.W~P )q,~mx>행-~؜VG !2TK!f:}ߴzꓻsjm*SR:;BaָI�+=hϒuxCG@tۣ7/k2 endstream endobj 142 0 obj <</Type/Font/Subtype/Type1/BaseFont/PIADRO+NimbusMonL-Bold/FontDescriptor 145 0 R/FirstChar 40/LastChar 119/Widths 146 0 R/Encoding 44 0 R>> endobj 143 0 obj <</Type/Font/Subtype/Type1/BaseFont/HHRWSH+CMMI9/FontDescriptor 147 0 R/FirstChar 22/LastChar 22/Widths 148 0 R>> endobj 149 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/file-reads/file-reads-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 150 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 151 0 R>>/Font<</R8 152 0 R>>>>/Length 12549/Filter/FlateDecode>>stream x}]lmTJnRDzdT![$<%FBZX;sgGW/� |g~$ۧw_>|'~_RuۧOo|Wdȏ&ϩ||=ҿ}Ocp=~?s~%/$Qasovn[Q(kJ<uyUլt\Ly[_>У9LSW>jz}ضvO"SI_9u|cB])99u|c\rW?_ "͏|"^_?h$$:N~/9Uu|cR^oϾ_PdN\o~()NjE{OSܛRs˲ON'+?=_/Ko{w;|MfǏ~wo>2SM§HwcØןH/W\(Ls֭İ~:I}[Cv# tL+Ozɿ?OmG,O_-OQj-/⿡*la~=G_=T%zJ>`%ꘜ4<d,zK Ǭ,8H_3T8_Zj35c>RkС:AC3 !PrE(LO59ѵƙu]m(CuyI/{/{&f_Fw.{$,wْx/{]K{ VNeK&lL&J姑 UZUjEglijϷ퍤\OY+:1S0.3:? CRZф?ou:_ʹZRkww=ԩSY;Hì9H:}|iM ojk6gN}Fco`jjY=/) r"57Zlc*~33쬛L[W m!FRӬit3Q!C:#0DWn:䂮<z1+E'^g5%datgROp!c)GKKY8DLܵsu15\wkepңc*?bjnSuw YVpLj[ņKXctrO[UL;3-TQ:cmVéG!f'qZՔ?Tɍ/! ; #Ea(C 2>2sV7(he4Y"e4UQfi i696SW q6e8+Vg2M! 0V36l^kNg2mg2 L+2dd:؟3:c2]DޙLN1 Ut&p;=63֏Qͨ7ҙP;K܌.QύWe3Y|ی.FM\=EQPR< 7Agqc^^؇7 ϙ6q,Ra7)-zK C=.6\< @sjj4jMx{# {%`&5QQ.r`qiw5{%]pJZ!gwtR2ިWڌ3P/P2ިW`QcMߙ8+JK`Dߙ2QۤMo+:a78ȕ=?2Aoq.ިWz57B]6y`yvW`mMbm A6@c0.!y{9w 14g/.{!ɱyȉ/{9w o@e/lwˁ^ym8{9w !Lg07m{!Iqr6 ^Z`]k^wCQ~@őuOso'o{=wyl}߮@<ʱƗ9v0%9zzv@69;.PONc㇚W1bx@p!S$9_$R6jxAjl@mLǛ%B¢<F`S7zٗ}IPn=' 1aX/o#'6ƘB3Ʒ^iq8ӤdA1MRH#LSUzia3)~"rӴa5iRTiZp4�Ӵ2M6i%#7Mj/4'5M{4isnJ d>ܖk;t<[=q^|z?>n}kݯQ{nO^q_ <|t9tukN<l÷\(GzQ0_Ҟ x:p^Ofg='?'3w8'3NPrNF:=t'h@W4`t"ʟҙ(go!\<Go^rٸ+bwpd#W:Bztԃp49`z5#YWeqFu=�=KYƫ BPwO'H҆iī^2OF=,Dok0WQ. n#WPn, S WP̻-*D/61l_Ĕja \U}< ;R+qV|6jg6K0 7Ѡ1F7]ж r(]&Zz3 yp[jJ!3_aWW._-5ڵU'Em�`JOtLE,a' \ƹfWDC2)t1sݸ7D 6)45Wc%c*2:Fݝ1\XMD[6fETƖ a? bZ n m ydK ѻ$TwQ-w UV]/i[üf:LY/[7}MGi#rm`{z] ´.Ńz=#=z[ 5�7Tm`A䆊tk.**tRaILP;Hm"UFFT P*IE [pR$4RW\Cۜ:)t$\A 7 Ta<H.)t= g7R,�#R(.0wb%nP|)Mx wͼ (@t@Q7PAJB=)g rH1=R~FBw(<H!p=Rg�T PLk= q7NLmPq81O@ D s,>�o(F %É 8x7N:S+l|{ϑk0}㈓h!NzV5P' Z!V!"oE2ܪ[m$=#P 05 [ Fh-e.F>BU*H Lee j@Yћ�7@McQfMkAVSqaT,>1z.`Ō*@-^Xr+`iѽc< Um)dG;!+zf.EYqာaK($�my"&㋰T&!z#�݇ƈˇ3t*fpKnw q>,3[fZYvźpLDفYjs#.z; a 1tpyA3\x}_8\r#cFqM6FOnƤ"D, l d-WN!N#D(^:AY"tj#"",сHDT@r "jn"}:" 3:(`Y!b,aePq  Qt_C"Je<APz �SpQ@!7DeYsxU<DtD<D¿CDټ"D[ADÜ",CR7m.:ˁ^zLՊ/ssr B.I讇<s1tYϵhN !̚"'D%…5Ii9eD @ )/U|N'co.cŁ<aϛ[`@rG 9",HNL4P `pV$FG08+#~$gQ0 ~Wu4 Os(Ztd<WgcX"t<x*%`V~4GTOs9O<,hNp&X+?Үư쬧9gE< HV*%,i'5i[`/Os(O 0 aD 4gh4GD_aPOsDx ͡ʥ%L4G~q6M29@s Y„:dTL~p+ب B]X^C9ކ aDGo' txB`ѧ`B90ay.K&ꨥ5z]&h:j$ ]Q6$zC-OutCu97] )&`9Ty9`ӡ:A`BCu::TQ'Ou$ A<QȤ;Nr0!IaTjR.pcTg)(@u.tij%PNӫI@3ՁL#k&zߧ3Y\ԩ΁TYLŤwT#WFY83}SڰߠƋ=~U)c$K8KL˜[}(Ba*fth};V؁|j u+uG/,<SS[?p<,mN+}3 5UNr^&.5Y[<ҾnXh0ȟ]8caN0eә1g4ԡ c&s8{ DcVA�uHɧk|_KRpH-N4e.8"犁uϩc EL1I:zeЏ:2!Џ+oc~Bʾr !uL҉}; <*PW/Ad|aBs/iXTZaDU3uź-ƔA(B?sKt8х?fʑQ8y͐lP&� xxaZ<ɘ_8EL][2Bp%Z4[WuU{dj0!97`_¸2Sg ^E1w}P@ M_!9dq!ZfZȯ8l~- lմPEDžhQFi…hQƽXv Qd\_1i诇.ǘ_B\2.;n1.T>B@FlAEgw2ژeA,nai~4莾3h>bQκwu }VFњD}Ht>%pZƨ@3,@C2ځP1KY%sl^vV9!06DÐ.^MݤhwT"6j&[-fWvDz3ٱwvALeGbKPQDMW6q, gP8@0*bTЮk*겫 !]aSlYػN]5p6c ?BbBTAVoZ HCJM* M JTᨂޫT5Љ*(9*9U`fm9 `iyQ䩂,D<USԚxSI뻩,Ⱥ (.P:TA5ەTIC1+\ (d@MnCRM\T5SA8T!AUW~3pTAѧ>T! M*?TAP!M -uSATU'QA-ZvT!/U~Sַ S;TA݋F 0vki8 #3Lwn6y+т>0E26UQPM, U݊npUUW}S+<sS}SGo`sn\&!~S9y/pU7 ~S+vKEQg c7UtTA$P UGe+Ž5opEUP<<TAPM,9UPP O*$Q+Bb9psUPdT ;PC er?T P;Ps*\qCJcP+Z~B̔;bχ*pT;P2Ӷ*\CtET"8 C,!*"G2b|T"p7UP8uBePCG '*䊂BfP];q !RwCqw\LxIW/58k+m(XҌ`,ʡJT(` CV,ܣS(2`4 ,`6 91[5*X5%A7EPV,j,`TAPpQ ,\A"H{"D AYIP(Ҍ`iuJ.f[S AaIPXY KEʂE' A+X `!@ ,VR;S((XtoQbex>cz>A" eAbz>fj`t} ^b|>c!A4 GԎ+DA,zP Aw`ӗ`8:3A7z"(Xp+XBwPG `QKlQ~4Q"h.,M)f+?u.,u1`ނ`QHQNP(``Q`-2EeE! [^Q`Q|Qh[P>A`]`Q"^[V  YQ"`Ah 4E,n#*X^ ݩ%*X}`945<?R|N'co.cA\"Z, l ;, ^PA"@ `{9a`Y`a+X`a*G D ,)۠`ۃ ,.J05,JFTVT:QR2*=;E: [ A >(X A"{KA?(XHİ4g,E@,<M,`)",ר`)RzTAR`h.@,j#(X A_ "C R,Ѯ`)ErT.QRJiQ"ЉK)@),`B4ˠ``a2(X AeP?BȭK )XY (X dAWJT0paBF AR -qK`)(xpTOuؘ!(X*WTˠ`)H3 ^R - ,[KAW0l,lm>kT%KTW$ˠ`)) CAR . ,] ;x CARІ.(Xm =`,W A1(XB , ] N )rwhC* |gȎ~7wCTv)*xC.ϰRJ4T| B( icQ%3+h QN1|0KSXf$3, VCeнrMXIHH;aLṉ+)զi) U`$v2,|0kdWWe_=FvHv"J;eN-n4֧&NRRI7ܥbr۩h]JY2*5 컔dVU>5SuBݖw/a!FwZxd Nf}4xc!ÆLyT@0!X0ٱ/)RE}9]6dm?AjE- 8k9xM*,=QJe^+VUeu9U>Xm "'gKwJO];ܲNYV>gYA%se/YVlϲx,+`\$/[V>(eљ/+P|ULle ϲRD&98 5fe gY!FWò,+P}`gY!d$-+?aY!o 峬J"_V`*)[.yP>*)[=8 Ujme,YVPe.YV~Y!J BsUm ,+nPnYVZ[7½er-+,ᗕn<LneGFs-/lg7Z###s5~w}JU|/{?E=q:&ۻC5tLf(hݭzbf\dAh<1;&ȮcmK;& Z,H.v1Yc25wL&ީb3r}C!Ղ^ϯCB_ߩf{#+Oly0ry0hZg3~C|ywYA5f[x WP 4z:BkiL3RnVod=-EVo:M-!P,;DƄT-QdLj\WiNs7y Rv/߱vWZ-I M-i!fS˶ *_PɚBW˱f" T)Zb1 "pk[ nhb�JLj)&bT 4ctIL^>L*QmT-9#BQЫˠ-)9eZ/5|9;>Y(5\560.pӊ8\֌ ]=ZY-2|֌\==Z.h͌V{f|V+=Z3bG!{Z ѪE?=ZYT]V%Tӣ|VjXGI,|V=ZM{ZjףjB}VӧVzhe1g=Z)IGUjggU+nǢ?s} 6d4˞Wr(W\iSNi !jz/ÐONii:<gQh և@:C~:Bu<aNЪ&i]�_:wZWRծ:wZ#$tZG9;C} 0CuTi0NӺ]uH6N{>s\}5Z2|yJ0Zeq)aWz*K @r` uoٚΈ1Lظ:5i`;T6Eik7=dX-;Ǥ9̑vs5i>]D&!0v#o!:l1cP!ऐwz/,@o:lRxp9c" 5l5 )(+8РԽ'`aw9ŮlQtaJ.Z+޶y�$LVrv ؏x�eʹc&UhM.!)Jdj^$4R*LeFm^9$+t%P4F俵 };w<%YAT.QI0D8� xۺ$օ•Rxk]mQ{X_E ׿䭺R~/~N/5Ygjļi3!,)ԁ;aR"l^bPcL: )p(Y^XJ >=ڍuo%1qGbM0- DdN z '6(yEi �,\Xx@=L˻�T#_x@- &` zii:W$ hyDi�j6<4]Gx�`d#<b� 2C�"L$4!+N4c>ͼs}sۢ[4c=.ϗ-QAo�`ь?F&Hhl-,-~,[Rh,Ef<X47ZX ΢_~h-,E2-y,4[%EDf[49h$ԂE0h̖hgѲ{UE3|f ](Gŀ;u 7K�U[v+xw t)%[=-k͈Q]}9 5b%K{DɃXlFjmk3mU5?#l)�n c,[BlmJY՞Tݬ83(haB6w@HYa,hBجn)I=XY(i}1 `8S& |?ǯs?{?uFGU~CW>Wp= ò׳0v`?¸! w`\pp=pe';3p?63C Ђda1Wc 2Z}~$1@}z7W�Ɲc֓=\{\k{\/m%GH6gؒ̓zgiC4V\Ϛ*O<ա|NM/w/?_Zgk"ZNh̎I1;[ٺbTl6E 2ZNnONPQNldO)LY,W ;!5GaCxt{�ϧq/5`YK  <cbPߡp/[yԗ,]$/YOKmR*VU^ʥd̷X *ب)Шӱ oK/EL|Ml˄dhg#Ԝ޸mT=T .UC *&ն2߅ϻ|dC,wq: s*hɠ=1mKih< dL <X2}d6 ޵8z`c?TDKF%n(cJd!ł=K'X\Rjʟ`Kҋ%(5+Zr6X2ԐђY9-Y v|oɷe%;YK~5<d?NsYK׹ Iй Ж 9ؖ UdбdSJC;1(b0c0b0c0 = <=q о 6.6d86j94ނ86͵BV[72 &bˬn-\;,t0VJ/Vs$6D?t“hCi \$c)Q Qpо%t"t}9_>AR;:!+;:-k Z0ִ7kІNLVq,!#,Y9UhoJ!7!UPGy: u%_[DWAkH}|?y܁ა�м}3G;ZXr? 7B["&Rd2d4| L%V]8o֨ac" YjAc Un^Bd߻5< y{Ac%\U$< ASiTt? c, i̮d+ҺjàI֕.MlffpZ([; H޳|ASrm ӗ"js΂GJ}: z| W:]B6u<s/@&΂Jh['YQ+@&΂vYށu<.c<,rD vuM sՁuT: zKy6sujO8=,x[`%ك |Kk>_d>\uG}/ DF#cS�id�7ҕ*m, =.Kw;GƲ1頫>2Mk,):?ʱTfoLNӏ@g`Ј5ָո*\K[d5!B{G U,R7y c{l endstream endobj 153 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 154 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�Jc endstream endobj 155 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-file-server/diff-ops-file-server-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 156 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 157 0 R>>/Font<</R8 158 0 R>>>>/Length 23133/Filter/FlateDecode>>stream x]mq~y$#^k=R6(Ӣ@ aA3o$`ޕVϝAۻOϪ^7O7ݟ>ŷ[O*? ݧ_{joHx\s}^_?>O=c|?S_;WoetKK?o[]/i^v/ % ^~O_<%?o?CocZ~Q҇SE%GBo#O(ߩ!o}(NEk(ۿPkoqX/r*ҧ<߰+<oN^_b[_}]T?y{R?-đ[?[Hx W7漇zgWͯ[hYo_o? "h8G7`X&Fo~7CNCL=7o~7o?܁ ]蛟]Tow8B?<~o_ߟ9m3;m "F4h}r[}ޟC^, +$_u!Fz 7d?~W/Sf>hcyv {Z_ˣ_|VF~w#$;&3:È!u O @3n/y_5_F2ƨoO|^ʼo9!W[oBj۷|_: o$on=�}t-#.`ݢ_,` aoO];% ^??{=@ tqo?3ϯORW5=5J}oú`fi@X1<�+ؿtk6HoXƍ$^lnX ߢ}.13ɁXBKMIxwvW<V\V y8xŪ>_쮌Y`c~3aG}WZUzοeaef{|]fahu|wK <>~mg"xX`'6,HO(1oGm ?9 {zcmC2:\i{$Ro]{ BǞD6x0Om"0~m_djwsz^aowpSi|wt4_ͬXmc[ ?2 (axOTm%3;!3ߎyD)O`>`Q G k22{vɹ;wUjā6~*Ͼ=zGt"=yU엙0Op/>}F ̦C@Td7h0̞!=hf)Ucq& :ДOٰܪ> jU`=|%C|]L�AJk ;?Ij<&w029>7;S >l}(v|Eb-{%{Zf#l د,[0 lBꍜ°4]l! 6e.c(c 9ۯNc0Sqz1f;BH{CA#P1+O8<JDl1<1b+1! Ҽ1B[@Ǝ`?zɚP`6 L[Wi !61T;74g f.-� d !CC1XOchf =$g HN�H{yNkFJԆa謡 5k>ag CcK9c0H=c0jyg v A�Et3/k| W2f=0qo6�dƟk <NdGccPކ&+2}]0;]SP㪼gZHo*>"x͠^3"l<3xq_NxƇƻJH\OA'|vqM'4{뙇5= <p@dj#ʙgmЖ)bG�yyM%RV8j Z# "nM=) xTn`]{RxIڅ]^Rh)4ac]:b =9Ck5 %50)`yO5|o&4<"jؘqMe`^g\ pE 9X{]@Pa6;CEdyI8uy!gz%3x .iVcJ~02v pʘ:;/Va�C?fRɮ(z'$oRPo=EgSygS 3 3){<Ǥ24),9pI1ՙTȤ,jI;f':*vzΤ%oR>a2)c3靏Ef--gQS,YlLv6mlB[F3+:92*3iT>3 Q16]ڟw6ŀxr6ewǦ}dIΨАL ReT hEQ {DiSYeS>RM!eePewA;wΟP hp2笺ҿ|+)~<g@Qڙy\)NʥrOO.Y<ϊAqĺ* fq̌[ŮE(Yi@a.*X 4w6dU;PcqdNd6oVZ KedD-I{= \Fr eQ+[Q{B?mFB]@a!*@Z94w$^ĘjۙYWλF9/94 1%WSO2wYd!['�7>Cy硱nN $IA@l�<<OuhG >4;4x;4<CcCc֡sx(i;4<=C3A<ZO?44B%֡vwhh:4a?Б}MUl{)QhCC#.`*e[}UWx~s\×n|~f~=+x50F�J H1ەXdH{`H>Б~05xٮ`<\{GgAG:d0fT}gwݼ!e �JCEn;'P ^cWg('f J^�"M7k!}kH@' )A'`A#z533 0r%*I@Y`P9Aҟ恎^d욤�6N/fK!3QJ"W.Z̫Vvϼd_ud 2y`Nȫ{HqՁH�p=ldU"\ R/3kf! YE@~QI%zA!:xz �8-+.ֳ}Upw7 2 2÷D ~,djQCPT#x@o o+qyqHɤɽ;%1g2Iv, _gw*`/5%t-vYeW><yc =zeL i(uCd)؋N]Iv{v=%PF ƑDM8mfp�&ffdfX,U e;caWw1]�@qG oqV>aGȍ2m#0|õ((>OdY,m#Q œpeYIUt7Tkcͳ%9*2�C>v5, "+dz)-+age](BL8yfOo*X0#N# DnTeup* }4<L{=0L=ׁ){VM SO`u`ʞׁ)"Ɂ)TJ?8e(pj 8e\`NH8ra)x@5nrY=-}uN22 r-BC@eyTݞZFV~C@E:m#JA*QT>D*R͘[H A6Rc Uۙ[Hea(G [TeHQ(!* Eb;5&P5K�سTT(Gw@eN3[4NXGX8B{TC qʀ*p Evd%NdS{ S띩S(:0eс9LT@Ɛ킩Zi ZU2 jTR}FRm_/)KPb)6Sv |TJUm 1an*>5:23*(,B�ITI�U1RأlS, vpm)te`)<r܍S+p*$l.PypN>|*@RT!B� hB XiNUFH<s3ޖVUjU~J.}ߠ)Y=NU]28">Hb1 UM i͓:'D'oA4`'ZuR�=S<Zl,6Օ>"l,1w^ľWfJC4 ]2POBxOs5E fKX!0iW%BT`uV vKٚus0&!  6^7p< KńR쓵i :JPgŜ}<|,UU%5Htl2rɡ7XYxeZn~O4X17[b_6HC)n/xaa *B, 0÷cg e95cd zavH'ĭ]&8Lšn1,tzX r?ن}@&3 J.@6/tzX1kLKз" 0~ C2KXmvLd¡ YS6M|,d ,:a[ ;A*=na'D5B!S`Y M[KȄ3cLK*sz&0Y17xm!^d "Dзp-y4z?K8PO kX%ЉK8PO J_e',<9v6V2JxRd ]WH0,E4/P,kRd `e?1(ty|eGI+|yXy+\GJ0c"?._]~4۷t\ɻ|%sp+|旛whjѹ|/Ϧ\|( Y+Ügû|cb|o.>ù|_sWzG=x| sRԝ7'h-㣢`w~<E]Vw<>* y|㳼U7ƷGI</-j{|;_ly|bYr 1a{8_ 3qߣP> /;ϢR~A y;>}F_uv1A/=qlsn a{{IgDa%ϡU/Ok7}Fm4}2 h,O{h FZtnTp= r~u&çr > ~2 v~B Np ;|ޜ'q%4Kc ?* 9׿+IH:uFO3V?9؋f_+F}{$FPu#HkvWifH F]] D--e"z 4eΠAl3HT'e i/O"r;O3k cl5=3EAW^qfb;h qfQCbĉyEKbm2K\H@2EЃRiw(C,1a):CV$ Z S@®-*bSBL_0EBV;%,݆-C<J*'a(,eN&aF]9{vL-*tj8,E(\h~d*W)zg*݀k*2^/gp'vRgzq`ز+ }Ha6xenc51 Ϥ`50ޣ^n]bܭθ<b +Gٔ\ԍ`  2v;4i]2Refcp֠[mug+ZdpWvQ1f#!,<0n}U_]A+\lꍱV=q12lI &" bVk,sewg ' v+gr1v9NS CGS vܓAx-ޟ4#Yv<"zy8L0"9<'sd]۟gT^?G 8۟^Lhls-tϙVޟObsg؂yQbsyI ޟ#[><,v9 vq9yIlޟVGϣΙMsylVu1pds.\qϙޟSbs&@熈۟3?LyO~y@s&u!s憼?A$ǟ3T?{?g*z.FW?GvϟF4ϙ5Kޟ#/n'-sf.$9l`pnޝ?gV:"LNFQ')ڝ?-O:S88qȸƷC(c̭we6rБΡ?86@plN! Pu7ラ +4y=LM+nڞX0U;,E6Cyf*D;,EN (j|/<U#EYn-nTC J#+{)3TH,Ddz?+"EhlROEōh|F[2Hzw(spFu=ʼi 7|WDSLDc,VHq"ͶlԊIQx-%a+&-,װ:a+&qB>ԙ:IQyBQʼnWTgHji2Rg6`>?.~4!:OS,k(0So6 HX35͆7^)OYCš9xԛ >Қxߑl^lg D{g!\a" mU(=%n;5e鿦\+o;5%VyM~DL5}jV5kVvrHIzQ8'wwgw(s@" xt5%,A$q<X(\5[^摅}LWy$0GDrWsha_ςb6%DV03( Dp)f5 Z-"q@,y oxML`g8$ǘF]lZA~1{PXv0q@4±@!±% K8r±@!r}<(Gq &nNP@7  8\=(,:Pa`@`;PT4T(,;P@<( d'=vA!=(,8P!q89js`8HuΕAdR8A;;Pl g6(;P|�Ձ9@AɄx@!byPPN9P\>< H 9V! JxP@q9 xP҂ؠO1Hj,PP(zP@<(0 EA10< J4%(0)r@#;HmPPrTXY$] 2AkjZTQݠa�ҪA@(t XPP 3{P�­oPP&ԃBSYܠl(@.P(=4q@!_w}\կAA8p[LFZdYL ?*9׿ϜV7AR6L% 2:e$|hRahUg�+sL^tnjDif"_2}_vQNQBE� (/R.@9Ccc\i)}U\R6p,@&K9/O:BrieP#rTb)$,S,PMQOVY( (bglv,> IEx&ijCf"<@_6)/Or?a'ԝy-D<lgL*oWIL)$SV )"E23v&9Ǽ S6ENS%S6M`!6Ba s"eљ2= 2H/Й1u 6ISL_A  yIhaS�hԅmHM#!WUBm+H51VN ӜaVL L8QRBZ9ʹm'4k!BHVQ12cO5|B236uEc̤mQpPHfUs(E(R:If6(!X.:K< 3K V~U, mOP<uÂ*,$63aH<,ʄM0,D8aŸ 6`!‰v 4 U1,XDf&,X ') v@;A^:Ta,,XD?',ء3 "nxXp#Y=8X0#쪨,XkBhC<,CT,DB`!@ tfm, 3 Hq�8ҙ HUlX^v!vׂs% 晻jљ ,a,Xh"Y@ւ*>`7zX#Y@|ڂʂ*4e FsrMX0}Hh&,D1a N3 ,,X ekB̄Ra$:` y,,XG"d]㚰@(ҙ \=*a,X0Dg,D(Ȱ`W3 r`,) |3 rYY`o!Y3ځRX1Eљ =0!aU3*{,юtu,=lB"7;@6 %>ؤ%k\]>SKl~#yYSM6N{g*\VT68֙  3:�Ʋuq'̿);eNeH-1on+7RR`D߈2i5ro$Hgz#_uo2;M=# }g_ίPo}h$A-Of)x)Pf"z@C E70j UC EO1'{ۘc{.euB n?ӛ {hxy’R4NR1@*[HzXP8RpVyh['dc(lLӊ]svN!aLP kʏ)ń3ARRCy)hR4ʦ"؝Z3AyR4rZyd^5DWH'* p׺h-ugkp⭸&j&OHkLPN]cm®ETZ]IU `q&pMHR;/ I%o [tK?Fq9Cqthuy BD3gjA`&~gKm<AclGO[)FS7x#R <DqiIm^R5u܋nࡈ`)mvI/q 9O}:ӈI -l IBRʂ!ʡ)5^G:'I`0yiQb8n;&E(KH1( Az7]gEs#iFgTuQV��g)s xs.C<�<'iih-SFDA E6Jhi4wԘJrSw_ڹ9C4E ZC 'd^(f&L=6xMf "Ca(FAk(,{Α}!/Khj ,F2ɼmuC${} 4Z SS m!oHٟ= Pm!�]pSj:KE?o|17iLmoB7IoBRyo rnE`p<":8T=F M}1zEY|#gߘ57LE#F� Q|#p^7_Jp? S@Ճ/@7Fno(9= u= =$�FzE_|#B<lo(PPՁoDC P9 4<BloDj\ ћ7"r|!"Ճ/zQ$Z|r; ?7@Xt/�}"|! fr|7R8_poc =b |bTpKiÃo@^|_L*)z[|$Hs%1Rl/f(p_|'oY |1GG/do@J yY|X�ĝo H=ك/�q|W"_Ezq(*tHTGJ5mJl|Ǣl񚓢?/)/OIy]E9K]%߾ +}#KV*(b3#k4Ɗ1iлwi!kvfM05qfjs(u̱j6wIk[6yҲE|{jM#8$blQsOl[^ܤ9N oR1 =ϲE(Vf5/%7Mo&*߸kf.eZr%]%o?B - /.[Dɗ }]#KTBf4qMМ6ih\]cu@9B)&NŁ >\"v.I]k~j[g/,`be:!%7j8aR!ŀ1!U%Ik SHܠEȅwi0Mr\1` )-@HǴyYQ6Ws𕠁"w|^ۆf/szMTH'Ҽ\` W8/$qDmi\iIKYk\n[gq4뽀diGi]qqMԼ\arFr;;V)؎K f0Xj|N@Uxu%]sqώ8sIw?;S. u,(-t&2?>q k$Ƈ^ӨIF5 ld)R8Ȓ D,EjYV_` KA{NYͪY$R$'p%A"_ʌG!źY3BȒ%'p%qFȒ%'pPd%KN Kp${U7$GV,YjY#٫%KM Kx${Ud dI #KAHvnda19dITEY(8PYB:G*,zd[ zq# ˷! eGA<P.{da19yd GYw! P/8da# GdPh>B䑅e dQ{d`avȢW(Cpf7P>:da),_Bx%+"xdcsf;BU%K#zdapٍ,5=ݙ),'Y(YD{dftP;dhA u@Cȶ-!K@wf7P# %Gj,F! QCR+ǒ5#?¬2ﳺWuìtgV=>XY}3+<B>fV;xf|ͬwbbVQvP.f5 3,7R3+̊IYwsIYQbVL:\ Y)JL9\̊BΎY%*ަIYQ3ĬŬ0w1ĬfV⹘Uͬ(왕 fVYRxͬUŬ0s1YQ3Ya6bV 7$ŬofEgǬ/f9Y̊*ΞY% ^fV_=Ŭп|3+8̋YaxfVtͬ:ͬ7JYL.fYnfY.fY#/fYjofY/fYof@\*Y!ofpbV_ì01+DV7%!Wz1+/fTͬ_Mnf<Ŭ?0+/ftŬP57bbVKU>g v3+B.fYr145g.s1Y.fͬT9̊Y)ȳKVS0+<]J%4ϬT:̊Ŭ̊<Ϭ#=YJŃ,)LìT<bn`VtzfeX?0+L=bmfV?7bfV*:dabV;dIT:̊dϬ31*=s?aV,o{ft3+V=b7ŬPh[2nf!˘fY1+^*iC1f.YAfV)<՘oYK`Vcf{pnfks3XͬT;*SY0Y6fYh7S)̉nfէͬKp9u/kj_>s“So#ʎ |F}KGܐW!x,g^sFLP Liq;[l>VY īh\Z`\`zvpխGgS|5(кףxխPgr6ʁɪYR>z`܃V3^-~:˴u=೩hqY۳>ֳŻù3rr`)Ruob~r`;\eΒճ:j`)RUj`h3ZuK= #PݦRV}n-<IB,T4W 5(кhխ5U�'֭a-舁qQ[/?CֳE$kvr` -nM- Pl6'3:r` _nK=3o5M4(9iC:j` hgۺ]v f a(t '042;dXBc vŹ]y%4؝S LICQKGj`L $=ڔ3NYj` 󼐩 %Yz/1q^qgkK-]b`M|YR[ N=a5isNΩ'7-r8N=aӋ㐶q cl86τtz5wN=A-x # z8 :ܽSCǩ)Tr\m7~GIo5ߝSOԹB<%>E{csNʟ:xs!p.=AWcs<\zT?.]:~\:=DKMǥ3OssL7\zl\[\:ǡΡ']rr|kΡ3ˡ&YЙ0r"wЙ^7“cv qr/Cg)xt5Cg Zˡ*Y.Qd:ۡ3m֢s&Й~+^e,hYCgy"ۡ3Y1Ρ3C8v$n|;tܱk;K^u{_ǡ3SڒW9t&\r"wsfِnP;zp1x^Ysr3OzfR"2 c^e|=A,]KR.h!b׹60o ){f!0;/- {0zJ{ӛ=`^fp 07{@qf 7{&=tJ8E {|`/=k.0$M0aHY=`  {= 8= 8A= sL]}`XrX,=0Qu,g^{n=0ov_=@:b|{0\?lg؃Ys,gIؠbXr8o,{`f[/8c[/8c؃Aqf=`A {܉&J,h7{SOPqyn�ə=p  ]s8{HAoݱRp{`[PJh]i7{ؙvh]쁝i7{Rau졶|uU7{.mi{4ؖvLE\A ӳPQ=\ֳ݋=LJ\:7]b|;PjpaxH/0=0Yp&.vɋ= UБك$Ρ3rf[.ΌgL\쁉=t}Ρ3At9t&.\sZ{P˱fƼCgbM9t&.d7Ρ3w&.0=0qx\t[/0S=4Бta&CCo{9UЕvu쁩ً=p؀gFzwͧ7)/?w?wO߽ۿ|7>y}G!^K?P^?{Zd(ftkT8žLXV@syG9,I V01ZpPй@PaGBs*auSNv+'>7A'lCIS53KMTs9.pm~H<jDhchST uzX 伕8_TK3p$Js}g +˽)Z�\#bJߣmYRkjA(�"h!�{O!Fq% 2ClFaiR/ʩc$ss0T1L^ jHMD 3upa4YsӼiJ͍u͹nf܊΁9hB34sqcVh )@°@h *thUCv G ;_7Gmim;bmQCNOC\glUI!:gWȋv HX*F#5o{4ﱄQA5953H 4efCqAjfu2m$-Ts ]rws] 冡uDlfi,p"=އ Tb:"Y&s;i pb>PqED/,HTm{5:=bE{-:2A硗nR@&bK*,�\]ImYMZ9in)E? y ̈#OI&M$)ڥH\lj҃F/mSLjZbv\)>'Q*u/!s}-4O+(#L%]TrQ(*k7mvQkpԜMs .=TT\%4xp)ʆʾyE]<EURp\ȲP_;y>e8:HVܦhCiG!>RJ?(#*k1@u$$dи`I!{ ޸뒒K 0BX'H1Hv)^w%>9*M9$6NC|i{{CZ;s\}Ҏpa*(FE8^Gm]Ym� z\QJ\eEMkڒ@Y5WcDk{rX]ň5tn0t<hgy-ÈÄְS\̭c(֠/^_ZsYU36>sКԲuQMY5%'jsR.qV{»T'?etS 깔޻L-.%Ny.؋Y.bV6[ȯcU~�; v Lꎭ{?H:hĕV~A䆑[k v{ wDNHK3SϹ.Ղcn?XU/_5u^YeW\Alxp*:ǝ%Z gP "gZ+=VG )~:ƙugh@`eg[16ntڞٌQS t!h+RvKeuGʞoY 8kZǸ"[Agy*bK-%q"[Y%LXpX 931[ zq9쉃rAHʕ'yYQr-TC2Z&!P4NbgU[L8m /ͩshٕ G)Z1:ƾSԘc-S/i'3=@@`0<W sdD\tLOD<UT&Li'2l>U]W߭i ge 4gv}.:E-Xvz3Fe"B' \P \1 \jBݨ* ,FM ‹UbN\PQ/07{'ԷoTDLp*/orìLi/q9+q{`vOl .E}5?[`sf FavgqBEȤ|q3p@һL>2ߍ(3&<ېDDklv;?Qga+i'&E]g/1`o^nMPP- \)IMI[!|%V'4X&xۣ`cxx E+H<2  씁/kf~Aq2"̺0kEr,b ,' 8q3sWZ-r`xYw~1d?kgl C=n*'kLzK⻶Pkj,u9.| Vū֨b ȳ!%--axn[fWׂ$>&) ŵ-{]6v\1%^٣۹O2ָVb!ÜZ7ֶ񰷶c4UvurOm\p8aV`Aќ`ܝ\x v6.nTҌ\-ЙBBSme 9�y Cu R!rh&;O(չO;OQ\ԓ8]S+]?sy$Z2ZNk5^#9ZDΜfWBYZcC;C,ORJ5:\)̉52Gҳy)k|Lu#a4D8vF)ReȫyDhK6a)Rhpq?r9e^#1P_"ȍjy!#.ŋE~_uCwZۊU^b�›&?ߥx /T*}V*i2Գ.A\Ĉ m5 %M<J y)C69mG ^J-RcZ[:E P?ݢDm GbsFK_Aʢ9r `$FT`q&#VB C}H1ЌX ez>!̓QTgc_"ŖbPOQL,kJ?q&3ĸĴ>@"#$K:RB"~I"TB3E=9:;-$ɇy tZW^ԱC$yt.E4=A}]Wt_8(_kg@E/hjתL;} .3"ijw4̲EX"9vawG}:ӇަyL't:+^L6}z[M?K>oLjeMu>En]OS6}7} ]O(wO\mhL ܦ msnG2}J#o|>C1|>CQ@M!m(DݦmȪߦ6}.Gd{>k?4}o~˺�~B#ckvBN6 _WP< {E:AWB q*t^FvBS v%:'2Ҝ.J`*8Af֠R&h,lX {hɳJ1u!xJ1]p۩RhP'*Nt0ѩRp 虲9U]`;NGܨ6RЪR\0!]Hp2:HSVe 5E 2,SHJhk.ShMU ]Y:E ν_cIk."g8}jħEU2Y:UHR=[�~W)r[ /MO&?Wm\?57{Ԅ;M=K3ˉDE_N+'Kre|91ArcGM(?_NdeD rb\_N/'&_N~/'O5r}9=ŗ9|91!3}9Q꓾>ˉtD6g_N/'&uDwrb<Dr"۱/'Jԗ]|9Qڧ՗%ˉgȾˉϮ4_NLؠZ}9cL/'ޥ={}((]17:o~Ζ~O'?g#: :-Ζ|cgNc~wr:QggQEDճE"Vj UBS>6PKzYCiz?]{>6NLI'>&QbfӘllRy?lҬllRy?lZ"_,Fx.Ox.x6zXLJ7lsE FhC0jc/Xk@F cGK/?^5umxd!T}ФEPР u: vIG$TA>p`Z`6~a ICM $s pt(SD%Q@A6ZR3muцUF7l`:%8Ā4=,4.5%9k[Jdc]I*^�!2p9739Ka Vp2I*OV*a q7 **bP%RmtQc J=1BE8\d$; OpÆ*"b~n'w}m *FU3% ڬ-_h rVIи>D:ٱs7{~G?/ӵz:~A߸OOFQQˬ8A?sci(nCރBG<P= cTE2F<P IeICΙ͘GᦱfyjEgpr) #\&t<NX=[[SĿTT*$W'7+ff]UwO/tV)l¬# gюzխ*:s'j [Uƴm') nS QC ڄнa8^|P:^F+i EBZ*`#nlI2\4U¨*lYQٯ ~)@59hVM+d 활q7խf(ve( Uܐ>GFKllk; N|?`Sov'h퇸AJ=N%mE@KmEt;?Ͼ"?QPG_c+FK^#+%q�kV>@gSS| G_C)FG_l*Fq5W 81 dfƹ裾#2~;_T5-6v :zlWvrpt5%0U&-n.5lw?Xmc7//OPg3cEG{_̩tI33F棲q~իPii9\nj԰tq*F͇:ҵj9Eub"kBi *&t^K}6^ U>Ԉt\:Bu=TkSc*ѧ<Vƅ;�:$j~z**yjLS0sEvdTI8>,9>eHG''V+;y}STA lꈮ\:P5s]+�~:%QmBGUQBmP;Sٙi ~-{_ܽ f6$Sr LFB(zLp\T."7$Ӡ@aeRmdtTwNAuviJ WO2gl%KR~z<0hMlgNgiY=4>'Öl ~`9WVxbմ%]YM4<Ӌsݰ<ԜP 4j<Di|VS270ՙWФ|D>vixuU WjX-#;ufմY iu9Ă<S {3@R+TUVtd =(8d?zVYTyViX;:eF;h@yIe&<zC[3~_zAIMVl%0)1N94+6Мwd9 ?Iּ~9_H5GF x:0iI@Kzh$ %sFlpgtQcYv6UH5n5UMgEsZV4W6H_g2Ym9Z0Mn齳9 b^?_t?Mwu6UŏU]Mn 1:5ıxV ǡ&>8h^l\n5pО8u6Q;߈;}Qg|M:.ޤzgo⚧k.C?>EW\^u/Gw-jlڻo݋vŶ5}FD}fVfL|gd9~YǦ=nRw_$_J["zi4M{ڶg 5XIh[µ[K+_qvQqe>rk8t-r;8tmw one>or~_4G +#Mg5X}ir[},ɡ'3W<rx~ѩZS$+�CktL9p"'-)(ev7h`Pfj+-0"rkkJLSsZPnLM"lCUW,~(Ce~cK\ !yMk#?G#qy<'.GmIYe6x@yOj}<Gg6x@{9u-ú9u-Cm-04^?G60Ѷzr{<zvom om >ZC<6xqdnk g2xt<<r<N̽o  q?b&9VwU`:{ e|9栭} 9y96ap1J)ЯuQAG 0vv]fEHr :0 &kQGD)'$A!f^: afLi bP6)9utu[e:R0g.v HALڜtbwSuDeV~EzvZ2&uUWg` &Quۊ00E?KpmsNa^"XꘆZE^gɂǝ*3C :tպ%R+$2y.b}";ѴЧu}W݇*tg}N?NotA+SR?)PST.;EmBvNQ㗝u)/;EStv"mE*?NQS4^vN9? r~iBSΏ;;N1=~)vNPb~Sn䗝bWm vk`vG㷝9{㷝SvUʹNu ^7 endstream endobj 159 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 160 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�Jj endstream endobj 161 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-mail-server/diff-ops-mail-server-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 162 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 163 0 R>>/Font<</R8 164 0 R>>>>/Length 26324/Filter/FlateDecode>>stream x}]mqhGHͮ| [u�<<l OYUY}{.`pL}>2W~\O/kzy??/C}:^RxO ~݇kϯ?G㥿|עVӏ/>~|"/c6^ө]zgi|5֟I N^y%ϞErE?E>igU޼JbR>T/~߼NK%}GL}(״^湧>k|!y5 6~VS0l+U׮/?_}Ery)چ~W˧&뛟M֑7yyO6roƿ?W}Zo~Q|>W8oվ߂$[_70r'cʛ7o ?-?ȏ:|04> OF;_Ðλo~wQx.˿8AϛwF˟ /_Ͼ9B5߃<z˿_r?ۿF_x_9ţ>}җ#t_9E͟>kאt= /_=:z_HYq}OO>;\?Okq{vm?/gO_^4k$|?׻zY�^q见 8;g+7 +dloopARo5kF??kTָLkfƸ>4>3u>suC^Q^×I :7}Bֿ\ÎR}_?}gџj=|~XũGbo_߿3÷^wq|iN8?Ոj}H+͉ Of_}gϽ׻^r/h@ԁZFSF~h"Z-·47DIUdoU?Tϯ$^"GdM/0C'5i&ր1[Mx�.pL_5=dom^{n;mw=^O=o+I=x۽ߜ昩\j۞Jn[smim7&QO7�F|pB֡|_;rIIe*N9ԃ ǔX Y#/OR~L/)EaeWJxB!նY51SU3Izoy CRmO(#®<5zpn S(͟z__`8eFzҪòQڙ\yFs%NyUo  \Az敓4c_G iG�OSg4Mf q8z$qqi/M=U3BTV )Dƛ¯OZ^xF7;5z3X{͏=B  1[FE><YDD)!M6QwQ?ӄFGhiDa*%}ϖK*OiE=JV5k -fV'E;zlqd,Imzg&nN,<oFϤ^!|Ngԡ>OlJ#)Tz&lJp F$8nnUiPQ}ޏMG?1M}O6*z"Ѧ-ʱ/{l*ĩ>6U%qMKUXDZ'tl*Qnm*e 62"l*!#TSg86aJ F%Q%QQQu}fFcU]_q*ufvnU] 7* zۦR`x׻LJTX9PSL*UIlRcX$>fm*U==�4NѠ845sc!*ECN}Uy+~oƿٸWF#J>Èx>%h#EC@ь'X/y"f 󑀢O0](S#f<r1"榮_(G.mgDD\(ԗ䈢P/ESqhW_2"f]}I(> E Z"f|BWP4kP EQh(# (FUP"N}W#bz�4E U( HJ,wЩ̓Y@-#4JP)P5'_ʣ^=u{:) AeQAݢ6.:jttS@P˞=]) AeOAݞ.{ ttS@P˞=] 2fPAAu:,* [FePAݞҞ~=m\2fN?99E\tseOۘ6~1tgDAG?_zKܭjWwM5dN?:=.i :KZ}^YKOx~_vHBU_?c2 L\ƬȤ?=jgeigGbؽN(T0(ַq:3xai.vqQT(;ɰ8-=8pA_*T-ﮘަ; W-~L6_ 1Tsz^Ds\_*F*y_U+7z"$Z8.Բ R3AJ4eW.2JmVb5}m^*GkD48ZEE]4aT$3,nCMSMհYe?]K'\(w:jIGR+*qTQ9P.Rzl@ZԲ:p.W6-�gpTm3,CEn ZKfsn:Ժ4uvpMM,MubCf'�2k?x)NcΎ!kӐC1М3]BuXf$`/S۱; mnvlӫ fvYєLff0eev*LEl7;0+9+0sFS җ0G;Ov@ NÉ ñ4vӄ} >ׁ4o~nx]Ӟhx9�NCln�8G0;Eevifٺv f7zN,X\[f\prmM8MnuIIvQbա$X]zү#Y MMJO CIY|M{ "ؿS?aGwzIh?:E;:y ~ y{:LEЭI)JV Z=V O{=+fbv@Ֆ*GZVN@w>&di~۫W1űeW6Rlk[{]g tXRȯd{u܁JA_<,yXhc:cRG <=Z3>&.>F<<v$c%>&$D3>!{PcñZ!Ǥ^}BwcPJ4oԗȪgz2w5?�YPߨw`WLc�kA<O41E0P}\ r}}V+HaAJr `d-P נhz> ц>l?yp S-6FӋ17'-eN_ gQ\(`6F�EϳdG;!-/�SV< Wi=80%in8 iik#Tyt22H5yMjv nX[0% +N3=vènt~VP`\'3[B TAE ˢHCC8 8x LlcW.N[G?t 2HJ]$mQa /33;uv,< MP K*YtTI\ нSMAXo*gQ\0`gѡR.֥`̥Co#F(x.-ͺ.Ӡa"B76f&\ 9ؽTMT}z([j )~DǗ'3a,xg΋f0xzp%1d9aǓa5P5aY4N֙N\K1}HAHa2`)>)E9B OҪ1>JD\ OBNa E+G?6TchG?τ.`skiEㄢD-""5$q'.aait �+c(k=TޱWᡐ:d SB1t)R%TdN<8<)VE /'I}v!jd<a0VsJzfλ<L}4%/֍cwҏXr�s 8v 8S8Mr 8^,n4cSY&[j#( cd=q` HL@2td0,cG(@be fC:U_@Y7 }xzT 3 3,,S#>f>f 3$`m/0zqv%�t X@,qˆx²>#m,j0f2|YG�f 5j%R0렀9>`l0,K)X+C/ |�(kЕ,`cYgD8X>AP7rF2}oHA nmo$k@L?6Rl$k(iL?}!bCYo@"h ʪ~$ezlThCYہ2=T$l$TtLf}o(%n(n<e ei 0sڛq^6Uqd07xfYA f cG#,YH`f <ԫ }Xp�p34cG"T'E0 `:ezjg2( R2f! dGo,n8%�_=Yj l #G#"d^?<] �9C3N7ȕu�?!'K3f9V|oԴ%pCm^|ZsyZ=hxT >Hei:|;sfD>lXW@*Y2$^EVEɍ w&${lIm85(r!NCyX.0%+JQ*֨%]m}ڐ9{Y5XG717t$Y[4+zd83\BѱxL1/Aļk`CP ۮU!x7pgp $Ugeޞ9p zq3hVRCP$xҗjrm@x݇ќ$;wf4D7t \*vk`0G hpϰ aIBu~qj,LPďBt9'# wތ (5ć-޲;d<S29PfkB3):s@XR+k(?j{J7,N~0; .*RT(Z}~ jhjQV_'^SX D1W{-*ww' 95G-Ujq`c%ab %bMq}F�ɲ}zQJD oX%+c0$cqL^PNj5\~E-A`8Ӫ D!{$JL$^ �`SkLgG`A!p|F-s Yh1LL<cD:a]Q r a\  2jd- Q@z *MB }+ }mh̤B/>B%ңq#gUX5BsXr!~Bq%]CH@(15d̃P#KAJ݁iF8$B:YJSQ2| c3R"F=0*r#D=@VO 70#D%28 IvD_QBt *5kc@ZÂƆ<�Q :,Ho T2:aC & hJ䈌P9�zTnQyH>%HfBLؔOoӃOi0':9@i\�: ЁG6�TA6@Y� n}T�ӆ^ +j 4TDB[*?QwBmuQRB�Q�Q $w/j#EBSS &R(qj0<8B,":҃S(袷rۃS w`( ŔM667< 8j)n]s�z[�,҄Qz';=�r6 Kb5Xf,&b@� 9jIWa#Q3AFv$I;.7AB,jcDˁBZi2,;jr21|txMeIH"&@)-Lh%ߋ~�=(zW9q橫(z5f Nz=* 2*)M*=K\j@.@4<%PxxznHef68ށ]<~XY' @SVaLPQ3=FDE%c|hL#UCT)Kۼ.@3Q`F lo-PJF'#2;ig Pp0rgn ",5n[Ю0-`WqҒaB VUlo搊l^*D_0b_h4Mfl6r_Q*24(7N7U9-)6oU@oopg2z!q=~Pބ{G}VAD:uiy[R% HcJD*0l]<THS>�xjn<?ƿ08<Yal�DP,P߰ 0dF·ŀ  )Ҙ2`ԥd(ֈ fdlL u8#2 iuJ#283bCL@Įm!A�C&r2D`^t2}F``KG`@,.``c\"0d}5E`z r ` {X2BB� Fa:a �1!"0J4 @FEG`0bF$ �D"0@&Y-Q_"0<C�Rh"0<QH�cD`xH#D`xgzI $I�\빀J&r60)�D%0'h` `E];@W"€ ;0|(el} y2'dd E. ꂈ/dבpd`s3*,i'jy|e1i4˞`AόGGIРn i&jusR A*dBbdF⟌%<Hrm&%/<Hf 0n0 gOCz|>6_- F@Svhk('y#/;DfܯH|F*#(\xޏ AD̰_;k҃mhHg̭ć )~vIQ xfp҅(yL~ArPH`tKFZԕ1De<J* h O;H55^(ArZ`pK:A2 a zQh ƀklv[A$u8KzCZdFYXvOz$:+]t 8Ϛd%<F n;3$i66XC#e\2عj|( {z6k.9G+Z"WPR kWB&p(V(Z ،(C߇ |yp1BRCpD'!cľ*8yTq[K+Dk@Q[bqrXc *$ٰV8sMQx$J^p2A<bN=kY>Qd18ƉqĎ(G 7j}gD] 3>} Bn@(#Jg@(_x@(g;J$@`o!;giPFF!|-PF>!>B9y;r B9YO [<"w Br@(#aAFiPF>!W zϴ@6edplm#s F*I'A~!Q > n) B-ӌ Bt2Rd6 #�B6Ls!r;rBӻ�ȝDc0 4kDc6Bq@^4!wdO6G# o;lG&AOaӊ`<u  �B 윥B$|,qXа B<Џ; IMB!ڎw�><Z D%aDBf*P-l*t-2ϒ#U0[ D$f]_ D/W%P_Bt 2!:|W s$A(A-�B= dsh+Ẵ&pH1G|-A[.O40-/mL0]:<Q,8O�c̼"+ hI=N2%^ked;�RLaɊs 0+%V"FF֟ M<Zd$ߺ `k,Ri@z2Ye1 .w5# 0`y, ƌc;�cRL32`)bU`0p0E@� �(.nX<~�ЇPYhM +�aYL3JNTȠb.�S v>@OfD`Ll}Lb!1ͯR0˜Y`Y47i3/*d=�8=MjpB#N2x3o)[za8>b$K@@o[8iE(닓2RS.椈Vչ rYJ)Y]-@ca�Y5]ә y0;�w9Q!wtŐ3\/}`4G4).͘PŌ!*L"3qގ)4W4d1 -rB}1 WSCH^JD4üԜ~zG īY S(uLyyՉ #` :,jTȈ2k+y 鑾ОyZb&hg#cE wu(f*1yVXeQ->ʕQll݇̍"ිQ8.a|Ă%dZQ69?<<y)p筯/,7aK) YaH&l$ԠA(,#hH)F$C񽖀d| Er,ӔHVdl ݄b o YFwdz;ALRH#Q=HK,RK@L Hб)2J#"YB'dd,}Ѵd\L)J2|%$KFLHFzk5"Y&Yi$Khafy#YYiD$Khay#Y % SHxSHYd9F2Ŷəd<%B{@%tTk@2Eqp,'yc[81P1} c=˚|̓c=%t{^*eNoKb4+΃_pt򡥶Q,?(b d$4A*=h2{s_by@14RDM{>{O (P+9KA (=KB~Pl3ah0 y0,. 39؃aS@,`0)`XJN^P-.^F"N#G(_05%F=D4K.SqX\?_ `sL 3g4asflYҭ6sOi.x?ZFG@13&@z1*& TJ�PV(B`qB4�ǔMCˆX'W'>:Cyx8MZfmΛ@Hel`W:ĺ 64&/F}ؐ}1.KaO&ܨ׬)*Y!І3DQM#$i'v_A>ŇjA|L( a2ka |6J!;%2Eb>1~|~DOcT:{(CK%K,"$4*㸄+G8 C퐜ka̺,$L]EM󗤣p*Oh@]]ii8qt : J%~b7Z2&a.[U'K,:kFvK309f@x䝆B&Ɣ QGՙAV#CQ:,& *=aL(yD #+ 0Cah1Q F?i0n@#eS'O tk#AӐҦ4Αkq,>fzɠ�^[c�]2qk$dK-5~&r15l-AT1w"03 >~^S/y s⺜H@eog C@0wo#a(QZݳI31?}kt0mg2Ȭ3k&h #FahUdڝ<,31/�/#N)޾ N8|.Ok::i톀wT:x-u]F嗚\ l:ΈwɍwyX+6bqm]礈wPb;=ՃrNs1m1H;cm٤:xLxjp;xJ{0ީR|;cm㝞1,x�̰mSG6 7ACl!9x<;x3]|sNsN=8q]n-h.S$ S0_.ٽw97).wӄD٪yN.S[?xtZxX[ܲN;j+Nmͅwd]>xG&r5;�]n; g8kys~N+w( wH(.{;0-KxG&Aذ;nU lK==]8x5\;n4f)qUL&2lXx'6qF" C٠Z'6erRD<U9ǹGijx6~'A<NQJ Q~7|+D}VʏKer+nA||Vʔya3CIJOW+΄<O"+6vN哾onVT b{i Mi;MbQy輺sy<ƶd@dY n6OF'[ldd/dyŖ#"qQC G|\<flM.6'c(6CC 1�qiΓL'}!.lB0P@!VNXlB9OrCHYl? !< -fYactaB,<sz%\7GCOrD.@a؅<+ϧoB)71*efN'ꌟQ'E$,cHZRT>iN[Cd>|RHB ;vIUHSMJfSd."+ TnB)u1*L`IS/B+"u@ ʩ-2J@g$n"o|i>B) A;մ#6GP�X\I$ HB<`Ѻ2 ]yCrym$�|Gj X \R .mASGI㝹bay~jtgneVT^DAӀ"^Hֻ+t; ?m}F#օ@HZ@  KƹHP@ b) X? 7#B"G0!< ?z7 = BV'T.A(Ea?܉tĠl'cB(ldԌFJQdq$eB)dSJğlIBѹB8H 6I? ")<'E6 \g}StefO$!/ƒ?}&_hD_Jl8Sh y:gh9O/3n3:< hn; 4a&  }E3P l U.IF5А}}7GAK_uy禒{z`a�@vqIQ7CNxi%) @}5%EO.z-uIY L*L""?H&EI=Y(dRT8ArIg֪%eB 7{Rڍ@ jSIYMKz\aoYi];ҳu9N+9×g[YU Ie -J} ,NtFKsO�5޴He((EՕgKoP_ZǙ;Hcoig a"?9guBM +,cc 8]l/ b -bé7nuÌJម rFU~:\md3:{^�u+ŐL+6'@sBsꭲ,Ϧ,@ٙ2`s̐ݗuFE~V*~ũ\%]&,H#o\BBF=Uebn=P8TK^ekfQg1pQe3iE-Mxh<@ VT ۦ+q^V7OukE�:F#lX߲ %�\,w6JSLVLVlc0|UzI?�nH ]qkQ�<뜶6oLapybZ ;:fj1ܩEq}.,X$;D6 {Ʉ~ٵ3W(̽ $v!fJ K(%. ਟܢ&0\RX޴2($=q&ǎ((Խ- b~B}\\Hk6K KUDbzj9|)W(pbXCI`J 1\#yn<V] %ET9(%H7rPJM.a d6nQJn̈R9 PJp cp 3RBIPr A)AQrD)$0(%H7(J 8Rhap0ݘqp J[~PJoQ ib(%7A mr p mB֊R„cDB 9fڦ\(DBiR|ēJ!9PB)AA(l (%6fD)49 Q fٷ8(5mTƾy [~l(6aDb>ؾ!RR MnrA)2o1 xPʙHA)2"JzQR(|R@-B))A](圌R+70 xPʹ4K6 Q9"4Zd@)P["c"6ErA&<(RQ8 Py82V`<3TFi (Qa(F)c"7GrF)o9 QʘN2`0Tw2pA)c^bS6_2VSWkiϾ|6YL A=u󤃢s\k,bUM=s&˃XyNU E:gm6X3m7sO{.. 1(j_dyPjr2EE@챿+mdmo%r {|^W\1T<ϳ*ˍ9Z sg 0c*̜!Mk sgQ0CbcFJ,f^ D ڗ4EY854 rg`Բ=?8*ȝ,%SG38jU!yngq1K^FX> A$F5N9ϳoOHF+9^Ãu 'C\φd/O=$Pcz5 mN>O[@%={ӲWnSQ7,tYȥlΈÃ[k;]d{Ƞ^Lp3@OQG;ad{HgK?[@*pG>J={pG,~l1݃;p^H3= =g4q?O_Cp*OD/J jU ˉdl*Avr%ɌÁ  ˛ pJ̫,9 .'9\݁*Av"WUrP%M<=eZm>pAeI9{*a~2b‡5ft']b5f\P%OfLƬ�UD TT XO P%NRUP%LPFhz{۞|H3@0C1߃RP%PJ &.d(bGŧJ0 vR|ܞT#@0E1僐Ir{>b�U %|ՊP%OH<I8w~_k>J|�U%&3 $ՇVGɱaP嵍' )U h=@ DQF";g}b|tH $'TAN(17T SS>HՍUs28! |cȞ2ā*^JLf 9! (7"TM/["@մbH(yj>ue|ٸW)$9 xJ!RbJoR埽<!x_I<u{ B=OPE>~uN 丏f.[ӂysh`}*]T=# :-GQuԃ괞4ɿ4RF@%7KsZr(m$9-șK\F'֜֜$5.#Wq iqO\F HGԜ&2e¤9.# R2AҜ2/KԜfq'j\F țK\Fh<[sZ7׸ Ceyei/@5渌K ӂ9e^9-L2Ը@7@#l oQs:Cayei/@5渌^I iNq[Is$k\F HK\F@W2A\tIsԜ2A<2G¬9.# 希@7xV2Aޜ2bys D5e:Y � 8-HG\E!EiA<*9"$)"`R*vRmH|ĶZ]�@h{q@tE%[$@Ee]qA6 )B� S c؈kKE`=!B YkVHVl2V帆@-!Rᢸ #{s2zF\C .@BOkHȐq � 2D%! !cH[w !#2Oq b� @�  M#!)ͧnH[w !wrݐ 11@B6+{ AѸr|5n\C~Uz$f3}hp\C@@k,JP"$ERnH ̸Ԇ$FR$ Aj5! cCc B踆ď!c9l! { ԭ[B!! ~Aqm)V10-0iU"$4y];bBf K&1kI̹~f%1vIb:ޓĀrlj$F̡$D;;QNbNbF$wȅX)$$,;`Āx%1t'1 )I I wS,$I Hwi;INb0M~'1`)I 5I XwS^I Xw yw`CޝĀ|'1ؐ$w`Nb@TLI wSg$ bC`?ޕiIb/&1d*^I fɯ$L;0Đx%1 $Y D;r+!QNbJb?$$L;4Đx'1ֈXPƻ_I wӑ |`EޕĠu%1Xw'1I VILf]o'1%$wJߝ`NbP1 ?';;;.8} $fߺa$1Q;ӕ×@$1[w[W#Xu'1X;4͕oI pJb8Xw'1̺I $uw +ߺߺy, I p]I $yT{ uw9+|]Lb v%1I̴6$1mI oI o]I voI F$swÝ$`Nb0[w%1PnI W9+a/Jb{'1I_I u$;İA~AF-$+jİa%16$"wC@ NbHD*I 0{%1Ί Ib] ARch\I Ԧ$D+h8_$BB$1!!yh2w淯??_|~%Zhjo{?|oP~|=e-1n qO]䊿Ȼ>PG#qSmJxQAsj0L! y@̴I`0Inx[X1Uo=/b4wɮUo-9ޒ3A(TqDeɢykTve`OҦ}*e/Q| {8٩p培jr-lH  ya^k{Y S"<’nŘz*>9:|O>EWa � )SX/C|Ͱ}1Am\? :sb0U=M]u yl>!D6}݂mA A`hA*ĎZ͇̉qgmpm)|nMPT5"1SLd 9cVB?I*{1(0].1L $~!Pl I@ƅJ8­bo IP DۉG3ĥyP fIsäE!ԖCzTksރwd[,ʃ:F@fYK4ć݇gL 0BQnΏ %R m&A-m[ ؓe{/k~`]]#{Rm dFu-,yXjѵ`:tz &jXPuײqsN+5Za*=a${r=ڏ9ap i 8A8Pn2QAw/A~ﶭts}.O\o59<Uq|eoFv�cMu;67D p0CY Txҏlkq_CODL6,lͤpnuYɁP+x "p5}q@nԽvEaΰo,} RP7�|obT F52f;0~1K3U( Zv韁m'0� y+׈Ym6a!{n%ƌ a}l^kVlmH >k1> L9;RȦȣ,nm`ofU{!bl;(4jY+vqs'XR6-t(M+rE܂G0 䮀[\vQXĤdҗ&"OA+p9KQu"az.ĴAsWoͤCMM}c.ҽWD-𐭱O2yZi *Um ^ڗ4@ p`7ZwB[\nXM%.&H^(B1im``DXo}vntm֒&){['/Fܗ׬vؾ yeܙ󟶜^?Ŵ'} DXĹYGc".C1}|!u24m6QKYuZ]|9#7C Wjvֵ1SvipEUuQ6\?Y'j(>Y>ZI w/9J"RDRM]5rƤRR!yVǽ.Rnt"ʡLm̶, �JY̯KEWx\ldBEa(Nky |>gmˊE(KKTL$م�V(klcpE=,y;;9ivFbs鰢ν㦫WYy=y ]{m4VѨv_Y;/<ml:.m b\fk17{w/~8o틽ᄋlV.4(FW:`]?aq"'tTT!N|h V/F?TnGK'DFx$4u`a"2pM\g _ŔVZ >4hm1č K&̄2nDXL~"ARXg pÐBwΘ*b~zGs3J/WXh6=LEF t98lGnuq SE4xk%{Od-̭2m.\ +AԎ) l<"5 |ל/T87U_:C MNl%,r4jZXo*L pϚL Bcq[.쌣w篕\|[<e]g[)m SgBg(&כ.зb,!bJEYc|Pgݼ^7PvuBusmʮO-ǰ#9Bquƪ&JQVT-=u+utTsm n;kdE=4]2幎Ot`4aNX܉vfY΃ <6'P΢c9Ľ,qٗsU1^TW`㹬IǪsix3odrrc6aL;G7>غ n%'礷KX6[NS'Pf7o}c*'\L$Pílj9%=&{&͘pͧk&IYN#j\rDGbC"J9 }A CtDr҅LRf;c$%|E3R/d2pI2D+ [W' v2ؙ&h,d5+N),(+ds <So'U$OP؝T!OLOt ޼ OL e.ŝ*<U3I3y.sz<˜PɗR$g\aZaoŗ'W*O͐-`f 6ض?ق@3Ya ق@_KN!]szL1]gVe fuug `Ord %` #[;5G{ޏړgB4훠9J{lQMrLE肱cͥM`#-/1bpPMṲ-e\{ONT3S^%aC�p%Qm6X(ζfC٘%{Usi>jiװ]VB-c k8]emmB#ΥLOě>ulA>7CnީKyyxЖ>wn1~c%1u F5J"뛙]fj-9Lo"I#Y�p 7H8-i(!$ct r!3ZaZ4gXfe?+0iwڙXْ-6BrXA*ZB"+N|V0E~5 m^y&h25]ä8v XrviR59]2k5 \]`,ݮAk5Tn pokv N1A {2v 55jmo\DnPyk{]Vݮr |q pnkhpkw5`nmLp mk�r :]C15];n�v~n�va[k@v |bp nkq%/]C05z{?k@V\/rd2 o Qd" 5jr5d�*E5єɚ`F@n 6Dd6�[# d2csPJl2xbsPzl2\<9tIlf 36n-6Jh ZA=6^ '4N AZl |`F9csPbs@AO9dd2csPհvf5 <9(j=6 HlJMk{|mn^5fbzE{hM8Bn e~mN.͗#Lc|:ҢvIW7^M Ƌ'|{`d%Z/<j^?T ֋�GzPzy[Fc<ݬZNziO1rUz'j0녑6_չ1j=X ֋Wn~tlo}!qt NUT3 O׷zEAUk+u6_JtG:wn`<gl's6^N6Rxy콵cmJ1C˙{]vjip=FgU$o.v_D]!R]Zm}j7VX9ӡj9e5M% & ;3a.$T)'Y +VMaQћUsc�9d vڦo##o/-e2w&sSC@h TWBTr4<m"0; kfMIJ;E}M10U&G:/ XrQV\F aU0Ä7ۚTqwsA1 m6AC(qj%(FoWd>\a._(oð"d})Dg紡BPn5m(ENZSa _zH}MŬ 5dǺk6ˇ\_Y1zH;S@k1sSsy35EkptӡهEvjv[c ܶ\ƣme7Cl[dnޡsί}6 ͢w{tXB&Uq7M|/SK@Ou095eБOӦx@/淹*Pk!Dp$BwVAHhoiK2 Em K35waPritBFuhhJ᫄|P�E>\� �X@GrAl"In&Z2&IfU$IE $ s 3G +֭, 1 ɚ\/$+1MuF+"RBcX#94P_L0o0YQCP=II'J&"0U} H2?4?AMN+ip5--nr3 ܠh؞n[g=8>PxN -ǛC更8k>s8~N 80ɿdJ˙IO`>\$?21_̒'y%H"2 rpPRI?\[#pzGS7NwEåQK# ddZH4Rvc (QK&#%0hK#p[$#EϜre)'p.Y%KA~$'JD~$r KN"cKmH(F2%j83P$jϗCNń؛o}_A|&hUUˁܞ\^I( VzA P6 y &#)FpmOcO.pf~㝡]7iq{nvRܻN3?f:ީ{a:^7i>] ]穼cNK~(!\;!]>{x7Sofqxj盥 <Kzy\@\Mc9˼7vעn4@91ƾ~䗋+{f[@3*~ptaxtְݦcWf5`ѧ)m\+Jh2jUAݒGW8Wd_j2mwK.}co E`9eN2-gK2X6GK>;y+i]XCt8sb;XL%6%|&W7GCq[67ɕpX`fh*<ѯBjC l7v;M Mn*KtC-sHg˨߈&i QpvIb$�`�!rN�_ NN*wA7v_{~XU|h$>}N6o~K[~Lbo x,,)e"ybXñnK_0QNlʐz;ɻ?Hxڛ;i TLȚlgIg1H7i]xaLˉE|L #&t wVg,b`fNM p2)Lc& NS1Lc$cdU ?$۔;BflX*&deq*d}4!/+.0k;!2 /~(3QMhT g$b2ƞ{yDkkmRE!ɸ>7vVj%W86 j"ВkelHABd0!k=ށiȱ rM211.馵 /!4aH ޒ?7~ 99oܮ Ie-�~2 [Vb]jH!2ڔv? 6ydz(Mp>9UD`~~Q!h‡l}iTW =l}1T=)7>t8E͵?+^3K}P\da]'a9p#\_J15LOSmN" e}'B0#fhvK"Bũ+@'ö6,besI$(v γn# YUYx~uyQ BP*@$KWh}CxF0\ 7nXnvg/$|$Jy!( ntъQnY_?ϯP&s6j?~mVΈΠ\7sަIk�#nʤOX?mڂ 1ymZD9nԓLvWX_ Mz~m6҈))s\')WOJRZ+#D[]+#va\ i^ NzWXlV='-D!hk#]kK#-P| ֺ60&Zq"z+bOXљmD$ZDh{k-b*kZyѻΎ(4fΐq򵪐gתB n'teS9?86++ׂbqNtestzbG r8ke/<nD�&ww4OdAm]mH)gTOW:K3$W|3+cƴ](+PMR&,HsiͰR ֺjYNM}c?ic5mClQ3"o@-6 ֹ\-,Ro\ʺ].qK:F܀?W2<H]ͱ=Jg'Vq؛'Z\k慭%jqG�56q,�K:65N'pZGp]³kR�g3hVpq)B9T'/"an\R\q 'XP ރ@�BM%lգXQnR& Ok�9xyG\NtA� Z@R�¸V?y2^Ȇŏns1qP'oR/NdɛdEaGcR+1.Â/$ #jWayr^eq1K=\\z8ҫo&GqEg5eY(/EnژqdN|T)l9`K!!mۍ&W|{?Ɩׯ Myձgd7hꖱh }Xl7d<-Ikh^ ?f 0hh p{p[3 gAkdK6)S)fH<] R ..$Қ}Sc$ƚ@�ط5;̚mU�5 S wX,n=}3`ž}Am6 l=L',@yv D[@=<=CAs1md4g肂={^{w'Ly۞!R ;bIj󰁉jۜqFcffcy2nA ߿% endstream endobj 165 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 166 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�Jq endstream endobj 167 0 obj <</Font<</F40 37 0 R/F70 105 0 R/F39 35 0 R/F38 34 0 R/F57 65 0 R/F14 38 0 R>>/XObject<</Fm3 153 0 R/Fm4 159 0 R/Fm5 165 0 R>>/ProcSet[/PDF/Text]>> endobj 125 0 obj <</Type/Page/Contents[23 0 R 168 0 R 24 0 R]/Resources<</Font<</F40 37 0 R/F70 105 0 R/F39 35 0 R/F38 34 0 R/F57 65 0 R/F14 38 0 R/Xi10 1 0 R>>/XObject<</Fm3 153 0 R/Fm4 159 0 R/Fm5 165 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 168 0 obj <</Length 3781/Filter/FlateDecode>>stream xZ[~_a*cEIbtw4MN$+[$f{.n΢PC;<NVd)BUX _"6ybh|h@x>=ſ/ mъ_Tئ^W\*VwՏZ|4kEnQFGO6zY"O{<g{~_.Z?Pt< ka-X hDEv<q;6"|7]]$zo,ZXAfyTKn*f/ Mkʵf݁gOfqbDnVu rV}n̵VO\=|4Ilʇ''?tpe͚sMe}q^>Uve2S#߲X/WvYF?%&QOR:eJw/mqqo8By0U0m ;cQU<4j7'˂8SC8qW4*r{\f]wa~⛘[wK{{J;ۊcG~P7ajڶyKoNTn?ӲsU͍k@Mi ;A |&zTP\xW?c500]렼hÏP[J/[jGI&{L=ϣ:4Py}Cwwi{3u.N43io(00Ս7LkcьArZT'j/ RlFCԹS;Tm;eM5_䞷*a5h"+g`gJYU%;诿gs>-qgM1 |͞�۪Si ox|:6bPTɮ7I>A0D�% P^oR"/: jIl}p_l>4ോ¯gVvՎmm%`p[GD; S%h\TR�/\;FkHaDE*^8TXndWBS)TqRؚ nyp(O6/>>AYЃ$zg*C'OXSn|ɛAO4aA'4Ou7&ӃNxBDKy\LPK:EU[s+0'auV3m}{> _YqX ')k:iM fQgƇ@NgEaY+L KnmVCy_?#1gnB,ٸ&E*yJǁ`I"3`x�n*"P^8G1a =,N!zv e"UG臨$<v|0[}[W΅><l ٧bߡC* !Y+i}=8zSK&wTMtoW;̴ѱnS}*d}O ħ8I$-mK~]K)6=H!bÚs홓ԛ&+-E+iL )oj" twS^kb!{ mtMP r-''rB'e,-gzl|BZ]RT̉,Z"e[,94Nr1}_v,A&16oŌ TtD\aFѲ (|WC·3HwkG'a+kH'ո OT O܎3[6Hב6&Ӟ|Ϋs{Ɍ!6QOD㌚lYaI)> ZJƩ>'A/F<3JE/]:i [07 %DH(a2 AiR!LIRD>ROu' CęK> x0 $HBk= 7 e1daܷ7jDzi5G7.Mq· 0G)8a"4?VC{� %Wީ=.%)_%JJyU׽X A?ZLqL)8&FG=T.1 }dRyGGeSZlP/NeQ7݉r,pXH:!w9v!@`Ե L>4F Wsm�e#],y/),x}T͆=n]S:;t&KOLM)t p 8I4kS[QLݚ_! e&iy" sUojvuX<RJ Jgtʤx}r 9Nо#Хeus""*'QR3<B&Xc(&]Fd!墼ʈ'qL,'H;T9dk"s'UP ,/bKLž�ؑFH0wlBΜjsf=].p0ֲgh;$,s_n /R 61Vll1~g_Xr{tbOow2O٨ŋ0P{W wfܥJ7:;@7*~8ݷKK vb-˞Fil'Td"(0P Gg9O� VL o'p׌+@7ct417Ŷ_"Xbkb/)xe@>;^07 k<JҍKL\/*T2@<bkp?' u A+]B\c« 2m@ɧWU>-y]W ̐<Z? \r8ͳqζ45Kzk9/_'wD�F vn`5bzV ʰ;ˊb >'}784<"\1$ γj&.Or{~γDl6gjL_܃, 7P^"T[%'qL<JyZOj61|:tXlNKմd@ KO-<*(}Z�@ql7Bj~aH'$A/G!o#Ӗ; ,\3:V_0:!}wd,iHDb3 ;92FxdɜhI0}:@5)wv:i6jTCJ}mq/̹[au`-*sւ1*CiV/BKDi:'-DH{%V<amMr %_'ڋZ.="膸2+A W*uS<O7q)Kuxpj5@yYJ##)Dar0Ĭ&CKACˌ)WϤt{jB�� dx 7�3ؤO04.8jҷ0;Tԑ"#-6rF酊T-.U-,p'`|UQc.mT0jμ7EGPG<-GK>-m [fX-ùM ,\l8f-q b-:8KGQ*HU'6;czqy\q g?W^qq%l PmꙠ65DuR-�0 endstream endobj 154 0 obj <</XObject<</Im5 149 0 R>>/ProcSet[/PDF]>> endobj 150 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 20:38:06 2016)/ModDate(D:20160927203858-04'00')/Title(file-reads.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 151 0 obj <</Type/ExtGState/OPM 1>> endobj 152 0 obj <</BaseFont/YAVGBP+Times-Roman/FontDescriptor 169 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 0 0 0 564 0 0 250 0 500 500 500 0 0 500 500 500 500 0 0 0 0 0 0 0 0 722 0 0 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 0 0 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 0 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 169 0 obj <</Type/FontDescriptor/FontName/YAVGBP+Times-Roman/FontBBox[0 -218 863 688]/Flags 34/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 111/MissingWidth 500/XHeight 461/CharSet(/A/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/a/b/c/d/e/eight/f/five/g/i/k/m/n/o/one/p/period/plus/q/r/s/seven/six/space/t/two/u/y/zero)/FontFile3 170 0 R>> endobj 170 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 4949>>stream xmW TSgھ1[T;hb."D$!d%aHI "+bպ+j;mg朿$7}y`0372V["apE\$LxaUY`&t89s0r8RYj|dxD˻|fƍ.]q'DK\01RYX;swLL TYDKHX8_H8eWdLL&Mvy=kV2oGƆ&%LE_[`jD*+aw➤Cz_|WwoUk֮[m9-`^A[`K1_]-v`+0wl%y`jl`{Ou'ۏbN6`Θ�fj9`b -N443w1W!G`Jl">'Jr 7N6m<k+B˳gήß38Os͝)iWklljwL٨c&]i#do9(lR-Ѥa8bMQ?|xr( ^%尘TR Id9JϿy> =FbTWH{2PUgJa%fF³ąs5@|,BbxMaV"@c$L% ػ[0S#i:;i Ņ&8 т@'?[-zSR7Ewlu]"{>B,[JfSǣ0il?^D@-MFVA(`&?VElSHh�N+t9;_- ?,!2#Ծ \:3h`+"7pQ3BA6y;]nse!g?ȡF5a;I[7_WO}8mh Q-_('Jv{VZC��ã�yBS,ʽ\U oNv16:f*!$9yrFN~3tBy'U5r׬/=$؁D,N$Ā +MnLn3pSvÒ&ofJ xz 2<2M9e�"^�FBe"nRjUjT s텅 B�<P\CƳ[I;ϓ2,*dߩR`#cd[DϧM%yf@.I ! +ۤ$L"{/6dʄ 묕IGD/)\%gz3Ni�(U;ll '�nZrb#J;ėRitM5Z]tin7ށ_PYp!Ors _Y`OGDCQ) F-ȅɧk4jJLu%<aBe!jC.&h!si4ht5\[\Ei8=w"�JT7ۜm[ q'цo@70p ygi~p^!OU%H3c*y5l};1�J`0[ HGؤLlvLKXLQK 2tN{HZ5d2Sɿ{]79~crP]%Ieg"I_OQvq88΅b(Gee#E 5ɉCOuNkV0C :{Wh;,yWVʝ]LntE*Җwh-oQf �LJ+K2,l_ %*Bvta{gECF=s.V]*M88`2$Xڦlf½h#6ép[Fp/ʉf-Q\4')JdO/g6*ڎۼ�r{gbuJmݩ܂"QNIn1("k$ jB\Q$߳/f_7|Ⴊg)sks f*&3=Y'2BzWϠ #z8ܜq.fbe-LYR@Rb?ډ9&/  xMlZmfv=$??v$Jef-аuFS~T ם[w 6djn3=3Qg.\94(ejDK# A JCDD˨yNo[Y`x`ϡ|7#G=wcǝO%JF߃;(XTDrC]*c pYjk*7UӭH2J߬R^hoHO6FDŒ, (*kQPQU-V@:X{@E8P 2SK@H7 E|/. ·*z$`5rNpЕ:3<{CklhO UV;@PQ@t7D?<[*ڄ u37Ho\pqz5t[ی6(`5S[,Xd!PܤF /2-\FUr V'^JrePAOs`(~˼ "57d2\09G AQW^ !WB,+;# bgE{:�AӦ$t}gC'WhllciJk)�,,5r#<˚^* H1Ir*{A(9^ ˶.4CCnԑ%{78(ahɹI›19-a�#<F ķo@GTӳ7MP'|2ZF_,3@ 9F!18A+] y 3B~TywCr?-j`Jf�/Ĉc~ h:\0n<;Ps }fb̴qlf ٚ:N$UŠ8ڶdAq`}xG jYl2-It$^.x?4Bcϩ̭O޺On۠G!k<}%J_0Y_mu[ aɤdC';45G^U1q;K0α߃i# v.E;S[4%;Jeq\ճA|;J$OMUddFI)&5)eQ$4O{. ;*A>ڳ݄Bg?c| �- %E&`˱vemjh[[EИYj&ar>bx1�ȿn C6Yz. {kS"BXaa$"@ȭZC5[ˑ6JlJ)E sKCsFq[=[FNN<֗j:6g0lĠ<O({Yq;t|e@rH\<* jfX[q뙉wή;}yJf%�:.!:+q}$?LdRiMўX+aU[R5#AO ixVV`#$A"J|uA2H Xy :3(W=myrg Iʎ.}Hz [hPHJ^>#o6EUx1xj."_M`d6knΞc,FZAMj-Fp o&:ȩASkq\[^) [JGDn:}H;ad*tALYlc)[P3eL2==C#+Ċbl!Jppc_nd\lEUxgnYP}JaPGgڍ254ANJR=a2ǥ!mE)JNT$̆GqZޘmΧ6!|6f ` "/7-N0"hcʰ2glz/Y-bva/I;"B8R2ň~Fe$a+ťEEk{+[WS?zKyJ0LLoKU? WD(�zAsJc*)閃q SG6@r$34i9pTl|\t|cSƦXv}UO8s꾃XQ Dq2⠏�qާ=Rz+Zu04EWG4OGcF]֭ s;&z +�"Ǹ{\y*\Po*g tD-b@>1-]akh%6奕 $=keKK"J]p[~ ǫ;?yp҉HeTsښrA6] OCgOUןq=.~NAMEӋɺd bq1 G]ePw !q>.|)Xatmb)c%*M| .ϝ}޵h)M*;95K{Ol;(Ᏸ-^h>ހ D3-7w|dS�C5$ EEw4M ۿC՗VѩVv|'量8e${;Uk@�cq85#&󦢛p; >J>a$Õp|Rp'6W 1)fevC9rg!DY_oSׅc>?? Pk 9t;Ǽl�m4B#줐c(EP>Sij[rƙ3_V͜ab endstream endobj 160 0 obj <</XObject<</Im6 155 0 R>>/ProcSet[/PDF]>> endobj 156 0 obj <</Producer(GPL Ghostscript 9.19)/CreationDate(Wed Jan 25 22:28:05 2017)/ModDate(D:20170125222810-05'00')/Title(diff-ops-file-server.eps)/Creator(gnuplot 5.0 patchlevel 5)/Subject(gnuplot plot)/Author(Bharath)>> endobj 157 0 obj <</Type/ExtGState/OPM 1>> endobj 158 0 obj <</BaseFont/LETFGR+Times-Roman/FontDescriptor 171 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 171 0 obj <</Type/FontDescriptor/FontName/LETFGR+Times-Roman/FontBBox[0 -218 932 688]/Flags 34/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 111/MissingWidth 250/XHeight 460/CharSet(/A/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/period/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 172 0 R>> endobj 172 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5666>>stream x}XiTS1dP`M:kBCyT&e琁$@K @eTĹZ8Vюz9Y`a`V01#264y[|lpeBhO8,&eձ.2J߅{gcLcyyreúdNɑq?'Ćƥ IM^<Ň;/v O NO0 ۼ#'~WS={S~| 9BE{D{zotf{>^f lİ%as>\ sǖcG'1慭vb]js`l[>cM3)v;c8l:6{{`\bVX(v8=m6{L4 k,7#`[dy}:{~3g<88k,W Zjyw3gg>7'6lgcgs%s掱ECD0A}ctHMݴKIL"WpH6j xp: 𞕥 ʼn@Il/%ɇZb2ؐfH"3˚&}d :1v$OՖ�䙺/x(PI<n 3$ ^8#8QeDJ=Qѩ4xHE(]TEr'VQ)@$ˈ G,ĵ |Er/�A{BeVE>q3Jwă Gɣ+ ׂ -C}� ?Z|ҷ2 8 C(TRb଑*FlL7 8423h*_hڼWh./3=vy%zN ɿ \˵X+~f 3'0ʛSYi<M(IY@Uj�`Z(r"Y |sQX{qN31?�$K/ FI(Z)+6`e( s*Ny [~T{w!#K߯pL=$jϴLGHA Oˎ&#%ISp nn=㈦sm}47s-}w0~p`Ṽ8$' ߼=x~{~qWю$s٪ )l%uݢ~{Cyȳg@@3B6v'U$%<KRšP8Ƅ쀆ؒc6-Esjb~w#PZNx+H!Uvgl)J͈ĀK7F7�H8ZZS+=8z?g$dF]$p 1(9PK>K6+H&3 BI\I3[eAB+S+S5|A`Y8}ja2 S޳梒\[!Pi>f KU{gvkJT/PxR2WIPȦLH N]Y1HnBLK #+0.%*+ĥ/j$&jS͹k|H-v|%ٹ@djia60[ݼw{jKL1x=)\!ZW tE>zPPSؐgZȝ)'v#BT9=-Z8*fBZ9 d#m(Wv$ѧDᄝhrC?x 渊Cd- Y<=#3 Ȁ (pT@s�-GA7nv}M(pv[w  U}ȆYQ{$qBu'4$*FAGAt fSY&j΃]o=t?l71L<bķIJwI7ݣ,P.N4>Pv CdSXo1Y\ce~rq-T|I$!@|X,MHx^|AEXOBbi{]ܾ>=Q8kjן+Lb8Y#+F-7W/+}wO<Gt3m2eWg3 ~ 8ЗlX+dJNy?z`(ITdt~)jSMPYS(ll>=D5AHEϹ?jOUmWaUz“~Ҳ+M$�W@w=A8Wy zh &Tu%EvMK;6AmFb[D%aK8^ 9@R0ey#F[%֭{Pcݠۅ#2VR331ٹ`QBLN\6KH @1 {Y "f,h7lǗFsۇ2~55f/E")~1YdZl*^qd%G%"vߦ(I*P++{T0sqcU-Y<<?5!ʁ }jҀBDC^T+8Rڕ&2oEa!A()/-V! ҋ<VuuZw)V!Zױw'rH~7e0(UW%jc~>!<` ؏@vnc|;OU3'~䏣I5RjOR5MLjo$rPwXSD.*,WLͼniٽ©/`:=ÒvR�Q7hDXVU(:"5AoqZb ؖ!s4xw(ȇˇXh6ɑȤDY =w! xhKMYm,?ߖ?m)Yǟ 4bMYȑ#["2tT-q|Z!Π6@{cP.et**}S֔~#GBw7Aer#|* 6ZXNg;$% ΄1aFh�[^p8;}":92+i@F{�.{ҽI))^( o|`w;SsosVlf@QxΚ2Y\"TVI Āp!)+ˤN0_f 4R=O[Кx /Ph s>fdҟ,ǜJ E Jex ꮴ@} zŝ M7W\-ՉX\^; _DUL_aTWaL*cwm#ҀX^K%&r xii~'-U.Na5q;[*[A'vw<%N5j 5�݆fC8 rkŭ$ʙ\>X~? wu<XܚkK ݧy%mA[5F1=>-xc|fM,7C5麸(A gO/#)ǾOYr)/*_bX U܄OS/f ZMTXq㟎ͷv/ sJ,@"OzDDT%S7 EbGlo>iZMc'դkjRj}^ܞjPOP|U;4vQJT*X(=QZ Qq1lwV.w-,LIGw?xZrSfmrB=3':{Bǿ=\ ɋN۸-.t0ԛ^,=v(,:V jpk*.GXux-dTWBBM^Ǖd-rx111L ^\jr5L#dV\`ϡˮ΁V7 ]J}YYLzy<u$.Uȉ-޼J2yi+?I:%6e+ OP\CCz"ޜPal % ft$7X\;xWȻI8mqvd�<$a9C_M[1>ƬY#)-+S[*CBWhWhՉ51Pzi/Ơ۲ &pP&aZ]qq}m_e k"vP,pI-rb ܁BA/Ds_k /U%P8p7Btq`g7Փq)Dڇ)qDFk@DCE=SA?wL[P]\n$ty,A?!;JQUpFu *Qern1jCRr*jʋYO5dt' 8%[X9,י!%uKwP3 u ^ 4*{bQzN(G $S*(8+' C`9Xnt]jI ŒЁ6;;ux�բSo6SV/aLmb;2WNmsA8@ O|;/$_=mA.dz:Jh1D!]4xӎ+l2+ZJ,ߘm2(IwA+{nˆU#ɗ8Xš݄fQǃaE> nϜ./Coc-{İO۳GMn/>";?yׅwr@~G7{.y0>�ɑS=o97bOPGY?1/UE3? gt4ܰ -Dw?ފ1U$ ' =/K&}8Rŕ^^oOmB[yO|hb~imvicr4cwK{RK+\oOM%~=mf~wىvG4w |m/�Npn؛OCN'xJvO#鷃e`I}]MmຮA['N]Wȳ])\~GqJUĵ RjHFWB&d2_رIIqIM- u- Op 6 װ5z`aX{("vG9W6r�Cu}4;EPxӦ?qy N{443& $V}X$K�Y]"ёY,;*-�P &cgJs(9([/ǜ?{w0nt(ZTk;|>yѵ7ʒP ,BgޥؗA6zJ;C~~`G|zq]^*Ey4o~C~i6ۃ<u7P΍M%6uj/"ySՙt>h/(d/\qtkҕu�D[_-8wrp_唗3ͬ/((Xf,4fa endstream endobj 166 0 obj <</XObject<</Im7 161 0 R>>/ProcSet[/PDF]>> endobj 162 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 22:11:45 2016)/ModDate(D:20160927221224-04'00')/Title(diff-ops-mail-server.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 163 0 obj <</Type/ExtGState/OPM 1>> endobj 164 0 obj <</BaseFont/RFCECI+Times-Roman/FontDescriptor 173 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 722 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 173 0 obj <</Type/FontDescriptor/FontName/RFCECI+Times-Roman/FontBBox[0 -218 932 688]/Flags 34/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 111/MissingWidth 500/XHeight 461/CharSet(/A/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/Y/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/period/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 174 0 R>> endobj 174 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5962>>stream xmX TS־1D+-DmBU['ZhT@ a&$y !L28"UV<}=wx뽵J7'{6S7;<JK}^s½?6ڱg/@ËǓwŤą%8.ŋ?+\\\uM�IFb k]ݑC#Sb)p#Kr\J'Kջã%;NF[% M Wp+sq5~[wrO9n!^{#FF[b[VeKb>Bx Ou‹X@!"b/CIl&RŸp#[V‰F o;UjxE&Y!$^"ežML%3 \†x1)NSu6m68r*i$K}NB{j`+g͜9m_p}/Z0|;V0Gx/ Ό2_^}y~~Ͳ˾j{(L#h&5 [$f* hȯ,4JT4+bcJzZK 5,c FV-L싼O~;)/-T/AҪ|3i% 3Q A铵m|OpW+(BMa ]ď(CJ;U4 i| >&*H�JmJt( COAXL%m\3(g(BC wLy:`2"cM8P@J.<'hs4\Θ)"eYpqNʮ򚾂_a>Ġ#1h~Cߣ$jb ܶHH}ODv.cpԵbyoc=P<S@ISPZUՠ\j*K[ @(ϥͨMNήT^m*)lV*PpLECYr�[W?N>LL$Id"*8G޲K )`Y^Wv|#@y'tJ&uyE~ } /K:#n0;h5UIQV'00})->Q/>#.P Jxژ.p~^h8h_/n=q{%Tzޤ0B4w ڀh߫yN1<{a�WzC-N k/w~ ~_E=uW�H\9P6ژgSO] дCkYÚV$JU5Y'B%`1Jg-F6h>hw#NH(p\C +%fRg TH bʒ-MIG87=rtؖb K0 wp##^t  堚�.>&љp(SU2~2GZpVY>NfVR+U` =ߢRnѻN>{_f*Ks+�}45P)g0`]0m DDLcQi\\*O7TmmKG'K2|Cr 062�Y\C }߄>зPY5JpuPxHW,Zנ%&d¹-fJ"J<m!-.IL,󮗯O$#GۓA(0Biթ/ѩHKAevfBu1f#o{CeUQbkpUgq?'JR )(m*bw+Zpw֯Coa0$mUMבz*H$)$)MJLǢѸqCrcR8<pV{O; )p8RH9vFf],6[ M䧋 ii;( 5> AVnv~6-u] 4]?&V')t@l@)_IJái9P )/-"F Z:Y5::-fjcڈeh]a=Cq`¾Snت3Ly@[>(?MA]˪?i( P1(@8'VGh:UhTH{ngwUc_L X:V8m<HaX՚5YϧK~j%;J94B5ʖ]a?>dw(T % џhR'v=�t{% 0X]SڞZK&&QZ>xuعbq_aMRXE{cHKīdiI.3#]1nT䔁М] 1!mA`0X^I)W_l}]ZǙ4|j8y6^p"Rd9@ &'ڗ*s`7W,ix/p:&#/X߯ETQn(tOiZd|z1tSk -,>quv;Xwz\b؏fF!U%(aGSܬ.jQ]YCmoMr: 7s E?aZx QUi9Yɛ Q(28Ly RQ $QCe <2]M@:Hʋ )G[n/H2v>_uV;>gA'&`Khv>ooaԆIKd5)ǀ\7C@6.^~ OTt͍͗iw_f?f&p5ϫys!Lm4:#By( 9iQ6oR`J4]Q~م,8%$U4+L~‡?𜠊JYcfTR"PR! r1凱:R˰9^ N:!ى)k Nc {p~hs@kIqhhjW:ʲʂ.9ՕUiJJC.}EM0\6~POO좄v.%41^d`t+:xl @PS.C[U^%uMqQ~`4|7B#ՐeHC)x* eUUtN9|qgAs9邧d#_`BGxW$t,Ʃ)[4x&KS%$L}GnY;Ew^s[y2^(8#斮cqUlu暺wCs.$جոr[фU[xTBFƒpiȮi�+WGOtx`u)jRX/5 mnSpa$dX\\?M?D%rē߲@hwg6ZQց^O%cמ\XSS e\SRGXL*4nba6wT.pmߦ|9ŕ*`iko,))lcN=6MuV&f3mCM4PWl;#)2- *qMXpv?`߸\A5fF\}E7m >p ;c \>HI9u__k4o _8l~pLCWp5)`18k$h_Rp\| 6,}q6gUpL7[1cot%fzE1':VCt&B}k0IHjMuMքhlϫ:-, ~U {0'VZVR1'*FE}`hqGzqi0 l^~f&%܊Jl3|G3R_>SdH�@_vO.nxr.?`13EZgNgObQ2rIPRd:\/Jzϟ8xb?gpr�VL$.+˕sj{8w~S%*姃 :--]#+a ̏4EKCS폮 6Ly%&"s3 T* ^mXg*3O{Nq31> UT}ZUUىDoxfF܀}̩VC'~ޝ49C؁tl7:6}CA0x?=)"s+rh-S 7{Uj r/Д4wB۪+sgO;y1{P AE=98哧,4l@^IYqv@Ԇm l&h^\&(\e5FBfЗjb%(^z$3QyQy-(8ȕ{)@ S9)C<\Mkiә{ٌ-I:q@´ڜ|Qڱt3(ueG9u),6GԎ3 5:j+[ SJRw℃c-\5V`"2+Ssp }<xSKlCx?@\;3 XG(kCSO <?΂]p& ^<9GIWa'!y럯Ƈ$PQy5 J')TY\LԻe4Pǃ_?2 . R"'o_}{_qD2Bv,á秎[~rkͺ֬Sags;x\ ?c.k^`+kǦqTs~JURXǣzOIN6lZyE/nN0elL޻'>�и8ITL-~R#,p:<p[6믳$1) OQvD?dwUG<81>f/Y 򋻾tH+V&2p$!6o9 [QUcP!r~XD/޽q]6JQPגh&Nz*;gHO;A3^uh1.j|g72y7"b" V5x!v7/>K?AT`A.i $B̳3S%VIr2=j$=j}E Q$Vpa&b ?rny?>pB{gs/mA1ob( B3S3wՇ=wlھSQfH GN~p?p젗T@f"gmohch9E�e94}4yzϋ52g@rf6Mp+$ՖMj{i̴ӘibC [8da_Аx.a7/㧍׬ך;><w-̽ ?o�[cNkդ:<vo n^,cvܣB]=R[RN Ы̍WLXpXD87H, + wNIQ>B7Dbla},B6O{:dƌ53fI endstream endobj 175 0 obj <</Type/XObject/Subtype/Form/FormType 1/PTEX.FileName(./figures/diff-ops-web-server/diff-ops-web-server-eps-converted-to.pdf)/PTEX.PageNumber 1/PTEX.InfoDict 176 0 R/BBox[0 0 360 201]/Resources<</ProcSet[/PDF/Text]/ExtGState<</R7 177 0 R>>/Font<</R8 178 0 R>>>>/Length 19438/Filter/FlateDecode>>stream x}۲lm~ȝy<*QMRTy%JLrdQZ YfO^910Ϗ ?}9]ۻ|_x}7Wz쀿ݟeZp=߿ӟȏ_|:w?y~}̴e>zWzt5!_^cq}usYz{~ϵ;}ӏ^#w?GFק_i_ї?WZ[~I#g}>S<K~g|6<=<w?/u\М/~YVū!^4}ǖEJ{2Sȹ.;c@NߝS{޷^! ,x?/y>?9Oů <΍?Inė_'Qv >RK<Er {H/N×?sc?3n7_ssŏ~+_s'j΂Pw_ʺVyWCLs-޿m۷|rK9L~!FxQy|ǟ~/kv@~)}?# _-^K`~~?dq"LE|w:|Y. 7|}זRGoOuoY[^_~5 tK|V^ 0V޸_'\@xspb>I43!7?pw_R<;)K>?WC/4b OEo_?!ûR&V}^|Y6/߼,MBi|y3tbpP>^+R˖ćPӫ#+mq{czclT8:U!w˭KEV_<%eҚ<x/g761>5)Ze1[e�*xNL_wEoۗ=d.{p˞�/{m1__-f˞x_z"v_6/{qi%\y.) {Pe0ջn-ͷĺ? noW{Z\<~x[^MNZ8q|"$BǞ�>N*/I>2cFB;A*~lYiu1} ˊ)48,4jy01φcīh`hp<a'?̏U us$&µYwWW{Mɺ)eO6ػ*wZ }LٙyLJ$jJl,w.Rץ;![3ťSIt|PTHWm\$xY' kk4eU'e I$\K..~^_6C{ ,fn ˜q-_S.<�k6|Tz☉/Ćѯ;@^xk&zb YЅ>ԌɦG+/jB{VUՔJpyQ+(%x`7'HSwNE]>u D 19/`[L\M9u17[oZl۶8-ۖr t%0M-&ȃ8ʂqv yve5t2恉<ڥ]AڮlJY ە]W`R<ve5î*J[vPԵ+,;veYa]9d}ݮM(0ܶ|vdzAgWqȳ>mۖWۗSX[n[f@^mK�mK1Pή Km]x%^]x׺+ߛgWf nWۖbŐT-Lږۖjdql-L9\~ݳ7+anWRnW M+ەyڭJdr3`Y]<Z0r.ɉl0:e8K)ݠ-ς.+ĵTgp He* PbTc,RQ|)ʶp2j{&yDqE01沿dQ<~,.kENsnYDq!&A12VOCf(}K=e"\2#'fACw<F E_g#˘(7l%zf(.7= >Y*_ .P^壩YȊ]W #nXiE[Qk<FrGewYy@=: jG(.w2*Xu>u[` r+ ޟ݆CW.R& 4;+Ƨ%SxCF+oE`Y{6~OM#_av%r /نri dOv씚AFeTٛLmgk̗m*q{ҭ)H~/ۛUp62{Snl1ڛr?+.q{S[l{7ʱ&˯`fEuޔ.?lޔU+7lcۛCzSf{S7X9nwIe޶9M9{q@9|9{nsyiv9ު~sr6'8&_SZD젩5).۝3/6NAs\{)`>^r첨)` >t\)R lsn6<d,ns7&SnmNAs,nsFkK|tnʿW9'7HOٿ~?QTǢRxkݏQi6CMK٫QCTztyS9l*7"N:@Rԙ1§.Ouᤪrux4'ޅ t7I{Q 6ŰE%Ϙdǯ'4- UC0#R룁]<ZeEvٵ/e]UaY]`Z) bN ƅ՛1ul|앜cN{L`*ucj?&GLo(`/L$:{L `?4c*HɆ$F3l cjb1ɳ$pilR v<\yLȅ ;G>_ ؜b~o=4p_Wƌ{=~-/p? y9æCv `^P(kUP48F |n$ $isF̊�( �Ӫv),-O:KSD{]4w$gu1ɸrT%bDەH>͢{eR eFREtAI\&R2,|ٍ3\UnYbW^&{`eό& ef| ])!l_)p@&BQ= P( �ScaGT\v3L{Xnnr'KٲfejˣĤĻZӾ<daLƉ}mj qta朤rW]5\%&ƀ0rWEx'en'fq!n'dcMK@a;1TX/#R MYᛅ^yop17,U2HNrr9?az8' c;5M]| &$;~jM;‰)k ԢƉ I݀~>ĥ/}&9�T/Χ<E=Qp9;t.ҁp]ĭ)vY:~ i% o9` w}4D̄`EE<\&cCx\ϽN1N! 4 '"2$KX2 f#Kq yg[V<޲)2iqE̫)zls^PZ#srϺ)F֐Ƅnww@=OLnɿ "p#^Ff!xd 9$:_f7( lU*ވ'|7]oģ@A<> _7%e䠆T<'MDqޓ@w�O0L#z7!!VO߀�x=\!<Y\6"roe$p7mdɊ<po! } o!ֲ<|!%v`^Fn ~c+<Ao5@/#6<2<{Ā3Hyc^F}cܤr[\rCXs&z@d 8țj\y>y [&@eG<ꈕHo"$ ^M}!@Q ^K �xY�oI=k�^;2p[xtz)ef4@GTLMHB"ntO?xY;!^GS.!^Ɍ.ɮ?WOB!)nkHbXƫo1:exZU߀z*𘤺bb⟿zO1pnKza)u(>Wn3,Ԛ^Ǧō͉\cprӭOD@aOJnr#(p_DU30WWzhy*.J840<=ƙ< ӒEɇ_iI,TO׿,qK4ocPz`|#$׀j.L4I]1}8_YM遅u@]@BLcW*}d 3W% М|V�n}2t.g!= B/]9뿕Xv`<4 tAKVW:?~`|q*ġp1Wëb.CcuoO!P" UZrObЫ/.A㲑rP js|R!'gz)xvh\Jm.ϸ$[Y5Ԏ9t gTp’5]Om<f.Ry՘K L;3YaՔS]0|UCV.3cd}ý|Pe2>pgq"^72)&+:Ř eogxpy!fEeNv�4y՘WgȊBE1+$g\j*:}TXj°kE-)1l[ .єU A:%jp܀I`J&Bj2Spg.P.�\b� @&;(ހW9�Nz^fmOxULv'+3f~��xy/*C$<YW~�.:GtxWdvxW�57C>5wy3 d^;YsxɂwbމZLq#f>ť~.7Ķ^;y;1kݼO 4x!Jz�oJ x eD#�^BRl9$p~oKB <v/ 3H&r #B7M'55I fm$@!>'4}n�Q<�O6CQ/F<@ 5l:jM<Al:#P1=Q,;-I  Fz#!^udʅxK�x,Az#$A!^q\!^<!rCX�pP"⵩r Z'=7 ruJ7͆rAU5Лr/QWzc^ey5YJ܈0RF<aּH<z<?o)y>4/?/hJq`gŃaXU@>|+3,ox)?Axf6k{)fkbĽԷFo,F,NbnAɧ´ňuO>㙡'\{~>X ڞ{ 4 X@gX,G7#ZZ<HQ깧İ ?1}pbṧ8 ~'<oeK*ټx+tXLX:,FjX:,°,�Mx+<X $2@؁˓O;`qmd7`1'%)sX %cuX\?؁͓ςѓς5|L砘zHO>KV砸⒕9(f2{(.k�( %|b 6O>KV砘O>KV8P\ҿ�Ť vn5R ( %O*8,�c @> )Cb<,Jc@?R@Ĕ=T:@> Ti|>]uרrjl|Vz0#E|,J}!wPAs'+3?s��xɞ|"u=H$J!L{�Ob~[|hAde>hXTDxY2_xHdO>Y{ܞ|wY8$Z"='A��^͓:I|L%aXZV<F{�OPۑϒ:<q{D5bgYC>$=:YyM뢭:C`S*yE[K&uhkA@}=J�UsO`y:[>* W�J乧j_x4۞{"�o:)fy7xsO<9d>ښ&<hqjA�⹧2_x%=Q'!wgwwཞx'�:l@='hds>SOO$K )OQxnqBUo'ߴ@Q&g1t%la(kDU 4OkRּg T1o)YLW6*[3bR5v"k51D_Y7H 4_6`#$\9Ȕ]&NJۢɊjR?fϕ@&F^5IK"ez'D._ZR o>= )0.#F8T^yE&VO^!dBR8!qPA NF *R gA}9Ō<`tSFf cA~X=eЀ=xExBHbd !JiյD)W\1C I(l"K-܏+5Šl_5uA54~\\fS ^xUGqH€pթWG ..HK6YOHʧ uEӮ~8D[.aQX:pʫr?곌 31 g{Qdkhʴˆ)O<@@\Ȩ,<S-UGZ`=)mj;@GG(Scgr{P7T9467@ʐ1,ȕV NCl(pbRR sF|P*R Ki(c(vUvhZ"Y9K']:Uw-ס V#he5]QzZq.]WpzjJjkk[nJ#1O|TMGX =в<h!׃O%7Zʝ- lK#- 0=�Qo,hECЍH>�o璔^hIs;,VCx%-e('Pb- ВkdLo6FJpeF- xH; cz%p7Zfd0Вv 7Z-e+!ђvKo,M-gRVw%eHhYX8teA-ec.dIZ В5HhYП BK2 ]zeU@F&>-YZ|鍖rb"IZv-@Znb\hɚ; -Yw„酖nd 27Z6 -Y3CKqR -YM- h)qRZfT,}vuhQ(eA-Yl$easВKr7ZǤKJHFuxYؿh8-o5b/h/ {̓ȇny7^R^ꍗK!}{xY):9xYnĤd1ŊT%bRzfRRL*= m S7fR %0=vsIAҥ3V;d1!7fnTMz̤knr0:fe:̤ެD)Ju}in.${ot]h[2>4 ^-VrVjx `u_N<0׶F5 *ʜ'Rq;2>[}aj1vR:FLʣ큠F#0frhѵvFEJi9Dcu,T`DԀ 3X𼚑B*%StnYubm� WYtkiFm`� .wo.CJ0tXk?W'pAH-xӼٚ�Aj.hw6Vh ;x0NB@J} 2&C@J}CMR:54z/kw<Xsl|Tz GbZa{ƞ)*2^wߝA:å}w.:/>cp-Cl"?p nzȨ2ڑ\OTj&D\̈́J(WDXG, _mW6ӫx[%c]j3 ;h$i^6m b h쎴xX(vF9im-@OЭ/%^-@Uͮqy`PR*ie#v<Rl0G[b!v,Caio Bwd%iVq1Sh"\UA[Yl�’SYb-Rd\: p*1ɺ7.O-%?4(j&8 M" %�]jb.d.5i.IÞԤ}Hbȅ.5Y?.XAD`G>RW9,>"ODtХlqX.e[.ot)ۺtxK lX.źIz/t L͡KY rۡXAe?k<責A^]8 ˲]]VRAY 2`<@xe"8<@xeQA.(�o<T肺 ƃ.hGw8tA\A,e]S@6%] q<2 : V.AotA ^WF6l]2\zƃ.h1dхdX!]n&5tN F6H](g<9%55tAc`A=j]2H\ F]2t% {1.ѥ-qBhC*]22xCl%o/7ENK弆/sQ+Mgͷ>EMf>}?-}zw8\I.AqAZBT>e*%&-EY+YkO8|XQZ2_bj8dlGU>�"/k-K)S�났�,e^kë|ƌֲqx*Qw9|ֲq8(Z/Z (ZVT>@ ke k-@ k-[S�lG僖^Z2Yk88e5)p*P֤|CAZ2 YkM$GC.ZkRqT>KZkRq|ؑ,ZkRqT>̩|jVqT>d5+8*vY ֚p{Yk$GÐL֬|u֬|؂.ZkVqT>Ω|jQqT>ld(8* ZrOyYk-J:ʇA kDYkAG kvYkAG kЇYkYkeY/k-hd|iQZZ1U%Zkm=Z ^NZXq|*2hAZ Z+zYkAK k�jxaxldeh�^}t74lx�Veժ<(< B�yY+cY/k-^ZLx^Zi=tt%uط7Z ?e5+tVV*rB kY< kY<>%xl}eZ+r˲</kI< B&%+֚g:'xd^MJ3OC-$Z :ԪY+aY+@AZLW!Q,qtd:Y+{|YkA=P6u+ ֲ3}QZM_1sDqe} k-( ւ k-K@XDq,ZTee*MZܱR+dAJaYfy]kd� x;Z u<?е*up&o)y(c;Y=}q]=J,3v>ܔ7G񟱝ԤQZoOϿv>68H\韣(4z~nQO·z{Y?G=1/;$IٟS<YٟhF⊈`SxwaFwxY?G=Qg<$n>푸fezbPԳ"P,fH\ |J/ 1DoS?G=Qg6<·|;ZݱΧz=c;Z�iDOz8ꉨhSjJ,UٟCbv>tC;ߡOE5){TUB;1&)!4ġMٟCiC=?ӄzBpBoS񣡝VOmOӣٔy$6]M=5"@=1)·B; 5BhS~/):$^?:ij+sHMtPxO|;bzBmڬC=!F|6סπWǥOzKq�OzKr�Oz[YrQOJ>妞ׇv>ك·oS4)]yԮ7ZglS;iTC;B]�'v>)uޣoS^G= |jS'Om<SO(C;zBԪQO*+J|kv>bWhS |*Zv>jΧ5Мv>*uԓUO=^G=!|jQ'OEFh#&ov>(=^Χ%zBB'4SϊΧPg-J{zY^G=!.jV'&O=+JC7Գv8t)OO=k&ug{8SϚ: {WQV䋧Y8ԛjR'SϚ:In>eSOJ_B7rk&4)(i|R!בϲSS8.POwb7VD=SkI=~}v͈ǟF7혛|b}{bv{֢ϑO yϊN{vExY |v|gQ'Q{fż縊@oYH�WM>6j&qO>Gk1Y?G>Nc>*ss\E7WM>+|Nӆ:ٔ99ˎyڔ99Ϧϑi|6 =6F'*gSxjA/ٔ�:ZЛ|ve|.=+tc޳+<sY)!])!g'ts]co ?4=1ٕ�05&Ck!C[!%11[fb9|xէPW!M>?*'J{֤] yJ` SdS9T<=8=+|ⶄg6OܖjA\�g=s:,V zR 8[1衟K)bFbsiЍłT^]Vqtcq-W-M>U.X\MK4nتWG>%й/kב,ϧC7VgE =>yϊA{Nemvs* $>ʺ?O>?yOHgM:ڑϩ!9:K,s(uX\'1﹵DӑO4 yOcs(BVz9z O|Ї嫞|}T{n-uBsrG>^G> rJ|\u+u!ޣ1t#]y!ʎ|v|.g'us19u#M# yڔ:99ޑϦבO�gE먘Dѐh3בOABޓbDO{֪בO '+! mK{RȄ'u41 M{R센'e=1i*TxE#:+ཁzJ1IY<ż'幎zR9& &7@%CyQ8.6/7}|^\׃J?"{B`P\ EȆ<z\=I1k)ѷn1yP68v1cGz  uAB$kb+fՃx$(m63-4A: ^²ԭo;pTޕc+�A0D>x3S>M ;TkǔkܫNTlz|Akg>∸s, 0Tlf+1i[l Ѱ37v2Z!ţamBLƂi:.aOm\;J8 O ~쁍FukP0՘%6{51:SJ'-mI{ ߇ũ3FoEY!X~McڄxV,mFhN[6me&{Z ?^,6K*8=Ѿ5 &\i+ì)\/؀0oڰIx715_ȀLݦEaIǫXt5uM,�-<l B]%8ha܀{[hxZvh?jvHd M{_/9[ +ܦifZs+ˈ!~pGү g ,d?Ҋͷ>e:0=/OUt12#8)Q#wIC"-|5?ݴ=\%mM.V4z240F#A4َ F" ]U4li4Z歊cAsu�u0L2R1 vŽ4.U)C-@AN6'6|TR,+Bcj1m�J_b mXvUaihV$ru/jAf_-ȻS2؎mr>TJ>dUח1O4Ŧ`zୖf+mU+P`jnI,f{j! .yϔ憡v`N3+K5%gqsSmAtd½Mu"ٖhV1g"\T.Ú7t4M,V[8`双0OYp.t}q0XX ulș0#�j&o68Wu 7;7mg6L;o68P3X͆,9%{sfCV.73>Lo6c6)`6òPp fǿafE˛ t,l.֑ݛ ʵ`6[l"wɛ ָ`6αzwÛ 4wo6ۛ O`6cx(`6ȶ`6ylla f¢5 FBh@>0Ei>{2ژn?+^g~pdc˗Dom0D@cRS1<E`pobC#65r*QСAնWg5\5k# Q#du-¯ZkӋUVgcxb :=e+\t6zBAP"ssYEm8Abxas0nI9Ɂ=`Kk8.sn`rCu}@AS[j4ȵKN6ޞ ,6թ[ZZo +fЊC;=f=LՍ0m1YZ#704NA=afΠq^ዪXEMu9FՀ+[v6!5,+Ǎk>kUh0J_9@t+[8kZ'a·m 5&̓iv⩵Wނ<5zO>}nţ\{jNX:/8d 3:]Z`3rwbvYg6tfaH4tLBͭc@-\EeaFb[/Tkt`Cw3_l GaP0L!1ib vѶZln5dެJy<6lԒqEfâ elaGWy0H;~Y+C0w"= 0yWn:RU6LdN@FΓ2Q6rHԬwrYH`!ZzWM �y]me, R�`~`FBa~m093via&B*sk+" Z:�ЌhZ J/}1H|h�\3rV_Nkso|j_SNُ~&g_e?@C4D|4LݓU@C4Dp6EBٻ!W!T"+~FRCCdHC(F4DPihH)DRCCJ)i,i_9Y#ҐtS4D|4D4R# *̞Ԭ[АV4Q# 5" ՟ ;<" EMCh E!bk!$HCHCj=yhiH3+ҐZ)hiHr5B-Ґ l!4Tr?!J^>V+[24lϻVp:-V}AOjBг`jf,ɰ٠)= eo6*gAH!Y0(azAQztCKlPD7PŠg@!Y0 =+zgAlgeo600=+:gAS}p]гIztAϊ%!IAϊ$>YЇ+=+xV!YK$ A 3= cgE<= Zg[Cг ApjzVx!YU5=+< 7]?�_?/w˙\TL0h @ݰ]ZFgY CAsa%58s_*B5f"ⓞmѽ\T5Vg`g.PKMͶiTh Ȃ(g.P?KPԴ/@q,URӲ9ʙ TR,7]=/l(W-f(X R5]2buo)⼁a9iYl,jH)k^[kj*);SOJb!jв^Py'MC_sߝJ䶜4b5o( Lg.xg)2,)]ὶ-WEd,:K3@H-ʰ.d9JN~d,}? E>=gz+⯣Oe>ʜɄԥ̹^}{88ՙU['$Uviu*_UV:3eypm`U-=W\VIZ�­AOzMNToB\fShWVuW|8Bn>x^[1|TQ:8)t؀5Jw\uv,L/; rTm슊Jw_Z\B @H!3Lc!lPVDV)<)s̘Vè僢k,&2A%رCRGJ8& J9qo cɵ{ZQ-XbXvک q>HB[FPRw Q@5'Ŷ`A1z$ sVxvp0Hk[TҒL!-8<$&:HZ$4SPdbx0L)2U"vWPntx̦noqtZ5amU''cve9&S4`ǧUOSAG9@^CUNr: tZ! jG*Cjv({`^3>V[[~BCytF1;_rR,1B RpdxT4Nqe Ůf栗>bU`;n1<Pᷕ0jsB݆axV. UtUxax`gag`UaUȠD+TeVq|, G#xkq<<c@w}nı:̰`َaَ`ǎaǎ`^SqVA\0Qg0QG\0 e0Hg`0?g0?g`07G07g`ٜàq ,m|\:T22Ks).D_ 2^}Ee}B_+j,C_4Y%ǞUٰАJƆTT/t2tͅNR:IeB(N m2(_hkT 8?-wb/м_C&85tfbjvCB%=8Tb0V}$f,B3$ ͐7: C#@ Mث9t("ЄElq>&ˡvBlqh| ` `!^>lZ�DZQG;o},+SW-q wx-))ύ.]i ]3es›r=G©f9f57r¬X61cx3eT0eU%P6Y\ |dY~>2"+>91:ƨa1b5-C3a"1x01 Ʋqh�X(]N%ac1Zc1.?E)�sx_0m{6<{ߎ0w.͋swQP\]%]9Su03 p5ynSu H n8vk3v2�@}Yq&#jwڝ=L(ϣѣ^XG!(v?~a0ZF qcM݃؜Ej"ݕd5Sg JҚ3lY%4aލ V3uIF�e:#Vu}L>=jT0Xԙ˳ ShLٙk:q9GpM]p*p,)jj97HVÄf5S $a>MӚ{>ME&5:̀d%N-3,3ft+d@1DUFխ^wb|_,/ڋ[ǣՋa䘽bޥ{uGB"]\{'eqӹFyHO2-9GzRn:׏i@OMz !艬Ŷ6sMjn:�}6s}Oen:׻86;yċ]asO-_kmGu]RWksp}X:ccs T jm皣"sRfўV-T4vyjjd%?5,mZۂԌ]w9SѦ>'l|ho}$XrEx zNs:%-Z<F~z?uW?vWKZGi5~p x芖fN-ϲ,f{?3+ZWYњEg?폍Ȋ,:~Ytch]UkZG՚Eg?OV-{>5ji=uZxGӪEg?&hZFg?XZGעE?-A ZF!̧X]}sel<8gkaCZ`(Yu΂ -Zt 6RZOUxHu>Zx,HMZ-HQ2Ʒ^IQV)gǗ3֪Ջ~PO'ch}UZGeShZGg?/:1ُŋס8:Ѵxُ~t-^tci]XZGEg?8:1xُ]֓1|؏~ ^tc勮QbS]~LV/DI ZiKC?,-_jxY+XZqoC Ջ=XOǾO=!|F(lMY+_V|bS endstream endobj 179 0 obj <</Type/XObject/Subtype/Form/BBox[0 0 359.999 201]/FormType 1/Matrix[1 0 0 1 0 0]/Resources 180 0 R/Length 29/Filter/FlateDecode>>stream x+2T0�B˥k�Jx endstream endobj 181 0 obj <</Font<</F40 37 0 R/F70 105 0 R/F39 35 0 R/F57 65 0 R/F38 34 0 R/F80 182 0 R>>/XObject<</Fm6 179 0 R>>/ProcSet[/PDF/Text]>> endobj 126 0 obj <</Type/Page/Contents[31 0 R 183 0 R 32 0 R]/Resources<</Font<</F40 37 0 R/F70 105 0 R/F39 35 0 R/F57 65 0 R/F38 34 0 R/F80 182 0 R/Xi11 1 0 R>>/XObject<</Fm6 179 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 183 0 obj <</Length 4209/Filter/FlateDecode>>stream xڍZKWJ~$'[&rr]Y@�@W>u�j3z7/O&]o/tC?ٞ^3/ %e=%7o?۵ דwyD74s ~wѹ]ES*LZqd]c?wޔ9{#[f/OC }2wVGSϏb0;y\}=7sYPlW|?=Ğ{#ľM;s7R|0uo/�:N^4lUS\ OmFnDл$\~uG bӍA(�Q@ /Nr83qIljy;zYaQ4n_1e}RF IN +U;.}$~*-4JvñGStFoSY46fx0{)Z8O r=!J" ` 43bGNY˛ǪN3[Ho]ޟ/)Diځv5Y/I'nܕ:nyIc[5I04KRxweE˹\I={Le5S 'ywͩϭ U^ǾM4~.!K" vHv<wnˮhXZtUgEw&v@kJܞtsX2GQ~9o|ndbW  7-l[ |8ۣ"`WJrubFеjޙmS(r޽:}HOEUAQa=Þtq]M_B$3\LW=gQeP⪉hn3ߤrT}=f*tU)RbMĺRlfC$cHq:t8z}id r3GOpXC11,N? Q^b;w,rtpf!ppW&Vw9c(4n]փ:7ٚ-o}לl- uѩmfZ"@OeEk n[ bLg0P8ddV3: ^(s[{B0@di/$FD ̕x"4ӣ3$U �<rB! u1P`|ڜ4[+2RQGq x=S`@OŘLZP�[*OLv*T{wEY `2tIuv"|w7C@Ao9s;2cjC]"8mz` f@!Cz)p =gOy{(l+wtNrQ8fA̧{`"\l(GN݂#(Y[G�y[ yך,rjЇ�AH ݘ4c- #L09O~Ym.]I2 GhL,BN;P߂0h*Fy]DM%0dt;$~0^Fb%[U$'lw'`Fb[$I91'w2Œr>'AY`L# p0<HC%{c3J/lh*CY(9@"@yu/pEXW~IV!.`*�xU-؅ǙOdpL CJs&�RuF hҙE&Gҙa?VF}!DX>-jT%"@@Ai\" rE1b8lԦ_)Fc)Ee -MbO% XӜ;a|Ũ*;@vZ0iVΌ:[~AoNFG6Oi[W[p^|(~?JB'uοr7:RROod0%]?80yY6%S/Ӣ__H $ⴙc�<C,V�ff6Ql UtE yq],6zY/ȃ(:/޺/߼Lt'x^FVCc {6'B=AqID0?m jfmZl0ۊt@<44I`6?t#i<$v#) =x1rzX\q)0Ŗ3œek ngLrS9˚~=&Mn@%QYhbjQH36!\J.b$ʀ yM_ʇ7c%4=Zd+푆P6qe\iz]WQ9aـp~J(zjrA'Kp0N㹃JxD1t<8Fc:RIC=;,d˞<7Ó.l|IVS#T@ ŒtSͭz0A76O7?Wv?0Ui$zgEϝZsb9ۮX4 > l&gj\!-" d;#Ȳ=F7 m)"0 ZcY Aެ9BhQXʡ~IaH)QTUI --& f,U/G=yGyh3ȓ|YV(2y*QƔj:ᅏ .c7z8P%$p 4 ` @g- 3qKf5/cTeOϕ%O. Ttb7KQ2"QlEDC#td?觼(Fs^/YB#%qMY8hw"$ /R?bVo:\U)kl^ri'J5/{4'd,vH 5Fmb\/:Fx6лd 9�b~3^> C{Q/ A;o*ЋS>K><{?8878*uҲϋꓶŁ|_!?K.,`Ī\K|ƏBHB̂"_Z'i-5/im=Ik#Mkmjz^6J Fr=s8ILUR_rBި`CO˒\d<#,|aN@5e-[A9kB~b-.d7251Xv/}W=ib �?FM o0~FpВg0E"+]+Or;rb.~t+51;ZwWs-/b2{7g֑ƛ<?Cb+zoL 9;rO ȥv=Aޓ1xZ9?E�Z r%.s-cvyjX4`]ɰRn䒊]]ũK~0z5r=T?Q; '"}RrkI-#'#Dٚ+_,}<˫ܡ9?rnFcy>{nD\#bPЉ%15W\8'ݦBRY)ϙm6mRf_�j&ΫW 0aI t۫P算&_i<2'B|9s) ǷpUGFjxԊU!Mb,Y .]ӜCC.^ȵ|b&aAtcg-ii2L*qD̻cj_\8[&? _mX@MٟT %\F$uܐQ xR=Jԧ{2{[I^h/RO8k +EoxG% y<P/;-9rjνnW){iu4f$Al iwBN@=~?yqן,/J*ԿrXPE,4P.+c`Q~Ĵ3BT*5$V\ 3Q卩۟^\ }_˱ʏ|? F7n]@Ďc^O|?L3zA%ZUN&rz.w%Ap1?wQZ.IX~qbE@ endstream endobj 182 0 obj <</Type/Font/Subtype/Type1/BaseFont/MZCEPE+NimbusRomNo9L-Regu-Slant_167/FontDescriptor 184 0 R/FirstChar 2/LastChar 122/Widths 185 0 R/Encoding 44 0 R>> endobj 180 0 obj <</XObject<</Im8 175 0 R>>/ProcSet[/PDF]>> endobj 176 0 obj <</Producer(GPL Ghostscript 9.10)/CreationDate(Tue Sep 27 22:13:37 2016)/ModDate(D:20160927221437-04'00')/Title(diff-ops-web-server.eps)/Creator(gnuplot 4.6 patchlevel 4)/Subject(gnuplot plot)/Author(bharath)>> endobj 177 0 obj <</Type/ExtGState/OPM 1>> endobj 178 0 obj <</BaseFont/YUTIPD+Times-Roman/FontDescriptor 186 0 R/Type/Font/FirstChar 32/LastChar 121/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 0 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]/Encoding/WinAnsiEncoding/Subtype/Type1>> endobj 186 0 obj <</Type/FontDescriptor/FontName/YUTIPD+Times-Roman/FontBBox[0 -218 932 688]/Flags 34/Ascent 688/CapHeight 676/Descent -218/ItalicAngle 0/StemV 111/MissingWidth 500/XHeight 461/CharSet(/A/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/period/q/r/s/seven/six/space/t/three/two/u/y/zero)/FontFile3 187 0 R>> endobj 187 0 obj <</Filter/FlateDecode/Subtype/Type1C/Length 5740>>stream xmXTSg߿1$P V['u VT@d CF؄ a>@ Haֺj].ھ}.}݋op<,a,kkh(v8?yZĢOf݄m̱=bYU_XQ̃sf.f$:{qTRLhpH\?Xrn˖-6I=qņG,H"Dq[m'mÓBbmEEa6BC 6`vjצá6SoqqLJ'};C7f_ pL:y(HSKQװckl׭߰qӇ6* [`N3-Îb1W}憭`0{l59`kZlfa 'Fl}cvlf-X5bӱll3aXQi; mg,Zdr6<n7@trn3f<y`yֻTٷ,=-x̩mnWʬZzϙqm9oUc*r"G5f$Ou!P.u:'2ew_w\WVh,F9HSݲ ,'PGB!K*XOCrj Y)X%D98pQ=2 1RNV@ ׆,D"8*̡@U,L_^.=_z%\S)MLqXA bV*)2q}Ls]72+e?"!dh: :uwO*5`3#VP ヤduhvPe+ 5n2r pcSg|j3j}͆y޲]hwˠz/( ~ bՕm9W`PmwP|>K|?4zW@߅"*I0BBY^zƿ\K/&sTNioE S^@B G}c c ! W)E6]n|0K{)QA~d^ؙN%u_]/Xm@.o)NdsIQɡglhF揎{D:+7⤲E8{\V/a<> ŁV" Y}ȥ3B䄃I2:a)TᮥEW{З$PS% DMa 7tm.<x1|�]sd z`q'O xq+s=D hyY%�BoG@YW!SRqq:50k.,lE\ղuD7˙4sy9A^t{=t/iVm7=gkdY^qN) ꊓ}(A%ߟ'5+[6K4= j Sd[<=->X$z3$�ܚ$Jz }?Ł2p= P!+jZcRT[[,ୂM2.樸YY)l_4UE\<쏶|-9ՖX abp}DPRB$#[=Wϥs,eY54* >s6Haeul O}8GQhrȅ_6+I\Uw`N<7M$O�j=a):AW8z-G~4$N/F@_\-ՏY}b r#yAKhRR4jwr{Ҥi̜,wwu|@Q#U~S!y/~NQ#qxy E JJƴ5K1M OuZkUewO(,*eVw}tnTY2*h=k⣙jI %r**>\ӽB^|3e2C|] >s:kj/9Wgc0 Ng3KܜF%S/?ϧ=+_hW\F(Z_XNJt 4eBHg>QVgt^qĚ*Su~vAVU]DCKupC1a9$M'C){uO^Pг4*mV$(c@O iT;ΦM, Ku 4eDMПNB: 8Tb6wmF|;3j^_%v]t�| k@݈wϑl@rg ]ӭE Fdjn 5S0lzb;/J=Grœ|Dwqpq)pW;{% ,?>Y"-'j]/o$*R<N& tMEaݲ'tmTIBgƯuBdܖLpjD8,L+Ѓ>4hr  ")lTCٳ<q8Gi<:^"i %W] J4RcO<s.(kU]mA=ȪcSK-78BKwh׫t"qeRWk. ֓5=].h564]%,o\qćrғMimS@k0Qd.Jr9K}W= ,K)Mր_j9'8e\A95jx j.?)Ǔ@& SifS#[>('E9�!E-Q%'iuЖm p#v4tZxhjURb:vO~q7!)֊vC?CSJER580te 3,ÐbC$) p@Yjqbl<=uΗimv ڑ;"y%SӚu׹7ԍ1->UOr�[$ ? ^z W%Ow\AXaviz\6 ;Ei㳺p>.6ׄ7Km1.z5h&pRKtOŅHr>DeH_�dqCDWp%@[+Qs3bl*SGV ʣK1Vִ9-M Jl {nrҞz7%eTwóy&32d}VEv=x .j_^ygT4** ɩ:_(USd©.=t'9qS9 ` ੮3|&5)ʚځ'Y^6:JU 5mDJi"&*{ܣN t6WN0zww" rcA905gk1K]:@lhgAcz@Ʌdi0 iU; D ΍5Z}YajnlF *CJ}@ q42״_Y ԯL-ǡr#)KEF @#QcWԳSuZ| zӖ SuyLzS/IsIQ773?X"I[X53 m&8{D3ZWfzi>k j>!jqA4(@FʼnqƚFs\M$m/M 5Y} ,-ҜHfĩTR^"[DZ˂_Ȑ$NDDŊKٳqPs4攖>èouvv/WO~}zY.K6AsXYt큧c/ jiL|R1:;@[|EqI5hQM;8׹ih[ Y5FhA3pj3NMuUN&л]f=<1d.6>tġ :/R祁t"%%M)`n?VC7Ɔ6 e4 O.�r"+%Қ3rETd~D*~jmO?T) K˟hU1v<,7<׈#6j(E S[`mvmŃ` X?>grKrJsYѢn4tT1_T'[N}9td~Fj +b/NBl++]lAnNhDu^!QʅkwɑUQM}Re'D8pIMuЇ<:7"WQ 0�3pv>&Hp Αxkr=$*WOib =wϥW@HHV<5 R)UiFPjktsR$l]魭[mPZJTfT0~L{.>%ϥ:Vc \~l'r Lg,8d<_R|AG=Oeby?1u1u.gվ HW/ q>*B8@bCG;|ŎsmR'$�M@[Z Ȍ&#^D66a�dt�/ٰ{l9r$??R[OSX&d̩ԸY<ӟicߖgƆGT%÷IEA"T.xw]O BeaRt[n}l^/D3GI-dn'7׬'{ xj%ydզw}fOvG%jkR-d򄐾;cO{C=#&8<ٜX-)5ҋ 8xy4,X%?bȰ?) n :˂cbb)0QMVppz_r墛Μ4ڊ~[ǹg$pf'SzpA~n+,A@WIE,[6gtk=]8'V޵=Q$R >|~Y@ܓ zwA3fbu;5rE"Ya;`cÆ>Nߋ{(|pQ�WB u @]˳4;޻+\dV71V&Tu:FjuB+RnU/>"870dbT]fˏ>ۮq (B[/E<>:`1W'!Wo _IK(ɉgս#C1rH L |zwH @F&S",̬oWIhd5ۣp.e-H4s2Ybz{.fĶA xN 1#E" RbC8Jza؄ i7:C3 ʹA>CO1??VZnSq[؛GgYèyKKv'K>8T1xm?8A?l8]ޢL~bU{d'XUE;F;3J3%fmD7[kِ!q^UXPS(jYTT&:$L4Qn&j6x1h֬fc endstream endobj 188 0 obj <</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/F80 182 0 R>>/ProcSet[/PDF/Text]>> endobj 189 0 obj <</Type/Page/Contents[27 0 R 190 0 R 28 0 R]/Resources<</Font<</F38 34 0 R/F39 35 0 R/F40 37 0 R/F80 182 0 R/Xi12 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 191 0 R>> endobj 190 0 obj <</Length 4349/Filter/FlateDecode>>stream xZvȒ}[,$k6kzN2@vHwm͋;"q&gr_>}9D;}w4 I(ۑa9agOY=iL{Gch<8pq$&3>ɏ8ֵ=k4kOQlگI]T;/صTVEռLoM=QgU}<MD`q]m'>iVc=?a:V6u}wI<)U%}1±m(u~RuOgRV&~ n|Dˤͫ{થ^fmzEI_N=gLk"BW实zUY <zi+G'j!"Ki!25 +Idu\* ;WwDuM BIZHJx6?n2|gq\7CyTi$o!:؎gd|gsy ʓeRn[\ƲI::Wmi: ˗)Z\kK D-˕gca v=`ҸK*tb+ Eg骳QJrWYKs,nN*/NTud7)d[ټۥ[7Ɵ.b F]UGm% U($wSCBI*N eSeKEּ5m׆l:) 26E*4Ӗ))^٦"˿ϾsLop\!Bt\Qс9)�Nlğl? ;^^6mn[8R4/By ӏ` R>;Ħg&Ar &+eaK;ZbB?*;1a&G(: t1W㲺Qvݍ|K]]UZdiNXI{K E$&k7Я_ئ2VUՖlT%6Oo$�=h hu Оudǁty^,ǚ:w9M ~{A9-Ny1rMi-MjiT8j2մU ~iT82quk ]i]1qXQ?^r)+/2~bB`k%(; DCa߭PwA9ew0bx0viGrϵӸyo.5\\'ue Kڊ[`X|}`=1H7}@ Y!\kѵNg [% 1? ^׍~\W#*,rqf2[r}D7B¼.X W1WJm[w([ #R@2M5/r h ]dIQg\?}lHWCq*!1i2O[E- �!@t=Qm+bNc,(#Iʒ5$�AA]ʖo&I0:`:!Zs 亟fvs{*lĀ~`0]Ԡ; otF!P(!%dlZ'a1SC[JY롲Q#Yŷ ~"Ќ=+c/3(2~\K<z)acYd qU?fBW.MDOrg˜+^ el|o†CkZ]=<aӏZRb"cu u x\z JgQ+B4Yq\'EtzL%o$n'T�|oj dT 8ek\\ ,AQЖ7vh 3? %b+W {yHwYpXwuzJ[MHIF/&lp"A4"DЫDLºD:xZ,rYJ!6;Q=|+?Al p@*nbno8}қXtTf慠ځ(< '%ĘNLžOг1N6 5 k΍ Juxg*\P/yք )ЏGS%<œ<Hѽ́7ybgT5>c <CR ˜V#^QE"JQ=lyj=اzK'!>LS :ܰ{1гcqusNBy*[sz7!R)Aoλ1IyR-QGe?CP*|uЃrcZ}VU1@y\Æؑck;a<b#^H Gza"U|*a0!4+43Ay煶G^Ի *wI݂e& Ct?TLO;XmVIwNV xn18Μ/;ψc,yREK!jVmMb=4Ưlٲ}"2#酮U.,r.@)6΀tGz6^~p"ޏi4sl]/ݪźz-3AAxUo7nw_3qM{V;$8W`HT.^'E,&YXe�V(: sڱO2RyD #:.uU7}9nhWב'=rŴe6ü3pmiUHދLb".c#3IWY'yAAjdT,r�[�.Ju'[/ͪ%W-W(^m\[gј$DY7nTEHhg`̘3eo/DOiHj7i{{h3g7ym4Ʈ e5ePww<jծ ];eoηOzsl brѬm]i.s_o%Y^߰*+rhTKu 5nd"_D`obA?CQq$bui4f~2:B˼i*EhLQyw7]ct|qlX8pb:zwBJe/yoMҿcR#Aw8ZJRRm;#~G}a{uPx̿ŁI( n$p;<LS'. =.q0O/5N;}.D%cQP<4L9htM#ٞh?{f?)3}Tel5خ&|GT 6�<Hގp|NvV}ěP RBbrHq|=;qp0(Zy7|Mqs P. )l}hmblhUPWIלۢg#6UwŲ^VYS~&GmTV*r%RMBVUD"<x D-WI"W*p4B]f0Xh'XTjdvOnUIYXڑ~<'8kΑ[|_{]'Y-7PUUSppT.'wK?HKuwΝ45ū|0S#O64~<H}s%+I,g{Qo=_'.\appJKxWhGx..wgZٶUA&u{kѵ艮2]R?x,4]XiDiWO� C7q<tf^K<iGI,].MvTM8YH#GJDs pT' Pͷۦ&чEzˏ#y횔⛤Nqdժkf3�DtNkUrݻsTdx6Ϫ}CjH.L#vvC3*m]s$oR'0J __ K~x{2 r/id2HhQ_2md$I-뾵 "ڿҽ 숶cN6"LC_Epgo5~<#UM:֙cZwsuw1etg|_HgxB(}H']3Y 2&z df=2RbnѪ /`/\[`=V[{rY>S71W{ `dN <.f̄2遑P!#ފC <? endstream endobj 191 0 obj <</Type/Pages/Count 2/Parent 53 0 R/Kids[189 0 R 192 0 R]>> endobj 193 0 obj <</Font<</F39 35 0 R/F40 37 0 R/F80 182 0 R>>/ProcSet[/PDF/Text]>> endobj 192 0 obj <</Type/Page/Contents[7 0 R 194 0 R 8 0 R]/Resources<</Font<</F39 35 0 R/F40 37 0 R/F80 182 0 R/Xi13 1 0 R>>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 191 0 R>> endobj 194 0 obj <</Length 3569/Filter/FlateDecode>>stream xڭZK6Wj,�/IxGl6N #q"=FH\DA@mfl]_>; Y꧑fXb|ijQD}ӿ2.\0S/D(Z59uYdy{TC_Pxx7Z{YO8-80̛9gb]Vndͮ*׶OkG޲m67w7`ozLxE^jUc{[¶Ǧ;H? ]IvJHއo^Q5Xtz&?ixU)w$<dcb;SF<µ**3T>1XrȮW[rZ�.Qi .bf$>p+CVr x7꿛9Vb#x-a7- _*s rb}_;UfzF�QA�jCkj5{cFLJF"@){ ~ێ8~ q-"kWˍc7>-9 v꬛}|0^ܽwϪPP,`-LE{7- Qx-S6潒QtHD&o'u6k S$QOa'iЩi۶|Gv{CyeNy9H ]Vg"=np"G,@܁W>R7@<NO>\ϟQha'@I͙cI"?ϙ \<1!{Z`Z5y 7! /lU׋5o $z#9nEQ(Zy,D4X4i@O@hbU#$fwOВiO4dI$ӢB�L M EV~;{ظ tW �]D;N}]՜CruKp''#�6lG]j>=d+)뀡^W%lGD].zxʃ ְl\{]^8re<IDaqV݀t9})e A-S>R"@]{c?YػSKU+5vIr]eKs w?냳TzL$b�P~D-ɱ͡$#TVkk9"a򩈏q_Dbd' KCkXva80Tb6z(^T[3c#R`2o&4e>51>Z}6% (׀Z='U_v]7UQ'e ?鲬!`2e 1"95__qŎ 8m$ ʄ a o 6_ӿe8VB@FKο]6敒c{s O�|t8FNsN-1�CIST(�)ˁ//Lo]TL67z}s+��ki! C ɮj+pu>^g`./ ѡ+m4~ZtGƐ(B ,XI:A JJƲTPRE40]v'=D.I0`#J` +{_fW7՘x1p@Xѓ,ջ -�֘kF01^�Ž126G:G(hC{5n#?8~ەOu~*E_pT-F@M;CNVO:[2z?Y DVA՘)Dr ~S .b! C [DU2o(]F%ܝZe-*v8̲KJArR)HN+RL=|{c}]G8$;;ĔS si3/@hmL1X3KZҗ2,U<4LY1Fc -x#H 3^G D++TV>q�Hw@1[M@UsqxM;C,C <&Qx#/T Iwnv\L$C9$:}Mۨ3b@4,$p=)�MI[�ҪjTźq#N ٩7"ϰ&XI|ZnK0 4i]$-y45)H }.'EwT ǏQ<im Hal2?p ح)7^D� ^Yv&iknՅznBw<r<h,H+c R aK^X0h'îp)gviq1wʷ^e$\!֝3~'(HNlm&އrs4iVl$,$. }xe;^U[UDQ1 Qt >krpĭ(_նVӛ׏ZQ Zu.nuul!!lLݫ-Mtjk\,e(0mu.k1FݩV)ٗ3/d43|rS7=޾3 RA<]g�gR :h@k]+zJ$t^b[jOGe Lƙo5@1!0_ci%2~8|6pTw&>Yd?Ҏ Ъ@Fu4TT م*s]4 "%.jk/L!42*6KFLbGA`NKE锹EVܔ%Z˰Efj? {g) .Cq# bօO~9*˕6twIJLbzJC4�+z8FU|Nزhy<3dxg)AZ 94|qLGˋ'͌.o 5YK? Y|t} {&>Gw3#C0XJ�n(iDt|+ /&/`ES7]~Q= 骟�iH<`�&gi\􍇈R_#|t& %QԤ {k Bw}AK,1qhZ=L&Fn& crB2 i8]]fC'3B?@Sٞc¾7dֆ)vWʶ} 9]` =gwfeN:ʏ}o(,cNjx,ehErK`�ͭ^zm{}:XcU;Am ]j<B RSWLک,9.;ZجE˩BBL"?V=fnۊtjHn6џc{'soTm"c*` eޕ>A K!�y endstream endobj 44 0 obj <</Type/Encoding/Differences[2/fi/fl 33/exclam 35/numbersign 37/percent/ampersand/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon 65/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft 93/bracketright 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z 150/endash/emdash]>> endobj 185 0 obj [556 556 167 333 611 278 333 333 0 333 564 0 611 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 180 250 333 408 500 500 833 778 333 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444] endobj 148 0 obj [619.2] endobj 146 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 111 0 obj [500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 832 667 667 667 722 667 667 722 778 389 500 667 611 889 722 722 611 722 667 556 611 722 667 889 667 611 611 333 278 333 570 500 333 500 500 444 500 444 333 500 556 278 278 500 278 778 556 500 500 500 389 389 278 556 444] endobj 109 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 103 0 obj [877 323.4 384.9 323.4 569.5 569.5 569.5 569.5 569.5] endobj 101 0 obj [904.9] endobj 95 0 obj [803.5 762.8 642 790.6 759.3 613.2 584.4 682.8 583.3 944.4 828.5 580.6 682.6 388.9 388.9 388.9 1000 1000 416.7 528.6 429.2 432.8 520.5 465.6 489.6 477 576.2 344.5 411.8 520.6 298.4 878 600.2 484.7 503.1 446.4 451.2 468.8 361.1 572.5] endobj 93 0 obj [388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500] endobj 68 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 52 0 obj [777.8] endobj 50 0 obj [500 500 167 333 556 278 333 333 0 333 675 0 556 389 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 214 250 333 420 500 500 833 778 333 333 333 500 675 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 675 675 675 500 920 611 611 667 722 611 611 722 722 333 444 667 556 833 667 722 611 722 611 500 556 722 611 833 611 556 556 389 278 389 422 500 333 500 500 444 500 444 278 500 500 278 278 444 278 722 500 500 500 500 389 389 278 500 444 667 444 444 389 400 275 400 541 0 0 0 333 500 556 889 500 500 333 1000 500 333 944 0 0 0 0 0 0 556 556 350 500 889] endobj 48 0 obj [531.3 531.3] endobj 46 0 obj [556 556 167 333 611 278 333 333 0 333 564 0 611 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 180 250 333 408 500 500 833 778 333 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 480 200 480 541 0 0 0 333 500 444 1000 500 500 333 1000 556 333 889 0 0 0 0 0 0 444 444 350 500 1000] endobj 43 0 obj [556 556 167 333 667 278 333 333 0 333 570 0 667 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 278 250 333 555 500 500 1000 833 333 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 930 722 667 722 722 667 611 778 778 389 500 778 667 944 722 778 611 778 722 556 667 722 722 1000 722 722 667 333 278 333 581 500 333 500 556 444 556 444 333 500 556 278 333 556 278 833 556 500 556 556 444 389 333 556 500 722 500 500 444] endobj 195 0 obj <</Length1 1632/Length2 9261/Length3 0/Length 10320/Filter/FlateDecode>>stream xڍT6tJ#Ƞ4Ctw7 0Ht#%-)"J7{_֬5s_ =& ,u@q�@�rakAvɱu08 $ B<Ȥ@Ce@'J�pe �@ 2;@�cKB`+kCL�ׯ_ 2a hhB!``F 88\\\Apv(JAX4p0l2@d5vlz5Bjp�py`4�`6`3�';;/g9qXB�U%v+�rmCA A�qu� 8;bGa,`! ; ؿ듂swrm.B mXuv8Kc �_qN�5Zn࿔=xy8Bm l8 @ނ<T7X@�3?`˿ �8߯fus_W̡"OVJH@]l\�6.^ xp8j ?u+` އAfHφ0; `�n?q?/?eV$/==bu"@ k {wGAl<@ lA[Mڿ�V!1^@1*a]o ~XCj{x�  p`׿ `w"\�={,0PEw}@N�r8�!5?zD?C"?C"? | 8W�[aa=`W94\0*B҅mkD0Bm$#=n6X$3)clӮ"t<sYMSsF#cEcad9i  `=HjXV=/fc]H e^)uT)Qh4=!na\Q;ƥ`osO `?Fb"3Yb}*#ͣ}#ZƸW֦q/ I$`Y9a 4(}"pd\r:Bpd[Dy�FVỒ^ΖAM/+7YҘ,{'u [D_4rs9Cʴs~?p>nk`w*w$p_^Lxu<2B~̳uHUb{:C>*�Ita;Y  B_^h 7soվ>r @۱HTPg݊/YdHS8pVnߣ%i5Z ֨rܦ{<?~ݘՓpJܖBa_Lןp[Q>=N~LcfBsov5!)Ar7Qwy.=st }#Wf ˥?:jtPi.9:ݣ堦MUcvN4"iud;. @UzH.)4y9ޱYN@&=v<:;ǫ&`jfdwτx]{!}ɩAWܣQQYf1)?1A8<&{hD׽#W,cVq 8BӤӊCQ\E,g䪯 h>ۤhys\lnR7cN2Shu}-h|-xsxX7I^+F1/N bEO􈀜z8#G E_ΣC,~(޼m;[793"H~ퟵ4e֨W_$Y2d=̴ZZ,уJ=BB::_o ^]%߬ʚpIX~ n<8 mڡLNv}+ +=ϑ"`ʫh{,"UScB/ P&^ոeͿ7J;)pCd2#=te 8B_q7k200 cb`({ X{(#.[WQ|}ey 6͗-p.3q×+\t#T|*t^u7Bc:(|&0@ " .#sh^zPnmYNqdV?ԅ~7+smw3{Q'@-5)^P>qy)T4G`, quxl%bBz~, GV:{lʰѥ>O5#euR0CV=޵mNT8)@L>Y^gN5V/b&8[M h3,&x% c}x2_t<vdrRA8ny0%p<:Җ#xe,o%ۊΛ"<zCC6d"ғU>Uz=J.>Ek'Vn�Rڽ7@qŴ0h 7͈ (ysҽwI!i6Xm;kd1G{2qtm>ڳ(v3|NYXnU.^~rrW f7nPׯu6e?]s4[P j6e)Q<Dg駴f/Y&U%X3/XR#+qO0ѷTFTTVʈn6YƾBLC-:Y;-r3%/^[.٪wxoB^P ϰfk)F <Q,T>N͋?&=<cB+]j'h(^&le`(R;l y.~Yr2{ oi׬ |OL8r&]�84YD9D1q'{V65"hP?AQl/6LvAF<]U+rArϞcvpO< AH =תQuUt?H9_ȵ .sКVn6j6uκstk*#gC$'VVqOC Cpu/6l!e6!Q0I>4H?Di~\ *I 'սQɧ P ?HubLrȪN[,F}u͵D_Ö1+PK|bC)~xǿ׽5a+ V:N'Q@)`zyG2c|-nsXIƓRSꖍж176oYY}زI홎7MDt`,I)ȎHGe?CqcoNZ>L&G)ls\i9K$m5} @Adf�7Q>wW|ukB{[jt(6>.]@H2,Wh2fЪ3ߔn蕍m؉8|́-h򀀻7pQJ>Szv{$W]�0ͿLb1L@5Α�T_C']##iĴd9IһJ& f2e4&oPV۸zvɶ<o6}!7~r*{Oz&M bW<V:СS+e\MN-'ef g`pV2.[=}$TXB]ߗk 09f fQdYy)zF]3Ǡc[l~,9#MB|,ƾcSvK_݄N y#|)aQ? `pS4;!YiIQR@@ji|p$R,H+H߽SڂAlcOcZfK7\qm"]V KSw?|ӝ oKG7űrª2DJ;jCKՓ}3uv~*Mf^2"n b$&Č �|Dֆf@sS˝Rj&O7%ў;7"E&e^-SOoڌ([DkI6Oh9 {lO9̈́com9<Vw⠩#YQý(e CGZ+r3٧-hƇϋ&yoJaM}JiW!l%3;wIrgC0(V.<qu'镬Ƥ/¯N1~ӥ2E^[Oih%;}u{ -eIk#XHbuy0"'衆RioV*\܌V \N?D3g=NOy:2; T' VlW-V]?[K �Z1^xM?R(^&`U$bOŘuk]Ev xط=(EOm2Kq}ylù} 9sW9TrA2[j5}AALa2AG(⪙eA/׎#R rϹВp+B6NiJ%b2K VTt_o]rIVkdcU4{&of`DAK$KJdT/>&92r-')wE 9G&=&!%4UּV\VLMef9́qaDpN;r!k,qGeF6͂>j@?0;q.l Y5#2bBF꼞5(*h((-21F&mNU%,ZgKrnW,1yMRr{s~dZύd yA yNdnK5w2B_o<W& O8`4f{-}95כv%v'7[ aѶ_l_,eĈif]y*0ݟRxƍ;kJ 1 &oS+s,R}ݎٚ[8R ?2gE}LaShh<~ہgt!G!,RQe޽s'x sd =O )t&<!y1PN:1+_P &xN쵩>cZqE JPDvkA Ǜ7%eI,W}$5DgU$s6ZsTG>U` ~@рo[Rh͑HTOܤ1uM^Ę<]MWK 'Ʈ_\aMl}=#q{|2DPnB]脜zv7B~ VՖƸP߳5W4Q ɓ'lL_:+%J) VF9*8e~p\)ФbPnpї̍2hȏdw!U>oPFaHmR‚y.Li${Lr.UwöY 3N'[jI*aw~BB{JA&JeYt fJDT 7`23 j*~Ejo]|'? iMUOku},lF{-f2aOD%x,hWXQ3 irA*;HL%CVصP}IߟX\mQB .zo R#(fmjc^TX+%@,QU<^U5 îJ A+~EYjZi{ϡA@n jbuKʹ˷Op`6k?>ޑR,~XwKr4`ê(VB$"4Y#O{�V+ڢLfcЈz0>>o$,O}z.<m t m8 N?gPO/Ǵv黏id11\a~ n9lg}T]-|ڏקDu]sX^j�?rh5&,谆r!jԐ=d;6^N|}"%l':.!|#Iz.X>Gލ}$iܾ10"cXX(ӵ$IK? VJ)) D{D)JӂJ xxeʕ{#N 3;3/u|L}zN ~^L6-ڹ(GMc7@p;fMBJMK Zs/x:d2p&|x)9\9ˣPG?05Fu㇊Y^o~4}}w+3vqĔJgx+{/Lj7,�wg..bM=Ks<6i(bp'uPB{w^F/Q˝?&xS�<1:}tq NYa2zب ;B/$ rb _&_2 uz^-伣/`AORl {0o'ϸB(K=a#/q^ LJ^@ qvU۸}_[A3VZ?$ qZW:gh;i.0,*8Z'| 62O0?ns͟2$ gM.i~Q8aj^_)_ Y%TRI׏HMN\~DAd+Mr1`kYe"[! 9C0zҊǗ3I$"Y>tǺ̊pf tpu p}J[~kv&wKruC拚) 4TK rEaضjJCni.&^h;Q45:a"$T ɛ&"4ݮI*7e_:P;9e tr1;Bfݴ K#?tQ{-%I:`}Tnn =+rUf:e")K@1,[>z@rM#%@Vy5M| oܱJfEcXNH<TqcJme=n%CcV&yt` ǣ*/[؞;vQrţ.5r.[1ʑL&5o6x/4b_n+:iGx.Qёb43(ƞrE S|;[$yAԌ䦹8BRu~m?\MP<ػxdOzTڢ5e樅)Gתe!=}y7HotB2[x 2#Ctք)}UБf1[[ڡPr>.M5 Cy'M_izGa)K;nMͫ.<Ŵ-43Fv7xel 1O}Ș5y\?\7Q{:?x\&{W)bߤzq^%![zۆ3^К-*Qt1C%̼`I] N}_u5Ff=HU%qv&I;byAv0T#&'Asa3C#|J2o?;=]e5v K,SOz+|*^e*nJ7l@R5a3ϣ|h*:%t u ຟ[%4UGF"ۆ $r[!i#ϻzi罺#eʦHmU; ?SeƲl~ EzSې1- ;牛)rqFMh]z}J1QUkf_@yz{̓Y٣c.#MS}!/bs×�,&@J2]ePfW݋]$SWP\rgv!b}e)QV+%`{'\b<u(y۬f&bN7B.o-4-"bEU}yWo<lEe.0P\.F4\t-A[ Wcz(=eaJ}6MQ^;~fôm@T rč�MZoc`\3; C__s|сy+z�1K[BƲ/ ed/l_h5aLOp[rX0i~ЁgJ̊˞f:.̧tUOp 5 ўCX.>@Ï v?^h"_uؔEm/'QT7ⷘؽ.SڪI*- ;VqD?U-4f0S ۪&#Yۅu ZW[Cs P/)M,OyߖE-ffbk pR,0%-6p?|`n#وeLFw˅jvWar?I5KDtZ|g0:DI0<"Vd xA~>"Erᓅc=@a6D-A$;e? 6e.H li$doAa)v<SS76ǟ(P[(R(}K TZE=v׌WŒ/o1g,N~.TVday K?_M AԹJfb|)HQ᎘8uUدv#EA6ʍ)-/Ԓ Q^3CU%<xLO DmuK،Yr^:an!Rc Ɓ-4 ,l1A:$,;?u/><M`Qxdz)x(m>8l!}<4 lx= ٳFU LsO(+(&hφ>]Ô55BsK?~Su6u/Y&T̴%Yuaᔊ5.PWӞ'¢+ʄckQjuP 9xӅL^pM’鼁:=ww\v/8h **OLmW=0 xw2gQY\aMBʢ\`~-7bي2¡JИ .OHBQ8Q|BS8xÑH*5|Io+J.2YQR:U.3S#V) &* fެFst;K\mش(]%Kr*u2݌@kCz5Kc4Z8.pFOq^%R>Y6j$k}=AZR1Hn\Y6v)ڮ(JYMǻ$NC9VNx5Ϲ-om1vvEM;8=$ǖB#Iedȣ2 r:ʝ/Di%D aΗmzŧ;ӐFA=C7g[-]s <Yr "pPrzRƖ%")}sa wz5�8n-oQIҎt{ eNywce@5:j V ib.].ZFʫ1Dj{kAn̗Gqқ9(\+|m'lW09 *DXƴ<7ni]_wi|#=tU;y=@k2y_Ro=mv@@Xi.SwUM{ (>0x)9273~e~4>h#:K>}o,OOݟh}LWIaZ=.?D endstream endobj 94 0 obj <</Type/FontDescriptor/FontName/NIWRQN+CMMI10/Flags 4/FontBBox[-32 -250 1048 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle -14/StemV 72/XHeight 431/CharSet(/N/b/d/e/f/g/i/l/m/n/o/p/q/r/s/t/u)/FontFile 195 0 R>> endobj 196 0 obj <</Length1 1384/Length2 6102/Length3 0/Length 7057/Filter/FlateDecode>>stream xڍtTT6)HcNE`b$[PB@N )iDBQ$D@Rq}k:s~b30PGQH�D, jJ�` ,L  ЮnR BJ/'Th,N]�HH0,/ SP ]A@ {TPG'40�"%%;Pr{"`P$ E;ݰ' 1 Q[ vy <y_ 0{=}=d or@B=�ኀ^ o=k�p�?n� f*@N`(7w(tp@_]GH_@ "\vXΡ!�w</'%p5Я2[VCګHTplE5H{_C{ "pMտ?>G8KJH p�sU;NrCpi=;O04`wD IS;DwXA�߿aeB{B5T,L2�b`��ؗV1"vO&@^ӿ:K�YLe-ɭb`URvu?P7_�h�tQX j#Z]=h(VJHG, `?~:no@ÜP\H ۂ+Opه;a1q� 'Ůk@~  "Qhl 9p@yZ$ E0oOO칿ـ=_om~p4 &\vT+2&Kyd!,0V`MSj3]> QvS8zqf?p_PU#]!TZOfif7=p X⯿&Qw3Qq`|9K%":^ה2Km.[LVܒ|I{®-qL[}Se/KW|Oέ(4�(QB>Y^Ɠv5xN򤕤doDW&zu(;Q"Nck8f{}~؂=~ؕ*ŧ77 4ծ "}d5T8bftepR|as˂ݲ=ij& 27\)4*+�ɇN "Gbuu1Wl[ sX:8NNRqXftk3AwkOdb۶r<RM$RƘR^S<?3  6rUXxjBC2C!((kJ.@GY5z,+e!j`T jFv;6U.\nshq^) 0K;(eBB<_-u" Q74DU4GWYe=h\ Tm �p_}\Ю]bXx<ξTD OC4x>GgB6ix"s[b(zlSQѣd:/xdw?߈ *S%]2;5kk^_ύ.;2}b8Nv9)4AՍ$GkrYMt9E6ws pt S\_1'a>S[Eٛ7ǡ{Ia[B17hR"koH}ðϽ1}~A—xN="$.eNР좮ܴ#6)4Kgap9azYolxz"F.Q;R>xk*wKErcϷK4υ@ߟ2/ʓ׷dslk͹;vS|`9:BG\3&_'l_}C5yrW4YmL_U/c {)1=5ˍwm^7;2K BH>j2D׿<.md;/2Fc_mt_˻ x~ݜ]XB%Rۈjkx?ɋKD#e[o2wSxXyOhg#Nn7WxtvA|K3;Dsxŭ112kߢs,B Ly;zIm%Т~42@Y?#5e;]s^mDNE3'dŌΈgP +ȓL{TvRB=w|lѻ2KG㜏 $P,wғ<9=*#s$ ~v|2 VT q$-=gdogeX ݢJfeqo6..6 bRZy)ctgDq৩ZwˣїWbw%{O9dklQ8l١rV\uV>Ҭ@9X ,MІp#*XD *hr|3#oJ;%D/g9V^][{9[UuC lR~CR ߖsgI74# rP)Q%GX;YT6o/qf;iN:jnp\|Jѽ<kUl5Ԕ?Y%5.h^ɺI+8]|B^%.AR 4 ה%-хt7mWI7LIQHNR!W$u3"_=g̅WN/6+%S ~&5ڨ42%KOZޏ8\ʇףVr ķFj)lJ4گZC-FdM]rz8wٓ9 =qOç3oq҅,qbVJG6㹐Q HoJ rkŨPM0ghOt=(2yne7\[pi_5YM/ǭ>6uXrxe^(|S/&WlLIRoӃEiu9R¬o XԙR_Cς7{EHgJ O]>"GYZXܩ)W:-{ZOʓ.ԺnF?ꨏ+gYꨦS}inF)qWc, YW7a<zT0+YvL>N HPo#wmr\.x� k]IZ7g+LcEOZ^Wf[= HUnMٖgr`njѢ|6NoI*mU^_zj8c4M(GC:_9̞Hzh}\9wKœi,v(c$_ܫAP-GWb~?^M ;ơ\Uo/rAе7Ž )g߽0e{]@ x<& //usS|Xiٟ%W>B'Z];,4XK"ːh�U[x. a"FM$rjfQ =UY{Y9 PT?}dd`bn*FuN%/ 7.5!K~W.Q[F/b"cIQʻ=KO9?*Þ0 S [qS5MosZ\)͙S&l^/:&eDw-<dy=X |"8]ۙגk|,cP.RCz*1aEh=5^:h k xixhN`5b}xmIDCrldB17N}ua'݄"}_AeoֲOҷ ijCml}qs7u%YUyAUFCl  G\V <L%5o?\*7n=V2S5pFG6*$SJNAwb)ӫMDg x1۔y?huH !լK:m=w/8ǠyUqw{mX׺*?(4!86!鵂+BZ<~q;g P=:XK>vқ}UHw$!(jצ?Q~|0m)MC~ۺpɘ@%'oUVt V/8Sҍ}Z/@L▸!D猢R'h-xdvʤdLMQ}=VH,Ͼ}e}>M:n"9Ln짓˷YJyS <?{&fѕi)EmׅBai7}_8&H0a->0 s?!7nIN ?̦ߺ,U6pTRhy+é Y)^ScuH(~GFGb>ګj'l'k- 2)RoRSAi[kM-="V֒wGeޯ!F -t䥪oHةr% Mq!U#SeWvvͦ R 9-nОcfd;Õ;j}6aJ@إ+qSG"L[d!V rFmctK!79O0!7]RJ.u3'%Ǻ@ˡcF{_T W3EEYջzDaiLc y/; Q oԘ b yxoL*κf(51RzC &F ]3yZ$=>RLȇ+gx֩ڴ󲨥x)РKB-D1䦇oa^ԥ ힷ�?hm!+!'5[EE89$5$ J|B}+x{GҒt&[d "_0V1vע^La+@;[aإ {#?;<p(!'΂.:*#$Q{4FĨb?\DVz~O`[~Ln-q: MJepiRQ%K!Qz;.0}pIbN͹o IݸJz4xܹ=6z8[0ֲ@F1{Y͋KeK 7D2}fʩ_ ė #a2]o7]e WB\K]Γz8g/1R},4wv80,h]gmxj*؋9k<0h}.UNs�vz69MkĎ-G6="MqW 8) ;%|x͆>s 7wB(Qi{+3 !87ݟ;vi6 a~{ Ԓ1;V{F019۽dVH?Gemc`M^Kя\NЬR\D:6~?| P&s0Q:u𪂊+k~ c8m$eKYUcK[Gw+oXB4; U>@pELǜuhAQPF ޾c_} bZs]vY<A =SLLZxLoo^kˍ/Û3uپg;e{+J~~qg4F6;>)"#ӛѓ-UyG:ĂOKc 9w_y 7^Ll"_O#e ݓE@p/)3Sq ms2l kgfTxc{v吭Y{R/N wCF N(QU(l:v3�Nd(u΃ƺKGǒ}}.Y*:Sɤ0%iϒoRs|'MSun{MdrfpbkUr%\[f:p%Aկ7ɮ FAaƙ% Ǩػv6SzJoDnnR76:gnl](UǏl)ZMzKXǔj%8SiAJq;[_7nMy,$DТq%FqCI=*MÓɐ$nsV;{HW||̅tuNcE,Z1'tOCUS21SIl)ᷘڄΩ @.]¶Ch󁏖Ptw[\WlrYH&쨴\Eɝ5\3v'tG|JӺ0.nRKL{$WAxIQ)݇ÏOo{vuLMXIYK^k7#EPaEU iN pXT@ ;%HGMg$UiȄt(K<Jekޭt{j1?m_iLj}N9d?F_u<_|Z8a=Nb-)BݗqHx|$ɘ2,6kM`7W|e5!nMi@@gKڱFrשY n+m'-6aa+`Gm𒹎5V@̒& Q7k&Ę\/ lnhH5pUaq';Q^,Ėؠ#uꁾv)# ԧLaxn+E3ݱVBUk1}{<$/{Z&0#kqAF턙y|~u%s}2e$;׮l endstream endobj 100 0 obj <</Type/FontDescriptor/FontName/GINCXO+CMMI7/Flags 4/FontBBox[-1 -250 1171 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle -14/StemV 81/XHeight 431/CharSet(/N)/FontFile 196 0 R>> endobj 197 0 obj <</Length1 1386/Length2 6040/Length3 0/Length 6993/Filter/FlateDecode>>stream xڍtT." )H %!% �CP҂tҝtHJ )*19߹w{׬ͷ~~~| p;*�Jtt4$@�(LiEBI8M $_�%A( NhzD�Bĥ@0(/ !PyA:�M8 $T"N(1zpy�B| n t@('D0`C!(vBܥ@nH8Q E9Bk`. g2Now@y� C`HL'�`jh!?`?�>߻ _ 0Ba�+-A@0_@+y ; w �xH0B FUs*0{%BO1+g.07�;]h(`\$9BP�1(�7u rc&s0C@�E"$Q�;#F7ᏍY>b'�C/{8?TWhjg>�?~aI� c^YF?08&O{W^W<Ӆch pV@1 URtu? 7_�(tj Z=(F 0G DP*bEp\0> qd 1!1jC^/?P',v�B @$c,1F (L �3s� faa/?=9_ouC >0)8AsMXImoi3aGD^O6+YZ%{T9*z,NCײn:O]aO$3MQ4a噦(2KAb߉ w5[=ES*ުQUM$il7`.n[\ZճT 6-SDJy;^!8T 0)V̿"}㵓AgvS~Ǿt'F }czϣ'h@[-IF^`pB"HK^O%ÐQTӂpE-V4𼊎cgsR, +WOo_hYE>GC+GW8 uxx^F|!6< [\&"`W& +s (wxNW0r/k1sOpoͥHeADKQ˼)A9, &G֚v2?.ȹȜPCSBYCPM&n*FhYOhP99uW2.E/Uz#(( r#!֑ܷT{H5=QHȔ>yN1-ԪazPv{K~{ܗi.<Esvz6!kVي #@[<:r wo䰁@5SY4^;aO%[D~1CNA�tI|nN)}|?'BJJ؏F~')2ۑ6^_RNjn6n2ޢrtȝtp8vH#/*vMVgѤS1CՇq"26|#k^,ﮩ�8AZV|RwK{ϗ* gYԎq)[>\tQE4(;~:Zj -z.˫hHO<E1I6nsf[KJXKೲf?"RX+34߳\iykjd-R>xGvŶ26@fy' Mӕ?pJл/}@5~:A_6߼0+۬ p":Iބhw=-#2.pNU6Qpt=,I4eCLU~}+bTlv=3)'J䤁/(-ئ~)ex>q;{>-Y�{φEhs+\@/0uSi<h%_q|*׋ngmY흜bWD}(Mjpq o9:u.PF{Y tb jS"Yiu 0b5[V~ Cć8zˆNl[SCG >gf=!Eٜ<OyOl5؟J Ôx{ӱf#/l6b W˜=ri`G|\㱡Tܢ2_o $H5aׁ-26dgZWorsYy:Ղ -:O}y.i69[,m "IMX>x?O+yfm`?rfy*4_uj;SCėR 3!o򄕬u?ẻ^I .*[(-`*E,eؔ|hWkB˥R؃N~'MϞؿ ^.oa~Ӧ1q*)P))qw8]I.\F9jX![얫R8ATS JL#)Ztqo1y-)gõ?Nd;\]nr,D(,2%z<g�b۾ *f ' TM^u%ؾ<;z:wOx:TBJ;AF!0~_9Lu֎?'~R{M)bujwV:չKY5E_[,:R6z, !kZ4itnbzre&\7)M#N!#\qHg$>{.qZmu=sڏ9*˿%,�צIePp}ȕ_(aO X8+Oxmxs VPZ2%Wklg蔷8`["J鼑 -\ T]PX\Cޖù(l~|;}G8hi@t 9(cl!; I쏱,BȈMVr͍ޕ+Hclg;ų\<͟iNV.yK|%Lb͓2Ö"1ƅp4r#y{2X\x>nCO{<-$,x~^-@䇗ugXKp]ݗ8&4Gl+ش9 \Yk״~hEE"ѐmژ g6>ǜ|h,Hv]qBD)!ESg4`}L p_s'.erllOLȯIyx1'%ϫgSASVu^{.imjG-¬ Ppi3!<"iS&Н;X0Qř@c(3f+Wwqxy4åXĥaҤ{Dq^*oza[2?17 w6i}ؘoxZңa_Ef7.eJ7v<Se2Hy$ߟSr1󪠺oԨsVul+SSь!c@|};~I%!s˞Hr_"-܂]j|6k6�.Y֖,)ݘojuԵ&$ t_NkR=eCqYI#vJi/Adq(LۍtrG's?Ox#Wsޅa=v@946(ܠlڂZdXJ:u"k)'~MQfdrtK\?UziN>֌ȸ;k>]QRuH'v=P!Md{pa]R ?\OIw`LH x#~FÆIRebj~ZLAZCӋ9*m(ȃIt@cHuPMp E㗠ʭ " 'Ņ{:%ɻnH4t5oZ3_hK{KrA&TYLw8=]O|| Q7zF0Kޜ b1G /; q~K/ ŏhmZOLҶ1U*h#h~%C<:sU.2xW,hܼ bGan*mf΋8+CKxQvJX8fqP{=qɏzESYǏ<"NŸ>u Ս'_M -p<$#S 3 7|ߜ}Y\;%%TFA3`|u'hԲ?%(:aP285rdvjetj4=ۥdWT itQh $ZhTr`c;Ey˛l C-VHX2Q۷fڋz"7^at*$[].} !er .yL�K$Zb:rjb4k85~&h>, T5M_7QT.<d 84T@LgZ? NxMObtj3\6H*-VSrN ȫO'/ Bhscng|J56Jx`DFE', 0z ,h9%:bcr4sj+icm|R㄂oR/z\U,L{XFwZ(:JJ5K;�zvCTU4O-@*J<DS3Ldl{=VV.j"28Lxx_Ýp{h2z_gsa\7h-HDQ2}؉L(?}t@uOXRs @x*\-wE.dv5j,n߸I4m7\DdBLr/©lù?'M0#*x3׃J睹}=nm_k2~RUcnAbzq>ݓ hcξQv-P,|]U@E[$.sZp|hO ,6R26T=_Q.a �E[%(|nխiKV4/<t=hRYLk ]M6+Όo �w /> ϙujǓN1Ac%JBtqp!{H]'�[p ܋9&6ngzEu(I 4`Ng7DfXM6=%,d Q*D6 y5 = Nb+NQqP)>n*ܐ@dzJh\5pniK˯L(oR}&XestDMŷ``=>P2W- G 5O]Ԭ%ؔ6%xl*-=Գ7ڶ"7vprLU\pgfM=)5r:;į[Bg$ʁGLbI3b9u" .c ةAi@Bj�FW;q;ki9v:<=\qkx뫂+iLfZBUmĻ؉0)9E K׭s,wB00d2 g,=CPj+$v7URS9U_=Wu_]Tvߏn-Sw6Ͻiܣ*߽/cD$w\%BwC$$�DtZ$:.  R}|'mCyfb~i]Q0/,܂ $c{ _XQ\n~IMؗAS-eo쮗-A4MXY_jMƔpbW²PG4R虇VtxI豿N_ I Π42jhi5aoHrbǛ~[D0<VqҏB9]"w%#+_{UX M6pBjwh'vPcUi< cZu[h@ 3"^5ƐDspw:ˎpk9Vm@פa(6P:+U3#[�g}\柵==z%O)/~qpok)3NHOKKGo LNKi}F!H.~NU!u]Saa>ZUUФ"{K2W:*t̘M=,L#ف8@,9G\ k7G(_Q㍩fSN)aE숡^�T&q.k (/wvJoz2ľ>+a,Je-IrG+5ɀ*?t/-'բ<hJqQ_PXf.OsוuYqi^9n˭+en[3: nOY6}/w4ZEGQ"Ö{k&�ujݼ;Jk @qEaJ*mFy et o])Gcz/^?Hخ.P˺R*ءĻ:mt0͗d;}p'T)?jvLevL�A5 endstream endobj 147 0 obj <</Type/FontDescriptor/FontName/HHRWSH+CMMI9/Flags 4/FontBBox[-29 -250 1075 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle -14/StemV 74/XHeight 431/CharSet(/mu)/FontFile 197 0 R>> endobj 198 0 obj <</Length1 1443/Length2 6987/Length3 0/Length 7968/Filter/FlateDecode>>stream xڍtT6" %"0tJw7"9� CtH#"!(H#1twt|yy}kz׎{{_{#l!*8W$ T6�A !>HnE �v K"F]cJ`Ե6pb P!) T{@|@ `WDx#Nkurq$$v;CP;0 F9AoÀ;(\N($?'ٍt􄢜7bU0P Shu"P`$x v۵;^_4T@6@>+lgpvýpGhP`/C0 q�Ca`kߙ*@ufܠ_% sp{E3rO ]?7>#< Pï"]PWw_&ߘ#@bB@+e+ R|] p]�= @J�=8BG!#^@s5_OGa6_~-uEc?SP@x}yA@^APĮ @?^?ӿ2�\ 7t׬�Mr #oEH `\ yepMZw�h#�ߦ?C ;Vy#g@ zPM !ܠ 5-'׫횒Up;H7גWz!^I #P.$WG%G]7q@ Ϳak7p%?[Gvkoo"]/Z@ vT㏡ux.>d_6};w?%4=xy(Ԣ2O [5Ig~։˵~궾MO Fr+~~&AOphg轥8Ĩz}jEOv,믔jXgf Ӳxy<$wŬߊ}4'w23]d$HF'y�ZͨoATۘWt/xo B -e KY/w( +)T2'ƵC&/&$}9mV5Nwv9Zl7͗mepd55)&sNy 0i  #- ;"Y~+tFa!W!:W_^9 )ۤse)7=˞ԗ AS)z3k=_Ζ'ڲ<ZeD"oǤظ6A$Gei+g"C!$ #qhp:d3R3nH,4YAF7TU[ln)x8WƓ=8]i|wyΙ7ݪ.7n9B!P@\XD鷧W&qZwV|WchtV跞Elb|aLPJ$&2`WLԥCTՌ|CYPdvWz!L03}F+j$)O)s|Ճ73??h4fU4 _9}曗j>QtKRL Myky,1aȢ9);>Ƨ|7Rf ,#$' -9ыBcq%7n I:/"QudUInWzЗ]}IH8`t�?Jvyd,}io&+tx=Ç4N:MjB/Ľհ&HDSBgnB_S? z2\>cbu Y`1O>RyaS9Uۊ]4=hƉ3PQw,mq kkV!G8F44i7:Өvi#)f'H49[uA&/KD`gEUI4s捴Cvz|:QAvdR#nEZYC^{GL~JKo,&G3ڎkѼŞT1lb\hӳ>i-^B*-wBpO.TЋyMܕE[%re?NY@ϛ̲>'l'G/T3t,BV+Z d� X=~aQp Q V .bQȻ94S@bA%:s'%]r +-agЊw[Jbb)[vNm^od'&;lɀճ) fK2s*hHwD(Њ9G0߯\wkބ=gŏz*k0xNք1Z6ݻa+&|iJ6{ɐXO콤v%>̪dƗRΜYțnhj룋is碫�<r=kL wD!TՄ;wʰcJw?{7h&׀:V%]V;uڼI$MRb#!Y'�H.!�Ӫ$w 16<0_z#HڬcθÛʶGcKym]å|ABk&&Rcu=$%|suGBZ<x0'Ǫ2wrlm{*$(s,iʘV;RNj1q^b$*;[748Ob\L:8=40*-:CG!Z[ULM] zɑ$?xgƎ Wѭؗ$XՍO {˱Zql<Z9ދe:ѿ z%s'ͤoVc|aͳQ ">>s1A[d; 0wGBOĠq5{)1\O/:rUմrIZ9Պ@.&y"X%͔;߁;8遌�v�t6 -lT@ūogh,91g@]/]<3SZ بUfŊ^Q8VhYR9CI S.<±wRsyEgϋoޫn{b"NBB5XQ&d\}'J?W%WY `C'Գ IJl=uY,f"BNH$mu lOds!Jb4$/ R|kҕYf}Ǵ 4;ot~Adg;*SP#kV:C{mpCvO:5b<#[ȼf11^lc[1{!äj1+dϡsR 0 YN 2u3ΡIpgCpk  ljB|9լ:*{VOlgOmJs,'խ ZS5+o~b @:_ln;؇?ŹZ"V7{EQ9ڗ4] p%M=aiP\*R_ӴrSNLgvy >%]oz 89"N7 Hug?[V!06|=͍Bad(9%ÃtVL4={S< 6 v8T ˢ۸ez^.54ByیUH{scm;c FA?NgG^TIP2% =InUAl% (͇Z+F0˦'Q ɏ"+;Q(jI+? kfIT0e@/?fS_%Ft 1|SGܶ j'n=_D"Di=Cme6Hi&u[T ӇM=Qssib@&y E%1ۀA'yR 8J.p5mPzRꭡwW؞Ĉz/Ttؒ{ ( pMŸ~ ]P4idhya(C(^#E{+`>S SEGDS 296RK){T=X*k�85 :r:M5<Ҫ- w wkȜHõfO `jpn,%n�utH.zqz aFKHe㈷ݨT^S)f'/K&`)һҫ?CT|l*+e.aqH_ݬ{ cSe3X,Z,uy(3Xrni_<wMI}U0xAmCS Ê*3LEa(vh]+Ξ8fRk{4<I>YNBNr*:J^0n].65<2U>.*|0.ڏCbUߝ퇛>`,Te]̕,gkM~\jA x͊0hiS MFNڢ)Qr?e;εG)Ŵp- ,~VnfΨbpڏG-S6U KS{do1ֈDhE;U!֛=PhڒsuΔϏyBjrS A}!O 6~^xp;s.6+vüϙiŒQT^aT]Z5qŸ3R\l`NY@1<LIg(xZЊm)}b3fZUPJ=^gf-:uW@A>x֌ ^<Բ_? y{Qd|=-<i`<hp{qZ0w7\|NJ1,?d[tLbp+k{H7:WU"ZhF ?'FD ZXGt/qHvlc~E[T#Sk۬&DgzـuGFݶЭIts t(g!aS,'jٿ5te[W =_RB]2U.Z9g862@f;_:в5_k۬,Ο{N5r5_c3Y0`%7&8pp/%d JP<<B8WwJXesp랷i] 鯌=/&ݖ{OȼQSP,/ƺn /nmq.쉄r p.x(=TO1zOcPqŒaIbFokD<W"TO?&ѐȌIW½;iX}[G-'*4&Z#yVݨT/s ][p;bZINN1*䈄 ެi<6uYҮ}yt2m1!@"2ͨCcЁƫzR7X4&eUZ_Y %0J4m�,0jCW83:lV1 0vKےmOW4ote^3"Mf,?Tp/i e nfHݾQI-(oRW(k$-7KsExa4Lit/^ءZE)~o n+ÛLapOt܄T4 }�|Cm_^͋Ө<Nj 1|="TUx䶨:sjԡ̹dٍNwAԃcTB>m;ݗTps~tq+'D祟zr9RD}r|7t*k;a^zZ9@GU,~O? th/,?5\nH'voڍ3$<K1d_9tn1ank6+@[*2q䒮[ã&6~yX t4ri㦇 v<se:S "!Mcљ-#OkFuKVx\c<׸bì(uX-f>yy4ȗRwl#آ>_aQz4yFM {JѪ!j)ͧoʻcjzbM4LfKU:CO&�5Kv*e YѤfhtW`')FF|! 4vA=IS.UV> ֹ,l%T�k(_Tiܖsݜp%'eE 8FDѝC&UD^J.ZsenFfzo{B}J}sec2]}T K#( 91?ŭ̐N/TЗEʙy{ 鯙ofyۍ%~+& PQK<yx;\38J0y"&urɃKIQ.b1%j~a'TC@34!'z$5̆jNFF~h~*ܙ'i߃TGIᢦ3缤T+k'S,\SU# _b*Ә_@~c\Ϸ:}E ۸˕W~i^$5M3| j 5햠X㼙r1x$]pm#Lr ?7ڇ_$"h %cՋ~Zwen@Qdr榚b:߆Ofr GZc1(鴢H魴8erÉbR.Fqa0LIh'+^"iHШ.4$VDңypNͫ8R4t?L1I}GCpKxcha@t7Zp%je߇ }kpNj+p+)v*~\FFe=pc܎r1PFQT[;:K` ͣ'#P+kD_ɆOb3}v>[Hg$u<bbx%ODAMC2mĚ>lJ㙤^t9B|zPC!P%hĚs/4!xxaJU~|Ek7~Y�vK1ֶ<:dEKtTRQ9kɉY`6:K`Š'bN7Jv|㈦vHOP8;Y5bT0}1<9G· Jb/g,ӡ>뻔Đ1Xc\xWjҷH!wSFn?5omsk0*]}W!ut$ {|8�7 Qě [E[<ˊ;<h} aƖ_y湕BtذۥI N`RQm?y*?zǣ!|AC֔,«2͋BȝtJAiD%mՏd* k@ȧg{g*7IG.Xp@$"3psL(3~B=d Qj#d͊4MdrLу…` endstream endobj 92 0 obj <</Type/FontDescriptor/FontName/LRICUV+CMR10/Flags 4/FontBBox[-40 -250 1009 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle 0/StemV 69/XHeight 431/CharSet(/one/parenleft/parenright/plus/two)/FontFile 198 0 R>> endobj 199 0 obj <</Length1 1408/Length2 6391/Length3 0/Length 7349/Filter/FlateDecode>>stream xڍvT6CHAN1 ctH(% H"!- "t* %;ٞ;;9㹡o$QX!0Xc( E`�114Jٕ0p(Sbqn:hP B$!`0P ## Tz#:@M4 QBaNX-9ya|@pA( GnA݀FhW ޻X4# Ez 1N|@hc_uHƄ<@cg8GyPp w7HCGq k4@0tEJ@`h;@9np0+~9B<Ѹx79. TU0�Bq՝' pz {"~u7d_)#0pn~kuE}PΎA&(\C/ `I)0œA!Ը@G\ #zÁX<(- Ý(?qjy xdÖ4U~7IQ  Pww}(*j@?>_;.X8[0CoGB78zaqAoW3^j`8("S wG`aGo`n\� `p O8J ]"@(Vā ྿  X\^ڧ{~)Ȣ@ @Xg \) Z`^ +?op`j JV!Q54>)L)%q2߫̑Br' 4πđMg ֚# oo) ˯4 u%hҦɓuR?ا[ͷl}Ě+ -BMbBx쟍3qaIi}Fis/94�AۏE ,EϽ0lgfdb'8}ff&㧀1+hv<keRɻ~Ur}7֝~l5 բ*S,_/q>O0^F[Wv\RueM,22819d9S*nD(8 (T)u^8Yx9o J;r�Z!t=6Y l,YI D:lzlL\ IΙ;*7JNLA2NPDص+e=&n.A-Y \r-}X jҚY5a78˻jmgqkAj^ wû}rݵvwkB]{fmJ dK>㣎p~IP[/)#&E*2[9yofv.5--|={ dg,8(z4z0ykf84ХS} !MS_!Nߞm7c3Yt !{/e+ISqţ'VTy;W:zWazXd 1_2ntA>; )cZ+,KTgeyylJZ@" | ,Ͼ|]]A51;61_e3|"f7F=~-_kRY1XXU>I*`a[ �2pQͶc^ᫀusqYiβ-I{7|Fm>'OvNz'|*w~}3\@"2^N{\HyF(/Weg}8!>_f"-bL� +|6Gv8*Lnh}[};*2W$ B z*r>R/v8fNP+'I]׹Re,w� eQIF"XUw&{ENc^T#%:hp xΩRxJ8ogU1ʥrMLQ] eƙ@u67KҴ\� elVVih$9m*'s.2P,Dd&A#<X(} ĥ3`wmi:R[|dۨ!6ۡ"B,{誡cr*$j=rB@>m>OgSlW�fL11+x ip6zWu�Q3E[x1Y9f݆qFKrx^'x?S-{8*뒣 +dZs{j=_l"BÒۼhlԜu8&϶yR=)| O%%NUڟDޢBRwGbMnum զ]!؜ T90E17.e6uNrf֗Tbk0M $?w;~s, * mz2=r] 4&і+d֡{N7LT'ϻHBm9vFw׋YBO7Rg}UxLZe9S]Zb<mw./Fh)'4bos_mF G<Ry݀! V +k^-Z9kI}I;;VĽwIs)HT pRFFYoEZ!SFÁG92n:u"\Eogt0gsaFݧV{{EؖWZ0u~k[Fys>TY4y?<솷zMck]s刜96oLzw%Jɂ)p<;7zsJ\Z%0sẓ6kϲ$a9$c�5:DlsϏ3;dڅKw*}" q0_FYH qˌ ;@s kKYZt뺃&|Eޣ-d_JXZ3|~RӐkP ?N =&&LVe7WHS :z$ngOk&;6*⯗NX\|ߣ2 3?],N<sX@t\i!_rW"~ZI]d_Սjpv݂7Q4'~ຳ*@py{ xve0$xf`ЁHL-'»6<#5 S{r5;$<1_yIO|W2b}HJw;G,Wze9}=:oVLrv-XYa=;<,Bd#$,}гI'w-3M?J;�ˣPlozL'T|DrRW ͩN =žGsVs/Shd"TCt_-|k3m<b;ad< )>h`M,Nϴp:R @>g=v ,j8`AoHV"(Ri S%ϛO*s:iGGTB*&%Sْ5 7쎦fB(f梸 D쯙veq{gl7Zx+IVo,^Y=9gNZ҃̊ݵk91E xw9_Y\2~%J/gR^P+$8}g"ZBͶH<Q8޸<//z^bA3\xvmQ`r0f[Sweʝ(!Shˍcv/?1K G A]Rk~؝: 31,jѬrQfHi٘Ij_^檾ysq[ KdKt-" N]Nv_y֝-:mU##h-BP.R3sy] 67S#zX.E?{u=qd] j6Jj-Xu.hէjJ1̰Edž4W^C䖓? [h*Y?)Ks8cwR]Q3R9#.7e_JZEƩ)QAq澋h{e' %Y :F£ӑZΤ (@)nZ"5 mA./?n˅PɏLs+zsb7]rUǮu\p_AJ>kQr1{FDdn?10wi7u$"Y(0iu'H1W%u(1p,كˣBgLzC)J눱^/g@-fڢH"huD^@,ѝ+KKiGט) pP_NWҜ>KLի`P.Lƣ5lrUd/uٸ 9=fQfNLՑj_ole)-p`9 xsh=XR`D7y"tSB>5?s'nꝽboDx ]XqѭϽ Pn"R$c&-AREѨܮ=y7#|&ӸAoXs8^i<L5]CSzYz+U_## Bf.SJdz:&|Gb={: Rأ #swļJ8:TbRMEǽۏcl2so!~L]ҾȀ)x7=mmlր9X1q3"oZ:!z&(W ܠYDp~ m[=J9rCc>7 ujݷ w;RXAh)l؏xiiuaAuإ|I K%P$cLRMta#~+|cgX\#K,))^#5X. 2ȻWS~bE}mGB5ߋaE")}>7 s6߻Y/O +4H#VRˉQRDy:=t=FUOߵ*}\fǹ~66RK܍ix*y8eؠmbV\GR@gF�x4Nt{ 4X্/,A Pf?=UQӠX1P87|p!-T2]f0q8ϙoP J,[dBoWL"-,Zg;;c%(!xR٤Ǔ\ [*S5�hdl˜en-7W*)jg\�~|UF-F#  8NQʽy;^HŸd uC&͍k<bwgFKF'lEvj]ʚt8Vb֌fRNήa|2w~r ku� v"p|"΅Of[a|_g-�ߦC;b+횄CC:f1U􌼟Q kc7vpG7-oPqe%jRe7֟ub{<~4nE 7qMb 7-8>& ΞWDQFY}N2^Qk˼/iNJ`wJ/, BaI&Wu"dRS!s U}V±f̺о;(oC8d-Em~xc�ya*z[ aM*(Ȱ%2OO9v$+SF(J,xH$6ŊCRߣ:TS3/Sٌ%,y¨{.eEsk@&B)C{oEAbSe; xZlj-(s8msL| Vdq/-A7Yx<@wM[:FF#`L@.TUv3`f}E<? Y5ת/2Oec9SQ]2ՇYR#Ws89k1ԭɱ Qmɸ g5o nWqXN'8s!w+.o= | zph=y!7uRw7+95f^Unp&/Z. U46}JȨ^:c(dXFj2F[< $$72nRԷnFHmoe f)y} A} kg}G;ٻ52n*3rWlŞZ*?eC݄fǃ\(yGE'<av;ΞpZ5IOL8sP }?5o{U.ʅ!&%&#*3Ev!U`JppU'IKBtncrS֏ǯ~3& qPCH'J/^Lj$C'հlr}8ct]IK0ȂWzb|At6k`-q cj/J}ut)WS[%46\P\Q kUv]W_Y 7v[O~cdd\H-wM\ynˍǖf  B&=n䶖D{Y}BvםgL,oL*y762MЂ/\> Y^mbI[+M9ύvW @H*+ 7ˍf $+:D9<csRv�k(F,-#/K?4fx|w):Tlb PkV+FYhST_y8N$L/fA u2<L;7QcK=_#AW#Q_BaEq-6sӂ7]S> GZ-MΖj? j(zo9Iurֆ!o'@Rdgv,V"ߜ,r>@F3m-Kb^UmQ1,W)CX DsxIsb}J7ѧ0>jeq38p̉H yl>0C_iKPeE%l7/cQF-mtʆ+k~QSطZ39./L%o~]$9kʑ XeQajڧ9G-~qr. endstream endobj 102 0 obj <</Type/FontDescriptor/FontName/IPECWW+CMR7/Flags 4/FontBBox[-27 -250 1122 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle 0/StemV 79/XHeight 431/CharSet(/one/plus/three/two)/FontFile 199 0 R>> endobj 200 0 obj <</Length1 1373/Length2 6093/Length3 0/Length 7034/Filter/FlateDecode>>stream xڍtT/]%Ȁ9H#) 00--J#tJK(ߨ9{Ͻkݻf<~mp/( P >"bc3?Z"6c0 $]:E ݡ�#�PT(&)(#! Py@�Z�u8 $bSz# (t\�p �Z #]m!`?Rp>vD\%<==A.H~8 /r胑`k\6{0~"6#Gm�Gy`�Z؂aHt;�k 4:`g?�;ѿA`ApWs�C`&? �~9H8:@A6hߍ�rz�zC" ($?54KV)]\0W} {|!0;_#ع  n`5ſ=*(�vl~%7v6Vu#!`/`mD ( #OvlGFo dƖ *&yy(OHD�̢ ݅b`pğfѷ=>36X0?�7E0C,w?Po+/a@xuGG3�߮OU Bs@�%B/.e*Fp$׃ *[gD &?K*lv%$" �! o"ђ708 @#~SX ~~):(Ool4~ſߜDp[Pֳj9OQ)ͧ\|6 R4+>+q.0_~kÏhNkJҟl!8N7\m/!#ߵq3vf:[8nՙgWmopVƝI8XiW63tx(>&n/)ʗcI<Dq ş,U !;қ1aSOoP;[f1>C6 nslj!v~ZIr `SĮ4&$ |R_R)dI@jHz&j3ڐR[iuӃr+Q^ujяza~(It)i/9K:*J(9镤+;xz$LiR8΀ہFmCRn|qnV.CǤ1K 2/tx;\<+1R]0sߕD55bM;EJp@*δ;3Ŧn(rD>IE7,(sA%V=0!J%a8.aS>h;Y&`=uʚK#H|!PSynf/1T4Shn^B!KIi!! 5J-#Q(ͼNqE3Ɠ#�GZHLwW$wC>4l(B~ב:S6!U/~5&, YOlj hy̥U1 N\Id:v@ SQ/]tCG2uk@uѝ,$ ?c}Q0@u=44mg z{ I.DmX6WD(LkEhni(9}d{az 1<Cʵk]F1/_ J[MZSgI{=4Q;Zi.fVY-=y vEW8 6DA{EE\3o' fCwF2I'셢~D=3nsio;vzc;\]Oz>,Ũe(ǻ3e,3&—$O^u'5oU;ЫM-([t` ?Rl}1Đ7N.ĩ2t7?ER=zYbf6]pD`@g31,ܹ�Ro>3kMonFJy_^t.~X] |N"K#вMd Cb.ך"&z B##]],P A1±V^aV36~jzwQu0<~՚ζoULby[p#i:m:w \!ܾ-onVIz6(JhqSnuߧpk#Eq",_U@i CF)(؁XkaD5lPB<Zn8loGk$X+>- ^K=&j2}EHLjq2٩Y 13̾< fGSiU[x"5O-ݎ7u>1^E.)a&'ѩ' J:^DN.E\&mدg#bCbv^~v& -ޔ*,lc@+nNG)d_LQ0:}_U-!8]0ˎqksm1m 6. Ǒ$2Z{ګvZG7Ym&Ќw#0<pqR,ꡔt[8!1F PFv߄)Џ;a'^O/HsE6ĉ%jqS]MXXa6#R3(L94es|/3r_ㄯY&d5);_5Wyǻ< Fwkʷ$/\RH=fbC>Gf}P${Ǖ])fDDzGbez"uO>sl"ɑÌxG^IĺO4Z >�A[0OT_q"2Wng]ŸխTw ΧRټos`bA=swǴ-Wer{*RP)N{^Ou/|fYڏzΜ~4N NA)lV#xbg&G=We\[i3SSM/:Xа�*s|^4OA#~kR2Vq`L׬=GY¨Eg dw%nMz.+1T SFv7rTr]LRSux·{pD+6:5YE#05.h߸=0п# lD)cZ͓_g)'IXg6}ܕM))=fL#C~}wiZ'I*屨{lּ.嵐]-u$#] pdi+t}%-ޮJ=ƭ? _(UwR&x@fTf֏;;Om-(a C䛨LQO'_y}#kjɔB̞UlU$uw:y�x4tJlRB7Z+&2Y'cdy䴧}+ݔfmycj'DUzkɟX ܝ=XE-*b7x2G>[<9ЬOgș}u^=?XecYʀߨS0z@\)"Jҙ/~nwY1z:|wZpaťM*)j/b-HΫIƹ A’C _?cG>o\}ѭ$JrxdU=_!;YH}U, - o'PWoܳ L|] :Ut&UZl¥RFQ'iSW%bgGO i,CG_ޱwȓRi[J)`\R!zB+l[4Ct?4wSK5uƾ>VkS#9c^z`J"BNu0Y,e,5v;4fc>ج]™k�Xp8Hx>:4"9 P6!K@Hf./+w52:' 8G'0c@|#byS�b?C(sv,l_}cu (g&1y6Qyt+z4TtHHVaGR#ikTʻ�e;m2 h v2\pI_c!@ڻ˛xԑm Pܽwyn@.=| joKLy[0c-lrF2[f1*1^5$WlyNvGZm A>Nh$!JRt6ܴѵ)cԄC]7ĔgWGScmVKZeWІI3/}FUTּXkꋪO%y~<!0|NrĞ褰D<P}Xͺz}<�[$k<??L,vׄ{1"<'GߑD*O cmUI'.N#͹pzG%͢�̌kb7Ffw_\Ț!g1O[d蔍 eK7g7RJc>�@5drjoSXz_yecvФ%^Fw ΂4:[Ay~Q5ewWHG)]3YgwIR!&y:gB;!]| +V\8t\GuX mz}mNv-N?(mۇS3o ;z?lt `VɊen" eԭ$ca~f6Us< /Gl#ڿhD;M2slFp^b*U �yµR69 }$ܓlF_7(u"R%k9y:t5׼I bKc`UGܾ̃#-EKqiDr&"Vi<Pn!KM=#OZQ˃J"vv1*NNL;{,I #W7O,~=>J|Yςc9(C"U)7ݣ6%{5!9i!E͘0o"ؒ]3{V�p_} v Jv|'n`#uAAUcmͰw!}> _!1+m%O=XX%cpW/QjpAeRQ}zsJrKCy3PE5,('v\W`68cZ >,.hAQ Pgt}h=,J\"a.hR;LRXk:2�#[\eCQiV[ٶ--dÛwQ+Bƒߕ^ȩԼUq)ey`ɖwڑ-^l7<W4EӼ/w:1ħ xf/綻n/իs;kWuo~8?UMl /D"fv2k/!"0RšzqRt./A,>f@7-�lHW0p+ YMyGQym!FF 2JcX>c3V<,oΦ jc-v/enHy.Qiʎ8UP*!ᅀfOnux\'x>|\vLgEO~ ͙T' CMk?n&_~5*^o5$ʽa]-M'}6qx,ez4rtxglޗt͛=!pk1!Z%xu@.;R Ϳ9sp Lo1;8!Z#xnÛxe<q9uܭe{c9ѫ:BT.<>ctk->g)6pzE ~F<obgIt~,tUA|~iQuŰwc&04:)~GJ}Wtp.ۀw/R1KK7C#o5٬xb%9!<K=shjt<ʡwweC:4R#iQn_nd.ܿ8TnK=1g_*ɬ'Պ*Sg_B'{aӣ K꘱V; Em|檍@Q -)ǵ+onGV)?152(bW}p`4&k{օ9ks-> u`2٬ojrVS8tl-\5\KF PÑ4AM7=G6}S[C]IT"2VմV.^ۡ9 xW_-]` =1AD3M&ī^?-~){?g>cAM]Q?a|&_5jzhg4D\%&J=^Dt[)þN>ET mM$m}'݅{M0}C4C$M'{@͖L BN5S7R*9?ziZr. 8$x7{HH=5=ۊs]và)~YN8?S7 -) ʩb ?I#C>u"Љ*m9[OQE >OwmX3z`Ќ%}]nk;1Eq*- IuF%Jz{rAdEګgJ. Җ`^]e|lw3`(=y'Ǎ!գg'8Ы|[qM` e#�&"VUp[&(D$_a1vy$�ê endstream endobj 47 0 obj <</Type/FontDescriptor/FontName/PXOHER+CMR8/Flags 4/FontBBox[-36 -250 1070 750]/Ascent 694/CapHeight 683/Descent -194/ItalicAngle 0/StemV 76/XHeight 431/CharSet(/one/two)/FontFile 200 0 R>> endobj 201 0 obj <</Length1 1382/Length2 6009/Length3 0/Length 6959/Filter/FlateDecode>>stream xڍxTT6Rn[A$aaARJA:$QR@ZZAB) }}9oZ{s]s]{fmN6cA%=T@ d�]cK� @D&04 ꁂ!2P*"]O8 "HȈHʀ@($7!`p8U0'g4߷�(A=`0n00FB`P?RqFen(!<� C;FP �A&D 8P9ho0„x"�:`Cu�6W?ѿ!�G }� xc�[J�3P;%(+ f*H77("՟* 컯uE a_c8x "`<Z0ѿmNP4 �YW_woo3f@w;sbQ`/(+"AP'1f_k{|�+~"�_w9 pTVF�Ҁ�$%Ł1�ZG$ W}e?#^B>H\Dtc,+#uO8/z1*Ebo9/B`nB1jPB8a-(r[t/; :X0�z`@aDq<EPjvA1g]5Kl�K9kJ̀Ƅ�Gѯ<h;&AO~�SoYC>P,"<N[(jWte8k~e; �&1rC5 XjʇCߎ_9AYNI,dvVV)泗${6}*Bmޓ܀Srt&tR~.cK6U}xJLO-+ǰ3XoVY߿;*b˖?<@{ KYT"--kNrt@{ҠgJ o=r=G̶9&߄I[289kN3j�^COidYbs,01OvqI"irpO% #拗w�*FN*-=6Uϑ[B_ê,n NZ^w D"9YhB-:wv^h0S@k!my q ڻ4d)JO'P̙lbeZ<R &'rݯ`yX-\h\T05U 7 {]I8%.lW}};ּxl9r(9\4ȖǸ_7O ,Sma7Ϝrso'wڦf)! {x 9AM�M�X㤅c7b3!6[Mz+(dktq̂TIJQ'BEgBXΎDpEڢ]+-1 ꙅApJBե{ l/W ]pwjn+^핲"˄7Ej:W붢7jZ+iD"t>S C; H s,FjG{Y '߹R37ҡ6ryƪof<?Nrn ,3{9 suwn6!|Q56AF՟+"6µ+ٴn>~}[lV/<*|oʨo>X0,Qu,[̈́_<N4W*Ym`[4[A6LflXjӐzS65^n䝼@zYU}oitF{׽TKps UHmiGr:])iJZ6ؕ Y TZC~<301C!M *FQG-k; ?{J*l>ڢé_Bygـ;ӑ Fvg2]]wpI/9:%TYb^͡XZ)Ƕװ42U7$9iaqEScm  Uw'w6֔Fvf/^,DU}lM?SJ#%p1|uyU_nG)\.�x+,>RI8Vlx.^oMGqx|dM!OKxj %fÛf/LrZ0ѰJi^(vieM$~%,GTX2Y'J`4yVAe-7*590X09FzsG -7N$ѫ:pD}>ZViC7>V-n u+OfхLgrQ^=exFo=6C3WLggdKoulxͳJR6i&2ͭ). {"2Fs4T9CKٶG%FJ 8>hw3^Vwun&fעXL�ȅnwtn#j]2J<Y7#crt4K޶i;9n n b?^RsVM앝b> $w~m\>TLނ'2Qߙy=;[ʁ ۮ K+F{<36 l˫nXcd�0 ?ԄQ$,zݤ<X\ڗ|'Yw`wN攅v=R`Ҹɮ!H\d ߺIP.el*fF̗jd#9߲Gw v#@)O7}oZ}){ѪXn }[703h9V\&jx0ߢ Ӽ*2 A<2k|V$:vay.FҳkJ'&zB9@,?Bz`ݔ~ǛR%?]|MBz?e2<({2̐tt0-&Q*A}mIː,|{ұ3Z .{ڧT>.mBx"uϿjUu ህ5"'Gw&,;W𠂟EIc $Cboe8D~)FƧ[TsQ'sb{lÚVP{hh H*_}{. ilLTXn=YÓ?/H1kNBv _7_dz㣒},pF~\dRUz ]PZU&}PUGWe smNi[-zZBӷRnR{^WU~9Ca !QL1(WBkצ`G #hMt28EşI;[ͷb݃dp"/!btFÌIG*EoV ݃mUXU N޻/˦9X𾉛:N<0 < ?#`ЋʖR1])XN\K8Pdڦyf mar&PQ:(w-[JMj1~7٨XWpGi*Hl͐f[!ǡZQmckj+z-Ytw<嬭JU\y<y ?58qb_䙁#[cYO<ˬ޿@T=;LgSxθP`a؍7/ލwfB곓Bk^9]7Y)'m$ƣ8ۏҩثǥ喑k4a18-O)3ͭ<t3IySwJ;Jz%}Qp-JZ֑tdAE~Z/ CflY/!Xdk\/guN2QYO7)|u't;DڝͶ ̰cw^FLyRї.֍c1 ~ {rz珮u#HX[ȳ/Hm=u-;GŜc5{HTZϭ;iv% >Y`X͢1[tfDrrXޭ=8^Ԓ+;p 7}b)+~FZf]R?f(Toisޙ q;ZIm}E5L0BoȸIk^Zѹ-Z;EJ&2C\ajŧqSzʁzrI_9)s9js ;b^rkJtҝ,N>@M^Ƭ|Yׇ<;D] )ɻ P,= 5m5x?rmofS^4m#jj'y0\;|QLY?6^wn_0qFWsv).$'�Ĕ(/RK01n뾬 ' MgOV-5YER5[l,'HpMT]82cTp?h6XTkP;]8-О7pnxhՂ5lBphB<HIxg:=52m̯]vFxdA˃)D]HϻbyA;G^;'AKb s qGdd)n&>"ϼn&{=\f2atUBIq t3ΩƳdI7_{}j#CUpڐt< i 1`pIdا~N'Xe_xo7^3NK9AnXp:PH~t8}xjRn7 j{y/yzu{ݑ q"j5//Y$cLQf+|7?Ī_jx8Ḧ Kh!x^rc^*?cO ,l} 2^c=foƜ|cPCB#.L[~pnuP͓ IvnPdGfPp(b^mV(H܊➊M2*\T`|9`gjDnJ4i1WUlY=2?H_xH4 .}bj ?p:!RߔQA'AH]DUܬJ>TKʇ_UtbXr=g)%cZ|NoIJ಄##'κ%}m@#ͯ&YG+o}֚'pc y~\<McK<7T'YlYj}y H>2鵣WN)1yc '6 )bߺo5y"q8^h ,ǃ{Kt3]h4p_Ghpk䟝7pcJ wlwDLF3TYG1-QX̩M.m *l>{kN3 9Q\Z1=@>q|"lVRQ^d?q`pȊfZ'ƹ�;ޒw).J#gf},jT-gD36F=$&a ,O: ߣL KlX|㝷Ǽ0R&\_|`#ܻv"Z æ,=1nqӃҠzGYwvÎ9W ތnz /veə-+Zt*W*8uynUr<3:sRtBMzru.j)Ͳvd^9o.֚XAfB1.q+Ux1h/O$Z{MS*oҏ|>ZxBޭͪ*1Iڱ,kj'nmV2%1j5Zfk^D~MZ/F@ o\OT/Έg8)}(w\0jC"vWSV ߯z5e!w|%+l\> m+":.uznĞt?@&$GfY*=L : QrF2[7 N&:s*9~Z3殿ىv(w%~zD,-;6>ǻ%n4x𲒝^ɓ(S ,~߷P*wH@]Z5æ}sƓ:c8eK)ŀESJuaL:LtBNIK-r2hXftAtE~H$ju ;Yyۓ0I AZ] U\+ǩ>JRZg5>PG kǤ *\Z%65�҉?GLX?o�NמxTy9Am8 .Pؖly~aZʹ^0W@:W=cnz]kJQ~a+|; ©yA|nLy޷A8EWz:ʣi1ʏ=.8W{Fo|Rٳ*b"+6 'J2DDcD& ^|2^/9kΤ\ Hd:2PJx_]k^lv&\{2N< flέ/RssOS'd=/xHp#,UhN4.�jk걀h &hk+9Va|rG_Cy TP G8u Aq "--SY&n{CSOیVw5* h,1Ehnp endstream endobj 51 0 obj <</Type/FontDescriptor/FontName/DVCMRV+CMSY10/Flags 4/FontBBox[-29 -960 1116 775]/Ascent 750/CapHeight 683/Descent -194/ItalicAngle -14/StemV 40/XHeight 431/CharSet(/multiply)/FontFile 201 0 R>> endobj 202 0 obj <</Length1 1606/Length2 5276/Length3 0/Length 6086/Filter/FlateDecode>>stream xڭTgXT[%J|sF2Hnt7DP@ɒQT@rA$E@$HMko53?]U{ժZKJ\ v@pR*�tšQ&h t000Fq0ta@FVVV�t~+pp wEo큄p ` t A>Ї`00rF! Caa€  <�4 UV�|!0_.1A±X?�0 G QM@|0s4`8\Ɵ<qn`ܯX8 ]P4WI}�@X؏�恁ᅅ\b ``` b 0_NT@Opp!](j_brARڡ^y0$kf $P4 @a.Ԓ 4T_"ELܿk} �s� L_;łp߿{-O &A JBJBO#{ q7 ap6RRY!P. 9A߼% t-DMGTYyoh?0Ѿ@, .((IJA"oΦ` &,%:9 FACM럆_nC['|a?ptm-Ҥ]= +__nIU~tZPrPollHdq9'aZ^QtBu耭O&3d R7,,^Pn ~q ]0$]S}%7ykOWuvwu6.fQ Yxp~N|[ыHS$:mb{ѐtIu7QnaRe[/KjVA4?mk2XQ13Ȋ*.ͪ7K"U}a'R|dB*vPD׽/WOUzwQh1TNhF+S.KzBX]󬖩C4+GYZD%7e:D۷l(w!5Opeۺw^ uUv]M�h9>fi~@S zchܱ'aY} RU*!X$7ʯ-�6\N]fr Wr*iEŌ#=^!44}."^;\PD4Wi ~嚧W,)5˟hO\UVu&y}�$u<l,9SNd<+XN {VT%!᳕ ~rQ?.8:R<dB9AgtXtfHߣjgUj)o1O'#OKu>ͻZ9J$f\VG߷$-!=O duc$oM~,]@ՌtL {HRɭuTIYd_J!pudKǀ>n;6}Vi>iww^>֑|b#\+'ӝ5(W^si٣򹔄/34C3G#L4ť0x}vR Yf;y'Ľo_tU YVb88nhl68Yr蒎6^e6<>8/U"2RZ_lqR>T16XUvB̡<b=+kF!)X/dԩ:wD$xJTX̦ b/ !mr.Fa}ym<^�6Xz 8"Y4Xھ!S Q':w3kou+_yܻi KUN1Abٕ_&V bWY^raoTwt7wQ Cz<(|gN!ӳva J4>N%Ny)=}mY$îL5Ab`pʮn5 ߼H%Gx=Xnp=1v6Qqt}|g;e7k'+Q̻R-u;k) KsvૹO*"6CS-,;]җ؂n!Z ܌vZ1%Υ!r:]1:k֒| +'c8OƐ9c` l./<<SkN/˯vV|?|W~v/UQVK$5o)  &6B~&{WXF:w0tnp+oɎ,[h-N iO+Uk} i}r]?M)Nㆭ\G3ƣH`@We)A7%*y?ߝ8TTݓ`'arf+cdCS*:d!Υs.r+P%D`xI8 Sj\v,xޱ*CLU hvlϓe _$jTNfS ;M䊍Lе$|=?|R!mժSZ6\S칀Å+yf04 0YL_LR[Aqt85ƹ"lpҫ\yng+!F6?k,KPm y:p^LdRMs[V}åKbR"+.јiE^ap_y[Wܕ [H?mm#=;n-#o7gfeGg XQ:ã48߯[?iQ]ٗVu+2{s V pM&^nz&)|O X *UT,;*Oo92_g]cǥ8kofyQ`):XKh.}9&„!5m7Øіoq(m|JMrLCIR>Q*yј/ On'25cvl*3CeV-,|o45yr\Say0ye%yW#@j|zי 8&JQw<ODSR2I.}{ϒW3/Nƒ:d!ÃNO‹=ӹ*J@ 4D[]uWB3iBN䁙?/oݠT@.4w x?ԕ濹Q}%U(7AΥ _8,YQ^hj A2U@4?:*gw쭧vhKo |:Z&/J+3/aM diYD3j"յjj)Av~Xˑ=%PFУ�E=,9X#c\M2Xnux˓zNl1͡+׽xZtjlsKW[-3\otϞT=z^z6!{͞U))JNŖn)9n&瑐wAGH{.4G P,"1ΡΣG70hԠtx@^"zͲ;WKNW)ZM+ކ8@6ff,e5\btY6j`Ó# )ӗbPʡ5om&F"I1\>|P4zUe~oHcg__/!ѽ;UH;|J׋Ⱦlrb|N9Sc4Uizn $9'fD\MH\6w*i/Y! Wl^i]Ќ{S[*,Baq͕G\9 I`3D*GDvɣ^u.F1Rzd`m:ykm}*#@ Õsc44~ƺ̛s ]>Z9E +M "!u̿2Oj#2Q??%<tayxLLۼ\co&Ɲ劍kߞ}y[̠ؗPo%dMI+akrһ9E إQY Y73{(Gdf :W+I4dsbR9})HR`' k _ZKSy; ΡbgMY=n4}-O*=iH1(3ur)uBZbc\*,@i>^YP*od" iEp&KZ֙%%ں?^ixKYdRLEVHYQCņ;WӇ?*p%6%I 62k?e,+4TW]L6G=}#挲O*Đh =!ϵ@` AR r)z>(EJ6ӕn#w\ie3E)\Nw΄"T)4Hi70}Efе4HBtShh;`*]Ѐ몒'{#A~.E�۫=Q{X2")iO2C!ZŲJjLU RL)U-b_(A%Ÿ%؛ .GNM02wQz)~#:Ks|8l'fF3G.rԑ#{ښSeR=u<*]6^y8wIwUF+GQ`SR^%5<[tVy<MNE+2]\aձry*6Sxk^C鏤D5fu}3jho<M/{-e(G0ZZm,q;*H2'̸֓".]O|.뉈2G_mFە+/U@Fy~JYR"fŮΒZ)7)_E$*!Yk Zc&'y_dR0ekZ3VX6o4P*,-r-k161 KW_|pxƥ#3QɇsR/ x3jO"M4PuqyY2nH 1Dx#±ks]bMC&Dxլ7ߠo8~thg ד5~߆}k8UmF7n-G>8HBDz*JSjZ7.!Fi0pM"QDDXQrRvu?RvOq1mc/�ZFy~>=P|MP�f& ujKC6w2l&l럢\iDlǦV?剼NHli%,qUг{9!40WKЅ!>G7#b r2>[�ShƮ0 'N^,K4=0BMB4jdbLYxN?ှ] 0uX&zgl-jӴ3q'a@L gM8NJco#F7PƷV.^U4R>37 @#YFlTJpy-~H/p,@t=g#;n[,ޢQh5RVZƑ�, $mKٮJVc1py[,Ki ~p!3|q\qeSVݢIhR\ mo;O;gJ hթP[EgFe Q#E} q֌&g&ُJSj>f[)xƂ'Cg a;4m=q}?LA|js񍩛f|^:*z#͎IE工a<3r6Wκ7/Ո yk`t!;z{Z<ҫ 66G" 1m֞h4{ֈ&e endstream endobj 145 0 obj <</Type/FontDescriptor/FontName/PIADRO+NimbusMonL-Bold/Flags 4/FontBBox[-43 -278 681 871]/Ascent 623/CapHeight 552/Descent -126/ItalicAngle 0/StemV 101/XHeight 439/CharSet(/e/i/parenleft/parenright/r/t/w)/FontFile 202 0 R>> endobj 203 0 obj <</Length1 1612/Length2 12802/Length3 0/Length 13638/Filter/FlateDecode>>stream xڭweTݒ.!Kh4kpw =9ΝgkSOSj*2u&Q 3K)++'>XI nDw4uq�KZ--��66�+///@ TӦc``zoX G{K;%h YĕUteJ�iK)f1(ؘ[],�V�?�s_0c�L.6,=-r1-m\\޿6.�kgS{\�6`s_Vrtvx8;8޳HH+.6n{_%{yڀ]�2Xظ8Ls9:MlO�gKkSg ;;_gV#GWK3+{Ns6`$El�`-n_3CN XXZ!(9Tfo"E?qUBK@Jx0�_;dۀ mYWfF)OK Ws ީ` Kg ]ѿ `b_|@s;_lE:6ÿԿT޵wr|'(:X/ 11O7 dbcp'ae ϳ'@O Fl`׬- ݜUſii?6-3ݵ~o7+ǒ:j.M /|-^_vq@4])gyDt=km {A,F%ҏz)lq}SU3*~#hcwF8 puD3OݎVQ[ptLxxwK?<8g!'/,O+;j0Z%4qRW!Wsݟ 0AxTӪ9ep؆H&z?]o86ꜱ 7`Vb<Kw=8v \$"}{2|'nѠe3�26ѫkvnKb1 >]9qts,UqD 7ʖ>>N�K]FCX\Skivi)omM=$bqXt\~3G9ƪBxb Q۸N4G Ƅ=:MWd $=*S/<s]g&bDKl1[RFCy}OwQGŠ磣x 7[zU =in4,()0hƬJ| i u\Q$ºJʅ~檣zRlTE jLAmMjPydp@[MFzsϿL=aۇ\#dPCqQ)qyiz\Qr5=mw$8s({ǎk! ޜRąA SAt[L(f64UMzqNM ~9[+3 Z9<;UĮp q9FaD_ m(PAtg(ǒܳkEQ.Tw\>!A-Xɼ~B*Y,b`)bf<j-J\C$g�:ɞ ob˂A(Y!g~W"9^ZDC%�@]<iV Ta:l:O.;Yqrߵn{Z_>(446f +0J?ŵin!/l1 q&`LҶ*Xоs=O;U0@n+a�8[uoQQ@\>Z>dy'B(:EDȽXтrW#>m)UJ"f]X )1cc4n[FmذR#q|X M-O^Sn<usy޼nkg�C@9^ ן2q`y~Xs1.?<LTf݊rW( f[*D& ';R ϙa 8 X-xmet_YӒG$q wWf~+H_&Vo5AU*︈j71NX8 z[vSgQwytէ%ĺ<|cyYɄR&B?IqaпxP7 qm.%§w @e͕G.8K?q w:-q7j*(+9cr+ke!wrI<Q\A?;fRx- ']ǎ"p1= \ SFKFPFixF&J Nx^+m`YMTTgksuu|&KLUGgg@ |[*ixUpH8MnM3)Ggc.CZةmIGbsYrغ*t�AS41Koavf7s77/'y29(c%&,һed0aWgPQ^5rL'V]IGHzZ+Mdm.x I`7NpuU'(i$E>yhN_ bƢ-r[Q!lY؅{O'kc?"Է zDksU ҷq@py&+D`l}N| V{[gifdZ4U8H:7?S}tQHC])6Y{ua `[mX~'i;h'߄{ZtڮKV +.($}ӏ+"&@ B3cv EO|GFH Z% V܊5vxyE~)4v5]"teًKO^κW\_, W;g4𰄂&7)˜?Sa~,Y/fZ\rdAP"Ecy^b +m)a}tCuŤ-Y= ~|YS*n\ȴ/蕃qx IglڰlIӜ0JCL*'=/ӰLI:KfdݮDK+2}@{akaoY�@p%y>唞ph¡H \ Nވ!$&C3[}f{G[Jl\QKJ7(ɣLm- GS>|1 jKHauO9|_6/L3!H;53Ij]?9fGbTXt/\6\:N(-[r\1ճJ "|@sKĴP"`Ns*̜R2DEk0Vq gށNom{J|(Y($I~Vwa?Y&ǟ VMLt*̕*-;& XFL$t: n̝pV,vLrrJBp$آ-X>8oekHM${M,[2y"3&QB]pa/r8> D:Έrl,s,7ˮC/aïI/0C@7Rk_Ip]kÔ<$9= '8v8-*4HzРD R1G _Pَ)y~;p![f){=h1( Ⱘ*Խk/{MCLC*D6vW꘷úN1"Cv뚍 zPx};j2qk01{+aT3<j*>4"&d?_zjZXZ)cӻ6䭩 C"sB\7т3P=v|v7`m",ڠC(5@B5u�RQv蛘 "6PgCѯd^kfq8O ^Ƚ\'Q^|zTfF*ZwW~ү--ؘ6cG2)MMHOw1Q'4;e|]H|?(;(B >1 `gtׁOt1.tr5G-jͫmQ_+–Жy UB"W\&@;]+ߡC8϶:h)83)^u2}ȣ|b| ~:xtDGE~נCR&[`x  us渹 Ωzko��g|&pw<_1ܢ , BP< ƩUq�e$46]rfCQ6vӷ~E#}}ē)#9'C|TGԍuvu=O m0a8x>gj;/?]1rͼS FL坾()3-;٩D2o6w2`D1rjju;@*׈i_%�Ijf竵gjAlŚ>ixMHcNjIjv,ɘgrH~\0OgjR0 f"T|^u=ǧ 1$b~umq`[n/;u 7$+ee:clؕbį"iNǍ[ׅå=b^ث ;bI쿥"N6Xݠ#52q4XRR2p8?$&q14ޖ Np$ݸpŅGry n6V`*NfdQk'>AKIψm7.mxecYl8wF9yd[Ԃ~uNU>Bl@ǨYlN+Bi"@:7yL'or !eیd�UYH WRnK|yn�|o<X"h$(,De-k`Voj?z,t16c6XC8_MTe /-^Eyp_HKXjj'9us*eQQ˔kE۾^{v9 uQ~NDn6/Tc%(=vUBmj kY׈dW_㣑Iv(/{u%'ZFBiJ&h {u[{&@}O%־GW^0'=5M)*v7-kާbq\|=y+30l YVW?*!޸b+ qjʟ sL-&$:\Pw05z7}We*7Hm>S֘HZR)U?VIV9q q8|D$BW'?\]Ud!.~s]~PU _鏍$ RvMO%^B#?+Xjy ,z_aa0!Zg]mee]/ϊ̲Ln&4q]~ZS3T,?V]@ sFv"ydnQՊ'0IffI.dU{n@Fh K@k!4KFy$kA\TX/ZKygDCZNj8ۧ5KytDϮx fGebe]ao3ccO:e*>|ʝ7>Z~  S(J!EM7ɼM񼢞MȽ9( Cw@ o>cWڹY*rLknՙ `kLgP%8i<7tgRrÄa^r=wvoTzo,`ƟedWW%C`d wlr!{V1Z̬L–ڀtɭ~{N(]F}Wه+ F:e iL7l[ꊆSQR6Gƌ�XU#?;+7l_[銘ƭ,I7ϓ]*܅|Hvt|~ [ ,|[ ZΡϩ=eքxMZk0pᑟB1W))D!{߆`tmut쌇.dymy$R݂ogQ b&ԤJҤ?nDmdo-~ *c="zƀ/7<oj TO08@y-$   u>a8X.#Waҥ9 G'_u0x8/V܆.VZ&D1 QstdWSY~WTpt F\-}}]aPoIJc{Lv*Щ)2<ku'ΧE`YS*ītS^ =[TJ5fY8(/2~ rxt[$2o*^|jIG'=}ZV ͰպUUI9LBO0hK:+o,8gތE*5J 6a[jM%p\KGAtWKsO4\.S uޒ޲ ݝh!;щi$k `Ă{4ȝkK?t4weGrȌPgS>碑F-fu8LI>1L@˗8e 吓˅ta#/n]?x| |T02Y>a$ЮXY="5WtK+z_z/u0֥P:XKO٣qnV\$Y|Ns2b4דw.´a@ݼfz=wd T/gDSpdsWČ7N6\ ?ai80'r2]TA䈿3ܛ+cn9@.g\p! O[ۚq~-d/uSHG >\T7E8=mI;4!K`Yj؅v(}ۉMIh[i?-4"ENkO&)(T`Lmc4b$[;<Յ2;%ZJ-C ^pJH\8v;zfdOM݊ۏ~CqVV)9 CѹѲ}v9KB꿿ט3|ߒG>,ʼQ{Պ�09K5qSECG)tpwʞE|Z7`{ @Ɇc{!X򰹥h>??!8Igc(uug4TwNRHslsnx$n?@Y[L581Bv[򪜼taQ}uT< KQn+SpXH7o'#FwGSÊ>0wn?%H4lw ^iv}_f4!z<eݺZd1Et5Yx :)=Cm ) j8-}8~aMR]<"Pʪ|dkl٩gWi?KK_zfyv|khPulĒ=Y?9M7)ʜ~eG&%(}慇[[C}Ɯh ^J�ƨFTG3C).S{�D PTQdRM7##{8U(Я gx>,P|90$޵ zYUodw?xG"D ,FkVY46}-'~%,6g9C(Pηɪ*S/NRh/gx y`[YTVᤎMGƺ^{ujӛ'A u>$ b^S#t;@υǏdmCzO4.K@?z5]R9J(|l.-,߬At<5 >f>O>|02#ߏsgfsVr7ΠHq"1r5^kdeWb,e'Hba4冲Rg<varxo#dž2SJ`Blj�G0Eڤu5q1Z,RM7B:3z_ۧ͗©N0%6!*hv A&�p0n{**%>!?|Pޣَ\4m'vP Nj$1#Rn1h�Ye]š=$7`jZz Eɗ6�K Z-C\KD s,kkQm׉1r?�n-y0>}՟7iZzRZi5{ 6/a;;eW˜}a힜\0kѲ]ǘzXk^/]Wr8N [*<6dUT^ݲ{EbjI/qs1RnTW_5wjD֥88`g7[wNvtG� / 29 Tu_ZnX1r Anc9%Ey=Osɛv\l=C"|1_bMQҭ _l:omZASe\.*\ K>RgrN/i|3Ls{Ƿ`A1'!u#1aP"+rυƑ诲f5f!aFU0=ݨ2&HScÍ>\ lڋ']gfI(g$;*A,#kֳD|xf )7ٳ1T b|i=n`{Bt9n9(`*?t7i#}fJ8ORv<{~['>qj4YL] ]aM1 i2a,KpN`lMs :HҶޢ >wD;c8$/![dBh"l=t֍{-8LACʧHxq~e#تnOvzK+:emsiӊUiH(=n5WQ/ lt!54p$ÀE(p!`Z Qrf%f֞bC)ϋΰjl_M"~XqcݖׄNʯpxc}z| @gobIꅹmlF6Hj+_9sc>;XBhch͵54ibY{UE7 S؎M.oA^ؚS7 Z9@OxՓB1TJ!#g8J�H'[>8ӗE&qc'5/T>+-I[}v;oDbgɅG[j e$EՠR-,փn+2id빐PC[xץ.ڥwySIr~soLOa1N& \޸e-+]"(G+X!?ϖsՂb&WyfNÉ�j"#r<7`dD6Ъi!/)k*lˏL]Ђob{gߓD=)Xeݪ6z<(“}&@#Ka#zFjVp.IH8b~0:~s+j u@UZuڈ>oVaED go"{ӜdۛU7iUO9XoJr/Q#p�~u5;W7=K; Q{=K ^z`c}N_4v*Q}rr "qH$G!ab =9"vEWf\GmGO5=־B5rqzqLzѩkO+Y2CBH4sF3{HDhSw.I} `*)XU.!pnj T#ց!"duL9!gc(n\:2Oa ^ü/_r ~=n!"Ph $ h.WGR~͙ѮDnaM ^ՉJO&i4fM: [v{D(z~)zxs6! H܆jqe>䈫E 7"WKGޚm<b(SLן)a79|XW)Iyֈ?bsVilsj{%;X~vklaY-k=oJ[5-6:WC8v63XEFFAx=Y)zWA4S GcπA[ąƎiK{Ez]qQ}΀GfhOljy8Љ |W}a-ꉳ𐉺p0B<-w9y~]䩕;S(# ;y|AkD4A;y-"siնb6,f0sy%]Ȅb{f ?f\pz>zr5L3b5r&W"QAN ^ jXkI"ڑoِSb -n/n؜;=Lsa6;,|nW<7)83$c0b m >a Yԉ'i |ngW?J4 Wt5fMGcvD1ƻeC~i"cDڻu0ci+v7@Y!4p2,tHǒOBillʟbHB@VMh Va`2?rVn^⸓T~M5&+,xFq4Z$"BY=/`|3Oϻ'tuKf!eM'З@E>!kO A('Ճ#5˫29NaRȈbEej)NG|#m<y6z]9u= `ˆ+yݑ3֧\לZEp4#bY6n2*[fl%-9k[?LvMYMӵ5k\!.Y>;G7 Za)gFcP ʀŽ$Q>/Ʌz=?_PJ7'ԤLjwԅX 9VrҒG әLFaNrv\­5FnQ܃&"Nj(/oN?J\؛3O(2&21\-Up(Lvb(O; f2*jۉ}|Kpv+]aFZ9 (L"O:? :yU8* um",6-3ܼ0ߚht%1H뱙jWnXO@$-CK խU$oCY2Imѵ;ۈJn &�ύF\5,y|l#r̷amZ,B[EjAJ!&AŃ2ϥAsz߽Q{N~"v[\c|R\I&ؘ\57LXW+%2~ .#t\4*8]/|Hl&9NV,ڲ^Fݕ>D= -$ (( AO 0,V:>^P}v!w^FzkJ&/ ):[yEr:rR<id7(˚{Eqt`he:}eX܉>oڜ^\Igt\ŧ3]Mb徳bG0aC  ոᔽ=XI=NCaVP/5 u 'Pc‹7e? Gri`MKcYSvJ9c>D8cc껔_(FjW</9*$C2iƁa徻p,8ūd؛[$fsi jGs')ض2ح 8*1ǣyG"\켟5Loq Vƨ8dWQ-3 ҳS _O h9,##sOؾ~F{=0"MB%SzKo(zD;$l&Qߛ ?\b:9M(gM,V<PxG~)Kaa8(Nˎ:na(YѪ2{t K14|\&T2VqZkN#E87k%I// e\fQOTQaoXXP&z.4! aMQ|F8oCgG)$_O6 Pxm01,"%̛ y8%,n7{|s]3R2M8c_QpYmj,+f7?M B Y7fgC?_z)6RTÞE�֩a$FLXZ$32b/twb30ZgDޮWk*]ga@Oq*=/MB@I5?;U'"Z0D^[2fNDg/rv^-k'_V4jnP9>lo+9*I@2dxU- ~s:ȴp,l 7{' 2ⵌ2fe^?MeܶaM Aug#C]`vV$~>7lIo]d6TʸdrQ%h &K~O"@s<ڕM9*!_?6sÍȲE4}<[ t0UbߓlMYYtG;8EJ<V꣚T%"eJH ByW*ri<V|DCa%p|S >$M .1H u6ƴñ6֏TI#}% endstream endobj 67 0 obj <</Type/FontDescriptor/FontName/LEWTPW+NimbusMonL-Regu/Flags 4/FontBBox[-12 -237 650 811]/Ascent 625/CapHeight 557/Descent -147/ItalicAngle 0/StemV 41/XHeight 426/CharSet(/B/E/F/G/I/K/N/O/P/R/S/T/U/a/b/c/d/e/f/four/g/h/hyphen/i/k/l/m/n/o/one/p/parenleft/parenright/q/r/s/slash/t/three/two/u/v/w/x/y)/FontFile 203 0 R>> endobj 204 0 obj <</Length1 1630/Length2 7362/Length3 0/Length 8179/Filter/FlateDecode>>stream xڭUeXFQ: AA;$F@:.8wu1sϺu׻^M"<|�u3B Ujl5, `= "2GaPys$H`�ȃ,�~111\ E 8e}`X\@ESu@ �C@�9 ͗J�v%u= r2�4K-AP` s@:�,aP+<\29�Y@n o�99g�q2"{PKְ? `7{2MtÑ剴5G��M+`4( E� 7X Ǿ';�Cm7 dcd!4ܿ:zs8?s�# 5.}LK}l0(Ca�~V`. ? b=3I[wW `? D?qxߩ!usk� g N߻oH@m e#n +M0`m CAi0][=A ӿOF j\ny? H]w8a `V<摕<"�(@TTDC9 0(@-aV'Gi~ÖNNy؃@n K D]wr~y~`x~lRXoJآXuY0OՠmfZO 9 EњhAk-$ݶARI5&`K )2"amͳSCUj sr-I-ƥ뒄Vb4턺H$NjOt�; wZO:dY~mU|m6lQd*iittE401 ֖c)oaئqE^ݖDR)ڈύ*rG{1CBm"K8GUtXX�e+T&1-~[FF~j@ cm%H+ThڠYie1,>ɑShK-]=#(OGh}~׶D/?H^7Hr%%=+nM2v 9ؿڅ`ZWNa bO/Q,r] s6J=<yJÇ:,tz5,5 y偹IZgBqjz?9�=I$m;�۱C }Egv:_6\o \ҦՐ7ED8dsƣ n=)CS ^-V G3)m%[;G@琰->R*ef5'=+QAΏ_¾uwO�DL`s&"*Xn mK5Z'ŧkUD2Y/=tZDVuUX<|aO1¡0ew[ j,-+ 6`&kc}z{^5f=Oe6 x`ihHNhe ɋ: 7B+W1Z{k/coRY)_NNczkC^ eJ!~"C/(maqx=A `!~mKQ~gCSȧ{8C8Hԏ}p|ǒ| 9#XJ㩃9W%|<t>R锻 :a dq?{ݛ<;l_�FYV>aulR|$=`BYHR՞ZǶ|R ^O1DIYix6nUSRl- kŒ^c9B}n ksȩN~dpċ)pԢG(Pw@R5Fϝ"hܣd-P\ xގ]mJR29rmU;E/"}rrrv<,x?xT*0үuWk#?CLQ2B0lSqQ0OY^FKoΕa\Ki~,=m4NnjKfQ ) ^8Ёz)eNLM0>L0 }2^d-c$oYjO( plM2"(&94nbQm\D*ϱ`l mş7, _˥"3{J}s1 |=Q_ j,khK}sQG6|h1pz;d@څT7{/ʔ 7@,,5#�[0(@w3ȼɶIgwp v<-ʿf3(A'M^1[4Sp(riphRž=a'M!aQ3|Nw{[|q2xՇOVtm}1/58vF>Iăsu9Ɩ3/y{#W)t \B}{{ X,c3)Acq#iۼ/ޥY$s%[7Af Z <,lX󣋠 EHƓɣ&;{Is?M\7/u|zP.JNtӸw4J_fDG|bK\qBi52͖4;EΗZ^Qcfrk\YRpne7py!8q*< 8SڗFuDts3j[V'JZLX=_3\IHWX,hK`a|LGS/|ͅ#ZbE򊽵qloHp)_SThd~bZP.6A^E?As9輷pi*ЭirD՝7Xkj5ɺ킎O~|v4ǫ!7=ҧéZtؖ7(Bd_xT{Gڪgg/dĔr{#˅KuD̓|2En(lS6HD%c#W'%Ӆj$ŝɝoPW#G նnB-}ѣ&٦W/GMPf#)F۲3pu#^uqS<+YUAH'k~9 #_/y y?Qx L*YDRWä_}7ɊGvfɗL\'=8.T׉XЭ^(:?B=PP1/@ރ8180 BGr usI\0ω/(a1}3+VGZKTwyEI=5 bvi7(ټ%`YXy!R @Fk=mJ(@7w؇:gz8ke!N-2E *9X1"/=x3)?[I*1QD(uVb C=v@A-H{JPtpnTvΖy]36"bMM Pvv! 8ig$?ߧ+l9 7O^S}!QG`W2+L8f_g~ӣ97-[%׽3H7vrPPX($?*°pԻB& ҽPHl>@*atqZm2!r2mWyՕН!u\ֲnlݬ$ I+Mt^^C+G"o"d4H`U?`̩KjW+堆OvOFHR,T"٦:ED,y !-= u�@Y\p1&(e/[3fnK%_Z4~+/јmx$NS_sa3]껒N"\L}Βr�r UaFço<ģzcp)K 10qU%Ob<3JdQn}j܎8(D5=ӚHzq\"Y. 94#yje.'G|7c6 AERNǶox+UJBJWW֫=aT ;߆/Qϲ i#BK[G=,PR^ ^ h7Vϲ4f碯1Vx -FnBumV+z$]1ѶX.mQG,i\Y(nK�w,eRly* @glɣ&*cF layx_WGM}qT:aeWE)rˋA@oZjem{pepHy~NݒjC{~k_|2>Ol.SљRc7*O%D#zkϸ0#7\6A}DE_iѫˮgL۴?pDGFmN s3pq[<geUa*,)c̟lA@]QJA1}k$Qq@TaG՛:CmrY4үՓ<ba(r<<OIKw:ҠjױPb_T:1Z7عx~kuޒJ(f<)2EeO R7g{4)�pzYNcap|ީhᧁ=G:�'N%c|w)>[ۉO}|ZZM?x\b g(!ͪ -T)̯ɻ (V7$U² jUzle K8WsrZ{6ss@~} ?Y o;vi$c\;wXy1M3@8s$P(s6DGcjvm ;y)ϼs\ gr˟vx5@˙ӉʳHyŠ#qgwD 8v+/ QZCU6;c%?=ΘGL(ڳP} ]_n1num;zruMHH%/Z>ɂ@#+2-!lԵgV+P@55')X8YO?8iܘ-#玩#"v[<%-A7vŘ:r&GUilgeLTCM{nQ0Xv2 8L<,zd&A'1upF<GSՊCxY㾩|s\͈4 }t/ .1JǯctzڻI0&țBXvi=H=^b!snt96]y!UUEbӫ:G`cyc>[%Ud ``腹+ّnO nTMNܸ͈31rMϠ#pZNvS^RAR$EEzXhy{VFOL% ӡR?POcr)hA1ƭF_d?SiT2ν:k} [B.w*lX_mj"I뛺ޟk 57 %Ϋu} xR*eكG۠/EK,1RZ)'R!=SjMU$==:%IJzϋO6ԮpHL8B'N[濾sۚ-=rn' ҆^WKB%Ŭ(~8x@>Wud$0+' IFy~d1K7Q nhヘ;:& t;3-Cgn,e6^C8ze;.Z|rbZu^= Y_5, 'a?l0Q"cpɌ&r+7]T㵿Papho>>N| &1<yM)\K"mKܣ_QI⣱Esq!f돷zC :dmf20p2Ү'vx}嚔Y,"D�|ᵨdN0G6Av?==vV/oUH$Iڴ0MSWz,%Es2!$y"^mm@ @{eGjbnMvXk\9!;fʯ�B7_BCTgly i]zz%+x~vV}qSaطv&W$Wv$S#47u8NzB^l.3ǦXY\ ףNt"}O.<g.9r=m8t_Ead2'+p ^n@BIsKfTLqVvm+{MdF0WkGk- hj%'~zN^M**ZbՔT/!''E lshK̚peZXO٠r(< Ȭ YyuSW '# BkŘ>:B>o EDj<Prɡ{;E{S6AT�DzfaqfjHָء*aj_M:$p2fzX*֒lkSˀKBo2sNpqs+H@B7"UWx0zSh ӣ\XLR9)nmV39|ސaC /Zbo9aŮ+F.x.[/gJ+^w(Kj EyG8Av Upj[pbz>C?} -|+q5MOQFk!-^amCeAcD-nЋǪ j8hu|y$9OIVcYS~Qkj|pZ_ m'VyF}T9hO6'X& c s" [b%b4S8`i 4·%^s;P4գ.!UpIJ⺫O>Woeu!JS"ݕ2#WCw1[Ѱy\2V1;JlFdUz_H,W?tc8;,/uk̶ГYw$rA ^UYɏGc4⩀}Җ={o{~AvzM#S|C1UQ.}L8pvҞ/կdž-IG*E[E X oLԉJ-;dr�Z a6XmMr.{F&na4]RkkRqۮ6dF@\˄u-p:K$pmr躹t,`AE] W ~۱>~e$KuLi)hr{�19<IWI `ٜcCyl.w7wb\E"h`JCGAoqёMF' d%NP_"Gi=%TG;Lܒʧi)q`*a2'cErac1^e⺐&ػޜ烘<d1*8FͳP6OhU'7vnҩ,U݅54'쏽y9Ue#>[-%76~GW&Ib(  J<l}ki9nyG pM2`j&Lj y$A*q` cVK}`HcEg UR Ss ^Oȉ^%;=U;bDܺM:pr$p;$T%zuwi#sm*y%^9+Mu|�B^ endstream endobj 108 0 obj <</Type/FontDescriptor/FontName/ZELEMN+NimbusMonL-ReguObli/Flags 4/FontBBox[-61 -237 774 811]/Ascent 625/CapHeight 557/Descent -147/ItalicAngle -12/StemV 43/XHeight 426/CharSet(/M/N/a/b/d/e/f/h/hyphen/i/l/m/one/q/r/s/t/three/two/v/w)/FontFile 204 0 R>> endobj 205 0 obj <</Length1 1626/Length2 16063/Length3 0/Length 16902/Filter/FlateDecode>>stream xڬct&NE+m۶b۶*YmIUlVSاϟ>ߏ5sO^s^3"'VR6s0J8ػ330�L\TfVrv8rrQg+ 4M,,�fnnn8r+J]E$�L毧=;h7*p̭l�QE%miI�:@ht6(ZL.@j3?0%08M=M�@g;+�+:�MmWn/@-Srpqu1urtͪ$&oƮv84s0uu5w=]eY8{7տ`X[:37to;:z_V ֜oNS׿-i{s�3ӿfnStW m�f@s8F׿)Tw,3@ -#?9.%lmw 13�9?blge֚q5Oߖ[B+ +O%o%W7:Z虙CfiejcV_QLKJYZ\ew\ռb;?aDD<>\�zV6w/ n&Cb'@oLaMUWc{Mݜy@SٙbaMj%E}UFua ͳ<^+?ehl)Ҁ HP(8i 35c}v t84U ޡfXaI݋1Mcv4>H>}z黁8ģKk8՝D}QGfM!Ѭ3K޼f& qFaoN>U#X?VLL٭tA mxx�)!U=)"bnR*ʢ= ⇬'[„7? {\IP?»IQ ~ G$B;{:hjT(N e@DF\7Xx=obV|ͮ x#Iv>}5Ọ4{j/~_! S/˥ rDȢ*`7q~JDe)n v. Zl<w�VEXzFez2�d#<OԂ ` 7hWrfEgcn^v_�!j@7 (K|YqDZE!+)VKX })G 3oq U҆FzNP! \EЮ .`֖OvA?u[|sv9%vJFnb,i ?ӫ'[nЫ~{pxc3[ %^72iB,ĞDN[ 3N6"Hd/HSLODu%ϱ<AːD&p6~+'X[2LCg NN_",F:Wl|@?j'doC5|Æ:OYXyR VZL˦H<}->yEK)S/�TYgCT_$91"f 0]J0WqݸXS^^֙lܹ9NKZV7x7'oDMi42_ы wWWӰ("`L螺=3# 'J]ľV Fbs|0Vn*x,0_J&Bza[ale)i92u<ףJ;2䵟?O6faz!q^-8́b$z0UxuݢuN5LHwnJ4V1}h#*եݎ et$^=)`$c:>PXy9/!Rqx'2]Et˞H&q:P1X"6U ~ΉB<iz1G"`i+$K({b^MHw],dw@ۋf=UhK>f &S#oeCP; J�=zǻ7E*]|*j_d"ة *eoe8z)vS@]O1wkHrԖ-*;閚r ;vE帓!ayAF&>�şAaEPfy"Fv &gq>h+kX)0m[YIxnaYkXfr(yۖh*β^/0O& 聠N㈸:1ZLաX1 cPeK,jje]KФWXS8j%dE6% #ZeaCQPlb`ǚBibnU}utBݺr)װ"MD7{NuNifbM-[~T]T"-O7U!#ں)ʳuڷ.<7弗_G҂bHk{L5 Tz.Ξjl7ܭᯢHFWCjP9wkG y~}+(k|Zu@"8=q_.{iƨPφD1ܮүQͬ9MK|G80U6^UG?aU3ƻ6tSF>B%F|?@SL^-4~y$DH5ƓeYzAx^4*z|{*m56i,@CJ IlF7!?=IN^Ce4O+1gP4Yuy|6>rKw}S:2A@ry[kqe1j!.TFHCnG~TSJ"a I/}8 v;rQ|ujl+H T+@5殺02RKvi%YZQM{bÙSv3k Z}'yw�tFsL&XYS 5\.* 0& 5AI ƅa0wݦEhb=u6S))R*N o;M(7@h[B=0ƴ'$6If{=9z\ )!{€TvT# LK$*s)G߄2'ʏB_?vqI D'q1:1]Ll %Otrj]Bf}h Hf}n/)oOZg߈<꾜c~_7`.f<7yJgSR1pFJx,^J=j o?8kwK>KFxT-WEZ/#٥kH&QqA1<a(5&X>@n {TU,C|ȗեog es"mdڹ+wY:K {y _Q64XוH {*+OM)؏WJ 4jYZ8.X$C〶i!ǓEqJ]P~ځpAU-fNY˟kK30ձXڹu*V:4G*dH/ӹngc3|6`<uK^k}#5cθe^ չ;[[2\vrOPO>1exs>mM3U}R+ Dd%,4Je]!)kcGƊ?�x-"U|ϐ*U0 c/&fGP®`A�<F#}cހ>އ{u{D̳: 7DO>sE֨x6N8 ! 幾\bܭhG1Z({mߺ} aAN@_d ;h d <`O!+˪g}DÑ۹Ta&&I=2.ǴY֧V-ԍ7 !^U5~ Iw!42Ɉ vI{jAŜTuՖT'DƳ$̉X;DzܒO0gjUKpVԝFe"b {g\i(=jS;z:Mxmk7QwyzvO]`A‡ZLI90Wl "U+ R`7 ,*$D6 P6}rvLjX[ѭa&Ei#wԉ/mMf.S/tZjnv0>>Mzn&ԣ<TOװzd̀y)B5%TU (9j(<g229ӫWRakf[ #S~FUkQlTWẗ0e2Ϟ-c 4zԦG!(t#J*ޛ}xYVY>tԓժy.K xM^۔ak㜳AOGL( VJaL:o\LIf::y&1\o˛z5/2�;}hKG.5[gfVm71=6sˎajg|#a2]p5-Bsh\Cht`_Rj"u ;V 4*-xv"b~bTIdσѬڙUttwqf,| #Y 36]1 y'\O*w|l-SY6=e-Z?ŹÇ^{3ݝtѸ:daGO4"HcSl՗00olAjv:K{2^%s6_bl&<AR#1U!HNw񄎤M$^y;EB8MfRF}N03MfhakIq$^]hEȵlb3WtmmFVKr ,>]㑫 :z" 7lABq (.edvݠiΐw %xpg4B4 :r*USߴT{J$h%wLP lwPADnD{(@.n >oo&//` Z"zeo˿tzw)KXe,@6 c[Ή[}RO3x4hB(nd<mɑB^qj QSIY;t^�=Rz n@0!WRɸ'çHiQĬW3<o8ZSAţ@&~56SBl>iϲ쿩w+[WR"}fE?d L<U&\f,x4K% ÔjkBr o"|R=3iPE|UnC'W̽qH:p~IH+DPiHluvm% oY]F%1> ~C6]M|s1唉C25Y)"Eb&vVsO%.ͮWPa"2FDxI|髽Y*ALmaUFvˮZXSL'` p)w �Kی|~K-(mǸvOME&Ɔ/kꑼt<h|lj l![qt<YM?!j sVBڲYй40aEG8-v;&-AtDq(Eomk< [[5/46siLwY/xֻC?ӧ"?G=$axF-CgEYvۥ~\INvi蹢 ܞ/V93J`w%xDvd <vaxҐ:ʔ~xr$=~1tI0gQ:Fk2Y5ɰzGܧz)!Qܛ7G=wLf;y �EaAn ᰄ6ΚamsETgTr)R  ^ZwP"|TR黊!ٛ?{ +vh.юj:TgFh<UwK/1 �R q{nhZf"Rmr$%)š:3TJ1""-[SVal LZ;B }ZS=13'V.߫\~b%&cQFl"l&oQRz'OFoaj6Ig^Ѹ|lZ1,}r4Iͣї@QʊI v44zL~i#rpⶒ2wEI|E-aquT !Ȟ5WݪӞ|/IoT >\1͒8 ?a*GI1 HLdlsp8|pN2zQ Pr󹇘C^gyJJxGɡs̄L&V!H1;Hush?[b<(Xn_ė?jƩ7V cMRf[TSlk@hvqg6yo}g�iU{q=3ݦ7 /H~[?v-;RǤY4:/ɘqS] F;?gd^rRX.x}7mmGjQEJ۰+]@l"DkV"d z1Jxa2H3P3fjqwri VLNHsF*n,HQT"1EXP.3>JܚVLG*bҪ 9B"Ǩoo+i"S@:>kY.$t:%,#g{+ET/tŎ'GϷ9eYe! Ȋ0Zg%$<':;b&{amr^q.j,w7Y~thz sZ۫2Bְ7u#Ƶ;M- ; d9Y?bBBmڥk1?|Ll{3I|F6bUUl +\*\7KirDwBׄ ӑ='/3RĴ_%i[h;�hOJ~'wQA4{r:Y=4'SާO;% m1UOgC_sy#w?\.'- h:ISi[7JNI-1mM ?jb0һ&o H׼GG:86Z]ԍÔG[NB$xO}Au&q\1[Y( q>elpO�צŒxXq< fws$cNlWT*T{b^2;XExuvv(h:C:gW *$Z;1oDj5f9鲥EqBdyk㷤e|_]ut>,`Ne5%#Ìh*BR] u0q1:}: <U9Ӂ?wx' ^AX9y;7bƉjPU '@vm'Wp hqfX||(7 !n2jOv\! NNE<9UOt<d87 ;bq0U+C~qCyc 3,(d9JdO ICH5!#|3}3!zo@"iLDZ F*!;dآ>GɲDl`')7˷'u w_q*l䑨b#U'\G Y$JO*u_WQ "N[KſkW*$ml!gԚl8{TsGoC҂(2 Ybs#kF lTrY+hhڳ?+L'ۅ|[7n 2,_ݓf>ʵt"?@fY nzLJO>C[?c6 #[eB <N7ӼsIdi[SZ)R\@R^?Qtꚨ](Bi9y2,(p-5gCg3yS A M2&C10\ӫe~=H]8뮼swꞁ4QGA߽eOZ>4X۽nD&soQovR6y ˆK}p0?}$NW;/7T㫗tEȺZ >Z t8H>cLԠ [R\`H||1M#.rP&z*,mIMS*]m;Ne-`I 8IU ?oĜ%%7i;0s2ӧv<OPVhL UrTڋf$3 V$ Ɏs.ONy̨ E!Q-4g8 k`;wƝ)1q àBr#׎B(iZ(H< đ2a`UZNn-h6H4~\?!S(@P*U:C'd x –Ef[wC~(cVt3p^b[J*4"}K9|p/-j`%W&,=/m)|!L&@tO *<ZqqD ŀSZ!pB`SZ}>Ag%mQ0XF2Hs0߮4k+lTu=#־?H'NoM­֧yfZUhK's}dR[ԣ�,Obu4m0k*ȕ_]"DARJ g)tID'e7pora K!^G8D9tԯuru" TgؒՕ@֯:/q&w{4|8h6qIc 싄NM$CG;z ][(�b!z{k2Jn[#f0Mݓp`Qcwfyp{&Ii;}"'~o2t#C`LĖdz_8a EC|(]KVl~� P ]<>su~럱JhjzSʸ6z(KkÚU)1d1ʙYw h_w#V;'UGU\QrY[Fe:Onձ T1X΁J6};E*vYkraUܝlE} ��qGɷ1;j_4Rv9Ɖm!,"@IF ,p6<kYϩ($#۟9?5Qw64;`g()a7iFQƾԵH+`(060tƹ)-a8Tr lPaDŽ�&d՗e X.|ΧL6-bbbu3d-gf6=?uO"D>K{ZB4aOՍL(j!k_DAk1MWv1q@3Y_]#  HM"LN!. UJ:e"-Nۈ�!ݏnFF%3 &-ǘ'ZO|fViyfd߻}vSh]PיQ~?|GߡNqvRGJp~EN,~oѪRęv*/9phUr�azV+\~ƜpsWXK h:(z"(b  &sߎP `EG'cdo x`I #Ux0^3VҰ;Fi*Wʁ: \2VH{, iۓq%x@-u_t`VICN[x돊,bi_2}dbBHY0g-,� ׉"!1tpk0QHwhN>mgdni`[6E^^cѹCOvFgO1cSƩhpfex1F3H'y"wxHXoC̈́nE]aVwç/A�R C"nNC8}7(3OE#I6SxWOޚRu-N _ $b(bSo#!Ue@W[PX#ET)h1TM#Mœ@9zM`%[s`VhlA$mpX0QrM 8 ~�זǔ5'.3`(')dt#2M6ҡ\fcY47 'WW8yDZm8I(k$~sj4}zK^zEqOnߡON%%Cg$DyRiPՠn|XHPW$7 P4Qke5$? F<tIŕE zp qofXG,C(#LGWMȄn9s?@Sݼ7ٛG:\]3pۂ߶\ʟ]$UV@Ԧ΂ې=ͩ#S 3hн}^;aܑ{S7lAbCS>*H@`{"l\Qo,˔<w'u=SṈR19`Tg $,BnLuM- E ұٝ1N3J qy=!0)qaڴH7|#nؚ~vPX78E$8jM}8y};Gv[޳|sDǃ ˕}/{ܿʅ&]Ɋ@wa`/x."-,q]Dn s#Rr0UAP,r>y]حDԋ?�,Z`N*vL=qL:P z}z(0߆.dԀj8XqSuB /4ٗ$yvM:;s@fPL@CY N*ۨ9*\_GFT؂bq*{I듽=<go[{D' |[ze3L貉yh<~?@8}+v OsCP:x:ЌDl=5V#'DUV= 7>+b/H:tB YiN3RL1Jp)][sw|.Z~J[e"�l:*A!�.U}Qj;lq 8f".EV\Q;|'{ᗎM 𶣾wwˏN1%n] HrzSvʢ{m䝃޽ϪAOoʙ xZWDx*_,55v-aegqml niF"b{$_8-?qɇV+]nqȪGJt'h"'c^g]T+}P'$p%nC-ZCT΃9 &H duІQ/2 hdIqTm^#4xkD=U >J~~c ~@rE3J lqnXKC>Ӿx_KqpIՂx{d' ُS:z)7 a3oH2e|Y|8O15)QzìvQ)aM{=fF{b̌RGmzju(M"1NCVٺwzd�ĿwG>whb3hswbhq=@9 ?Hz,Ai8-<]R JĞ 3HzFLJiI@\=T;i�#fϰYŏ̰얹8$)D 6>ꄴ`ozj6n&f'q�ǪuFc_-մKLoePEk(%Dz`"4$Y M}I[MJb_5.F3aNɔM1"h&aG2 -7"'Y@ x!m5jnz |^QU X^ձ@y=_zK5WZF 'VincӪ㟖}veEXЂ\5ȃ+@&~ :eG ڈsWD'Ψsڲj s$IɏFgeqHk X�:%ކV=A^yMCX]%eV}rqϕBFC[|cL`7. lW>Vb~w@EsvNOŷ),ŏhLޜ޴ ۠i�!V^5bf+I8DБ_ȴſqS \1eр򭶪 d�Gsvrd[6d݁Z[~:;@]C?|:UƂ]gPzD]"u_h.gLhF̗J{g:"ϔB?:tZ@;\AmT=SJχև/[4Q*GK)٦ eW(Xqߜ0O>rtBxbga'.>e9K$L`$jub/1gx8l زuh>%??KQ\̈́x- ECd �SZGF^+MI.Cљ@,_B\<4'+z)g.gzgXW?3(@k6T=*03_mePUwz?t5KW3o4 NfWUI)dy՟OO BL+Aq@<TAph=uGj"{"B,brs('jc؀%t;{md[Y(V `ٚ_UP1&2AFL�C�a*1빭k0RCF'5Wls7)p&w/7iilZ6矧kXPȒf> oQWIt|HV@ՃFZm#| Ƚd:f/]˪�v7g#;7#}uY&�u-mKDdGΑ2@lȭW\Uᧀ6f~sn ' +L~HzZdf9 5;R1A*khk ή#QLƘ'pҜ2 B;پqchJIFa 17x5یe5THLj_Y;kOc3Пz� ..0Ykϫ:iND3}׎B-[*k;Z'5X һIYօLa`NL:B<'ek`4uXÙ>e5~,e~e`W4L2Lgd{.5 X'4ؐX;A^*UµHZ8}u-/##VcWcu|FH'љ] >O|I359NZ{f!p aʹܕnhlNbpo]܏Dhb$̅;([�9A2ahJ[ u6YƐ:Izeܸ[ IԻ8WA_Gݞz" Y!ðE4 Eu^g)QY8C?ilyQESwEŞP@X|W#ߊM~Sq`zCPA\m<:Ka-}7J} };b`fFsJCNºSm uG铇' Y)3K}M F:آ&ibSzk_Ik%$42'*)nz�mY)Elxs SQecywdOn{%42>pS^*"Bۗywǣi-L^dh߬0.+ƎW3i]S+.+Q<ʛ?MT;DM.YFh6tmh)Pئ髗* ?^ya]@lrVnx J)ՕGw5`cy.w`6|"=.^ mӈY[ g=rKVe6)-!^s6.tR)�RZj!ޖmh#H[P\gk͌gT@F8<�AiTQ3PKxK"lJw)KLIumlh{:[zʮA~@=4ʏ�/#\i3 lM/pI$%nښf[]4�E4NF G۲+4G|de7FeTw\6j+Uԇ(*) DYRx<Ś|[Z.sd`�LTRDt[=Q[JAG97noϫ5ZJقct31r-z(eSfrp\.v1L^]eؖ 6lo<pyfjx- aévHL5jYArWq-M'!r섥:oپ15gnݳ7[]t%+M;j=A;N.)NqPo5@+BuUb F 8fO #P~ A)W6"ĝ-Dsx|Ee5HvDM[za3<QsM'/:Սx%H?s U.r&-5>aǖ<@w q* >M s@fVj^{ 뻞o%rgbZ8U=.UtA2S]j,2rHd"nG@Z3s)IzĎeY~}|T -]= "ԫ5z?[%-B}1k>$� "T̂4zuH*s="uEq M0ڝd)C\/ e:& eG~Z\B^m@vHC`-$?"qv޵b=@ekZT&! 0}qc{of0<\čoi>7EUNJ?dc.Ăez[hz3d19yl}JaA2'đ OɤI\yXcyUN2:%W|Qnef"o)ؖh9@9 e%gw/^jƧ4M*vz\q7gGNRbמVk$<8ކϰ? O§1ieuWPC?){-I֍ =Q&X2{sKy2ScajPȣ ?pi,t(~Цe~y ;./~J}}J+RMlu )~IԒj\5EX.+' ];1OK1^Tj&Hi=HmlQ_ H<HSc(UF[Yy'WY-&c\b͇g` l- afc<2@ًTYΪ۫;MN6Y<dhUv,*7j8!JFßtc' guJ!ũ2qy, $<Oݝid</q<>5F_!m`o@ǛXL\sW;}L*cO�ĤϧTYfmڼp'Oe v"PL9gxi GrlzK_ЩMӰD} 2p츳Z A O��E]¬դȋ*&.4{kv¼dLa$kb/c4b_s;>"r7Hcbnpǎ aȭQ=ήg*!V9&*�ٳ8t3`&wS{lAb~TΫ\H)>q4Eg�8eύ Cѐ IVm9D5|)a@R?>f$O endstream endobj 42 0 obj <</Type/FontDescriptor/FontName/DXHQIH+NimbusRomNo9L-Medi/Flags 4/FontBBox[-168 -341 1000 960]/Ascent 690/CapHeight 690/Descent -209/ItalicAngle 0/StemV 140/XHeight 461/CharSet(/A/B/C/D/E/F/G/H/I/K/L/M/N/O/P/Q/R/S/T/U/W/X/Y/a/b/c/colon/d/e/f/fi/five/four/g/h/hyphen/i/k/l/m/n/numbersign/o/one/p/parenleft/parenright/percent/period/q/r/s/seven/six/slash/t/three/two/u/v/w/x/y/z)/FontFile 205 0 R>> endobj 206 0 obj <</Length1 1642/Length2 7677/Length3 0/Length 8516/Filter/FlateDecode>>stream xڭveXknq-RE{%@  ݽ-nZݽS܋)V9|:g\3=3=$Ԫl@Hjgdm͜`P[2B�/&=#IAB� 2pq81P{7G _c$ li`x|pA ;#8P@� 0|+" `UȂ@M:A�%9bX@�s5#8 ` كa Wso`r`�0 `hj8�38h)a=Bap#x̪*%Wp+S0# Z<zN[=<pS �2`=1##ON0A@{y}; /ݛCDCx0Xcrr=4?a=/vP�'_v0g b=3̏Ev7�dJ L `? D;qy?e Sk�q��-?`Oo_EK@!bY> `xqZv@#lz>qpӴV/dg)8i8>N\t~SIH@]l\qs��|<^x8>+�}vN> A#mg! qm ;9:>g<6\AsPs@dx9IFϐ~G;'RO}^fvo)')tE,zDmjnCf_;1-t‹k%&~MWFy8?u"='87WԍroQ_4q;^0:gҝz'V~ j~Z<k'C9cWoOw/,0MIwn&U(g⾘P\Xl LٯsR 2E^pc.,:&a(FXFɀN<v҂�KSw{&i2﷞ |mBZ$7Б^NhC:(ªFE*BA2{nQWȵC@XnE HuY/9X}L\3v,1nT?kےUCdNzr?}7vQUTM/qe"nQTeľ<j¡>BK|�5LA I2Յcu*Z5UzO AaW#kkX9AADbX?(wV'6h/�ղJx1�1T zS:Qp}6W j2,2wQޡ^#TH_ ";vVK^9ŴWd{x]CСdkpTx=JPQ޹p/'=baW m[[ 2U1p n4ۄMΕZpWP'3 |M7|?9Fmc!9I{aq錊:2$&-EwiWZܴ,W)> q`lb!sfe1.hۛ.0o1@Qc꣪aZ /Q&?+&7hSbJY~FОzq']҇)o�2^zrvMȖ`^\PGvTiT+ofT{RAIa#JwUM}]zŶ( }"hs~h0\{ؗa!Eq2๪;mVl5qb�[% <VO+s3Ze{B -g2Ӌ~kEhvY\CȢ�vBV?Esm}<:[x|@6`x Ba\9@X=4 WvTdO8[M/KO+y_m 07\ƈ$|colcY0von ~qM+kFz% wT1ylH<o*ŵ ,<8݊Ku% wL([.am`ןgAS j<W#|/X?"/9|XU&|LjX8qUU"PC @.:mgb"enhqƿ%Sw:tԖ}9Z8a LEi 7^) yǒ5%!-b. >:]/\,!1nСb9(\]thɡ5Q%0T)ZK}]+chK*RS:۳:1x{@J֐wu-yڔoZBXvHN\c@g*1ۚd9TX\/ 'WU@t2-vJq$a ij`/! M ؊{¶{Y<`UC1y#ݲgXeM(Va=-/c΄)lD/fw*˶^n%4 `#ML/&Ls٥ۜrԝd+.L {Ѩb #_,A'jM_L#0.r,{Gwr~N&"o @ʼ+,d/Rwa@RKt :֎.Wܮ{-uBWh,<z�:wáJBpBΑTs?ɽ bXf wuy[f"Xj}%d-nE,¿h{uUҹk`.e:6;c( })m7l f? dp= qw :O`2AM ףB"NYqX ܋hB\?']CQF'v϶ȱVPT}|[lKT%ya_Rߚ_4Wk_+Ea8ͦQ K#ţbD':j%3[>{/9+پ8t:9qpג2ZYSY>|H##$i&Țo5d˸`KQQqĒēZ1X&ɘK%'?uҢ8V-0L@M-31u'!!S⃫_V�%f FkنZJ׎?s:tECd anOhqNVN,]+7F*jGxaOD=1o+Rory0teYucMҁ8PC}is?B2iV,YNv*<*d{Oֻ@S_e`S{EN Q~59аOsJϊ)5c׉!2نL{͎:e)l<�QGy1[n418㉒7Z<5d<+ȯ!]e&~91(:!C}T19}VUU@lv2v!*SE"ir"bK~;Fm:7k<p}8,%9苪$n@^~$ z7K _W*::P&#.6pInl yz·O7Z.4ޓy\lيG#nZpdAcW"$-ޙ&oYJeI#c:q )֥Bx]~rcY|(Q`1ta0A/qUʆ΄WMMid /3g8wf46^.ˑAl<XM 4[`<2@؛M1*YuRyQh2'Bx sUY:zO*뤥Sf%E&X85@Et6FvW~d3ʯNnXbH2> 5"Dd#^5;k dM).Eʉq>bnǾ9QC{Z}7t+)K ]Wbvi^%zmk|D[tJlŢ}q5-m x~DL /JpVڈwc @laIxu˙oXD 8OP OM 0~$>1'lWq[G)%^�ȾWSO@n*Y�QJ =,4᠞FH Ԋ 7t@9VXƓ=ۃqN]]w[g<GO~*UBEaG`JpCwjF?gBt8?G$JwG3͡~<-8ʿrWG8ǵxln,nVo˔$6-nZiv`h36KMzOí+0aKaUyѻ3bKBڅ!1##4TbBU9 LM/ "XTP\ī0&|Ώv~qRX%. U4ve +N*j2Ns !# xF^x~OFVkYw(]V*兾ѝ uǗjY.3^ɟII{򵥠tO &L l۟q,&8S�t:Ȍ4 Lީ{zUP w5F~WhkwR[w9?|q鶉,%gwy}`1>y1aQ egdSj^HKPiӭsdd]=w),&8]rʌ%n߄/5rgbqɺx iImȝZ؇Vh]L'=>ۺec ˹rTYTR" Fb+?ړ瑎]CAL} h OE﵂pL\) rӾ&NO^4JU2=Sn(6g=k3hik*)ݳM Yp<^k2S7YU64rWq\)cpߩU'KUrnB7/ &M`:Z-@ 2]%8m_QC^$4%j'zS̺>^Nv&DA&*Cmzs.7{9ҎF6:tgJXPsp_cdhF 7l n\OǯwN+F#f&H3' W'(ﱞ0ZVk>w`CulZWIFtLWd5+0W9 ٘n einj7i8\fڛ= ۰(;lKy;F@xzWɎTlSXT/B#tD4nkp, d.ZEr 8ȽrXHռc# MI^4JɸPNS0�`횑yx~Kĥ�\s=E̲74PWVKhe 4Og~qfġӬȸG| vqTl}(.[V*@^?jM VG'D})*iE:cV 5B\wSFfEi $@Ki,ǽ;uĢo}DjǀE[>._{#%m.iJ[2SK!˸Vr%]wzzL+Dm㍱5^lMW[/V6 h#՚Ap.ax<(jo|2 m=il|*}; v:on2JO[oj0#=(]kr0o%R9"K>A02r Od9Bx"-+M|(o`=*'uT{aZd}j&=kS|\dya%:%gI jUsZvR)VYv15{+_s]!CꃣgҍBg*?LM$,iGP}e-BkV+巏ac% R7g~X5Lnh:`-T&G�^#"5w) j֟娛PaC01V#'` "]%w|K�X"T7N2=B' W;7d/3xR<,O=UK=yS,uEe{9쐵ž=u=>e)<.SvYVkNQ#h ɴi6yZ(g翬:\:~XlIΣݰ|rIRx4oaYzJk$2)Bg>3iO{ylb֖Sվ^.Tf;Zwyl# Szg;OCc<2L:DqN�i#e \IK%!h-.leo!zX�;$EO , bsi2zlߦ%xg<3ff',"i"]M\rd+`bsC#e [鳴GPp12._fD}fJLY9ƥky|L2\- `tFs_2dR"r {鑉#)1wkMGGtM<]t7t ~fuݞU-"7CM&bƽ#F-tm}/;unVe}#1[#3ݷsVeh+Ln;ATͱ2xŬ6QJ~Gp CyUȖvQw֓ǵrE:Fg}$:kzuK.}v؏ց&X |f)p>lS }t o%PyG<͸R")*F^ȯ*}gnmE"Pm<< Ey^t5&Rkĵ]Z!Wb5{@FreҼ/F|MJFJJ$K<hqljow/ZE;abH^�)^˵ AƑx"w#H4O?Ȋj;c4 �B4)eg2 F x9;g- )BF'R[m} IQ1*~k,%,:JvtxCH]%RNk> 0` k҂SW_z=vкm=ky�޻g)дF)`Ԭ'p:䨶Xwau-\3SWJq [4ly&m0o~لpбPzA a: |}c$=}nflF,g+7!Q8[ӗQBR՛i2ĝ=| TiF1+彧&.QBs]:7+60WonZ34 0)xl -1=hQb⧃LJ� cI� ¯t0M6#}x@uʧ/~9OZzz ԋddrЍE*%M|'Tٱx |[/7k{,up(#U]T_=�4ŏ8<rAk瓁[x2HH%VQX1g.@NP/!YoCz1ŮK%)oߵȔqZB `䘭3Z1oʒ̨ r+i2yoTx> ƖKv>H"cr%*rg;6LF:+߁(5#c'v%tJ)! XN(XK43P1 _ׅ+ddP^I.şf&,Q->c\U4o<Kg!A5¦V* C̓:ʝp̅,/FOC =Qq5L:yd.PEC#믎TX9^̅t -9jmރ)e]1Vv`)E ֆVR^S̔@ȌW謣/LոP~ZߖV1QR'HVkbTƪ(8ܖdlv}z4?* endstream endobj 110 0 obj <</Type/FontDescriptor/FontName/HQFFHA+NimbusRomNo9L-MediItal/Flags 4/FontBBox[-200 -324 996 964]/Ascent 688/CapHeight 688/Descent -209/ItalicAngle -15/StemV 120/XHeight 462/CharSet(/O/a/b/e/eight/five/four/i/n/nine/o/one/r/s/seven/six/t/three/two/v/zero)/FontFile 206 0 R>> endobj 207 0 obj <</Length1 1630/Length2 19105/Length3 0/Length 19953/Filter/FlateDecode>>stream xڬct&;[+m۶b۩ضm۬$fb|ݧOu>?5q{ENJ/l`pwgf`(Xٙ8)8pѫ�-�p@cW+{1cW @hXX�p�QG/g+ KW�&5--jqxo=;h:PZV@$JRA :LlLrV@{ 5`o``ofOk. ]��G0)lg`p6w;W?՛; Ggvm\\]L]*INWKcrX5z9ҿlaZ]]�@Or�fV.^sstWn.VYhalf tq g{cGG[E;`5gcfon +{8vEo܁?;Cc3{[/QoJ� }$7PB oWG+=2w ;blOQ[r`Ү"lo&+\$<fJV�scۿ3^ lke gfb/65K+SH` ho_;K׿gTSP9*]W5/ǿn' @-_@):[yt?%#no`꨺ۛݶl]=p+!ٙ XSbC࣡j%E2"CZfx>;?dh0m)ӀW~ԃE(ݜA则1>Kr:LS*eP3ݬ0}G'{roуP|vNё[#<ڼxXr^c,3W/#fOWwNv:duo7"Ǹ=4KVB/aC&xY08$: Y5ƞ5& 58+_-+%;(TBRK:afRL\șlߢW tԪeh=We$$mFsR'x ݂)R5m]0 -׋R!{uH"mB#O~{jޯۗ!(h4{2"[<-w R! +Pϖsmɤ~N7^5^NHZvA /A6 ]P0m13bH8!Ll['[׼0J,dm?@}z Z L#�,c1={9 p#m'K{Tϱ9qk*g3-f̴@3 MmZWU3SfI.Gy(G+K_+iFZ5 T*+VnlVa袁X* h~BC,|%Ab5S6 ;P3+P訿|JR/};bgu1dۏߴP.1Ff_,W; ջ8~wPI-m1k @F/é3ٖVPkle`⟁\ 3Jpl%lOc8_rG.:Lҝroe {HUj 435ֻ%9~ɵ,̜E7[]B٭Ӡx.̛[g~MrY`ts8kѬs ƜYFђb|f7:eM u߃/ho `>}]E'W9(-=IHB˹b߉(My|ڒ>}ɣO" Gz!C3݂Ng%}_'*(o%{7 F#vF3cJ$T* "ŠE-UnYIJ[yg"!Q^aCVՊײN!VâG:Byq&դY5n߄)T&ޙq}(m_͞a}ә�@ mv�#1zD_H#O#gӌ8u8?KܯvZ vY͜M^^{ 4yDb1Fn_ЏLcw|F>9}LAt$iV4ܵ3V;<#ei|ЇޜhJz:|kNp͞։S$nBwuxţjwP@BψD|^МU'^5 Ey3xx0)^݇"suZ*MemP3)hW"3 6/,ibz38nir`kOYott11Π?ӔqB&Xd6#ƯF; djI=fmÊ>x[Křv�K 8S'c ,2FZQBȶx<D�dMðƞ:_1F0hSb{bpnO#kA;ZGi^"Oo[th,HC<'QʾCY=w֩aKKdpw7] )bÆ'rTs0; g!7`)=z*X)gFq( yCdޭ|>gVvynڇ !ފ}Ex@'mU'CؘxFl#&bU݇usӅ8�##rH"u4?J1wS1,*VHbU[QBuY_!;$L*(\֒5RkG|QmZ(NG3rco+(y VIW} n Y �!ҁbR#nǺ :E/1uŋ) 8+DQYb<V. $f[\ϻ9IA)q(U.|Ξ@]V>HKS$;|tB8w~¸܈  z J]wOmftߗsJym*?m4 { ]d:̽GN*xMwi̪Au}=mdԗK>'`ȕ+u,|&JtG3˛hT{5{7@ ĸL W [ ŦJ[0OMWuJjmW拨hWll,Fj/߰=lڱVPF3u)ru{`A C=3qgf6&ǐf4VkE�䃥MQ"^ p4:OF'8Q;.i]z1b^+N 6MvbubXeLX;a`FgՉOD(^< <MsThȬc t39 Ϭ(C /YgpuKz)+{[]Yg84X:*J=E:Hn5E:^8w5baBt5ғ?%'2|GSD#܋28%+.VfPGae]1jC*b)2+/oItS଍TZR\bGjXcB2CI@7,2a`hX͐fj_& vY+Z GD| kLXP|<dqo#@aM1sA[ZGr-ʝw 5͈ S` s"6~hZw3/Bl>%.S"tcpIC]Mg PXMIDtS=?FrT>yִuŴ m)iaz<1"<5>2Ûlf ʦiv;OTe31, /lb_lȹ\4+"bRM=m<go|J(Ԥ}^ 14"_즘}v kKȃ Gʐ Gv=WA.!qIp~楺][ W@18 yv0CP@s:{[ᚷb\C%v !Z{ܾ`GhW0%') O ȘB)0h<:\I'±Y}WJq/)y�\S=始R~:EX7#U,#LU) OEcSNʭ0K8ҙ|c;1 $Qph»-r"uRPl?.vsg?sҏ7zv �BhɅlhM"~0#6jÏ@π)@EUԎo <SpF߶-#Fx`]O)Di͢{-Ӫ IHc6=ks5!6D8}OrBzIჭ>Wy۷1>4Ӫ@@)GaHXً/oqJ01AL;.6r-6UU({R奠ًYzΌ~ěA.oflKŞkOK[V^YQf4 $*\9ũׁ5cٖZz_8)V,4]axO0VgRPGV- x6> !P�33 v#n߁[�H}}<*BF%MժMgƼ5?P|arL]D8sG?(,F[O6^bwiaf\, Ű2 H(BHU@89X*yBemtvtIټG Gw%O?зLeB동 *^Gs>ЈEvxjNs2&p^(=űsvKͪ`ň�U^>:پ,'1Ŵ'.$SpA%􇠧:7ՠA{kx,p<(ɟ`ZmlnTK�?sZ9ݝ] Vc!Iղ3EY)aIEjRQ,ƌTok-;^\(W˦,0~\g|e2_wH<6CdիegxuHW/\T|F养LʍsԻZO-j/gVrw=.9q P:$:mTV:}+kE= ;'}'ߟ" ĺ:4Mx5phQ}q\}uԆɯ i@SdGҨ,Y/Mi!x]cNX9+V%A$5lЗQꛍnZ‡2~{] ܎nbEE(' /F?+Ѭl:Ц *lŭa^6>9p% V!i\ d83w~z!c ’"֎~5?HCAuk[/y�Ih�)Ѓ)"|v�NAH_:;vt> wLӌ-w^!Q$λ {RDJEP+C*s Y$WS~mLE^[҃,e._,y49mʣ;,s+ /iDyi(+̫ћҟΠK!uG*% Lgs7=?< .R1A*Q(DۄGav܀PZPa9v }EיB&nr� {[cb a.B50i~'Xp"uAL5,'y/Dtvj$ԩpnq=if1m U1b)ۦ,irg͟$jT|Eޟ뉇KHL# )BMZK2W}XDk%"U#PҶYNdVV/ ~}ͯͅ31@MG_DmH+VM*-<R$Ĉ+ l~Z8D4TS"F-+>Y-xw{I` k3yd;-kYu'4JYCO?n`}yBwuMl- X;`�h3P>XG’L'' ňghZ*ҮUTrIoI1vjPѶcz)&a0/CvSYaVXu{½j܈ 1~mu9ʭ@ 6lr:}7Q{*3A 6Z &:f.1ޜ-7"S$EH,-<"ߎ_ER ZT"?ച$yX[ _]:W=Hx?Y}rn cQ#;$V*!<i?yEд A˚_ᡇR C>s{Xܩ{rZLr1wHN}ږa|=@DU;$ySRdç=|:lVeZ.hFK1Ҏ O4\<߷3{,YEygVN l u.q4n(%8V(ղDt^OC(;c‰m-}<v*نѯǦi^JorB!)E=U> 2p A/ܖ *Ѧf^R7"^2{8qyHرUEs>o5KIW4 "a88 5|6TYJc4+QtYjH5л'p{,p",E0r`d:L&ȃP,$_>.{*N0ANO_U}}ǂU(Ҫ5VBe'>:HU?.:B<22{L\Rm["oڷDFn@b O趗p&5K'^1XLO;5^R4Ο@mGBۗ SShX~?�*0thd߮ q&@p~XaUPq{*jv lX@>$h 6YԲ'QP }_IB2(fYHcROBԘsR#|#.O|z8v K'{Gȋخ]Ɔ֥[e18lW)O>(Hř'd' ԝ:th´FxV:Dդdk-.\Д!=W֢>څ8Raρezybmutե4=ɝ#INz<8*5mmb V=4VM& fx"ђJe*P# kH}#xIz!bƓl &Y{Ia q1[>g J,}^{C~nN'EmtS-n)aJxԬijMMX?[Q7j=> WY><b(  ps 4?k csGLMrȜ~^\ia~3%q(Qɬ#jG/~jyF r ԢE /U}PChF&َ`+ЋfpP Y̓$"4n^$"gjJ_73UI 7@UOsǰkjY#иh|!b+}J1V4b]m(:</C߼Jjd^0 Qy_ [`?c_pt9o_Lַz+r6%Dɀ dFJ@`]P aॡ=ՔS6i;oNTH!F&J�E;B(Ae&PIڃyG1,2 tthia٫m387=D2Y 7#5B Xnw#mq] cw Gᶺ I42% %J#p@.SP8�$)'zWF2qw"f!a'?'T&9py̩ 5- bQ`U r#\6%-RE/O| H b`^[U?񫀉 "O a6}u'xmSDd7|%|[0xaI$\sK.UQ!9[ Úćjb:N@r`IܸH\ӮaiIf@¼sTrPLo >m{7.Hs">asm}B]0){ P7rH t]8QeSX\m b^%LďnBgN+sۇD}HGǣ5e<鵩.0=PЦAamQ䟉 ݧՄH;,f/1 ʥ~a:HQ_*vT'OW0LƫK"Hh#~6` (, U8. mp9y<)&Cv|vt",s|&Ĉ1{nMamnMg1˹GZMVc‡zܱ?{D) {:%؋<[p j ]|J yY6wV@,!s jaXnjPZޘrvsS' O>ۣ: &ιĢ)s|c+=O&0UqH\cDؽs"*B[:.l E^.*-shW}F*9p7@-rI`(KkИOY/;܇>*C/uS ؕ#[ɔIdpB+,/d2}Y"OŰB%h *27ʼn_b{ ؐyO&UgaOՃ?UCFhNLqHۇ|d+qu-rL]2c8H"N6\$- $X|E3NaN<JH~h[So;QƵ@tutݬ𜈼V,*OZވEv5baPV ZV>YjB^ݺS Oɛ3yRus֓~,G7uۍ@x>D�oԗ/k%@^>1CHb|9 -˟_ͮ=ɓfW야.pEBt<"<_.4Swa ~TW㺾HR9J=r]UƃwN�X: JYƀ,"%Έnf7 h~saaP$dx33tafLG?r$Lΰ6N?UuVbȚE"(nXz>ijvg(֫E,5?RK*" [ZR€e;:z'0ܗh!}BW`4ȯjz&"vQ٦'O|Cz9=7F{hjөPr*Rm-/t.<tDE'4dW6]O'\v!%Y�ڔMq6 XA峋T`&QB[ө$ܔS9Iڢm0Mɐ eY=&04?]],˫~#Z)6=~Цd(i f-sB|@5:|M|7z&/lU.`Vn&�ƍCMARLOV [6Q 1ijݗ=9hI!Սˠ8Mls WAѵ@âM _o̡5\uz}H$7;qϝ[j Ŝf}.޼[ZH`@^ߒED%̛g%CM]ؙHK#cv :սɌ_=۷9W 󺄆\RH90$O`M&Ds OF?fY(�o3JPbb9pIue,tl~C4u{Miv dTOb0Tr\YL1; g iPW(ވn!QR6-XPcbM*&b4GS Y, 7(T]/UNjQl;럝?>ɠby "%g<*- {ؔx wGO{@njXPX1'E7o`8Ade7XEXGY[6h+^CcOwr}/Ruj8 lWD7U|:k 5DS+o |G5U4w7љ13\MUuϖἍp/V"L$CN9#T ʱM:/ (^854 EF{1Kt2jdކ=O ,n\ 7ϼ K^"oKW"밓]tRGENɵF\n[vWj_ݑ}xazs*@z e4Hѫ Yb !q0?E͜õD.Jy?GfWM/SګflsC拙L`G MRg:&HnO2ɋCChcެ|bhFCᐺ׃Cův,3y\ e{Aw\?u78.np(059߇_^5b;Yo; LG 8ʅ3un y <#aĨjpf',{klyJwŊ@+pvU Z2@ 6{öcFxVΖ$'_pOG!x.nXo'%¼Gs ^*aUP<իQZҞXWeմu)CÔŁPAܧTF^xq)$KȃÝɟC2K3k-z& b~oA 4'B˨LIF CНLJ; r]&\2W TuGyV\qǹAQŪ_<6P94 ~M NoT)"cL:>Z:;N=Ü("\N)"ɚyQ`Kը3~XL|M.,5#kvӋ ѐM/P32S|b<Bl>jN@ <W#*Qk*D^ t>n6}pWfBMJ+8 9lPZ76q*ow>;C8zIfqfpY6Ğ4%Q@ :O=fl>||WKǀ֑@CTp8JO$KɮX84BEqm[UВ5ЉV#hbi`Z_kŨȪDvMQ3 CCrͬW!AL<zzi@ -="N~Wō\7@8S$XgXkkwDӚ'|dGV`B&yS@R51ҝx9["%kv'Ӝ(OFX4R ya8` 9M$?,Ős4ɦo1;4y*Y-q̥9V}*0 CGGeea0I'X]P.\lp{N{U qO mpG.Swik+[5m ^mf$+>6\ =qu0- ;Je9Xo|AX^7ɞU~L2YE)u:FSADp8ݜsޛc?N%L`�I}z;oEnz!sor.=2L!6|hahl zʙ]OV߼(ic!1\W~'m*Chp)ZLlR;ԫ5ࡥ~ fSCc46Giw"'B1{W,@K7A.# bV{Vg vyȿ 'M *V,x靪:O UCoG&yhNSӻ#]XqlP)Mm2\$[0JI� :wY`>n#G2Qe0'fE[%4_J 2HRa왪adml@εbrJiQR9t1&;Nq,jCBd3Le|^M#VS| 4$"pV_jK2<Xݢ1^C"ꚙ&+2$w7SOM1PDN=g w(o2{}@롕l \g'j)Bd*%I𗾮O4۪ػIODk,,xYao* I7]WfLIq{P+QA$"\blL0?r׳+ujT?}hCt@dN'+F &ۥ fG :9zڤws, A;NO-IӒelG99\A#ƴH.ΰ$= ~a7,0T!b{umnHՎ_s 2 =̴Z!9h御1ru4n9O, PtvQ uK7Mn2U-V1R^(FWjqq]O2Zr~v<4=|CR{ieWd&e?# ,}ܯG*7\ʯI+\݉VA9bT1 Eּs!*Fc٢>@/:JÝĐa8uQRh/{(u`&61Pn$XVpuf>h12|E.8h4ּ;;* U%1~kX<s\bf?4ǡk )/ȴ fKzk9J-̩ޅ^Zms!C |.=]EMsrQ+*=˿ƍUQo-ALS3#AV_}p{2\30N%C( ˓dgusb./jZ+QbՃm')^7Z;$4g788H}8LUU7;R&#\4٥E3 :`!Sqb+wrV Svtb)QܝyvJ@UqCZPt09E+#ce*+lW@N=bq> :J"?R>twc ]IܚOK6{|kE"3, B̌""|*CJ]Qxv<Gh{S,b{Zv٩M9iI!ZM]Lon8q25K3CȖwdcM OR2ܥ<!~mEM n: 5#kU3H{HT6@!{xzLFGf!~N * Flʷۃ@i (JZY\UWcesv  8y~ٱ5մi5A_!>?38XaH؝TD!KM*`N;|ɸysnn.8U9}+^Lp4ig `fH͕_"Is5[&̶G�͓Ef]:uXŽts?%^c~bnbKI颮ܦX ڢEOPiM~F'rvϣ|G%2]fhVPy]7ȑ%%"` "ֈk@ ToeIM'G{"jѢYMQZ;ߛ{ۑ9=FmݸӨOURDn3cFoT4;xbJҦ޳?f~]p9gM՝ցN' {z<FXnY;R Rb!aD]-(1(IY!}<ѩ,W"9>0p@ƂK=Z X%W8f)Ib/pBz% +MaE�T2uRn2Eo^4\ȋHq.ـ ~\[=4I6'hȚT<*N?Օ-."yEOų(9/Yao+q1kGIQ.lM=&Rs4k͘THOs2Qf؟&hJV4~Dգwc8m Z a{-xf䞇܍t2zP3JHfi츩kӋo, Bgeu3A\ms-ihbg&HU/K٬:$l_AQ;IuӦ:ĝg}W;$)7`4”#&|e~dSb4b b" Q%/-.oy�mKXvItq9X@iFc.J1#7O*[+\PxzlF ~tdI"@uN2OvLD%-4<TtuH&H&=;]q3;%9)buܩӳ*~7$\.{=#ͅԬn}9X)ÎZlmw6=Ik%aTyo?=3ߝP=GUpye'MAՆQ6Fʇ8 4qKp#Erf?AAeʆl y΄dq!&,7>Lqx$tQKQW `pJX۫}btɟ7ƘQ%Vv:r�*mpiK*nIq3Ks9&g% !r_ o^Yc8y%4ĽCnrDT-o1h lSAV,#s8_8~eJ?5FZora0Ȉ;pX_]L dq ?gƆhX9\�Շ.eeOExƒ}g'1U{!o u}nfT: o7^߱a1l7;-nTϚn4սeShHɠY9gNA<_RsY"8d;LO96?"koAg=po:]F 1i܉1<%VTTݴ8osKU#h>g-Ȑb� Vp{%kWJ 'XU*lE^"%c\Sd[0y="[;y/br[ JNX;}JT);]")!󓎇EF'7-*|Rų銾i&=Ve.=5Bٵ@AuƬϗJylNaO~1^˶5Wl\'C%d dBӵ\}(qaɜ<Sk#,8i Qsw .:U4آ~%HebQz Bw,нEė)K #mB`f|uM|v4JO2(m#:3#H 2_uc5;J|4_vy1Y)ܨjЫ85=ҸtU٠7Rh~LvQ`o:x� fByZ7k-U/+q%fn2ht\U:qP܇pW9):tv߇ovdL W(4h] G; 2÷Z1ÐPJܮ' _wmHe99LgQU6m>?:o |z>-|@^m΋PZOiF<ss:37LCE}07`5{vȑ eAұxk:+CLvlM̦L8zq H2k}wgDl'sSY&e!An"k,^EMtWO&{ rd?4 ƅhaD |qY<QD$)ӧBppX!hzOL3!S|{8r!7y{&W #zzPhwF�MI0 M?"bKrCMev$G__H>0h^ݛ !bW$OƬSqZ"^WGO6@&]/~A7B*늧ٲܫV-eiKFDٌRG$D+!ӿ 'ۯ_-NY 7@:Pigѥe"]Z#y *Kr臨{#~`Ufw&y;s1U#R)rP2w62Ro˸fKbCN zs�cM:yMGl̺f/^p0wUN+94wǮ 0pD\jFʨJ=Nh1/r *4}2h(QJ r)֍g\:XO-qVJY՞TIagvI\]�Ps7~HGr2Y۴0ٵ}֪F鹆\.�!Y!́5eMӥ=`|K!GDK-\/GHR1HT#}L|i\uCwyV~[ -ނIrd]{z-yB"vM,B4šu;G"K`6PAքJ%|3@b2"Do8c}T!]#IklхwO~ĸ wR_:AcD%0SRMݭMaT[;}찥c*eĔA4iXknsWJŶ&An/l4eFY*mY ׊'ػ˭R`aYܥ. YA/܌AFLy~RZĉ{nd=͒<7"֐x(E-*8/Xͺ_ltt"NTJuk.^1m\#lrF`|>2<Ւjaꍏ,X`RL !xZ|7)F4G[R(|ǖ[C<uh`\+WkE~o)^9@fۯb9;w%.^:`<HaF(HKKb k+ŵu*N VUQ# x=aM,}s.R q? :"/L?i]$4#\[ ?Ī*S&V;+oB `mK'* +8epi虽›u|Js)]?Z EuOoH{!Lx<2nt<U׏&p-GX&DZMT9z؊1& nA~ ؀9ͳ_@J9s7k VK^f > _mӚqf[˞C@p2U-?qvVy;!=!X֝tcBr[I7+Js\ƫ!KZ !8i/J~- Hؔ�M'yI:P�|=yI�p,7V�m5oMw6}֤$P/)@aeeMsWtXb@prf<U.ZqT,iW流8a8p3vXG/cE`/SRw4SB<[[;${3sڬy=P6m6LЭʜ5xQf2Ԕ@N,T \SC~vK&ǿ ?(r =q3d4(,Ӧ/V^.L~IsHfsJf`Bn2c . CT]5xMrLZT~z=0T7pCZt b8x׭qJsy'F8I+ps</Ó'5ٍm^;K*Ox<2jݭDIo ǾƤڶad}]V=cP4@nWU#4e@ )L!4-ENV(qYr9Y2EL3fff`(ELN�3bf.,"S8`Ǿmq"#,_uZi"FխZzhuN]!94F©))�9t$gOIH7Eʩ˴6нxst}a(-K:^R-) X~PV0[q ~(]"p:*jJ<W,fS&1掘_=<4RyN$X{ߞk? >GH>Xt3:IQ5(5ңN߲ Cͩ<Pt'޼|ayy雊Ti~{^XjDKL' AR[&n 5+X>WȣC:.(_ٛlɀ/;DGhfBo+ץObJUA5Z03G/aRQ+Хyq}h3  Zq^_`@&v27G?f.+!y%8^H26hd)k-5xjf9&|0p}ym�Tךsp>1 bلxrn8\f40\ؿAjV떁-5XnK.2 cyaNXRlXy@_y |ηI-1Z##EqXxwH\oMON:s 0x'&0?m˂Qa59 }aT;iJtp}m=~>ٍdwsFA6VJ!5!q 8Ȩ3·�~R5 |V~C4/lH|Zdj6B(n2WfGdF^ڀ֎:Bi:$[!գD%˥}b^ v¨zUVb '" ~M4# Ф!l#V# endstream endobj 45 0 obj <</Type/FontDescriptor/FontName/NCLNVQ+NimbusRomNo9L-Regu/Flags 4/FontBBox[-168 -281 1000 924]/Ascent 678/CapHeight 651/Descent -216/ItalicAngle 0/StemV 85/XHeight 450/CharSet(/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/R/S/T/U/V/W/X/Y/Z/a/ampersand/asterisk/b/bracketleft/bracketright/c/colon/comma/d/e/eight/emdash/endash/exclam/f/fi/five/fl/four/g/h/hyphen/i/j/k/l/m/n/nine/numbersign/o/one/p/parenleft/parenright/percent/period/plus/q/quoteright/r/s/semicolon/seven/six/slash/t/three/two/u/v/w/x/y/z/zero)/FontFile 207 0 R>> endobj 208 0 obj <</Length1 1644/Length2 12541/Length3 0/Length 13397/Filter/FlateDecode>>stream xڭvcx$\el۩mc۶*vI:vұ펭mvlx7y3sU^k]EEQ^L΅ 5vuV疥W6p|Ě\vF.f<� 3S  GwtP)kP@>=v�/nf6fv.ώ*ff�K39 %%/WHٙ9�]m&�Y5 `4gO.!gfab89?� '#;v&6 OL (*<],\v~�{ϛ&KD]v�3�@go? �NfFN6fΟ4_ulmϭlfc�3Hٙe7uuQ$LlL]>CTfA[5_{wjqWy#׎|.#;.#h5L-Bvs3q1q �:=L.&�s#ϾcW35sڙ}OkLLZM /߫Eh%Kb9lrQ ofOC? =�^\�z.7 C,g�01011d? �37;H*.Fvӧ+0303[^7 JHsf vYZWe]nZ0۞4p7 eWEwR|u6N�FiQ^sLJůPmN0~n~dH>&)u1( h5' G#C]א={1F>I'_]:՛C>q:Wg6kT&{,Go3Y|2 .ȸ_G LJ׶/H.q Zg,qG"$RI 7Qթ%2CL%ՙPҔ#8j񾒋 E %n osN RWuH?|q$(eBs�\u,yKlª2$jw]rgPTub([$.T1�ٯS 20$;AJd`s}ؒOߐ挫t"t3[3"8vy 3�lX;n_GC<"fjФG@ -l4a�mڑ8:u"lb}e_'֠E^}r9DR;zeTbvͿv7z%bk:ԧ9MAe&Z[Tj$CNt(5JzyZo<f*ٛɒkDxvUJkJfVۢ{:<y:`0+ (4QDo 5TCFAMq wLsW| Zs+D.X <XlQBnbV-zhaΔ P>z*!AGeuo[EBovJsBQ ͦwKr1`r2in.Y_nHL>vgkU0Fw})Q�|7ҍ&1|ŠY"RS6vI&sj0=DyB%3'oZs̳iF#(5~3+&:tpp+i\HvZvTe#Nn`aAZ/"e3~Fh~?9]md7l[~yzyC)u=BG!XxOIwaud{L]sC2u1G6f.\6)1|!T&.q<$1D!ℇ56HK7*rjU;ZoĄ( <>` ^@,(gHaNsD]q'In_)Tɛ^>JJF<XSdWSȕ+Q^jU1f?pخmbHNߴXp@4MAQ^uX`S'.UeHkwH#&ˬdƶt&8bo\<r56+:Qd@()9Az~l?|C+7ue H=@[ +F܉K<Bnx6PcB(6 P"eNHfWS U9Y)ѯj1'P}.Ǯ(RL`dǍ.JNT,;?m3STsܵC.ӌ-H}4&K 45:wnf?:C.S7XӺZ"Pׅ\S7_*Fˬ@ f?:),w[ɺ<ŠVS1V"Zn'D`#t8B$3-V6cE+d_lrhs s05}*'RXZk9\>IY'ݗЕFg ]kǎ~K]#0IB:4Nw3ҍ :}/3g"J/1 *.j>Nᭅ ^-8kWrw G%v怱g#<ՊL=~4Mxś>` `(v\=kOQb=*ǵ(&RDfR.J!15Q Tjo3{fVd[աt`Axsn�>}Hg4h?hu1lZ 6~y"="(3u҄+O:ޅ/. (+mf+<JՄ-> ! @)}Je١YGi/7[n;-~)VvGIDtm{R"y7\>'s8>\gvͨ*0CU{| i2YHOu?k{6΁Pjb<* FBQfn_Px`H@+-xJfDeAa%=MCվckdb1�}'J\j!8@G6 ~:i54X!Hmωj]\9A*={q,r|==L#\ßKUHT l~0(|mNI\dmi1O 55S ]uO$AV8sQܭȷ[phF?41Jn}7˅PlPBtz0S^tw"TM=uĶ94*:];*ϵ*hi\s5"{9zl"@haޅLΥӫi07,Kl.}Hxڴ7Mw҈(K15-CKJŶg8-dc$Y?#w:AFlj*{ vj\Z s%;; D4,nsqy(0d=B?YxGƽx'z{e!ʢ{7Z{8AѦ֏QdJQ m|6!G0]pT }')ɯ`%J></A/8{rEr$dAbu\?´Ւ-u/cXV7`<TE{w|x1i)(?\W8aIsƜ|n <۳pߺC X;Ldϛ4s(:E׀ۋې礽4m)ڍFO?P7H#v<PJ;0CGz_)CX~ U@g}lRhlxSuv;Jrb)/|V_N)`^AߢL龧*&O]A!gژ5:}RBBUA/=oJ.g3ui8201gT!f~x3Vϩ) ի|]rz:(Pt/A /uQ;Z2}0b(4ZR'-&E\d/Iǁarl^yzt?[ʑvx]!%$%NGl|8v)R5* ume\<^TsF <g1[zS:}u$BS;DvVýy;T1E4O*E7iG[$,>]2�x"㢾= {H&<$[[ ;;5<-hP+Ѐe@.Z<|phIzPZDRzۇMzv̞s0 ,ئB:.hWEq74CMxHPDL|^BFak:Oϩk\V6;0痌Aù%3`Գ>k5jT= NVKz #+@o2ItO?l$ kq5kFHP'˖r~aG4L7taBӴR$"T+bg w]Y=;S)Gis7emgFKc†aS|^y H#`a"څ`RFMJ^ڹ^uAm99/>b;HƁ7jwaQ-eTU~ҞU!j.%fsu _N*j-@{,-|)LѩܵK mwu%J9/XC'1Qէ8R}su'lSdwv*Ş3j݇ ΋OuqaapsAD�3\\+L Cf&(8Z `+D2s{ݨ>Z]t|E%Lԙܽk<xlIQz,0]/EN<1 J\r]4\>}V~yPPPJ 8"FVRV] n(PR!gba^E&8l\l dTYsVǨ""2[v,Xx+sK`c%9>gw?Q xoM-8I k4Ez -3k|G\Fsܯɏ'o -67ND/~CwReXQFG腮bK%ݮAr\.( O5'72(hB+rI]6ӝTK야K_qjH[?*'y-ȩM8*'lרߛYdbѯ1- OadDˠ+m  mPg+QمSZ҃7"ݝsY=s{=Nb5N|y;J+R<X.* E6 D;d\<Y^b*GP.ڬ�]`>O kqkIm ߸̴H{-c A|:%T.؊^GR$Re>xgXG1h-`cu'/f)Ša("3%BZ ^VmTSW|EܜhmG;hkNI`y3 ^Ȉ ׻VQqdWiìi (z!]*- / by/`{z_eWa9tɔ|ݰS/M) Lmte YKL ARF٠ ]$JjW ศg {6~Rc<u�3cT^O^L޵ NXe. &tvO=HOFC9ߟU("*3#jxĨM^-JlmKEY xUX=Q=.q;W;݀ThPb ET4}#V:~)$կf~HINL]fS:!UU+ήGlZV#ˢ""f",nE/? r W4;{5QlL;uVwEm'uN|#Ÿ́x蝰ԕ QR,cw0щW#": >DwqL#[Z!u33hPɶ]U6A?s;T08m'oʤq"cEσ]sat"E #{kjб(� È+4F.Vx%=XST5 Ipc3Gyk0VR.9PNQ/fdbiy+mLk�bq@rb:P KDkIk/,73ne˲FU').=մ{pre EDKtv#(9W %&n- C4vH"X|R.cp||gL8Jܾ{DJt\)j4iHƌ76?:'jc&ƴۜgvo_ qNPDR("6ᐧQÂ%ZXU 1ʛSk=�w9WD,G_c!6,ӓd((t~a}7 )'eQ5W|t/"˷*0;Sb MB7rYM\"Lqn,4+5\&_pMӞv!H1n-m1fޤ,an8c"R1߯2b Sڦ5衝*meBqHP">I_*,Q?~Qotl&(L:39lLB/Ni{|BJ9p I/?h]^zN>mȿ թ'&ZZn<` AP/$3V<Ѩ$2_x91!8ٟI%UYKKMdh*j[2j~> VZVUR<\S[_Ƃ+h0A2O(� |w*@,>IyIxτͶbw=&cY Qϫ'` U ځN[c;C qô GV_I:ھB{X"ȑ {D%Gwˎdc2ܧOUʹCkӋ""U̎@'QX @5n ŧc8L ~|'/}IgiR3Ih&3Mp8.Em7d2~6+73>yEI;{R #Á|?9u5YZuI3Ev[;I2?*#JثI?vCU0;s<.V(Ǻ:\*(oQlQQ(y^t?/w 9`trfOLN#m3ј}�&n^.) UG4z(PCb:me]fVx;W(@үn1yTdl!S`9�+TyI).-FFfa+xhjdĊEXYKL,qLr9 dJʧfݷxBLIy=ՓK#p(tI<$;-eTpwCUт6N{lP\MF*CuveBuOp2I Sa6&brn8` 휟5nSD] 0C[_j)_T&E(+>Q6b&[XoӪ\t}#Mʔ+…ύef\B cFmpF6n,ȵ<wAU!C5/'럽vV_DrCJ`VJ7ڨ 9emx8/+SfR"!<ŪY#-) L$J+& ZjՌ2BLk*h p%lEEZXX%Y1DVs3 + eW'=da^XqY l.Vfl8 C&hh1"󈄋#Ʒy(W y01MǙ�JӢ {SKMml,xnnz2!}o5qhnhu" yQ׼oaKvP.ymdcqQ0YUOS<آm p./8$y#Wf`CbET¼.wp`=uC}}#M!c5ySb5)qO$px7 |H=3xaf?ovK 2T:#Z]~$X=MʍEz=\2>u>ZV[T6%R!J:W@ZjnDv#ŚH(980|\|"/<?)$Onddk1'  $YamVy9N;p네DLvxO/-}9n{e%ŃFeJl)z ۤ3`t– Qts 6$v]dU^&QIqs!@?r(q@A[]KŸqd7e{!1;u,GO0qzㅉK<sf3f]#73&2'XȘ�Jߪ{̩O@\؅?++pzP@u ͨIpwu ZZ3`U^6:($HBB _q@ ~XY4t}/Mץ8ij4AI5 N5GSM-M]C୭L.1|v{ ATP8[lR$foYL#zamv!x%U3@1wκ 9ojXUe?i>:lH__$]gX ^C섽h_Oh]v]&3!=}w@zQ5pdz4xdV6,/ي5ykڍ gg�-Sq|2bl |# յ$Z!†+qLiqOJ*<D8E(Og*@߳w_7+ e]+)VFs,^E 73 9 V1G2IyVv8#V~ج"QhD8j̽aDqi)No0,rsq.Gs@] DCtEhYDЌTYW@}f=nC"VZ0d&-B$[LLpuXInNMHlC<딿F"ulS?'1űAҺf&=RS8eddSt,95^=g^_}n"&Yq&Tm[ , _y>6}j F $1H4BRm)(7lisEHh=�a'.c6�hon ;ℋƥ:ʔZi;/I9wo@qm,80�]ƗGlΣ%">)&<Ғ'd[�z`gdD*cx\�n0,e/%sTkrO~GgOL"btG%کz;L15$E4RQS>ow\e]t603ɧU,\&ϷA+Q d[2,A2~&,3(Pdp~+ݛ_F.vlbB-KءvT|BTI[71چ 42f QxD oaBV^WFZ0ZBQO;yZn >" 'pxNnӴ+nF>|0m)TBxAw<P[*}_^A@4Z]6 v,t@qPzmxTr2H *P1[\)-ܘ23 3cSJ[0^y&>iUԴLj VS[*plP]15usC]UDP}�$OկfE։D$+k8ֹw쟋۬M3Ң<JÜT3 8ҬȆ-g l6Wa}=ٵ2 _eLgʙ&*@H T?% ;Q&0!9R>{x=#x\ާ=FC4 g7_~p^JeAhPmަb| |`M+'M5WuIH~g UE*:%zn`DbeNZnF  [mv?r5LZZ;tE]x˹YͲ_~>d3׼eғ\oUv^ v>KѐFۨckGIgNG=�s+p$?=^�v:j"/Vg†XD𧋦;fdIX:Ƒ_jɪOȨ! u9qc$` G!Ik! TDI TF6ATkaQ0Qcj9"V41-,44Q,R?jlY'|0�1z%_-a@Vl{ `Nq#ȉhzdKBk`WVׅOt VƌDmFo :k">^_zR*5dY/L=7+<'DNI,NԒcoSӨ]o (DmN1yfL]hfąkN<DleE#D ;[%/.lt;-ef1`@oc{IJWbO74)~Z_E[=B; n8]!U݆5?<I@ 7߲J:Q9ς4 W`4hC}4!% ` GJY_zYr%prgXRˠ@!&V4Kk^/O7қN1N@`Z%&t-]rLJg,I2B3Y>rGm'YSՂJ&<toۿ;/1<CVof4UN?A/XM�`KG?: &b#=+|lH ɴW.7n!ɷJ(òVLD5Rb#9~_8aHz^Ty1mb3H/!~nʜA[tP5w.%" ) >w:sZjDd3Z~!NxuȷPɆ�-Oe?fox+]v̴r ,HɧB7,RRd|t¸e=QƝ. }*vΗ j [!brU/PB2Wf"`;VY3MѦ#muPF-Q.KSSR4gԢ/j$$ؒ &XxWVֺ5|<rh-VVUOxp# ' }Ofk@"(Ȋf2 N&;\=:m`'"~ c)S+kdm8(,MjleVCq_-3fc~Bis[_%0>b~LrY(t=c jɌ<SÄ*øhr׏FENoS -pv+K0~Rk"egQKTc,jcӎCX\zsSJks><f jgD@LAK+Ρ_|h5MY-e`ǃztA (ECv8Nuwldak W4!k T+31_ٰ"t_Kz�A4HxT;Kݱ#MG q[M&XE~/8d5>)`]o7w#- 3aT%](VYV~^z9WTfi/7WثVt7nJDLQ qI_PaR\NC^ircTql݈ۤwK\A%h>uV?V{hW\i܂t82\a\݅n`Ź=q׉{?(\^lqt3Z&ZLB^"6b{ݧE[uL d] zUꚌmEkc}X;cu*#DiFJҼD=ks\^F{5cC*QM]G3}CνG#PDSe.|?DJi*& PUVrn1Tt$J왛d )xjjzG](lIt~z.kzY6 ,",g_E̮,| 0Md ,銹(vmfe0Ċ=*mbjhԅBI*#gˤnEP+/n i fV# * Yp ^Q]pi-YHnG`?HBe;/\AMΜ^ff!+pNo֔Fbt|z*2 0nrlL�JdQ \E \_Yc= =`dY{5T94Ncpc/<R .($78VV!ۿVHD<o&^M IfcVgX/�6G*[ih>#"T`,} Og׹oUCQWbia 1 [ܶ+%J)HY!7Av.V).Yg-6` ꋹYWCh<h$QR}MBHMI۝FEgf[Bψ#djٯ",P61^[BsD2Hpfɑ3? Ɦєa7?}fDQL!w(191PE 6$㍜[']?-+{θ nkL=S.dQHƅįai)Ts endstream endobj 184 0 obj <</Type/FontDescriptor/FontName/MZCEPE+NimbusRomNo9L-Regu-Slant_167/Flags 4/FontBBox[-168 -281 1000 924]/Ascent 678/CapHeight 651/Descent -216/ItalicAngle -9/StemV 85/XHeight 450/CharSet(/A/D/E/F/U/a/b/c/colon/d/e/eight/f/fi/five/four/g/h/hyphen/i/k/l/m/n/nine/numbersign/o/one/p/period/r/s/six/slash/t/three/two/u/v/w/x/y/z/zero)/FontFile 208 0 R>> endobj 209 0 obj <</Length1 1647/Length2 17228/Length3 0/Length 18093/Filter/FlateDecode>>stream xڬctem&Tl[;VŶmٱmJ*mFvŶt=ۧO}{{⚸=XĊ*tB@q{;:&zFn=,2UWKN.4r5rr4�Q Kwt4pP)kP~O?&�ct4P}p;\B_;�� QPԒ�PIȫ$v@E(X�d-Mv@j?9r&݀&@T@'[Kg�Kg,Ll\MI_ 98 hldUQTyX`o Wbdi pz0tv1/pu3 FN6@g0 ߪ7rpW.@3zX&1M\6eg^L: Q3302`]P߱LG-FBEcdk�ciq5?9Wk ࿳UBvcbgYhhbb03ۼLN6v$N6ڙ TԔdi_ƊE4M(aa{7;e{Y} ?rF.N�FzFF&Iٙ؛3G*.FvG Q:9e_oq%�=&K&<!Vi.X9:L`á% =\5Sܟm46=<|_R|Mڃ 3W [;JS,NWOnd~&)0:APk N()Fzn ha%'x:7|Bq¹#x}ڑڋyͽ0PuO 87Z42z] [C9|kIcLnyJwi<4$#3PKyjiZQ)d< *5e-L D5ىQCmaKȍNB؄z弞<; +W1֭3WXzRFFCaxjaj59Ӆ]>Z2!F-I\#&xy-V;s/[_Nu*o\р0<-[oJ#kC pNmɼ(n(zT?N 6$˯%~n|Z?cD)^ GPc,>0˔.e:;)dB xL^v!m9CŦc>JHՒ)^h{ezq�ϛ"TO9w.o TQTV޵4Ɂ}YSSErQm~76ѧyDU"=DMrdq= \?ru;C`j-o,52*t*<YL:ok&D׍B''y0Ls<-Up̈j8 1a(#w*=SL!le8j֒=*SqPBo]vZ1^M3L^*0/o}B[dɨk۴t gP?pOk\qT_$@U (x{fqӡc 8=q,lRm)NLd"=R;i4r(`O%ܶU~a3QsHNV~A0$#ąy4ug'rC/cZrːJp%́9ZI@xfn]$ �K =NOgMu:`d6$Lf<jl;5@6{\9z2``s1;VDf&@1~C {|_y7 UXws.lw]Dl[O`Hfp[_IȠ4&̩=3}pTuԮ+?*PϞm&HLvGqTj̀ޞ ?-cXέx]F_mCqfr RfzYo\i;kPw{DSI'olC!b} kb&fo &N^$IrEԕh{,wݾJ!g Unfgm9|,eE#k1 MfZڸeٵ],�(~m04xCo Q5 &)ű뷨(Ff(=t Jt!i_/Wjܜf˹il? V{f L~@u 5Tm[4J'9Vi7},Srb r˔wtQԢ5}u?\ E&FiK۵�"c2:=z4Rw:A oeMM3@_(w5CF)_ΰf4!{5l}ΦA 42K!q踆ƪijIt;kc!vG"e&6/&~>FUYgK]Ąvkl@UJyD^ D3u`0]pjBgOˆokIctD8sA'0<b%>':'x N!ZmoC$J'Be,RwUIN[ظXI>vz[v)=IQ-`�୭?q?]$tVr'MT9G20ZĸL*Bi3Cmi:/>G/gBz3; -dW>WFM)7 `oLE)ۛ>ԋ.Q(]} k}ˡ8U&8mR):ؓLGgotKݎ@%&wr"byXZb@܅s1Y:zeYДɨ iQ޶'_]#յ,ݚؿ ("aB%>LizMqV٬y˳X$J0olV_؄rRU[cً[<tW$0@]7KJ5vv-!~!S_DXdb*D݊ ~%_J*0<lM54kϭgJd:*�븶oL]:2r@PItf<_y ͨXpaƁ[jsw;{�[ɳgj*-=K[ %T`x\K9Ny&iΩy-\\UV{`[>΄}V̔ xZj'E@LniL&tYWf'3 W%Nw-}bT�G~ek )ĈШݏfr:YqziH%G#x#A D$׺n_f6 'Q[i=M` 5ߣrĕ4qu~H8K { R(/F0c0]yV AF Uqsfc`=Zme+CI[bH-"^*sW0sFRP+%'Ly\"2_d\pt ̴u7+v*2h=G`�;kQ +S<[|7~殕Ãno(:XM 5WBS6}M[H_Š hQ`Yo ./ Tљ, f"D''ea9訄z̻p�~&J^[ya:c2m><7J`|p*ӻJQ\1Dzߎp_ X4)7KžB?쎯c)~!E;*[\R )z,#W`i+8@j'SBz2G)9= Xospa4& ?/P~Oy&j;il#2w"c}-bvb<>qi>4KClv̎Z)5g ۡ3:ҳΜ3%aqj ]t’L[rbNOw:3aN嚮"K' k,CKR jm EKݒ 6:݀' o *sK"_hEI^mH4#Bbi!}K c] xUA^ nX(c)}E n؈&_>@Ou$f6j&mֺǗ7-5`',&T$FU~n} 7~5db6g n5\&@,VWIBZ_Ng^?'6" yjO! bt׵"UtϞ/LO$l#]"+SY^ѐE1K}]c'᳭ !TQI47&jVɸp�I.ٴ 4U2\ƿ>:9WqSu uّbͥ^{4*2LO-r wT{$K3<7\jݫu./J F-!33m5t:gy<r>6Tn (XF<Iefٹ/qTod<:Hf3(Q 3e`\g3F ym[,.XN䁯gU؅QkaaΝșjrV2]LFu%Ə8TMkIƮ)࿀T�k$-['}%#. \-K#hvqJXvttOd;Dbl<?:.auSQ).;P#l/k$e~`tx>*eg^#_IvO]N,UXOywj\L2[md&j,bm�!4ǯڦɏWChUxY0G.ָK*Qg!E5iI)YD8D2/7 $t!Cd`ru=kW{� -W?ln3:WMoScs%̅ppfA8͛OwRQ:DB3~х;H8V6, V趮RMw]bo"U>g儑c`o q>~Y2 )7~v ˚[f΋yU9mlH/z> X]BMeCW+f}<S"xmE[W@Jp<s;7Y,RJU[ [G#kdk%W죌 z6UF0Z̈́%"+[Gtϓg "K-k*GWY } ? lG4B۽={Z.!jmG®;>c|T vJVo�jmjU̮?cf:="15 0Ts $Ov3U.ܝw ~s)#(03,: #6{m'>##� 7a>ޥWNO* 2 32~؂iPxY/b"R6Kԩq3x^ v9a"rW̠ S>o+Q`4^`~?S.@+Yz{ylßx;0'uv7KnLao"kD] & kj!|1-)1,EZLnpg+.XGU6bq\tv-W9bh+N7 ' zxtkCt:ʚb PETb$hxΚAFlq+y ~(�a25+I]?5h,4myG^A=1j\(ܷQEmExcEn{</Su_C'fRRZZRMKq͛[#4gh` ,`Ǖ]=PY^ ,u  @($3'IXWW`ݺN с)E'y�o1voYbHHxZbDx^{(+I +/2u;@m ca<6zf q"&i8|fk%hu)<D5 2z1"|,ޭIEc6C6"V~'4b Y[ Loi(> fu\el@j08H,-uHgmA[Πֆ7?g` 3J;*{#Mz,Xuʟ10a/}ev!3;f9T[G!v�' UʏLԉVjRĝs -8 sn>\ft'1׼25}Xe/Ӏfj#6=O0R�h$9*D{zK"tae%=p'*=r8mJIhQA aoWWerp�܍mK@^/]=,"+i M)e'[}‘?}&AH.\ӟQl{BԿN/eI~^ f*fް}l/4ag~ /rhXev.vu㔃z;'ciQ*Z7ѿ pK%>}r'EMv\ ۰%IHy C<:9t*q'R S 3-#,fi0h1zo2+IR@ӭmuA2g Yp1U Čg tAILP7J bk+<A71t1d#&L#ҭ9:u3xf yAvT`92iP׵r@/ɠvk'6 cSJP~Sw~at;DtUbj i:{Ǩql1 ֈo ^R~x {kt],`)]f8m|UMB~A>ĸ>״}.zӕT6"eVe;GUǙtu)OI7^w9)T\_8ǎߐH RB#dSiF}u뜌qϹRWT` q#HQRͮl$Ѿ&+,M8`?7uķ,h.LFx2lE؃aѻ:QXWWCךt{)С~ EV!79Q[hF__zhH),sY{TWST)t Y2%'\-eKcejq."9\|)q@cγi1u�M??'wƧs072.�Fy7s ͡9f뼷n<jͅ/<f`h 9,n+?rBL۩\!I{Gu# J2ꢈOs8eqKu'2|�w=AAv/["@X0ß"^sth3c:jkLr4�|b XRwkW*^A8f)'k&f9shoHxVFV,ҁ/gƊv(¼17%υo'[?}XͲ@+nzN<g ҆=6an 4^ a]7;Jt0r^Qi[tupR=HKށћA5"X>Fdu]pdN^-BVv`\Ыly^lg k!95~ '/y5.7)]ʇttXYH덎F.#$SZ@x,DhjHcӂs?|&(N|B^ʇ[Z&sfa|a fTtcYq*.%c;Ԍʯu )smy.N:�‹Z9^ w^\RY ]'^#/a4]ޚl=%,Ax g5ey\y}^|E esPGFW.NمC:<tˑ!Ԙ"wkUuv`ŗfmV M #e7[0ҫɂ^lHɴVAbg/ybz;8�tc&eɫL?arəJ/?Y^rxlvIXd|Of^o3C1Ld`T _x$C V'"9dRإۛA@8PJ~{g>Ul9:`˜˜,..34p _l_%04#߷X"aοAbݺC](HF/ M8=Z$R!P%y|ۋzkn'uM@qd-SOd)$M:VASH}>ɫ]ɏ2`WGQu~4#$YjMpVۮDGΏ1u5JդMUJG6x09%6!S(Uj[ 7^Z3+Ơ‚-Ax#aek0&?jv h~oQڰ)XY5rV\v=nfM[,=C:n$Zu%E4Hhh$(T#ӽ|pnHؑg^{0%NzW Y^}̨p^<o xUUVz]hKsڷ}ic@jDy䥤|F>]½~k6g3X/>7We[N(ٌgjԝ'?4TAWjGK+}m i{振nvS7Ꙑ9Y˚]D^30uבԼik7Nꥲ?h)PNxUWߕJPԄ\Wp<*["]A˼) pkx$(m18`1%iy[uyy_;SS{HU"LIRGU0 Z($lN|#5fhc:h6Y{(٘t9K7AMX%:iW7i4 RFf)x)e#:ޛ-,$ǪyY1uCyJbm$'[LrqӼ+(Pjɍ>p?cwl!<Q ތGI.fv2"eZDḫ�Bi,bM>>*~%sTK>Ӻ/*=tb녿uPƂzO Åew dS~0WPȤye%7fZ0w9C!aKFui7E[xn2ԑ\;u7Pbݑf g5�ZKSȢ@;<Np}:|JɥHCSUR~ Zh|$%Vf"iz-DIZ[9"Y͔pYdn34J9\?GŹZA]^iG?d3aBKVi9 Hh[gbĎdioyƻ^* )'5%XiaB/1Lymh^f%WY9WM~]j»+,y udl[X<.8 pR!&q{l[<DZ_ndtb̟/JDp&GݨmYu@F66C33HϼIVA澘ޝ?A(zNth<Y&D鎸%OUb9Ĥ^)=H,lR:C-^OkL"s8ط�E&`"Ǯ9%b9î+08:F^N7dtO9[I]Q46pv;Xu5ٱw?e{w%wY+~-Kǫ03A K"8Uz"TН{σ"ׇeO\TA>J TODlm7p* ֣1Qt>'PyhK20lL]b|/ErjNvr%׃SoI'!Ô6+ b;$b"6mrCl< !&Rhm@01aHoxTl| 7b:_m_l<!7@݌q#m:Aapج2W~R`'55z?Nձx>~+O;TJ/{4�U x| ۦƌ t.Ҷ731UdQm> >~~6O0N?oIFHQT\ix| *}l5V`nQO\/gO:pЛ[3+!EnU>9`n:fc2cc~XqkO:uƟZ(gs|Ô+I^I"%2r';" W" #:-]v�pо!tnT?ԥ9-W,GI_|#Һ&-4_4M{)AF]FroǤûA,!^$f6B;'-+"njޒp܏x0TQq(xi(NZ|'fJZvƫ?]{ϸ"|LQf1Ii&<`j*88}zcEd 0I&aͪ(_v 1$KЇXzP:Qhq}<vr Z)ݫ:W`:A i1|3Vځ*Y:v0f62Q+G TT^_Op{򂢶yK4 KgZBغXN‰dKj;x+ ~7S"6#//v=&~be?&2ȺL2\<.tx h Q0U1mŜy݄D.5f:+P n'PawKM6@Z B醪ysYV&"OpmeW.}zp ,.k&(+Nóp6(catI cz̡-5:bfӛwJLh˹+̶~4Ko[v| F.JMM<*pT2<xræM31*Sar3ft^DD†OMcn05f|p`6%g홖Iv`tY J )VRfjز0@9q 0g>~gQ5DBj Ͻu)hv o6Zٰs^SnnHXލU<ưe_{D<͎}H%bhAXK+f};ۍS%ֻk cjp(ki}{~81~3I;w3B(XDĽV8M4>$&hRJ2B$&f&kbg<h('t. >pF]&6۽wNxؼ^+\3V$ݥ]G&-[ZؓC.R*m̂,Dqrn`*Íļ1UY:c+Sn7oa|BMw#no\RW]ĩc7ڴ䦊?&P;Qh\Z#3{SG);źI֧&3OA"ZNZEr!Q8wG;kD@A("#J0ۿ?R;kIdV?y9XLh9{EnȨn3ۥ4mU1Nw*%9�Ǧ@OU/>i;˂1'V{UÔERVI<C2Ιm!$F xݬYN3[xJ캍s<08bb6D*~`ªI@֐>?ŗt~�⩒84xׁtǡ4;e8=Ĕ|;R",c\,Qg8[D+r*OU /GBc/To W?SKzrBxsjDMҞ"rgR>TV7N25-԰O ǥߑK0fg0ihjҪoϡVULd-b1 9|"|')$nd3]fM 0ۘ$ >u͍%!`)4뱎3>le.Y zw{e %4g]3$ /W pARyT9l\m=ced`5Q~臾/ dtTM 'eFSBx11LI]<#/4lNs#lNRbh oQG!HUeyEt`EO$pOQ v�`e ߯3cwrg ln]h?&lBk[~o ȕo8WKecF}k~+LeTgFR!FZjoN awy?;h'rCc-F_hny8%2�=7LgnR{DT_F;s9MIϠN: bsbƢ lы:8V0[0Vc+&˛b! ؑ7qLpZ\٫>c 4:@M3nYsÙ=®J⊼GD.CsؔRBR";YF K_J�~I~,V bY5k}};__0i7t u'A1whC;֥a` EF>ݟhE"}{gLpG[x:K%q.T⍔�˽~H%IhLvx.Po>wߝB<F P#FE 9 ;6b4͟!3dpLct (S#l'9o,˖8H9!NC!>$@{hOpH;nf-Pc6 @QȂv@,~Zb bxȸ!,$,gFj2slFJ~?kN6r{T!8C"4@?)|OՃkxK*tzgit:6Gb&X0'>KOyM|^#rz˰=Gs3uU%F%kQ$^X,BYgNN˧t~QUz\Ta"jAE>TЉ,U0z ; L%nضk 3 d�IIh=u0.~FuP O1^FhQ:^)U RT(;~d^ b2A9㎅xʤn\Z4t$4M׳FUh.~^XqMiðǥ<sauyf'w�"ucX0fnnnϱP斮`M?":c۩%dsQj颶02 .=m3OЀЎG`MH}/8JIe,L-Ҭa}CaMg<~3 $i!ylvB{Va0!~%ao9,&Q}_G)e%P{T@T33OErH5:grc룊MsrCrԅ6ex|%0**a) ~DJTrAxO\T"=Pۨ5Ԉqº,1&nOp6yC{l<7~I]]d 5L<cf;᭜jBM8-<l)d*ȕ;mk"Md /TLA$IwGWhR睂hx(K.F1{fsGy혌>nʭɥ_� ‡;=\{rlM5G|$ki֫<DJ1#)W}GFГ-ޛ �ژSGXvS֧"5&J WuCFHXTW\ @;4t= 2 9(j݊%�svR R8qmXj, Mr$Crt?BDMMtBxrǀ`2|LDvq-(܊[ kH{4&ڃ&=;SIbƃ%2qPh6qp{)Ρ kYA/J]{tzQk! Nr$W蚦5tɘJ\ ipDVg'd#[|=\؍ô 1bTZ_["3'T<kw69 YmO,s v8L}̊@EٍDVՃQ8 ]zΎT,N$Lsu4\ EJU﷗Nn`A?b򰮗)nu!}pR/dܘjmN21uӍ_S!P{"/ҝ>�LAF<&1)j%#:Wa>YnS�\tNafJA ,]:?6 hKpêdJ)^(TUF.s7I &ogm4LhXF:aͦ.8/nGRH|.P%}@ٽ -ޥd&\ U1|ٝ~8+eƗK ?y˿SЏFp VĪyR5i8J%F[|ͺkl$*+g$l j'lwιK?pA D9c7QRR?4u6AV|]I<]`-PAdAvmj4 9 7vipzC/DkE`}�\2T1s\bBd|k]ªo2eaϻX,|$ϑ5H!#%l$XTCpQ.>'͈NZeΚTT~k8L▗ 愺V_c<Rkeg-!e)%^K};tD$k~ri0cB* e=yމc+Asș9Zqil#lMH�޸_ (G97xPK}q *WQz1BKL$GW[Jm/էezF`K2#$+X%INHL9&h(jx7u*a(\vsjY{]Z .=e `.ZU/{ב6(-<Fx!خN,fb$KOxyB(3B2َrףr! :V;ў n41W݋&m[7I{,O ˉ[%zΥ\ KQ2b0~C1 bvI"w^�&IꃊX?F(g++|>GE2rp`AR欞KxArAHԮ9 |#JtY z&C(?-R~@cSk3>wUen($$C7d߳0>/b]I ezS[k0voU8 @1aڙ\ya#\LKWRoӗӚ({`%�d &Yԩ\GHy*xG~ +R3Qh_iN5<2tG�^-9Dg=dlj ²zXGNԺ'#Y/xHDB;A Lݫ ytƇЊ46^ m/r*G\ nGU|)j@M3 vbg}|0fԊni"fde&M%C $UfFE'j7'ϊۈ;c>_pD$k 0+(׹֌E 6J}AWװ;5Ukrc糥0Wb{W]z$.Tsܸ)Hӝ=EEc/ iM[}ᾒotzGL]kUkW̞SW),@L!_Dȓ3IJl|np!~Wao6=&oXM`8]n-?Xy O70($ 16W@Xݦm`mcA1"5iu/D i-ǫl6vPP;~E Kvɽ?1"\]a.dt~ e]V Ge*D=ԗۇ,ٍj]=HagIƭNȋ0#.NPɴɌK ֽ1n;,8*OޯٵDRwpc#[p]XOrqtq^a#BSԋu%hˡ9jlUB(\RqttY'EafcĺN5D8NG;0ƀKyWoGFvi7,|2@K('Q:k2oJ,%1, O lՋ:nS#lW9ћT oN .%m+R)~l^@'5X"پ•#C oHl.'^d <.4|(`j~I!cA/ STˠP1'Ó,I,}T߫s!qr CcCHu [ ʥ..6W95.LC7es[u ~TU-D-W ฻:ۑ݀y-!)_ $$NًkV`g(^嬨Ҽ7 -|]-N^Q֙,u^�ߑ7M9T8zڲ/`Qbjw@ xVH"|c:^GwF�z?ؖaOnrchHUaڰ{` qoiZ�-?Y BvqG{ 6"w�<p(NuF^ƅRAs] 9B>ɔ{6P+uzT:)obI'TLj)d&1]n8Baֲ endstream endobj 49 0 obj <</Type/FontDescriptor/FontName/SUQKZJ+NimbusRomNo9L-ReguItal/Flags 4/FontBBox[-169 -270 1010 924]/Ascent 668/CapHeight 668/Descent -193/ItalicAngle -15/StemV 78/XHeight 441/CharSet(/A/B/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/Y/a/asterisk/b/c/colon/comma/d/e/eight/emdash/endash/exclam/f/fi/five/four/g/h/hyphen/i/k/l/m/n/nine/numbersign/o/one/p/parenleft/parenright/percent/period/plus/q/quoteright/r/s/semicolon/seven/six/slash/t/three/two/u/v/w/x/y/z/zero)/FontFile 209 0 R>> endobj 53 0 obj <</Type/Pages/Count 14/Kids[41 0 R 121 0 R 191 0 R]>> endobj 210 0 obj <</Type/Catalog/Pages 53 0 R>> endobj xref 0 211 0000000000 65535 f 0000035071 00000 n 0000000015 00000 n 0000034892 00000 n 0000035460 00000 n 0000035774 00000 n 0000035850 00000 n 0000036069 00000 n 0000036145 00000 n 0000036365 00000 n 0000036441 00000 n 0000036660 00000 n 0000036737 00000 n 0000036956 00000 n 0000037033 00000 n 0000037252 00000 n 0000037329 00000 n 0000037548 00000 n 0000037625 00000 n 0000037844 00000 n 0000037921 00000 n 0000038140 00000 n 0000038217 00000 n 0000038436 00000 n 0000038513 00000 n 0000038733 00000 n 0000038810 00000 n 0000039030 00000 n 0000039107 00000 n 0000039327 00000 n 0000039404 00000 n 0000039623 00000 n 0000039700 00000 n 0000039920 00000 n 0000044681 00000 n 0000044838 00000 n 0000044995 00000 n 0000045122 00000 n 0000045283 00000 n 0000040024 00000 n 0000040229 00000 n 0000045410 00000 n 0000390684 00000 n 0000288194 00000 n 0000284370 00000 n 0000420072 00000 n 0000287619 00000 n 0000337123 00000 n 0000287589 00000 n 0000452679 00000 n 0000287016 00000 n 0000344394 00000 n 0000286992 00000 n 0000453173 00000 n 0000047008 00000 n 0000062066 00000 n 0000075515 00000 n 0000081296 00000 n 0000086895 00000 n 0000045512 00000 n 0000052849 00000 n 0000053106 00000 n 0000053148 00000 n 0000053537 00000 n 0000046892 00000 n 0000052694 00000 n 0000047225 00000 n 0000364784 00000 n 0000286646 00000 n 0000053786 00000 n 0000054070 00000 n 0000054156 00000 n 0000054392 00000 n 0000057889 00000 n 0000058909 00000 n 0000067543 00000 n 0000067802 00000 n 0000067844 00000 n 0000068249 00000 n 0000068522 00000 n 0000061950 00000 n 0000062283 00000 n 0000068787 00000 n 0000069093 00000 n 0000069338 00000 n 0000069554 00000 n 0000073247 00000 n 0000074936 00000 n 0000075401 00000 n 0000080903 00000 n 0000081031 00000 n 0000075730 00000 n 0000322077 00000 n 0000286572 00000 n 0000299097 00000 n 0000286322 00000 n 0000081161 00000 n 0000086518 00000 n 0000086648 00000 n 0000081530 00000 n 0000306496 00000 n 0000286297 00000 n 0000329765 00000 n 0000286226 00000 n 0000086777 00000 n 0000092377 00000 n 0000092539 00000 n 0000087114 00000 n 0000373404 00000 n 0000285907 00000 n 0000399719 00000 n 0000285604 00000 n 0000092704 00000 n 0000107485 00000 n 0000107708 00000 n 0000107751 00000 n 0000101976 00000 n 0000107425 00000 n 0000102170 00000 n 0000102323 00000 n 0000102579 00000 n 0000107316 00000 n 0000114098 00000 n 0000137565 00000 n 0000149540 00000 n 0000219486 00000 n 0000263985 00000 n 0000108187 00000 n 0000108565 00000 n 0000114015 00000 n 0000114284 00000 n 0000127666 00000 n 0000142495 00000 n 0000142719 00000 n 0000142762 00000 n 0000137230 00000 n 0000142435 00000 n 0000137424 00000 n 0000137809 00000 n 0000143204 00000 n 0000143588 00000 n 0000149389 00000 n 0000155468 00000 n 0000155626 00000 n 0000149793 00000 n 0000350797 00000 n 0000285265 00000 n 0000313798 00000 n 0000285240 00000 n 0000155757 00000 n 0000223666 00000 n 0000223885 00000 n 0000223928 00000 n 0000168618 00000 n 0000223606 00000 n 0000168812 00000 n 0000229797 00000 n 0000230026 00000 n 0000230069 00000 n 0000192277 00000 n 0000229737 00000 n 0000192471 00000 n 0000236719 00000 n 0000236948 00000 n 0000236991 00000 n 0000219127 00000 n 0000236659 00000 n 0000219321 00000 n 0000219755 00000 n 0000224356 00000 n 0000224703 00000 n 0000230515 00000 n 0000230908 00000 n 0000237439 00000 n 0000237834 00000 n 0000243881 00000 n 0000268740 00000 n 0000268968 00000 n 0000269011 00000 n 0000263649 00000 n 0000268680 00000 n 0000263843 00000 n 0000268510 00000 n 0000264231 00000 n 0000434111 00000 n 0000284765 00000 n 0000269455 00000 n 0000269846 00000 n 0000275671 00000 n 0000275766 00000 n 0000275965 00000 n 0000280384 00000 n 0000280545 00000 n 0000280461 00000 n 0000280731 00000 n 0000288670 00000 n 0000299333 00000 n 0000306699 00000 n 0000314003 00000 n 0000322310 00000 n 0000329983 00000 n 0000337329 00000 n 0000344605 00000 n 0000351038 00000 n 0000365119 00000 n 0000373674 00000 n 0000391097 00000 n 0000400011 00000 n 0000420606 00000 n 0000434478 00000 n 0000453243 00000 n trailer <</Size 211/Root 210 0 R/Info 4 0 R/ID [<38f81177f239849d69c477e8e6c3bb41><f5913c882f2b13eaa98bc56043cbd120>]>> %iText-5.5.9 startxref 453291 %%EOF 4 0 obj <</CreationDate(D:20170131183532-05'00')/Creator(TeX)/ModDate(D:20170217150125-08'00')/PTEX.Fullbanner(This is pdfTeX, Version 3.14159265-2.6-1.40.17 \(TeX Live 2016\) kpathsea version 6.2.2)/Producer(pdfTeX-1.40.17; modified using iText 5.5.9 2000-2015 iText Group NV \(AGPL-version\))/Trapped/False>> endobj 41 0 obj <</Count 7/Kids[220 0 R 39 0 R 54 0 R 55 0 R 56 0 R 57 0 R 58 0 R]/Parent 53 0 R/Type/Pages>> endobj 53 0 obj <</Count 15/Kids[41 0 R 121 0 R 191 0 R]/Type/Pages>> endobj 127 0 obj <</Ascent 688/CapHeight 676/CharSet(/A/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/q/r/s/seven/six/space/t/three/two/u/y/zero)/Descent -218/Flags 32/FontBBox[0 -218 775 688]/FontFile3 219 0 R/FontName/XTUCJG+Times-Roman/ItalicAngle 0/MissingWidth 500/StemV 116/Type/FontDescriptor/XHeight 461>> endobj 134 0 obj <</BaseFont/XTUCJG+Times-Roman/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 127 0 R/LastChar 121/Subtype/Type1/Type/Font/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 0 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 0 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]>> endobj 164 0 obj <</BaseFont/YAVGBP+Times-Roman/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 169 0 R/LastChar 121/Subtype/Type1/Type/Font/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 667 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 722 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]>> endobj 169 0 obj <</Ascent 688/CapHeight 676/CharSet(/A/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/W/X/Y/a/b/c/comma/d/e/eight/f/five/four/g/i/k/l/m/n/nine/o/one/p/parenleft/parenright/period/plus/q/r/s/seven/six/space/t/three/two/u/y/zero)/Descent -218/Flags 34/FontBBox[0 -218 863 688]/FontFile3 218 0 R/FontName/YAVGBP+Times-Roman/ItalicAngle 0/MissingWidth 500/StemV 111/Type/FontDescriptor/XHeight 461>> endobj 178 0 obj <</BaseFont/YAVGBP+Times-Roman/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 169 0 R/LastChar 121/Subtype/Type1/Type/Font/Widths[250 0 0 0 0 0 0 0 333 333 0 0 250 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 0 0 722 611 556 722 722 333 0 722 611 889 722 722 556 0 667 556 611 722 0 944 722 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 0 278 0 500 278 778 500 500 500 500 333 389 278 500 0 0 0 500]>> endobj 210 0 obj <</Metadata 217 0 R/Pages 53 0 R/Type/Catalog>> endobj 211 0 obj <</BaseFont/KGORFR+MyriadPro-Semibold/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 240 0 R/LastChar 150/Subtype/Type1/ToUnicode 241 0 R/Type/Font/Widths[207 0 0 0 0 0 0 0 301 301 0 0 236 0 236 0 536 536 536 0 0 536 0 536 0 0 0 0 0 0 0 0 0 636 0 588 0 515 509 0 0 264 0 0 0 827 676 704 559 0 0 519 525 666 0 0 594 0 0 0 0 0 0 0 0 508 585 449 581 516 319 573 572 256 0 0 257 0 572 564 585 0 356 417 351 569 0 0 0 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 236 0 0 313 500]>> endobj 212 0 obj <</BaseFont/KGORFR+MyriadPro-Regular/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 237 0 R/LastChar 83/Subtype/Type1/ToUnicode 238 0 R/Type/Font/Widths[212 0 0 0 0 0 0 0 0 0 0 0 0 307 0 0 0 513 513 513 0 0 513 513 513 513 0 0 0 0 0 0 0 0 542 0 0 0 0 0 0 239 0 0 0 0 658 0 0 0 0 493]>> endobj 213 0 obj <</BaseFont/KGORFR+MyriadPro-Bold/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 255 0 R/LastChar 121/Subtype/Type1/ToUnicode 256 0 R/Type/Font/Widths[202 0 0 0 0 0 0 0 0 0 0 0 0 322 0 0 0 0 0 0 0 0 0 0 0 0 260 0 0 0 0 0 0 0 0 0 0 534 527 0 0 0 0 0 0 0 690 0 581 0 0 540 548 682 0 0 0 0 0 0 0 0 0 0 0 528 0 451 0 528 341 0 0 274 0 0 275 860 586 577 598 0 380 434 367 0 0 0 0 523]>> endobj 214 0 obj <</BaseFont/KGORFR+MyriadPro-Semibold/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 240 0 R/LastChar 150/Subtype/Type1/ToUnicode 254 0 R/Type/Font/Widths[207 0 0 0 0 0 0 0 301 301 0 0 236 0 236 0 536 536 536 0 0 536 0 536 0 0 0 0 0 0 0 0 0 636 576 588 0 515 509 0 0 264 0 582 0 827 676 704 559 0 569 519 525 666 601 0 594 0 566 0 0 0 0 0 0 508 585 449 581 516 319 573 572 256 0 509 257 848 572 564 585 0 356 417 351 569 508 0 0 500 450 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 236 0 0 313 500]>> endobj 215 0 obj <</BaseFont/KGORFR+MyriadPro-SemiboldIt/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 251 0 R/LastChar 121/Subtype/Type1/ToUnicode 252 0 R/Type/Font/Widths[183 0 0 0 0 0 0 0 0 0 0 0 238 315 0 0 0 0 0 0 0 0 0 0 0 0 0 238 0 0 0 0 0 596 553 0 0 0 0 0 0 256 0 0 0 802 0 0 0 0 556 490 0 639 0 0 0 0 0 0 0 0 0 0 0 540 0 430 544 479 0 0 550 249 0 496 249 821 550 543 0 0 347 397 333 0 486 0 0 472]>> endobj 216 0 obj <</BaseFont/KGORFR+MyriadPro-Regular/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 237 0 R/LastChar 120/Subtype/Type1/ToUnicode 250 0 R/Type/Font/Widths[212 0 0 0 0 0 0 0 0 0 0 0 0 307 207 343 0 513 513 513 0 0 513 513 513 513 207 0 0 0 0 0 0 0 542 0 0 0 0 0 0 239 0 0 0 0 658 0 0 0 0 493 0 0 0 0 0 0 0 0 0 0 0 0 0 482 0 448 0 501 292 559 555 234 0 0 236 0 555 549 569 0 327 396 331 551 481 736 463]>> endobj 217 0 obj <</Length 3451/Subtype/XML/Type/Metadata>>stream <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c015 84.159810, 2016/09/10-02:41:30 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"> <xmp:CreateDate>2017-01-31T18:35:32-05:00</xmp:CreateDate> <xmp:CreatorTool>TeX</xmp:CreatorTool> <xmp:ModifyDate>2017-02-17T15:01:25-08:00</xmp:ModifyDate> <xmp:MetadataDate>2017-02-17T15:01:25-08:00</xmp:MetadataDate> <pdfx:PTEX.Fullbanner>This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016) kpathsea version 6.2.2</pdfx:PTEX.Fullbanner> <pdf:Producer>pdfTeX-1.40.17; modified using iText® 5.5.9 ©2000-2015 iText Group NV (AGPL-version)</pdf:Producer> <pdf:Trapped>False</pdf:Trapped> <dc:format>application/pdf</dc:format> <xmpMM:DocumentID>uuid:c1b0e4b5-d14d-9342-9145-22c99ffba43d</xmpMM:DocumentID> <xmpMM:InstanceID>uuid:ecdfa650-f2c5-7b42-bb3d-654b62e4dfd4</xmpMM:InstanceID> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="w"?> endstream endobj 218 0 obj <</Filter/FlateDecode/Length 5836/Subtype/Type1C>>stream HdiTYǫX]U]v_hApAt$ h {Pр " qAҶ{.Äsz?y{a8 (')>{&ad F`އ%a r>˩A%Db(֍0a; ={(B#cm8Z(#bmjufy:Am٢6H /St88SNd\0  stư$6fH0W;lyaw?lͶ5aauؿx^k`gkI%wW A)KJG=Ї|Z}xI8Hh70_JJH=hՠ΃9*O;s.ٽ!+0_1C,&EOK 4Y]Ȑi*g"Y-?6}{ksv~=t6(n=?XnG>)ȺS)I~PG̎JvխޚPwZ:TouKYpAeE[-2VON0lS4]Y*{Au6@JU99lSVɔlO ׆ l[mu:~`4QcQO;~2\#HVr[(x+\AgQ}jȡq5XƵ.� Y #_HY=").._` A#HMŃllZhTH=E.ϕ3 ~bRyC1#h7mwIX%5ue8ꑈZ$%!ɩJ#; ^>0{!|x!<ѣtǰU=kU̚Y)"KIU⫫NH! )slWQ=b1;KP{4?IMb 8UmOݏ-B6ےq?,em<V#x)~᡼Fgka-׹ޡШ(ADR۵;8Ayݲע1O n4C s>h o Zl}' ,f`=y(;+wP"53l!%pؽ˴{#;#Թd T +̍O=cqƔ(ecg-.CwS~hӯ!DTX0_FؼUD,;bFXct;{< YQqJo&=`0F,s9"-+=gySZ%¡ғpҴ~'.~*\Fܩ=\p 6&n~ml.wyvojEy;tWq)bKo-tuw5/uGCW^^׷=q|[#Lo m̶[ߩ \( ig-m?J,4ӟ`XpAEvC|0$r41+`ewnM壵!ɡ|пr؁z b{+-ܺ سme7œhKor46%WzPL[ 4eˍ4Qikwzȣza?FfBm?,2GđqۓRTÞph_FUB\'\Z_9 7<&$&<.ӸrޫKj6 aakp1?m>ǟ /{Ѷ/bOEpY';xZП+/ 7TxǾ<]*4Celz/u!+SdNȳL k EgGImNwJ WcK,F|s-0Mh#7MvuL&e`!ӃwdmFM(ҜSGq0H8Ff(L2T'ۻ4ZЋՔTn*km$phLuouSG\ bj^` 0z\do{.ss) E;0(/O Ssb6[*|rV1k+pCvEq0Mqv6muc-n\bf5I-HPa8d8k.s@De5qkm#[5co?}O+Ou)-RDt@ʠUJ@T5Hh>~` ^d 2[&+9ѿ`b�!]7nW1R&͟N-.m̵\ &pq0" wD"rڞpTl @*)Bf%X97z@wuTzڱPnL*ImswBw}R6ߠcU[wR*U~)Ԕլ&9R2z=*(wxeJrעE.J#|Yq, AJ#oneC׬;ә| |n7̹̿#'Gc u<Y, ">P21WHZԾhJS@jԂ޲O)aaTlMily2ONy;d,h5 V+IkQmԦMῢ9m ɰU9.FЗV  XDQ/V/B؇W8ʳk|?i0{:udDywyT`!Lމii҄`:@CW2r)q'CMűms]<Į�8ŭVzv<^vLE R:'4i??9DD>õ ˑ (SUZƸJ8�77'+4&XtiivrCpntO+(MQ\Zv8;qVZQ2bX/.DAx}23,#&^DO(xXRHa۲k@TGN<CVd,Cu+:T}>tMHTƄP57'ɭH> ]˜4I'N7Id\ZK9򡜁A ;`k8uah!E|uz.P"fiHUsBn}Ep>CݠiG iR%vYR�[ ӒULNWHs/Nx|\wHF!Gt чqǘVٝ7'+8Ɨ3︷fߕSLPm z5WmX#M Qp\̐(7?W5˗5p&n/%_)E1wSF-o(7QM֪b:_X XoMZCAܘsymKHSHƋӨ2)!)<Lnl=L{hdCOv,, -"Cš=ema`)7ΡzH_t?b9Im{̬a+ĥ %+$cz]*-T0dd5pB+ 5eXRjb9ܣ@[U?7 }VUD0šue*-lXӏ[8wwo^6G++*K]C[,ŽfƑҞ;lF[A_bkUT[wٶL�kwg陪G(Yt4#T4d{},;yba^Q&e ɽìMvNM/Btd>v-²]d$8VJv%><r` m#`O|x&zj16{n v.~C[u^!j¯/Խ]@.\htqrunmF7fDza?LY .gL[39%uӎj=.D+1q+KN: "~Y ʒjFb/bh-Q*ۛްdb6횟jL mt^oo}~62\n@.ȷED'{;.&ʁi)LKt3ڎRDZ:,Ry8Mٹ~9 1xޕϚzdo +$ZޣbE$@*ڤxY[O۹`ǻSGb7qzilwn҄i@3QAbc[X_liWZeUm%ɖM8C&4Ief&tinGW|w~眦n[ikų_8sb`CP*fu\>5U-hJ *a> 3pWp7́ʽT ?0`p8T&ESuܛ^X.& Gz_OhH <~r\3B{Oҹ4_%2 6K!s_:VLU6#칪̥@laizk655mMGNn"U$|;ٽ/+S8zc@Ћ1o 4b@;Tƒ7maя Pwi4}x&7IKE BiR yd.l:Ao1,˜xbx �]>w-~ZfD8w25ߑIݾ q_ +D\,aB;*bU6YKU( x#QŏٓV7cM Ő9|Z, Rav/@E+='ǂ92шxD< b"O^o{FjPqExPe3%r}Ff'4<ajiL{mR]UGHUTw2EƉ+%_BhÑa:4 9X CLt{2Zd8^<̃F1 p�<P^=ax0Xq@d,Z¾tIkOA"eP运O==I4x=o>3yQGk֌n߸-:ajQ8vP!|">O8qG|:1kOZH]SwWk3չW^Eeq깙b8 l>4 ^SMP3~=&&Sٍ}]^Df9(f6j .CLկU;ȎpRO>Am|1=Yz9Y7q%C(9fqJypo HiPh#'D:K;H# st_ĔeܜIe7Oÿ $H7DP&_nɾyDo<ihS)ï$/JcBH\cOZя>4T8^Zi7QT˚=+A'CA ?%pwPvjԞ:at79$A %䅙SW.L(S,Z 3 |ZuRmnࡂ<!NI'|y�6Qqɲ01h q@冿;?JQX+Wsq΂qDd$QqnA�`+&&>x6 ] .hj,}|!>AMu~tX}:o2}z}*۳ps*凴�e#ITꓰ6)ٶLpͱO'߃O� endstream endobj 219 0 obj <</Filter/FlateDecode/Length 5670/Subtype/Type1C>>stream HdyPIhqt@e<]A<mmnAE9UD.GDE]/D\utuu _&M/1K >ajeLo:0b/rx0܈QHX1 4)rйj Qa!17M>}o;ݔa!)E2\U+#b\fmh<p厡kÕ*0V':M>~yr0<0+aVط6f\ cH<a[0Ѭ +x.H%Rx*2k+6}p9rROi(nT¨'F:Mc$YcTc([.gIxۀLNR?ud6m^-bn_&~U? Kƺ\hѷv}d='k{`o@Rܙ޶Uu-51~"m:4guq }|Ŷd'Z^q *Olvge֙kW gCcxYRW/ 8+ەo*M~@YO`bh^!9dպWuDwqM5Bץza$ v0}gF2ϛH;8(mɀ */gf.y3# Ta᜜tҜC\AJ h=|_L-؟tSҬ>Q\;89͟C$,�7図}ُ'l̫DcP{mh9"RDjWm瑲Gvf3J޴BwY嬣Rm|`:1?Bb?jlZnIcBE-N뉺';g*U!Zc\n6+<C!QQZѯY`{C\XnވL$5E %{?|" !RCxX%>f`3Ys ?[OSwqHI"f,8ݐ|qTv% JU̡8.l~𼧺\qѵ^+Ѱ Zj0-o D6-a[FآuD,;zn/),{(E\S` W(e."kdc oavGX(8u{qq;f$!tvѢKIe qPӷRMRMg{/hIkf^Eve_fs _~h0Ͽv40xzc7j9]!-lWsMo%;3+kO&-&҈Er%ˢKe}`4p,8+Ġs I{v\IE>9"͈ =i;hMpr \rJ(/=q{EZ H{es87m,cSͱ$1MII{l)⋌Gr"lNj# lTb} d8�F΋6fKbčLqdܮx.MJl圝}k;C7q-O G N�=HXX`7UŚ2ڬLW'\+I$ 8㚒x7/ A3+u/߳}.wV P%)eL[?aN.h?0 r2۞r 4`9O-,d4$|DNKn vg=\B̢'; !3wdMV͂(ҘWGqLo_* 3a"1h$euT";;r7藙%:J**k%*F߼N$]84y;DQ)C2jQH 0zR_hnC? (/1W$MtqZ^L�@j]-v|wm]rV8)ўXKWBĒ] 8.5);?A(V`{L^ҹNX~,i@Aj jLÍ|uKwf8縮tV+wmgAܑK b�r!! @ȍ $[GX" -/U٩cw;a??vƿy{}-)3}^8i ac6/ꔀj/iZI5E9 It( 0Z80@"ֳLE TwmGz<ZT{0Ǖ_y}, =~ܢcy"cmpl~PIbh߫vK.?mvľ3BQ# ) DW*LAϐV0la-@+ȡoP,M'ȈBjzI>zOg+EH^q<tgcʗ3 5(l+Za\ *.jPl]Q! .ejd�~ykM,#<M\5QVf| )%oWԥ_p. oĦI$�AHJda9=wBG:%vM"ç~{up=LqUVV{x D?Ӳyyc(<XSg=q-o1bdBe|em[E֮ᅅ]ec#,okԵ&y=sVt7⨑'hdHcRFW9 _Wm 6XQYG+( "na)UM koXY]6jx ) ҂kLT+w<njPde"LVb4Å[xxlJ-HI+v {# xye* WF)?n<''DdF4%9}#;i㩭i"C/CX"wep[xpb͍e%!yf{/F"9rTg<$<im]Z/[.T3ۮWXm7N�kU+;3RdAA_^8Ÿ=>4-BᡉY9 rOJ O9aY}qαRUNtu"7sJͪP A 97p{T73ݭK&UVP&(7,77Rf0MT2_Kg[wzԋ6ҿ45[jpI[/ G{yp n^ƭ=6S;1j\ULg(&J0J*J'OLs>>3҂ݕD{%B%`l'DB>@G?, Ko;Oznw\ 9&-Te*݊16U#7%OH][֯C+h; 9x])r<!a4vȝ;?ągE3j:su ^pt@IN'21AL'i3<>Cц!K bfi Lvq3B!]8A>0piP["$iF_Q q}[~ 8x(=NLD/@~'>,$`9ߡz O?s sVqY66I1lk\8=Md;~{o9?&\Nunhq=G>Zƹ @'-8ḳ$5V2a cMS(t?Hafj>C1ԷDVbi߂ |兄(s psapW|( ?qEDJKgpUwO%6G idBjξ w@t~1LEp787[I]0uI-MQ6mqp'}v *'1MҤCN>D7x*2BLP1x1At~doC;6,\pЙp`'˫kdk1 w]$%RTXE9$&EdǢ/ ]ص=pN5`^jm .=ƢTݽr14*xH=9{{}wZ=h;V6l81NEhG"$sbr<};ynXK:#O00:C&̋ܜ2MnFW g<ޑ_NN8QѺ`e5YTW7E9j0+~~Zh}^ 9}ԠO[ Zxl1A@@M~UoM$ց&& Y=ΐR~^f F'v䐋 Ph治n{w[h,lUcΡa+Io^vE(sӎ#MFhʕ7 yJQY|z@F>fr:gd[@wݚ~&/\̪٬iȜcsrw P}ڽ �l@ &(py=04ma E[Pd#$é$5=ϠZPd2 �]K`\V;ͅ\g}Rq|`vwJؼ 䅬_ps:/ag r"W\'n68F“ө`lΠidnT o$w.[٨ٱGđy|BܐJނ[Н/OшʠOV2:<}y-m?<ƨf׮>uMD; gP}Ur:q{na&VkF&. 0:ȏSpm�ǚ⪣UO*mӕhMc77(bSL0E_l&Ԧ .oJ_5XUQveKOe.PnK $h=x.oxݡn7RGvp+$\veX֏x$DRAjdyGf$s$Txh/` QB$jhj\\1񥘮_vnqXmA%1e]=xfꭢC {K HK#YuXU^ۆZrHt,%Etm%sA}-QCq̉pbΈ,4�Xn m6Zfd^m)`0[Vrws/O28u6 >b_14 b.,[DDl[Z>�ۃqZ}Qe_jLl%z[Sߟ:|@(fbcsaLoSZ2%2NBґ>D�J,a[(@FτAE ^ "n$ղ$۪(fNQg<n 0rt۩ X%1ޜh ѾZ �;U endstream endobj 220 0 obj <</ArtBox[0.0 0.0 612.0 792.0]/BleedBox[0.0 0.0 612.0 792.0]/Contents 221 0 R/CropBox[0.0 0.0 612.0 792.0]/MediaBox[0.0 0.0 612.0 792.0]/Parent 41 0 R/Resources<</ColorSpace<</CS0 245 0 R>>/ExtGState<</GS0 244 0 R/GS1 243 0 R>>/Font<</T1_0 213 0 R/T1_1 214 0 R/T1_2 215 0 R/T1_3 216 0 R>>/ProcSet[/PDF/Text]/XObject<</Fm0 222 0 R/Fm1 223 0 R>>>>/Rotate 0/TrimBox[0.0 0.0 612.0 792.0]/Type/Page>> endobj 221 0 obj <</Filter/FlateDecode/Length 827>>stream HTNA}Ǫ鞁'Q0j4>H!rvWvsz. S]}ԩE6z=.*Jj}.loK'+S?g_gƚ(sU6+7 ei VVk#;f,к6k}_ɦ'ӥF6E+3ryœhuh#c[6ˢ,Arbn~\_Temrt{xfe5ռ󥺢\mD1T`DI A@=y`o$kZA<%*j{Ev`גEѶ7Uhz 億Z9Xr +\Rà$MO}1g`u.wW9á@K(Fzp)S"LbڡhQjE6۬ ݙKZ6rz:Ka-P9p9ngh/CdUw<>tM> I`dʮ-\0 t_ n96|xakY+&59�*D]G9 [ Qy--r3y �%u3e*O+g{Zq`p <KdTfN�J0g1k0u?&B8XG8#\-Wpm>·HRΊB`^G 9q~nz C5S˂pz%,hzR舻1딀oj%j$QBap|ZO5js'œ#|.(IaelI`5!xjw:͏VQZN`�y_ endstream endobj 222 0 obj <</BBox[0.0 792.0 612.0 0.0]/Filter/FlateDecode/Length 127/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 245 0 R>>/ExtGState<</GS0 244 0 R>>/ProcSet[/PDF/ImageC]/XObject<</Im0 247 0 R/Im1 248 0 R>>>>/Subtype/Form>>stream H @ yynn^ ⩊g3$31GzG6A1Ό.xܟzs0ZF׌V <xX'-R'61پ`0�O'j endstream endobj 223 0 obj <</BBox[0.0 792.0 612.0 0.0]/Filter/FlateDecode/Length 12291/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 245 0 R/CS1 231 0 R/CS2 231 0 R>>/ExtGState<</GS0 243 0 R/GS1 244 0 R>>/Font<</T1_0 211 0 R/T1_1 212 0 R>>/ProcSet[/PDF/Text]/Properties<</MC0 235 0 R>>/Shading<</Sh0 224 0 R/Sh1 225 0 R>>>>/Subtype/Form>>stream HW]o%}bg$lWcXFkH"se؋sjxcdY5]]No>8<{*L)oz88<ɦׇ0]0} & xd]+4jRk.~<|=_,i淋'%bP^Zv]~OK_|C:^jכpRbG/{bu8(+liCowXScXChLq-æ) N["^ml,1mJ3_l1Ö/c㳅KJcln<Ǝn 'L7 5Z>=X2WyZ.~3M^؂xD~_"KΕ؝Mw Als/NJi=a|{G|g2S -vfF`Mn_\6@I 1%6XiKc ovY̓m`/(�ݒ7,].1"JS\AԖB$Ê #|r ~͏08zT`sQn4b F']:]j G>FƈӏHP{k^@ � (у=(!<a/8B< `òc' :bŚ<Bls4iI Y3�Rgt 3D< zZ+`&7ˑ ivo pbuqO(w; :dHAn4y#PB^U$( a)BF!)|?jxC8C!1qHC) I$xAN$`['9s@"a߁m/{%dRPrPJv w7ιc"Rich6%xmazHl Eʌ<lC w(zU)ULؗJglpʔFW <]_aeXL(t}礆mlʘ!T3Jt nиoa>!#SD =5扄Nu�.HE2ۥrgszZ:ƨZV-,9MeMj='hPOش*A(!JEQmmGR4bNZ_i")qtR98kCh<m P^È2!�lN i<$1oWe/{�0}w?ot5=A9*yp479R|8}}U�_͡P5*k3!)( -cMT$D}CDWƐbu_0BɖX8W+6Mg#j.bYbsA)f5a|F)@HH/uk\#AAz l@+ze&8ȳtbg##ԣcDrվ=iJπ-kh"4,Ckf1"2IzY\UKF5:QKbA:P`tjf#P9g'0ɦYA %+T(-Ƿe%RX2!X$8R*Ȧ$ 5H)]A+BLm-b  \ 7T <#Q-UG_a2HZhFr3ET5WdpFw'Te|č\(w eH ɽQ@ NFߐ3`ؙZ\LxQݬm(rG4&[iDMpBicf$k<wJT/;2Q,JtJ;#ṭ=,Dܠ~SX62D`m[]kN`V�ڷrMk͋Ț:jˮoےĒN^/c$�tHu*g U7\lmҵQ܃<sdX4Ԛ2Sss*Ãr Qe[ȥṣۀ\7s+ 7*%n.WOm>ѧ6FSGc�B�kHy$)vGT)w۰} 7!mC-:S9OV_R%,݅`E%#iA2~0h§kjl!P2#%1ƨ$R9n}?^*ÁXF]ˆ4cȒP%iKE%"ֵ4�$d7ۏoF[Úrj ߈?ʭgπټ桢cKJŝ=wAaUnuX(N8аiME,0�C"6q]L1Aia EToHHϙ3fRRr` df*pkqZțvǪ^tCtҐC[21sPݥIĨz'Wg(RL)#()]u%a7]c/Ւ#ٍ>E^ E01Sr701 .SO`0H+CB̙$ٖ4 0*1HB6ȕ%kBQ< D,6ᵹ~7]Z7 jde�Iϔ]g?|b_ `/,O n4iKىHQԦcZk?~ #,w_X>~KQS9IBf@OyJ0lK>U& ĠI-Փ{sU}Umbm0t{)7 YmJP[VC*C sߖ?;үlUhLIyϿ<Gұ}~'Zhf)OδxfS<ZH|+ Hӱdnu.豵W0!Dηo(Y+izޒ:CW=~/ ;p^N [ku]^\9˕վ\-lvo=u^>|jz�rcqwqڋzhyp<=Zjzd9E+9ds4؋1ԍpp=瘓HXgȻK3EW܅JlۋL)l+�I3EF%Jud7CJ^kBFb7{9fg�BAQ-1ئX5Ι,FRЀNLXԳ&+Yo5qfQ]fl‣w$*n~XcP;,E+o ]JcQoqU4w/+Ϥ!~4Iz2Skݳv¸ 3qU%>t$$Ǩ5byez 2Fi&S9xBU+F͔ch1SVlVI|_ w*J?/+{y@̠Bz' >}2a ]K{F0E'jʨQ42 aYe rTAA{BޤF@U7G1`3ȘL(fe_!EpZobm~l>`!>nrSન* .$ f[Ƌ95f3sdJv&㥪] B]W̊0E-e{ { r5}@%ֈ_j\E(NN�zi0pg BN:/EL/<G^{׼nd V2h+DžlKXOqؐ!�)tMkilJSM1^jsKE܊ Z>>?JJ c_@觞9儝rbOrbr}YNߕv TNCOCO(;D(c3WYS$8BaJt5oI/1YgATTS !>9g�_.h dOtGb: 5c,Q@>Kd�(gQn.#fаu.N-fb lUyXF`jYhSG[&,k5"MT# iMIIT47̹55:"z; U3J) $;N_u"JIdG 蔠TI'EWQ,# P3KEC4>=XhVy.chJ^N�\ԝϫHe3$p5|D֨|uTCb|I=eM˶'&;fe5ZC J OkVjh8oM:vq=7к k'6ڄ]XDg34&#SX[F"]¬FK{Qgΐy AQ£GZ _Z/u_oTZH꠬ؼ4X/AfU&26)a? xSu}} P=6 64K@TI|H )/x195;*4AYD HP�)*(e+|s8U*\2930 ה2DLaӣzmrAiguq$'(lؗףY/=jTɁ-gK6[2Z1EL^H]HM_3.SCI *K)l4qۢ* J;`&'U't$I#DS:g⹾W%yYy+¬\ RP<tX. U.nm$58ɃSoEtvYzEjǯU2.ƥd7} K 9$+6ѻAP*q+XT kwc@* KNԌh#tUӋ2iRtb=z^J)M~7wBۙgb {[Ml6却)#m_KS|G ;L #a3M)Cشa3afmeS^ͤ)l2҆e['J*m6f#ﭶ)wmW:͔!H8n[0sJOM_%yNy4WySJ7+fqrGs{ZU7QZYz*5J0`~SE}pT $P4Z׉KL}@RAyr6#y&eU h=<tBlk{jwj Ix퍔1qW3w@S ?McPSB !@E۰֤RR~vz8ݮnny,%~#؉~#I@}^1H3 >hFhi/,ddvUR)݁JFGvS͜2h:dRY%rH/)|.-D481L/4LuQLڟcY_Ŵx J7X5c`ؼWr B-\h}QOD[cSiV؂MJp|z2H)w9uRLn1ϗEγg_uht+`72}Ws=J|Hs(swH JJ>s::FNWX vβH*/p(,cNB`]GOJTh$\9Qn6/*K0%R5"ir;)cX~2<N؜.M<LP%ܫ&ĜNa |hI<JN뼸T5n�) Q0;gE:sM׌8^ř`Vm Õ�{o*u]dEu5h`BHϚJNfpvv~f0Z}cznN͚y^|"b\ruwȤ`bnv%ؖA(FzmO·vp!١Z+eyeu�kwH@y+9WN Y*Q XZ ; ] (gˆBs/WPĕ.R|4ߛmeU {R? jOjWgC OƖRd0 I�]3d_tQg)ϨڙYUARC-w.q7 b̫ rt?和[gv]aREO[zɅLO1 :{7po߼r,F+Z<gSAɕQ{O6z<r.+73}&F"B ~kzp6 1hH!b(ӟ!UA1=bɼ&rNzeߏ8P&uci:{HrZ�PWqb82TbK!}cDӤz2x3-+Ch㙬/!@#lU6gVCG~9D7Oq Ea`E%g�[w; [6"U)i9q֘e>a2{ڞ2TRo˃֮[R}rZ)_\x9T5kZ >fL;͘]蚓&wB"&=믐~~:dž׼m=g`T1E>vwg}jB]TT[ĭq7?foCLRM}uiuͣ*!u8yHUx W@FQ0T")yPϏ]XEgpW734 "c?ޢ4[X6z-{8v ] I9ejp&"q [J5Ymhrp#~zkMt܃ukf{~j a!^p!+ ;%ƹb1)*w KK*i/lE׮~Ẉ+48C+&G6^!It^ 6u3鹒T4v4Hvs=oҗnո- 72*ڢ 5 _Po<74M䤱Iޥ[kC Uz[ u1@ @,0 f6X3Aq0oLHoULAFeme]AĠ?ʞK p/~Sny=уdm5 n$qʮjdC rl8Z+8Z9 @9s9$[Ԯ@$bv_2=8j9_sB"$y�Nsi ⤆}tu^A|]?OQ3 Zd4KV;cxu$ؚ8ۨR+<BjEQpHy/9ApmSWg*QҴR>vj? ތv)9bq-3Fd:m ۬bc!Ņ!kJ(wL{-e +(7yCп/UEM;ԉi:@]^j~qj<:'4" RP7NÑTy s8/3!Ȍ"ԫbU %X 8ٯz �.Q.Zce;(4Uyt]pAz`&j.֯1M)_$=DU#&R n"w,sp'$EScDh0Xyb)Ckr:*Vuu#W<Vq* h]'ϣd15cT&K̤$]٘G_Ck^W^[ָ)qʥNS5$k}f.MN=StZ0E|ΦФԞs8 7|ȚrPdS]ĚCGѾoG ܵ3=ߠO!c\LL9(MBE*zT ʽER8u(jxyFa!v1.҇菴rksHsFht\% StrUaRV#g [d,,M8=.`ı2/5|_C5߲{  ǾD6>=N\Gy v䲏�lt=/M,FTp9<Z\4%Sj&;M`'aNwpH+K-,Fd}<ΎYF  -25E&TF 4muImSYz; N}!4,? l!lǔNgE9I5?%G{BN<�-J!73g$j'Q3`zH2"sㆃ/d婇nnQۈlFqw_& =T4T2/hW]% ר]BSU8EԔy^ ># j:dL \,*VVeI/:fe565N%n2 qaeAP X-bi$ĝ3T8BFDxxDn&Fr?!yrlE5+G5S J?)SF*Ff]1%H [Z)QA^!G(|⣬94$oO1?> <8UoƗ3~;Ow/8I$?o$/|ayrr??N'ǷǗ~r|s|r/'Ƿۍx;8~|Ǘ8 ?7]?�sOF?#|C {HGYn_K.lNLyهF߫n*Vo{O )hE xkR $N|s5,}i8T7j'qs i=$Eb)(F2KECɋkǞmu9d F/VX:b`үu>:6Qs 2fe*Ar6*Nd2n ]W3g�I@�Ƒt}Zʊ({tC7z^[e˥yϔ*nG0ҸD{'K8^N'<Qw)h'N?'5' q¾'R(PW|/@1,PE>xb<1o<1(_Oy:O;O'GX8گKyʳ_<Liy,[yW U�Qq!w'=Yj&oEj(k%䧬&5PFQ 3ϏfmXOUz!8'c2վsmHa( ޙ>6VSUkvFd8X[84ΌūHLvxKXaX SimBNk?=pvOnXq(D<;aECJ VNJNDw-r3 ] n-ř,`zASK@=P(z0~KVrw,آP=Wxkiu%*+ԊWl$3z?iChk@_q* Cq4XrV7"VVq?Lrrc%kCȕ|8{q@(]Uc u�JwHODn8)0I{ΓJ %GD1G/Fep73>CEmkX7$3*qJՋHjC16RǒXiUYsО6wYL][̫[#ܩ?s`?ڈ{21q˩V-֡5qU$V�^7#q:PZ=2*yةv'>iQgb`hphX xxˤ[f=+~0Nt( wͷ&拃 e@|yL5)l.AFP)*( = 3~e0eofjjH ]tԾU:1PbC.&dۅtdY}qtlM"F?f;Z/QT жhCր8)IvrwDUQK4H%2Qus%{ؼb*wNՖ_7tL-զ2.zD yIW=H`ۙVO޵OwD8e w?º8eO'>%2%Q/9GЛb7CqB|NgeD>~\{s<Ux 3"3_lJx3SAȳ *b&~W! ElqO:-YJQQF ^ֶ˥\^8⻸|w 7}+8F[a|zuR̭<_K`ngUZ;VceWmsj>j_5G_jƪ\cj|yg5jVgU'hnwtV9A4:x#Mʝ d= s ml<U ļ샹&,yY9*Ptu "ڤfjmx%Z6z j6*M@rYTuz:H,U`q{jtW̉QB!jCPaC _p7Qge|v{ }f1Ь?\f Ŷ}+sù_{nƙ@ L#:7Vmn3^󸭾7/R<,z9, glLMq"O,g8{-3`BӰ 9Ary'yt;T,n8.htmB(~8G5:t*|75<#(r\;{8Yqk3oZ5e"q 0mu5pq%3D[.UQ×m  a.tbE Ѵm lČx[LE5)K3Z ™gZLVbc>]h!J=p9#3EFB=1&YRŸY#ϳ)57z~xob%10anWŅ9Xd0vɠa͂Wmogڕa`_1I &IE{Х& J..<.,HfϦc GWrKb ,5qrlrXr4J\+ '=~;affw]Qp$kSU'nlv$G`&;4E ΄@ª*a7 ]V |l6AiВ<Rkr8αʚoPJ|dl:.+156!TT ٳIc M-"$1PlHγɓ]g`,y+}Rqvb$ēY"23b%N.N$QȳFa>sQ>G&*(X7ND9Ie e҈ySI'Q$ m>(|Ssxb8 b]J򮲓ASU0K8c S\~]"f�@ɌEuJT @ =63/kX^eԈGp6Xqa7_kuUcQψ#H<wL4rFQ6*I͊6w\ @:btMGoM\gϐ (DJ`s34" 7d4)hSp Zz~SO;@"I65 Z=#n [UR5+ɺ2(W=˝!*Z:ڑxlDz3>*1&枵Qck'Q ߊ8{6y;z~,h$6#= 3 ;uZ?GoTP Yn6mƈ6b̞=9gm>G#c~�P]$ nC ;>o̦�pu(x3]b=&FxS{wÂM5fiL(aOˁOeM "O#23`W>+?_W5%_8F,R϶*P aq 9mth08YsuY=NQJv }|0Xa頾am�i14gƱA۸.hذ`( ,L`q8ȡS,n4H51Ksė/$˅O� endstream endobj 224 0 obj <</ColorSpace 231 0 R/Coords[0.0 0.0 1.0 0.0]/Extend[true true]/Function 232 0 R/ShadingType 2>> endobj 225 0 obj <</ColorSpace 226 0 R/Coords[0.0 0.0 1.0 0.0]/Extend[true true]/Function 227 0 R/ShadingType 2>> endobj 226 0 obj [/ICCBased 230 0 R] endobj 227 0 obj <</Bounds[]/Domain[0.0 1.0]/Encode[0.0 1.0]/FunctionType 3/Functions 228 0 R>> endobj 228 0 obj [229 0 R] endobj 229 0 obj <</BitsPerSample 8/Decode[0.0 1.0 0.0 1.0 0.0 1.0]/Domain[0.0 1.0]/Encode[0.0 63.0]/Filter[/FlateDecode]/FunctionType 0/Length 206/Range[0.0 1.0 0.0 1.0 0.0 1.0]/Size[64]>>stream H��?\\[ZYYXWVVUTTSRRQPPOONMMLKKJI~I|HzHxGvGsFqFoEmDjDhCeBcB`A^A[@Y@V?T?Q>N>K=H<D<A;?;<;8:5:19,9(8#877 �>j endstream endobj 230 0 obj <</Filter[/FlateDecode]/Length 2574/N 3>>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽�'0 �֠Jb � �2y.-;!KZ ^i"L0- �@8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'K�t;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-�[�0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c.� ��R ߁-25 S>ӣVd`rn~Y&+`;A4 A9�=-tl`;~p Gp| [`L`< "A YA+Cb(R,�*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=G</z^^j^ ޡZQB0FX'+t<u-{__ߘ-G,}/Hh 8mW2p[AiAN#8$X?AKHI{!7<qWy(!46-aaaW @@`lYĎH,$((Yh7ъb<b*b<~L&Y&9%uMssNpJP%MI JlN<DHJIڐtCj'KwKgC%Nd |ꙪO=%mLuvx:HoL!ȨC&13#s$/Y=OsbsrnsO1v=ˏϟ\h٢#¼oZ<]TUt}`IÒsKV-Y,+>TB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O�[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-�u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km � endstream endobj 231 0 obj [/ICCBased 230 0 R] endobj 232 0 obj <</Bounds[]/Domain[0.0 1.0]/Encode[0.0 1.0]/FunctionType 3/Functions 233 0 R>> endobj 233 0 obj [234 0 R] endobj 234 0 obj <</BitsPerSample 8/Decode[0.0 1.0 0.0 1.0 0.0 1.0]/Domain[0.0 1.0]/Encode[0.0 63.0]/Filter[/FlateDecode]/FunctionType 0/Length 206/Range[0.0 1.0 0.0 1.0 0.0 1.0]/Size[64]>>stream H��?h j k m oprtuwyz|~  !""##$%%&&'(()**++,--.//011233445667 �6@ endstream endobj 235 0 obj <</Metadata 236 0 R>> endobj 236 0 obj <</Length 36847/Subtype/XML/Type/Metadata>>stream <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c137 79.159768, 2016/08/11-13:24:42 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#" xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/"> <dc:format>application/pdf</dc:format> <dc:title> <rdf:Alt> <rdf:li xml:lang="x-default">usenix_logo_full_color_tm</rdf:li> </rdf:Alt> </dc:title> <xmp:MetadataDate>2016-07-28T13:48:14-07:00</xmp:MetadataDate> <xmp:ModifyDate>2016-07-28T13:48:14-07:00</xmp:ModifyDate> <xmp:CreateDate>2016-07-28T13:48:14-07:00</xmp:CreateDate> <xmp:CreatorTool>Adobe Illustrator CC 2015.3 (Macintosh)</xmp:CreatorTool> <xmpMM:InstanceID>uuid:6be068a3-6f66-e14c-a412-1d9510750738</xmpMM:InstanceID> <xmpMM:DocumentID>xmp.did:047e04d2-fa69-4b26-9428-764c0547b43d</xmpMM:DocumentID> <xmpMM:OriginalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</xmpMM:OriginalDocumentID> <xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass> <xmpMM:DerivedFrom rdf:parseType="Resource"> <stRef:instanceID>uuid:2e734826-cf82-ce45-903b-dfd35b207876</stRef:instanceID> <stRef:documentID>xmp.did:5f37efaf-fdf2-4ef4-b6c7-a42aed802ebd</stRef:documentID> <stRef:originalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</stRef:originalDocumentID> <stRef:renditionClass>proof:pdf</stRef:renditionClass> </xmpMM:DerivedFrom> <xmpMM:History> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <stEvt:action>saved</stEvt:action> <stEvt:instanceID>xmp.iid:01801174072068118083C67C01A9FAB7</stEvt:instanceID> <stEvt:when>2011-10-17T00:16:06-04:00</stEvt:when> <stEvt:softwareAgent>Adobe Illustrator CS5.1</stEvt:softwareAgent> <stEvt:changed>/</stEvt:changed> </rdf:li> <rdf:li rdf:parseType="Resource"> <stEvt:action>saved</stEvt:action> <stEvt:instanceID>xmp.iid:047e04d2-fa69-4b26-9428-764c0547b43d</stEvt:instanceID> <stEvt:when>2016-07-28T13:48:12-07:00</stEvt:when> <stEvt:softwareAgent>Adobe Illustrator CC 2015.3 (Macintosh)</stEvt:softwareAgent> <stEvt:changed>/</stEvt:changed> </rdf:li> </rdf:Seq> </xmpMM:History> <illustrator:Type>Document</illustrator:Type> <illustrator:StartupProfile>Print</illustrator:StartupProfile> <xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint> <xmpTPg:HasVisibleTransparency>False</xmpTPg:HasVisibleTransparency> <xmpTPg:NPages>1</xmpTPg:NPages> <xmpTPg:MaxPageSize rdf:parseType="Resource"> <stDim:w>170.000000</stDim:w> <stDim:h>60.000000</stDim:h> <stDim:unit>Pixels</stDim:unit> </xmpTPg:MaxPageSize> <xmpTPg:PlateNames> <rdf:Seq> <rdf:li>Cyan</rdf:li> <rdf:li>Magenta</rdf:li> <rdf:li>Yellow</rdf:li> <rdf:li>Black</rdf:li> </rdf:Seq> </xmpTPg:PlateNames> <xmpTPg:SwatchGroups> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <xmpG:groupName>Default Swatch Group</xmpG:groupName> <xmpG:groupType>0</xmpG:groupType> <xmpG:Colorants> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>White</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>Black</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>100.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Red</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Yellow</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Green</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Cyan</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Blue</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>CMYK Magenta</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=15 M=100 Y=90 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>15.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>90.000000</xmpG:yellow> <xmpG:black>10.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=90 Y=85 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>90.000000</xmpG:magenta> <xmpG:yellow>85.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=80 Y=95 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>80.000000</xmpG:magenta> <xmpG:yellow>95.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=50 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>50.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=35 Y=85 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>35.000000</xmpG:magenta> <xmpG:yellow>85.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=5 M=0 Y=90 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>5.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>90.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=20 M=0 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>20.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=50 M=0 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>50.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=75 M=0 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>75.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=85 M=10 Y=100 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>85.000000</xmpG:cyan> <xmpG:magenta>10.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>10.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=90 M=30 Y=95 K=30</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>90.000000</xmpG:cyan> <xmpG:magenta>30.000000</xmpG:magenta> <xmpG:yellow>95.000000</xmpG:yellow> <xmpG:black>30.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=75 M=0 Y=75 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>75.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>75.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=80 M=10 Y=45 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>80.000000</xmpG:cyan> <xmpG:magenta>10.000000</xmpG:magenta> <xmpG:yellow>45.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=70 M=15 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>70.000000</xmpG:cyan> <xmpG:magenta>15.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=85 M=50 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>85.000000</xmpG:cyan> <xmpG:magenta>50.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=100 M=95 Y=5 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>95.000000</xmpG:magenta> <xmpG:yellow>5.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=100 M=100 Y=25 K=25</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>25.000000</xmpG:yellow> <xmpG:black>25.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=75 M=100 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>75.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=50 M=100 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>50.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=35 M=100 Y=35 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>35.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>35.000000</xmpG:yellow> <xmpG:black>10.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=10 M=100 Y=50 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>10.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>50.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=95 Y=20 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>95.000000</xmpG:magenta> <xmpG:yellow>20.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=25 M=25 Y=40 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>25.000000</xmpG:cyan> <xmpG:magenta>25.000000</xmpG:magenta> <xmpG:yellow>40.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=40 M=45 Y=50 K=5</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>40.000000</xmpG:cyan> <xmpG:magenta>45.000000</xmpG:magenta> <xmpG:yellow>50.000000</xmpG:yellow> <xmpG:black>5.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=50 M=50 Y=60 K=25</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>50.000000</xmpG:cyan> <xmpG:magenta>50.000000</xmpG:magenta> <xmpG:yellow>60.000000</xmpG:yellow> <xmpG:black>25.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=55 M=60 Y=65 K=40</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>55.000000</xmpG:cyan> <xmpG:magenta>60.000000</xmpG:magenta> <xmpG:yellow>65.000000</xmpG:yellow> <xmpG:black>40.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=25 M=40 Y=65 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>25.000000</xmpG:cyan> <xmpG:magenta>40.000000</xmpG:magenta> <xmpG:yellow>65.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=30 M=50 Y=75 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>30.000000</xmpG:cyan> <xmpG:magenta>50.000000</xmpG:magenta> <xmpG:yellow>75.000000</xmpG:yellow> <xmpG:black>10.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=35 M=60 Y=80 K=25</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>35.000000</xmpG:cyan> <xmpG:magenta>60.000000</xmpG:magenta> <xmpG:yellow>80.000000</xmpG:yellow> <xmpG:black>25.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=40 M=65 Y=90 K=35</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>40.000000</xmpG:cyan> <xmpG:magenta>65.000000</xmpG:magenta> <xmpG:yellow>90.000000</xmpG:yellow> <xmpG:black>35.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=40 M=70 Y=100 K=50</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>40.000000</xmpG:cyan> <xmpG:magenta>70.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>50.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=50 M=70 Y=80 K=70</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>50.000000</xmpG:cyan> <xmpG:magenta>70.000000</xmpG:magenta> <xmpG:yellow>80.000000</xmpG:yellow> <xmpG:black>70.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>PANTONE 186 C 1</xmpG:swatchName> <xmpG:type>PROCESS</xmpG:type> <xmpG:tint>100.000000</xmpG:tint> <xmpG:mode>CMYK</xmpG:mode> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>81.000000</xmpG:yellow> <xmpG:black>4.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>PANTONE 186 C</xmpG:swatchName> <xmpG:type>SPOT</xmpG:type> <xmpG:tint>100.000000</xmpG:tint> <xmpG:mode>CMYK</xmpG:mode> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>81.000000</xmpG:yellow> <xmpG:black>4.000000</xmpG:black> </rdf:li> </rdf:Seq> </xmpG:Colorants> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:groupName>Grays</xmpG:groupName> <xmpG:groupType>1</xmpG:groupType> <xmpG:Colorants> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=100</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>100.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=90</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>89.999400</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=80</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>79.998800</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=70</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>69.999700</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=60</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>59.999100</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=50</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>50.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=40</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>39.999400</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=30</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>29.998800</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=20</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>19.999700</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=10</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>9.999100</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=0 Y=0 K=5</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>0.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>4.998800</xmpG:black> </rdf:li> </rdf:Seq> </xmpG:Colorants> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:groupName>Brights</xmpG:groupName> <xmpG:groupType>1</xmpG:groupType> <xmpG:Colorants> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=100 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>100.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=75 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>75.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=0 M=10 Y=95 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>0.000000</xmpG:cyan> <xmpG:magenta>10.000000</xmpG:magenta> <xmpG:yellow>95.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=85 M=10 Y=100 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>85.000000</xmpG:cyan> <xmpG:magenta>10.000000</xmpG:magenta> <xmpG:yellow>100.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=100 M=90 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>100.000000</xmpG:cyan> <xmpG:magenta>90.000000</xmpG:magenta> <xmpG:yellow>0.000000</xmpG:yellow> <xmpG:black>0.000000</xmpG:black> </rdf:li> <rdf:li rdf:parseType="Resource"> <xmpG:swatchName>C=60 M=90 Y=0 K=0</xmpG:swatchName> <xmpG:mode>CMYK</xmpG:mode> <xmpG:type>PROCESS</xmpG:type> <xmpG:cyan>60.000000</xmpG:cyan> <xmpG:magenta>90.000000</xmpG:magenta> <xmpG:yellow>0.003100</xmpG:yellow> <xmpG:black>0.003100</xmpG:black> </rdf:li> </rdf:Seq> </xmpG:Colorants> </rdf:li> </rdf:Seq> </xmpTPg:SwatchGroups> <pdf:Producer>Adobe PDF library 15.00</pdf:Producer> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?> endstream endobj 237 0 obj <</Ascent 881/CapHeight 674/CharSet(/B/I/N/S/a/c/colon/e/eight/f/g/h/hyphen/i/l/n/nine/o/one/p/period/r/s/seven/six/slash/space/t/three/two/u/v/w/x)/Descent -250/Flags 32/FontBBox[-46 -250 1126 881]/FontFamily(Myriad Pro)/FontFile3 239 0 R/FontName/KGORFR+MyriadPro-Regular/FontStretch/Normal/FontWeight 400/ItalicAngle 0/StemV 88/Type/FontDescriptor/XHeight 484>> endobj 238 0 obj <</Filter/FlateDecode/Length 288>>stream H\j0EY6 CIRi jYߑ&P4Gat%&|0`8OWx1VhmV5NHwpl0A9tƕo^7_n:#�4 hK^A&ٺՔ7aYϹ5i]h4P?hZ/,;.,@|d>yb sd.#o7;]=:USyO'GO!62>܆G=ٓ$1&S 0�Q endstream endobj 239 0 obj <</Filter/FlateDecode/Length 2829/Subtype/Type1C>>stream H|TiTAMOb.#@@7dqAZF%n@v)A3*K *(( . qcIbsR?sN;w{޽&<lvn&O奉7g )OWVvwL9} 3sE+#"(eV\V(i{ Md/JV, SxkjZctX+^(*ZWZ*u`QQ?z&TEqUnV,VԻ1 &5d&ba6fa+0luS`ya*;&N!c$?ŢQGGxt#GHIM'2=o:f6fIX=.3Eb>ik"tDp$Rq"&q]-pH{BjDt odttyޏ嗴AARNǗ)F@Cx0J4'oJn:qօЌeYn@!Hij/ wi|_>)^{'{aXzwe#\T kU R/J֨^lF5ɥI /lCvh<O"B} fd楎!{IRMUH̬pR]'4uu?^rGD%rt/琸2[Sr<0`a 9>Ad27?v.}3q] wOy `zZΨ^\t{W@wNH(G&|U3'vz~izH%I%^o`:~69ѥ ͍!χHR#M6sw/_;4*g%,՟W|6{ Pq.[m*k{Yib8t@]^iD:;0ؽ]Ewk߀ޡih<!)?f7 a])qzS U8KHJ<V,=>13BL`.RTA`f czѿ)p'dAT)D=G k63`җ}++ɽ$iv!2-Wf3>n3r+ϡ>CCRo_ l lrb   %RI:o}'J9\Pa N<2̦KVT/2 B+BI*"<6)%+w` Ĝ Iibb/_+e=֖wǶ10ozDd5g]_tW^SNu܁![ήa3vn57\{|v?Xi-$RMVfzz,7dk&"rKa~՟Z O z Od&_]KWCiEԟO'#%ɗ TݡĤC'S]ɜtSb7+ ӄY7͏AT[rPewglf,#+df]:I,Za89,!N;?sN>CG4zf 72"!T[wIɜsbK2E@<tc7>h9�L;Y~v#3S{2;pukQnn $[H3:}.VV1j45ؤL/eIj,iL^qe 1S4Wpiߒ3m6l=F!Ԛ̼R\�|>LI`ob>z Ds*gc[{!45 57@5?i6jmq3 BbEGHءldBtT4Rt^@಻[?'4+| ]_X  "f 5kj 9+8|dBv)le#OT$hQܱC+W�Lȥ5v~R;^ j OGj i* ìqi17$d,щi۟n[SCV+f̘E^je: nߑO3s}9 C#~"+ %Sܘ?+oB0Ok0}f7XER e>sP2<C#/ϵ ^lBbcG̣2_WZj:ja[%f}* <{#4tG_sC_-ȔZ@}Ŏ�L/*&$kt -]NnYנjq_~;\#nE: sRAHjj/{|[Vz/zEqP5 7<e*==8\gOn2:VV ʮqZaB?\3+-EVz 7]Ds PjB%gDy+$z8<2�["lhxi7yJˈ+["ȉL(ʥvQpPհWul"z>>:##w- "iβ(<Q !(\4H 4X;DHGj|!E V`SζbQJ@ ,G3�ywP<48@r*VYў5 ayмUr)&$6mݦNGؚX7`߻{3`7ݴn`�Lb endstream endobj 240 0 obj <</Ascent 889/CapHeight 674/CharSet(/A/B/C/E/F/I/K/M/N/O/P/R/S/T/U/V/X/Z/a/b/bullet/c/comma/d/e/endash/f/five/g/h/i/k/l/m/n/o/one/p/parenleft/parenright/period/quoteright/r/s/seven/space/t/two/u/v/y/z/zero)/Descent -250/Flags 32/FontBBox[-71 -250 1198 889]/FontFamily(Myriad Pro Light)/FontFile3 242 0 R/FontName/KGORFR+MyriadPro-Semibold/FontStretch/Normal/FontWeight 700/ItalicAngle 0/StemV 124/Type/FontDescriptor/XHeight 487>> endobj 241 0 obj <</Filter/FlateDecode/Length 430>>stream H\j0~ CjJBiFqyj4lvG3ff۹Yw/sjJC7<kwj,ͻyv?YӨ-.pU7w, ?neYjR;DvznONvaƞTKޝsG5E<Vy*s5 YS(-$/k@~\Fo]KpEll#kk\YQ,`7 ~4Gr z3foo4Y^ u:BPG#,,++ԗ,,o+,,,,Y,Xc ?~,XeUQ&6+,40?щ~粻G2i1w4N*v̾�&S endstream endobj 242 0 obj <</Filter/FlateDecode/Length 3890/Subtype/Type1C>>stream H|yPYǻ Fv &p *"$ aft׺! Zx֊8.*0*هUkꪮw~?Al5K"WS5zݗj֔]Ʋ;̚sgBqw/]&u"H~.O9xyy]Ueekfu >>[`,6B,<l8 = fytMJzؔ,6kSzk[[X]oU\вFD$"+  b!!)AL",Mך y""(BmMD BKG"‰ q#r'yjQ*AuuEPNĢ d@B;V$%̲Ih65vv+!aC7ґt2]–tro:I)("0nχC # X&(1q]e-3zܕb PCA`ļ133!i!o2Gf^ά Òb%zy(u4L2Xv ?+>�G`[{R YXz/Ix /a yCbZ{ۀL4?cj]�>-�qstS{GZClܖԍ)mJ:`!(%:XgrѥO ɍ#ҸgSgw~j+7XAV̊ N�3+!Tv72DMHKq9>^&~a`.~]Bӵ1f?T㥺n`}Tx΢ %24X xhV0%O)Zck /IX>Nrzr &<N]۴h<7h8H(0"KEy[.7v Zz$ %9NU C-@ά7iTQ7a )s;a͟6MtwÐ"^9 Q0a*0e WܑV]<PqS6zh(s?ֶ^-ֵşRF/[@xYRʈ90c} ? m Pbܡs s3 q`LP-nsF`&Fkv6(-5o)qQ}Dy'`O8$XSҞ%&~!&l$B0|Rq&pnz%<v.Pi!!R|7O*p4eFC!u"m \.&^ wdoڑz0hm"oDyb'3 qދ7<z RB?ebng͜tB:1lDŞ'"50. Ha-?Aы=J5Bq'30»k}Ei?&>C|R!M(gl gDb(?3xW{< ] @ 6=e )_7?[ͯrB,>)rW֓y5QѼiqoyU]Aꍞ<OZ>闠%kgoi8}-UV=y8GN(&Ѷ{zGt(=qsY#^qvYХ+eGN[OlY Ao+08uDX 70Z~ʹ'a _o`vwKvS q{;J!r< ׀F9ִ5JFA>yOB5\!7xGCem|Kx@q4 㓅X~-*Bzw -�;ߌ J;- NBn%n OgzM)!H9ym8"%{ _� Z[-ѸB(x*u#LA�YmWEsy+qxr݆{X&JƸ1; p[#?Uu;;8<f,kﵴFqM%1j"Uf 3̈0 "(2U6(iϙ>Hν;~$G</{4cQBgn)L:\5;}]7_v98]o>C%]xH(|u̝Z(Ie'bʄDBU9(?woߕjXabWʽ0 \k�,Q{$e?žZ_i%GgBz$.NdOQΠ+fMf6uyJ›9XSR"i*X])ST_kC/Z I, !%4ڴ}:>5cIRn5Fܨ`[6HjՂ4]>Բellet!'<R]3K"ʔ(<,zv\?X讜X l\ D0J@,֫XO7DNTjey|o+66< ,=CbqbiVEo#ẻ)B#D`#"3RV FQS`;_(yH<LsEJD ZuR%dkjxӟ0)sAk35v԰iG.c݈mzAY>{1'LyD΄"Оjd* XxCqz ³vS 3`.Ҩ5沆 =s^,z�^(.KY2#NQCn&!J9c,\,6'jkb[4IՇX}P0vZ?A d _b Lh<S&g ;`nko@2uL{yw 5?6LWVddK7u׫YԈ׻eSw|];}&hu+nH`d陉X{BR<wy 'R 3)żR#|i664sLl ^oۚ7HB+Xqx:;\P`H.ߣeh ~m ~bӆ5z{a@^R1׍蘎_՛O`+|yWX5I =&/H].O !"jO Kͽ7^\C|=~ PEç@H;2ΡדT?n,fhkA4h2@@딹Z9-u;fm[h6ۥQ w観+B;tg<Z}Բ⺳g:;ZLq*>@#bϺb[iY1ؔԀ0_%⿊bk>E}!!+ N`+zrV=I5itpV-K_ QWv~vq. 0DԲ|%+tyr~Q Kz B!խbs!,pg಩D>,E  5)T6UyQG(t.ury', Fon\/+=b2cS'+5w+22_7w /gx7!2ND윹ՊfeZ(sU39֍_sA:\eZ[�Z MI>PNbɫu-oNⵀB \<8( ѲW|* oחRp5K,)z#?%Х _`d|uޖ9*_i턠xA?�� endstream endobj 243 0 obj <</AIS false/BM/Normal/CA 1.0/OP true/OPM 1/SA true/SMask/None/Type/ExtGState/ca 1.0/op true>> endobj 244 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 1.0/op false>> endobj 245 0 obj [/ICCBased 246 0 R] endobj 246 0 obj <</Alternate/DeviceRGB/Filter/FlateDecode/Length 2574/N 3>>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽�'0 �֠Jb � �2y.-;!KZ ^i"L0- �@8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'K�t;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-�[�0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c.� ��R ߁-25 S>ӣVd`rn~Y&+`;A4 A9�=-tl`;~p Gp| [`L`< "A YA+Cb(R,�*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=G</z^^j^ ޡZQB0FX'+t<u-{__ߘ-G,}/Hh 8mW2p[AiAN#8$X?AKHI{!7<qWy(!46-aaaW @@`lYĎH,$((Yh7ъb<b*b<~L&Y&9%uMssNpJP%MI JlN<DHJIڐtCj'KwKgC%Nd |ꙪO=%mLuvx:HoL!ȨC&13#s$/Y=OsbsrnsO1v=ˏϟ\h٢#¼oZ<]TUt}`IÒsKV-Y,+>TB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O�[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-�u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km � endstream endobj 247 0 obj <</BitsPerComponent 8/ColorSpace 249 0 R/Filter/DCTDecode/Height 363/Intent/RelativeColorimetric/Length 23095/Name/X/Subtype/Image/Type/XObject/Width 841>>stream �JFIF��Ik���Adobe�d������!!##!,##!.333.!>BBBB>DDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD��5�kI�"������������� ����������� � )Q�����!1AQa"2Rq3r#4BSbCcs $%&'()*56789:DEFGHIJTUVWXYZdefghijtuvwxyz��������!1AQ"aqBb #$%&'()*23456789:CDEFGHIJRSTUVWXYZcdefghijrstuvwxyz� ���?�HỬwZۺjT*֝�9ް ֏P�.h%}`S h%}`S> mXԨ8:K'5*}`MJ`N{ԹLZ npsAŌY>K%"T�}`JD }`OD8,XDmXD G-�tX�8u>HbRT'CB23GF_C(RLxg&q8x[y+o2W2Mq9>L| +6 =>B_l^+74w*8kJob:sNba< "]ʞ6*ocqGwV_csGwi8"$^xZiWR 2네8rF.NBFEHN!&8Z`]a ٙo&RC2@o]+e* $RuQCfhdlwZ1&P(6'ÚS /.TrBLn*w+8 C`LEk Flu)4Ȑ鈿sy/qtcIHB|$Hl\Hr@.Np@\z{ pd`a!SrD60[#Zx:K$ΜR F%)NU[ hvۀ#Gc6n2cYՈ[QqOy\$�\O8$Dv �bR ԉtjDJtԉ"HR ԉ5t%)N!�)5�ЧݻTSRW *BT*   P*D*T%AJ .!uʷ;;NqO"z'%x=!sJ$GM[jL1] r֭_<+sXh.:rܫdu-~Hڕ=#›W9.ׯr6 譭pzG.8=#›|F [[O( ]p:G7ke%Gv4ѧkb;i<KHnVk4a4L*}r]\!@<)~PIˏ̹ԧi9ABp= ^JΨd��`K]Nl$bz-;+.eY0i`p<:v2o<)ZM˸9XcԯJMVr\G 2K#ѮOϣΈZ!5kG;m\G=ʄ=⁔e]rjuNc#ʇG8QY𮢦W lD-Uk!eBxTT^l @$Ne֞eĸ7حRqa$'Er7'YEQ!hTDa,$/ :%t*Sb A:KhĘ ΤNhxLʻ咞3cq])o*w+Vꎳ2L_+!ܢ!2rZ6-c M6F>ecI fAHƃ#B#H !F$)zZp쌔е6)%:HAѨAѸ$)В QeF(N8 ),$],sHYIedt%h 9],kEh;'4ƴ5jcZMZȤ]v=i6=hIǭ&ŭvN9!tc։;(Xֻ!(iM] nLLѧݻ jvn)T8J.(HpBDRÀ Pp*7K\Tb4xӘ8nJTޔ9$²{ْSI^U}={֦e3g*V!}SiQ,0)LP�s7IWe!7E'�&sM Oh1(9E!F޺d8˓$5uLYWAʩ!H<LSƓ$. <.uҺP:T'j� Ix<,qR!rtl:`w q4Շ*NjӍJ#u nN WJ\ 6Zۄ)AWy2oNc&_b(Iϩ7leXZ%V`w1KW;bmXQfMZ-+hU.Db7)hKMDՠI".7"`vF pTbsڱ#sUӱ x\$`썳B{MB Fu͂j4LKSLw"yi*֩Z-@RPm6]6I;[6.9nؓ1#BJڏmx@l0lvYT.-ȸJa6HI i |$9+&vNe!vNFR] $- dzEд$sB|AѷO$ .J|@A%$ 쌒J}!'9)$BK!d'JI:WK!%s$+@INt[%A9IҒNt$ eI.Һne;JKNW[ Вt.GNQiWk Вt"N9Z:Q'JaYnHJ9cԴA\ gҨݻKS?wYHg(NH'4`&+ GJ pZ\)iyWDg"ԭa7xq 3c:RAv9" e.tA9�f3$ĺ ZR4OƺrE w #y2;zPrD\ӚR[ Ɨ NQyWU̅O<ii."I{ƻ:víY԰.;8b Jrw p̀һeTKL\dB+;w]K;!S^.N74HeAɰPӥ!cz*goNHkНeSN̝d]- ^?H uVu w |6� Rɬ-# kroE[$bX<9-ޔl:Gŧ]E'=#Ҵ`� 7yd"$񮐸[m0ğXxO`7"- eye{ Jʳe Nӭ hòy LرYsI&wȮ<K(6RM\u.3qP{\Zf)lzlI�MJKX8LPt28 ]|13p=Kk1ֶ|QNLlSs�I8pc}F5ΨMܑ� 2P>W8q̢l!bb8!(9m n4HpapTvI$ ֧?'.I+: dg6ӎ8�VN異Z3Zw LqTz3g9fT,T7BJhҐ%$0 'PHdղvRӗPM-nљD{\K[e٘(jM\5@W�%DZIi� i%"KA%E@[[vHВЃ*DƄƄPlhI rDݐhI $MlBĹ7.rIөݻ]L[tQ)&9Ű:WAM9 lm̚ˣBJgiR�lmA"Bt1+y92/ `( |8Hl54 iAudiL jX p9PBƠ$� dmPr: 1ڇ0!tJ.֥,ε0K\t/)`plq]Q �_{\$D_*C2RDz:Z1#r6[Y]KK9<cuˏ6 FVW7q@q76xK$fzDLeBfnֻU3m '*"ȓT\s@%avb8V>SN CcPJ,1NNeQqX5rT\,AP/&n c;Iw.o.3]؜W=6{/95o]Z ]"$>"B*فH즥1HIKh?}L mx&-Y͞Tpyx!vR[ Mm di&%[fv"f*.7.q|W:;KӅ4 $뼷H\6fu'\HimdI,` J!pNAϙYYnθUx/m쮻^uΥC (s)$s\ Ȧ4bG:)Qk)'BFdZ9 ɐ9\Zs$Yrv K$�<8b5s 8AMtug0WdlE.Dd ,[f'<LnĒ{ #�5 3Bi5)S2Y-kXk}7jCmՑp\;'SuBCq�@dhHu}EX`<j:rHM!t!4cT kRRQK&.u4˪`jHͲ5 uѝtI )Re$\IJ֥=fr�&M̆lS{PC V+1n4xɄ)uJ.UQD"2\݃ iO)pԉtjDFEX\}RsP:V[<YU2Q9+KKfIk&jJIzN=H 01I$ƤNHP"*D9")j�DCX(FHWW�3h̺vHɥJ-iw.! NF$)Rb!]:58ԁRử#RpnKh]F:&~#s$kΕÑy/`u=zE>P x.NtPr,:$v-!tЀ: -0~xE;u+{WAIOƛlfKL&t #C>h ՉJ* (O #"ciR'q*8[s)p7 )"w!٬WxFx.i10sntڨj EՓTA�74dm c5u /-5WV^Х Cq+u=h}p!m!)~/-ρt@65vӾzBČ̕ }O+i1זt-Yh "5Tw7-'Qhu«+�:z_@Cj9@z,9,㰾.y= yR \4[pIR:�Hϝ0DC%5s%TsmcxMy47:ڄ:ӄ:W&Ģsl#i&#X\c�e9լ88JT#TlrR 7rM`Q�8 ΰ3ȞA%kL� 缆=F #CmG�X v|6jz*E`'$$&\b5EG!<K.%.ҋybp[{[N-# _rV++˅˛iZ$JZ�+[(e4 H3p0n_Ĉs5D۔I4�.$so10Z %ƴ)ׁpHO!! $d$!t&!S:5Ш{n9Wp viȕ[%ʭG8Y"Hʎh6pv*6SxjTzsFeRJiO)DaM)4C DNH8fJR.8Aѩ ґ9"H�5"rBR  @QAaS|4.2SG҃p\I%u HRơ*,A]:!MN SPuՀۈ\L򥁡A !$nE3�3&]@9Zh=5 Q -%Fd%>k75D5ާ s\gN 88�3'@Б=si+4Z˫ZM)5כN0]Zڵ.h5\7NN^멶Տ:xm[mv2۱7N~rV|1'kwUo<خ6<J~&DƛO6$lt'vUfl̞P6Sv\Fi 07s4'$kǑ g:EEmD ]2Vm4peo:]yOU("lɿP'l]-6hKaA@5�^@L50u9 ҨG)Mٲs dĪ yZh>`B%sMһ' |6BT#B2j̺!�Cl`\X%~ ":VoӚd. ΂WQz8$A&Y:sJpK 9'їK`0\hr=kD�Ɯ@JӇl XrИ lDS\aTJQ f .{H]w8�, <u$�ZI`}ٴX;EwSs"Tk@�C"j'd.oYO-16o2"nE+IB ]OwDZ3"Da<&ORxr(7k#� )۱]+F: /r-斒q)hVp~Q"ދ.�߸~P= M+0 *f]h`ym9B S 6L_:�ڠ TOq89ŢCiڄ}sXXXYLI$Qܸ�6F hMf aɪO̟[сs�mwdm\IR.k￑*J3Ϟ(:qHW�=#!9m0#e3bgp#$]�r"}܅2T;'(19)#.5~֒96"XP�#rQ 6Df.vAr�Q)-9!]Z9 !gq8ɐ ux3:yn3vw=B0@ֻЋfs1EuhܿapNӚ )`xo}pmy8ڠ)i<Sk� boR4 [s'7"p%R%uQq֮sm'vN৒ O9BLݓg: ݍk64!4 5tu.w!s"D21/]-;BJx'8 Is"WCLy SH^pjvwSÞ#/+d.㤮^%uaqg.*U<Ww ]JHURZE)U M<)"46 T1kκ*J2-8(Ҽ GIR$4IBě9W;EHV+nv5 7-OCfw" FLLͧϪ]pk"qw:�&]#x3+byGp0cq!0 % GpymI)(D!8&tBpASBUӃWN #,pTJ��[nHZs& E'iNc\qO�r�&:WD! RŢWHNh$ ^*F*Y($m!r/BXKyBGj"�Hu�$3iĈ]^ךLkLnwA-Ab|_ y&dWR|f0 .[5SH54lˍU^syca|kR"R�!ܛHğDm 6�^4Д&괉Wey3R4-hmɕ%oִ C"ӸVk/28c"uiLtX'qTԈR \ bҪZA#-z`[>J+1ܜiUp� q]Qρ9̀\xWSD�r~2s@Er%q4uUaOq եͨ;A<cjq`BQEUDPI#ԕ ]64%6Ȟu-ȷ�8/9$g^ֳ|M9@P\;7�[u}xrHHĻʦ<$f|O!='" s$ɸs\[uwS3 qiq  %H�1I$ٳf5MA4 c;v��N$ 7L'1 BݔҼns;,7\@fcYRW <P.Lac�5~.�HMDUukH(NDo.JMMQQ:\ׂD8b17n(IA+icp;` PCH]>R'�b耒+;ĂƋPDV`@'0P^pnǹ7n܉KK%V$ة*lуyM0K4Ad(� )4nk4Bzf./]vmω]!)Z.4`8kV RR-n[2mf%h~ ֆϝ"c뱗Lv.Y5XM*# uQҹ7hJ$$8'МpPS<A'@t% E<y JfIE&5Ceo4hǿz rJo)p JVG܌y'sRO*^щ&xRG Hybȴs4%Xrܺ= J- fl: kH#mw"K9ln`T"u&耐 n8@�l:BPHqi8:A!4L) @&[!A\O,Λ3T RcI\7(]r @`tWA2+2C?ZFӲAŽ2vB:nqs]ND:, -9Z86hNF n]2tMӥA_i7tfOTZ&̸:[,9et$]09MR#�j<5Z10 ffqJ#imFMqxֹJNdmғWjѩJJZpϏ8=yRj݃*V`sfNftAE0e.K(j[<gDhKH ({[Qx4H[N˙u'1'.9YvO%٩[s*}i*u*O3y7Z3QԓdsNu7\[Ll% ^mgt*ULIz#I\R-0u%#Ԑ%ɂ[DZB< hI0!)YuՀe@͠5=RY0}KgAvOH QwrvODޏ evU3֣A=#rj^:<)6." C'ã|txTra$ ip w4)yh)yh vRPN. 6pŽ"IcIA::2Al009ҝ(;'sM0tl.fW;$ԴI[8a4.;S]JR:&*<^퍜05� %Z:2 @%q|txSNȐer]DN,gЇ�o&`_p�`) sG1Ik_y$mȼ4hs<ļZϊ�8rMZK%IAq¼IIJֹ1yy6 K@hr+4S)dio~y]MI"J `se*$a#ML:ȋܕ9l ǜ c?:-aZ'Pw])42Io)d лb"ۨ(cnu^@&aTe;:q04КhJppN =s4N 56Z$T2?tF! :47S^jBočTSe1e4$}f0;̹E)wPHV$)n;9hvI4^s晀¨m?swtl7^' qysNGDOsI{)n_Q,{6խ'?]Cd}jOB䝃0s^{f|c$cC[hKrY(hyM/1<?Nsx=iv\a+DezONiplx[Ewc wywGZrjGH'`{{]W GthYfkä'�bZ 抃KLb*& ~ڼh p!׃)Qlj Ps#@8Nc|>x# ̀` ͦ:kv1& J` Rpp2` pN�*fɃrLjɯ $ *mS�@" D\�p/iW=WEʘ3.@8o] & Qtz%9;q]HS׮J$*pV,76!?#`:W<x6W_/=^asYK% /Mp*jTt4^T깕Q*H{"O:l.<Y~;c`fdi&بQyniĶU&WC`@޺کkӯlPp)f$sA+<]ƞ+LUXΥs<E8fʥF\: {llHA^Ȳ Z&Yw{{yzȫ:y'N\08!''KۉkvGF=m&358."4J8$]ldRYI"f'’>OdtOIe?BYg%!k8_Y(;#"Z(.AOLqa a:>B dvĞ]Oй-ZKNt<iɬ,luT] ]jo=H+pWQ:lGѬݻsh$E8DLλ66hBLyD,ȫ5O /Bl0'vfsTށٔӹ tPj�O},/] L$Yuz tմfḾŻ=%.3rpĜ3h%Ø\\|cp~Tp`ev,4K k.`g2%rI*3=: &'Д  $uvGui䁣r 6I:ilRC9{7֬䬦H P&MFÁ"jy̭TEUWHYi4K"q-y-NY7ֽrڕ؛ae<kR[Ҧ٨A kÔֻ-Nɖ, r8s1G+5Jdc +rfH}RjgvA١cjvKN3o9l5۪8,7λvadJ>!:zFB!�B!�H P8,85`,s<aΤD +[wsvFiɝ>P:Lia#*U.N&v&w2PT�t.&ŗx e<]jN' 1n#RLJyaր5j0s($ )Z셆Kƕd =8ޚ i4]"8Q )Ȑ\ HNtkF9ӒJ%U3 L0TIb:5R@s.{.K͘e:/(IuyHST1gʍiea%qeF.LTƣ\W]VXF\G{nEOHQqm)dZ dMTL+IׄJ[h.7�$S+mC le,\)|gSw&ɛVi* 6,I&I.>תE.QҚwXu}}IPU[BZ2ί7n` a95nuUVٖSN)cmN̊l#\g2A\Y c4ʥ6$!w+pglht[Zu==RjҏUH*iD l4ԹU'Ը%Sν&qѳ<ZLNQ'VZLKA^6[4 6I2u@9*юhMkAoPwYnǴ!tL@!oAkt' D8@4fCNAٸact.V7vss wMsF$@Is'n9tv1pRoqxu8Sn䙫, fG ISRn'D3+=əwY'@\]=7r:WIRM!:(NMJppN B 4)tXI4wj;f+IҼJJϱI:O1QTfSxu"Hnףc~詎u cp9:�ĕm4cb٦julpӲT6f93&ʚYHfDrHc0yBӭ-`WۓS9ʜEx:oKkkLcjUjMx=)is%K1SIyqܚT0cE�\}ҧxÒkF8nN1)BvG `o|޺cCXZ0䫱\.!�B!�B!�B!�BέVxBqi|O(铹~]SN-T»9p-pqBsjdd)@o-Qvi({5kNCpwJm)taygYZȂ8Eg-H{nƄƽ7r޵7Kr|iK*5̺J-2.*M:T|S/,Kq] t)ִd"cΉ;[Hę:' L֤ã .rfWR,֤?RSMQ: *$/t݂h|]$q$hKE%Wg(ƠqV7d8xHjRvz.Hr.\!ĘNOUbX9dm%WAIRZI: ;q,X橇8g \ZgRȸȺ;;�t.2j-%r}BDD)siԸuAŵ DD*#`. ImJ/)V% -"PW2]wȜNPuȟQ%tۓv'8@ Zv +>&zѕ2ޥ"G9hȍj-g` ]ĸliH_tHgH2oQ@9pϺ"C.Λ]* �sD�8)¦9Nff\mg]0lT j)b i rycIĜPDBt(˨7]08zV -΃0:K$oy 3cv5p�t790PR$JtT$JH*D8*Tԡ�9uIw4bsG*duk:s+caAF 0rgSQNiF;H&Jɛ'=w[%mC#@3Rƣ>+tL�ǘ5WyTH6r{RTgUU-XD`!Cϕ:JLYUqI&AliKw,fn~3Х)6͑רna%M{[=LdtrPKsݿi:Oub\i.$t ViSda/ֻ5cCX@ ȬD]NkGa]"܂�!@�!@�!@�!@�!@� D:j""wlĺ1 'Ru` :BDpUi"QힷBX6 ±߸hsHs\JK ]=l69Z2$nz9m/@; C)EL5K;*-NQҝb.> xpW= O}uT:R2ul@yB\@a̖AA4�:Q)Jfuѭ.7A\! RSI*$$.+`)5ĜSE˄spN6& ê@S{ #!VU\R`I'YHr3< RMiؘ\mTJ9U~ɹ^PIt縮rAT,dNeG&]-qQislT5G%qsT $IL6,h J W(Ÿ r:T3cow^Wh1sRI8 ;e5HNSzL+=«8xQֆ]f<ۺ%Jiݫ$sLݤ6vD Tᱍ\:)5Ԏ-fAذ]ޥֳC1vMHt7n;E<I ܙ'өEdrB OƜ5Z8]FOXZ:P Sxxe`rK[!7iS; kԏ0*vU|9-8LmCwM7U:�R*9e˸IٳKTC "7F\,p]vZKsLJ:pMi%L Yvc6u<89 Me9Q np o9WybE}JmSKݹM�CךE܅FM9Q ]@ZA`qM-5*mhQ6FE-83T8BE՚ך]b%XɘjF j7(d{B}Zp38U9^DAʻS.h :˝ySX^̞F#jg<a~**TbC/p\vѩvJW͝?<q(ɍCk*v�L}W?,hZqkM]w903=qp.֝NkGR ٵ .Ĭ! B� B� B� B� B� B� B�@p  )_M.APl>'Sucj4ZEU"L0!Ƒm` <5-TZ5̪˯iT M¦Mڔ̆�2w/=G̬F c*8_x qe(Aٳ]j\P48| TP`ӯW3Eǂ{:d7KA !,ʥVu)e%Ttܑ�D{*o& %v�S JzI\:o̚ie9CRgtB;/@iTdHޞd\bzA^4N4pO2r9m1zS^ .ok ,; :V59vX\Hc\ *̟*i+p<RjyE*sKXLSh.+w1R+e5E@,\NYpE'N+g'n)'n+2@&)$Bfԗ1Xx+rz!qҦ )K!sfWR\ TB95_  keV:RmlJ뷲,<,<ff]9lJЬ̥{& `ܫ0#oe@xf5[Mgm8;ucqsTj+7�pLn MawSn` "Sg1Ҁ#T̹7 q<i�];!vht' XkPu{[A=#rzM3zx�`#tm*1tyWˆd]K#w9T=�;=I'7{x!ʎ)X<~ғ`g񈃲LU<�R+}T{D;=n|utU.jSfm,baٴaۺ7dvG_ZosdKohvhVAn\ ppvAصxJAǶ`KuQS=fjCP_1fN5RΣOrNI^uǥKn+{jNBT{ւ('nz &NGfv:ݐePaԻdL7Mfibz0KC nJ &96 (4S`&SFMV~Jz'm֭WQZ9 q)ӰSߒdySB3שk:ۖ 7&cp βޕҞGR3NVAw9:jpD_qs3'oW:SR05 8sG2Ff!]"B�!�B�!�B�!�B�!�B�!�B�!�B�!�B�!� 3M J95Ae$ Ҫ1l6]lѹx<hXNN̦|ѬhhspQ̫WNznhӨtnzƑWe91t{cd ĖaouJ}�\]Š]jo-]s.;]m5cGxS p:5 0Ъre*RΦfy00ȸ6 ֹtypN럹:s.sNuթDp1g)JwlӢ.lʂZoѝ:OQ)'B'J�8J&5.0W'2H=:.sp)D6W Ja%L-k�nÄ&GXK\b&lS5Ѩ3ĸu5a,oּ <Z!xŧr2 3Buj>s'=׮Y>u5q 'Ih%`^tIqR."TDAOB#\MD݁M%uتsORsrJ05K)c8*l9IkeD3LIx-s9ұ UH]#-[jc)=E&J d[2R%āaunFgYs৸wu)R:5_ϺHin:|W LWpNmd"ۉBH$ .܎R=JIޭpsi*V:\h@&kq I%i'̶OQ]6͞J5ٶyJcꎍ�,;kų[}HL%<. 'UaIt%|N+4.&7Rl oDA\I51­V^;ŔF5'Bo|( ב딛r 7]L <n'-/);!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�.VKO*e8o]jl:NU^OT_20cSD0l4mm=hNrjyU;EU}c +RlwbeOm<MeDa5{58bcBrmlNVnjN.pGZ*ƛSLǧH(HQT9[UsTW&-"RvƑ(ʦa9CQݣ̞+6!Jxe)CX,0to]Xo].<)zg2OIܤ eF+kSwֻ("KN z-i6J- `Y\E,pxn䤳DJiksa!k @ dfpxJ_S81)Xh\;hlu8%4y[lBG}#8'iN,i,cWmρ4.v|^W,$9mfE:Q=%к'O+}+; _Y' 8VZ6 CLql!-j{rz ̸dFaF`w,S:Mŀwu�hw9 { [pL#"8{һ›m#,w;FAtXf:SVZkzMңկRzuR-ٶUY9 n4Z=(qΗM2W Y=zu$@rz,wٝi'}Zµ&ov:} WܹXZү$y L5ds7uӢ<(Nظ㙷X<,=Ҙ쒱3^67Ts EqW*9n$.*2Y%KZHhStl jq:PǜO%`op(7;>翾MPoM6S[57&\v#ҏkO4ЋNg1r0yK4dtBqz䍾VӪ\ K�8޴CbOB[Ssghkm6;WTpj9 )lr;@zRZy'0!gl C2~=J)1 `Aq)6zg@.JKoeg%� <W8fݝ�0; sĻr$ !@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@Z<TKO8D QN)]5rgq=Y2V[k^�ª QbFjѧ&BYd\<`UWQ;5,t8=mJ#bŇM+풙pgCTUCao ʲf2(V8<v<}*jQyY95R* $bD"'JI\{) 5Pл-tYA\CLԺ)xn &\4݉ƺH7#  -L&I(h|+;v$H5qD $mƦ(7-3$] , $mâ8� aΆrW)•G`-Ȗ4roZ:cUq+rG:Bܒơ0EħI@N2yӘ~ISQkiBO:좻w<u.XJܑv;T:,76neG$LDiГR_ZH;f @y'&JRA8.I(:Z<jMsT&F܂Imz.ُ.1N SUSVh%F~P #Pf-I;gcqRؤM9kkc[?[$t(&Is ۵S-PC긍�sS@LHj:jU;&F] |M 7uUTҥ Od̃=NoEŔrCN0w>*ř<p*] SPRn_^t9G֡Kgd7OP+OZ@tNRcY/<J̩e/mYJiӼ6r/Lr8{5. mG?P6=(ڴt>� STY5lG!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@2Ʋ٨8\ kUߵ"ff:µ\Х3c5i2ŚJJŕ"0Bd 4{GujPݓe&HMjf9;M;#{lTcɥrS TsN-4RΊ,qŇ\#˙jָqrđ52C3$ڵZ]Fn\E\ \c : !$$).@G*H(ruƺ |v˴dK µ'`#8<Һ7'q"K0 Bl m xN=+C9w@DӰac<S' C߾'R%vИ줋oUƗ_VCa&߾$̹pHKMPI$&ga7 gEH'@09$0Qdf�馣F<#8�l%9Mg#HD (; sFZѨK-ВF *UchhuzG4D+i1-clJ{^{keǔ QC&6 8ZR7e*4q.B̚ w;Re.Tk51zt+U) N]6'n s6{(ҧc[<D"8M! B�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�(9_c(e[^t rNv.SeywcfXZUOPʛjô8@p!A¬;"yP^�&EF*VLeEw^Uvșt2Iv2 p+C,gd)7 u+ ,4^x88rƩAO4̭2Ѡ_6S)a&3|U p+4Qͭ3jjyu$!@YMH$`4;CUa0JD!"�$KnD(:߀n7G]Ù!$oHə5\�H $B�Lɻepi!ɻrWȮ^dI5zlJ4UV"z(Ȍ (ʝf r0dܮOA~XGˇ+Wc)2CZ0�@J2JE3MWUV4(fE!Z,B� B� B�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@� o b2Z)ŗe`Ɠ5MSg+3tgfhc)h<= 5PYGJ W=髉ZjD}eAc|vQJ͓Tn]ς،La-&eTo x]3};)0x*_Fbui ;,ۡQ*>KH֝4 䝊/6X`Sb2ӻ[=kN唯u7Q*5de4KfCBsa":KsrLj9HJD $B�!+Z`.:�'�"E2b.h`SU7ת 9%EO \Λ[HJjE|ȕ\冫)cX, Řlĩ; O?ԃ8Rov'"ɠ<G+)yi]E`!S*!�B!�B!�B!!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@1֣? Mr =P!q:j֑\o~W3 B95fZe?'gfOρ\!snwoNjeHH7Ϩy@.'cۋ7+.SyhEgc{ Ik hh�IPWu7{oL.!�B!�B!�B!�B!�B?!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@�!@ endstream endobj 248 0 obj <</BitsPerComponent 8/ColorSpace 249 0 R/Filter/DCTDecode/Height 359/Intent/RelativeColorimetric/Length 22354/Name/X/Subtype/Image/Type/XObject/Width 841>>stream �JFIF��Ig���Adobe�d������!!##!,##!.333.!>BBBB>DDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD��5�gI�"������������� ����������� � w�����!1AQ"2Raq3r#bBCDS $%&'()*456789:EFGHIJTUVWXYZcdefghijstuvwxyz��8W�������!1AQ"aqB #$%&'()*23456789:CDEFGHIJRSTUVWXYZbcdefghijrstuvwxyz� ���?���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������m � &D5ȳ<s_.MmV^cgdEd|ehErW8K7/&}Ęd=>(G)wRK@` N <yzV KNmr<%C%Ft.xrQ_Ygc1w#ASׇK5Qfu!O/loduFZՙƮ'v(qTEN^xIeT־Z]l5GFT']AntbE)(-s p :rS^aʀ������������������������������������������������������������������������������������������������������������������������������������Z0yNsGӳ)no6\n:x{K(%d%8v1BO(�N������������)r\Z5'XItI� N朞ASfE'[[\Fftɼ;=)itJ.VGͯ-%Z5L.XW GEO*!V*tt8ȼ!:Z:e_X U(铡%o b� ����������������������������������������������������������������������������������������������������������������������e2uZ`]PMЎTWdŒeUGϏܶi/R[;,* {$W%j3ZxNZ}HԭT)9z~udc{vkȺB4U|5ɴ(qcy9䔖m3+Jݳ>) nuS^D\g[UsQNeȯ=k{n~!P]BPˑ_ c=+/ td~es<}k = TJ<5jC" o5H΍vO 3<W"xEK UMQ?5M{SRo.? F2+^f G\XS]\n@nE$S(p ٲ* 1rO>&1obsSKɩ.V.լTq=B>:j\k\KghU澧cr~+*r/:9ԍfd5+Ilg]1X]#z P\r:j{;*wwl%VQSgT_t2aUS/[&*p2})cG(,SRtGC((qMPղZiQ\W){FqRRWMb,Ȩe+,WkISri<6tl|&க~m6javс'hTZ2΋OcGRi����������������������������������������������������������������������������������������������������������εz9<sSSe^#w i#J8,QDZV=RSZ[vEnS̎Jx>Q9SίQbа8ت+3@H19*,ym{IQi])Jo:Mɽ-8NoUǪRZ-}*m!(I!8]V܅Q+zAnB:�8%P/XF["(*|TtL9.T8H>OEJ>ʛӁR.xh^F*V[^kFKeJ]ipX99\įCR]"<:S 3(QRC'?1Ы1o%lӘki�d>(pj9.,ωSQƔT4)wCÐvYD;է^xQ}D|%:rϧ' -qveZxhʴiVmXFk\Zq*yá𗜮ɼ1VS<x.r9DsIk΋*J";CT\K[DjtT]e9?[/ +O:%J1qM=)7P$pzcvMQ'Cuc;uiՂ9)EAR:tEJVi! KiZ\D*O(jjQʣxK}Bw8eJ6Q^򬉹QhlIk\|/SΆkzj<rZu㌠mֶ2pRWWLXVUk\/� Sˡ [m\ib.�������������������������������������������������������������������������������������?����������ib��"WEN|>Uἢ㓭<m3Xc%}NYdί5 L2XyWT28f.<V*従۔I(UGvEQY"@Ołxu&W}i듻IkĐNg`U$\z[QGL5PQ<;z+ (%}cBPv՗s;S^>ܑcVm.UۉԙD3<{˧| Q{[9k6AɷU T}HVߘ(LG9.A�P:8 t[ FXI'ΎRi [٢ZѾ\NVi٫2(FjI$ISn܏#ʜ%c5cGӜMN$�\dR<z. Z TWXdY&][%yK\g%ǒ/d'L4ahӭڊ= =S#TΥO+[Nk>R]iކQK)}'uhkl Cr3Lruw Q\ҹtKMI)E5t(*2vdnRI: e=Cq9PI1H~d?F;ڱyҌp*IoGsh׊:*B黧֞Ƶ2^ r+Ƭ^t~rjCUNk-C[+L|#kAoɦ* p� ��������������������������������������������������������������������������������t.<նr7r/UH%;g&vW;6ݖׁT/h=s-5dY[oR^ VԒ͡tIl.Eƕr͖CbqT ]ګQͨ.C(ѺZ_ueڑU/Ge*j7yɩ|U=Nz}mՒˡ¶3S}/L>+NꓚKJV~9O]oRVBUm=Wl!V8d𧓭]:A|)-8YWUgFxGCGťooYVUSVr9)p|&n!7}OѧKTg?rv]G)Uuj唧ؚ# OK3Aiߓп UI{l8d;R̰ c߮>JT찻0~\O)zԅr:>&.5Gwd['RqfQY[ YzՐF1j\H<u9ʫV,wnMb℞$%C {u)͹=ںΎ;) 8ntvTuˆvoE8�)Ӏ-P8N�(�XPN*J]r 5L<ir=iBPv,$I]pVFS%q[5,FR&uR%v5! o*.oX ' j5MwAL-ރlek^tB$jYKЪ%떩L5o R*kqʢ2{٭*hN׌(%_'t' a5‹u(T, {5ڶ+PR(' ѩ{8yj'dt2Y{R8N;9W#;R2Y#ÃʞS-OWYvI6ԯs?1vʧZ&1e2دqry]ZkOZgbIKM6h��������������������������������������������������������������������������������ײ3*tV|/0T+U_+OW5Ml1"TTIQTכ+mw\Xm􆒱45RU/R8_GƟˮOJ5ODhWcw:BN6]UmYIlR8+.e?.cT(CbA&N t8%,:]Z4םɊ>/&9O̳PK Y1ڞKt.\nu!^>)SS}m6rSg.G'n ⼖<czsMF/S֝g3W\B쓖EՕ]h'ƨGՠeL*:GV#P[3e7NlQF&>�'R2vCe ru&Y.&">S%(1iGm㢶-#N1Ru_8dv7#v*wcaFrc QpaJ1a,dkmG6Iy2ʲhts8' HTx%Jx._6i M]5͟..QGk5x-],bnG[k1~0炳NYq*ϟ6=7*z&H' ^\NO�j۾_`9QNńU}91F/?d8݄EJh_N}|;3%nkTްӄjCȾ{uN1i0"CYM{ɳQ �tXXV*c[N#ȭ)vzddLᾆ1.d{Ly3̝GfnNB0M4v$RfHEZ+C6zXI5ɲ, 딻Vh*.%p8WU^exrZ]8׌ՊSJӃ4Ja5 5'%Jͯ URKZkt^HUS^to,ɳy} Ƥ%tFigY\%b S!A[KRoGC".Y D3 _.�����������������������������������������������������������������U'WR0xi ? 0W5=\\!y> [S",SJB~+#}=rQ}@1^r6t# _pey㾺Sb^-JqEpH<+Y: O{)b%b]pis/6s k,%(|!R|9J\Ѡ9$ @QcNrMKUpiW8{ӀN^yfhН_URy%vG-k6q:ƍY+kmkRhCuc.7ʕJϏT͏R@?D&bXJ#}BuY:tIGlrhu,UTF ?o1t>4*8{"3nEO&8'j?oeuPr)5yGq~j4Fm%*RwJϗ\܂ho'5W{RY{B8;pӣzRڕ!OyȮzS*JJvr9lIXbnM!:ϮHk#k)Uإ K)([3mb%]75)Noe6L"OHTuuMI?1oKojH[)!}*QΒzҖ8G4$9B TXm=)LT:KcYV-K'څSB/g%a8pמXԖyJϬ#"%)pl]2dXL`3KjZ|Hb:tM -h�� re-0L՚Z (T,v8' rJ5dϧ|(cA.)`diEM_E8>uN5c.t 5Rt'S˚^Zl1[P% <ܢ'l$mZܝe[(鎾ngSU#kI2ZSqjsyѴrJ{,l` Ri2> QOr6_'\*Lw֩,KjRXhѱA]kGlw/3�*�������������������������������������������������jrxg֚v-l)ЎBFY|ܠ^몚Ǯ:q*O)|Y<~.vlg,%KSY%8.'njjyƟ=\e- .o 䴕Scj|qVJg`o It1=5R9}r{9^Sd Q{$2x7ܰ!e~rM*ޔmmz^,Ƭv\V BK&K_ N9OZR[rF柜vlPZzM]# 5(V"wKFPj5x:kJz9R_8v9#:Ǫ_ :a<֞ Q�i\rZW::8z)ӓxR+Oaރ^gy29^Hz786Iv kJ,,ЙRF~ث&=ۭ7aU%€XDƲK7V3C⼔'V*\F�w-QoʚK(WKEv7OM:@uc"vzb6SWלOOACR y5OA)|JNN|fZ�&1&͖Ї�61k=LbRZ"2Z@iQ.ڥ"a~kc C$p*:{ Ƈ[׎YhKZ{Y3Tb%ME+)6okb:u*JI*Jy=BKmS+NMޗh]g^'TWqa Ə[Qe9:̚M4I/jJԄԛ>A$=gmYxS3^ȔZc*'{Ǭ-Yu|YBɧu4GCd6Ǭrgu8WFR\E$*ɧuY<d[B 9PօtvLn} F-&kZ�d%tpK7GUzjS:EsԼ$%:<tMJN8gEjֹJ_f\Y H9賣Z:jSr8_seM9O'[RΏJ֋!ڇYLpڹTnͭI[9֦@j(ܛ ^diR2jjJҏzQ -VKzN'Z$.@AnUj=5*Bq4W.��:D������������������������������������������&J9֯ )^򜰄#~qY<<'xXsbrdB<SP^i89sR+խM5%bF lHKpȭ{K(bΦҡ]};1FUC%ug,#'EbȮ^ʱ<i%:spcv2urΤlK}'0EF[,,/QGJ[c7RDK;FC 5׭-ëy֓I9pb8 ﱗغp yL?`�=(VҮ75 x\pkVkD l${:i<(G q֕Ύ+tqڛiY6+=px\*JGC$gҬ'C:]#2xnSdliLZ{hYFK(}�v58 9Szػ&ǠpIΊ2"2Sz�èK@mxät!8żVe1ϸq&~(m�.lg{csoo.$`։u\EIT=#!h:*OiMupv!*Q0r^.ȁZ.sM;֦icg''Y696ھG\GCrTQKHVITA&)6xIv+P*ԚJJDUi:jҨԈIκ$tC Q\^ۀlz(bmt\mQH1 ǝx+D1\Ӄۨ�[\m}>8�Տ9ƭ xKa`'m=~50NTw{ס|TOJVjb3M<ʗV-H4׮,TXGl/Ic~)NP\Fn<}s*Z&UeY<J)=̴V5骐lz%zӚ+!r~P5خwxLfJ}`�XR���������������������������������������Juu߃i~q'RudPviQ[eZ)҅(AY^m-ld*^@4#Iɹ՗ -zznifBԒ*!N)կ>(+عH2QeZZ/j4'qDKUGu˯F{̝c9Y+LSOҒVPYz-U_+I77&<%m^ʹ/[dBF*RLѣҲ@: �� : *9h]:�1< Wa)P>ʜ׉"E`ҵ9iz.p Rqy5薧0 kSfG+j{+gOLJ2WM;E="J)ך?Ӭr$[luS=h\*V|$XC˅[FJK9[QÈ($1[@|CϾtMm�crHlX09p$,.Vp5kLeۜ&4ΜcWy`Y8 I*M_: <+,dܔw~;(5SޒFUV%8Fo5;$2;eȐtUUJЂ;үJPP8'qM8fԍ6)E1c{_KrJ-_-),7f%A1(ք-8PkRv7M>'xksCkhڒM$;2)9ɱɛXT=d#FQq;N鹧+1X9I]:xcN4**[Qymh$ӲOo1RV|!ӓ)78jS9Ө<3jkhI#E9jgUczsVc]Ck@ t9 MV:pU{I&1<Nʎ,֌VيDpO vx?3�fh$ϡ:XKl  t7::R*rWסhҒWG RL\yHTVӡZOCMw<!ݒ]iNPkkwUXvqLdv+26GZPka(N!WnYZ\W w.TdO� �������������������������������6쒻o�w9e-ƓqE\iezo4J|#ͦNZK$WKb愄#N*J1J"QԕWd)T~.y5ʙdG#F;*x$rFr$=EJ`9y鎲)dɤ֟ʞ2oduw.VBܰГm.WdX!KmC XƱ5&51bbvvMo#۲ŝ#BOaJQUdExP׈ɍQ�MYp%M=2vb\>cLԩ,^)pzGo6sUV-N�ss;-gQDTw8�96sQf:*qx=�65[!'A)|rt %ɉ&fD#\Àg9F%�ؙe\^atWN_*nb8K?)rv FBs:,"C\6GFATҍ֡iRWc| *' VSgHN*I2Vz}J9#euD"nR4g{(L Z{i>uX07I&Uqt 2 yuxjԨ" T++'ɕXjt˯ (D]'"ŝ"t tMm7-8Ek9+sq(t`vKy56v$SJԅgЂ@܆(٨GI$)icc \3q*$@8+3N/4i[ ( ~p: i<Sө�RJ QŮwNJ:{ֶj:,^2+sOGӢ, j:5gR>vr:F:hֆtzVt!NU*hwdb4<��������������������������ZhΤ0\'.P@!J.svKݱSv]f;Ɠ9rjNjJ7//`tZ+O;a+ʗ|}J:F+Ke6Wz%<:Y*zȹ=^VUS(W+}\Ȱ8f猵l\(BfaךREC5R\k9FV˕X9#5Qrv;C&Q 6FRv;&Q"JbddE QQVp]W:gAμ8s.7q.%k}V:} bɥ}_/᭷pu5[<-OW>,aeg7eZfO#đν`,xթpzf=BlrG)IYhzI]F=Ck-omqBn)]j:qc6M)$y�T"9YhzǨJ[㢩YfːǨuu~P%4Ր=B=@gfc.c$SZ=Lz=D=ψ_!o tѩwl{iӍPW cznvbZkXUQ*Va'LUS b,kqqk[=<)mޤ:j+TRAn#&NpN--,CX{ISZ-eX�EJcUZI#T!-qN1ya% i)&d7:[RKR%+te}(b X~SƭVc}pQJཚ/ȭ3rgX4Y&Zm9RN&_PBc Y UQB\B,7PY([EN9,v7wcBzeU$`WI]!լ6Gwj:tZ^")E#lt$VMIEYj8Ђvc:Dcvpݶ*AIYjHs)%"X']G�3\QΜ2Q1�jwX 8tbO %^qpIk� #!SѮ,2Q:8?3ko ;~*nmxˡB9Z֧kƲ -(F7QyԲw:6~f g7VA� �������������������9U)y'sܪ2E򥯙argT7]YWVvU_<gtFSܧ.匟rhW(EGZXB:zYFW[)x0KEĕt`qPW2 BZ3Iԓwm6ԯ1 UcmX1X1k`tk:uM'f GnvL3Ñi%FUE%".;80V9N8`ŋlQqK<O"KBFY7K}#%k~x6zsth&=ohB.}-xU#Fm%c8׋N)IvMS-q+B^K)IX/1ɓaS(O+"Tߴ/Y,%LA <N,fs7V}߆ɥQINqVwzYW(֬pJLD7uN�\ JO:@qDLF6ۻ$N|[>->c#}gU)@:G9J,4!EPEP8*D(|8C+p8ҹYg^J!FS%e򒬭Xʚ,Fp\dE^e9Nh. '9f99* hm^ T 'ڀT*R5#/D:u*ZgXTLᦄR qI] IV)Yଭ缞c}hp"AKԳ^ ;2VK=)x-VJx'%|Solr W$rEậJΙ5mڒV.S^)2ׂE pňUPRZS-!%8-eL:eMbrLVv(KC!edKRZ"Yz |2VU2Q+pJY ItjnDŽa v(nR7`v#i,53⦃dVH;zI@5 MǼ*MI Ӱ:\QL:�22z# #ZЍw�Ғbj�DI�"lyRyٲ`(ߝhcnzZ i٭a-Ϊͪ?YъI[pԫQd6kGHVS< $:8X0%:Q N.w\Y&Q¤X9jRH(f>E8=O4(�;HZ!yM:wp2)-}8Mg/SX�JEIyM(<ĂΗe'Cb>TnONoz TRE"jzXo%.H2zm~[ZxC&2pvKbч)ﱒc}Z[ ΖK,5b0:5„ޭG2yoW Pw;jExpcXՄ9_*#Kp^o:9#Խv,2vtz'x*SRdG<ޝشN0бbG(R> n'(^m%DF8SW HP'%)|G9zܹ81C7:\\EĕlW,<#jQo|[r{~iC7!TqjI*gX̀VzZ%N/~Rs\pWp[yˋ'U�u-n<"3GڦӤNv[ԑuѓS| =% ?׃ RWiݥ=IM37IUe#{v>.OZilBJ6oCD[n؎B!꼳l:*؈~Έ4ۼ#qc!BWSVYS* UxʜXYeN,zĥǨ_FǩE;3%=Ltrڍ6=D17+ŏPo(b]\Ӓ'4̅U Fױ֊4gluμTv_>t;.6})ϕy *nh谔%YZ_ )A6پwJ3:wmj3V:ENghE*iV|;2N r_tgҽ)3ﭩk JjR,HSF\[y"1)h֜eNث,qIr5qV]Nᱶ=cRt:s̈́}=W̓wV;`J\fAܼtRIZnSv؞c,rQ7jHo1 J+URȶQ.WwxX9gm]gIzX_F vβ}`ӇhUT:v򎾎BDd2)n\2ʬwLD91roMŽFYosgS a7}}8(QwtZW8f8�ue iCwR {Gn-i"IR>cG] 8&8FY^q/:bp\b=kvFNJĴxۥ#8tU% 8.[B a3A)J\)72q$mzcѬk:(J\srwiƄzIqpWNNڜq8ƌ!k8kiit N1ӧ`MO'{Ўj.Z7h/\d6san^原U%DI N52rGmN1wX*U <)]X$|R͎߅8GT:zVLV5yrk ytV5%#۵8mZH\:yvoz/LݻFB+~sle&nj*nl� 'w8kg0ɼ-"=\u@$ ^�?8cϾ@I6wHЧv*F֖%.Iz&ټ' Ө'vWՈAO?s\g_((sYM,\{= \ŵ]\%M%$6L:&>0{$Ұ W6'b8i=88뭮㪋3Ssi%6}m"knfr|.OU)j|/MπTYg3gf+QR+۠㛯t3l,8EiNWht(^@^<RJJ cji>{%URN%p9NlEN[vvo%ai>N-ѓMSKw[ߒAc'VtnOLչ*mpqqdJv}A)'NS*Ww�Ŗ|-wŠbMOsrco\JR̳>t>,=;Ǚ+Ιruw$,RmE\XN+*ߑ6e''kӚj?\*Se+v*j]OJrJx)/nLXnqir$p9{(wrmx wJy)QwDRIenM&QߨG5Stܢ& :;\>dJqauU;Igrt\FJ9ק$H7.X2#*X)(2R-%tJ㔖|wDɚܪf)9a&UqN6%p$B);{XJJYƝ>sgHHδ]:SNjRmt;d+XVkbNEVk!҄䜠k+fg-64kmykοTM JSwN:BM;)+ӢΉ[T;*HF89 p.v!u,5iZgˁ)8BU͋$Um)Jim%{Y;>] Ԕe9J5kЄ#9nsi;MS5+c)Ԝo+.gkuHlj9a9a21Pz0gvU r^28ɼ(dA7NSxtͩs:8II;=,ma>< n(wSksYlJgDѕ!<ƝqzܒΖNHMX,j8J�868friX̎#$8E]XJKky(OCTUj9'xN'ZψI"b;qnrU`juPJ׉CG- ᭀ#ia`W;lXDzNpBJmGb^1<_=&JҶpZz8𣝢%EhbE򊙫~ep%U˩S;ɠRS]oCfiŒqg9gD[l0%s$kt)<-$c6S̍$)iw"Y/щJ:[ u&lm(g| q 0:1�J9-Xh|ٜb"|"йvSW8Lj�M\^vh?)S9JQs)LlѪ%kk:@RGpVJr8ROc,pzKIRZػv%t Utt94p'1�h8*A@ࢍhppG� 8ECELM- Rb9 C#ބeDtu VеDbQm_YP[ѩJu2yHMRŻ$vT!SȒY֔Ԣ,H)f,& Tatq;#*a9P3ͤj=ɝc8ڝmm$ɠ!/%,흅oң&AMSq9Ҝs ,c)E])ѝ䞆.ER\):ԣ+IYsa2ӂW.W[QpjYѶ9jo]ť:kUΦƒB)I;Rk^ЁRdڞ'UE]c}̇s\m!NQ>3StAN2yKQƊnI3g;A0i&%JW(U-xc:SQ^Zo3:*D(M7avjiҬbyF1"ԄSY9P&FQ]"jtUEx9ƽ7,g]:1:rM @h `8Da-ѲcÔlT^2}f'=-؍$ua7v/PfA<^sܣiRI$U]/0R^ocu (yTஶ5¤vw\LKKe6yM:qG]]ыsqҠd*ve+ݑ'i-J1/KLF1Z-,*S/gi2 rueɠe¥R弤EYE{ZVX]GkE(8',c7hh,W9wlAl8JH)]jKX8J睾;y�)Ύ֬E͎S F)͎:6rMY Y8] ]p;DsӔ}1rRjg^�v�8~|vqN Hry҄Zxj8KhPIHҥ�S�AP>%sg2X5V/]HI|k:<]@p::C EA@࢈*D ( >0ԥڂԴ\am{rZۆ3[LqZeXz9z?2`vˆ�pr@pz!Ɂ=FP5%kΦvrfmnMʪd7G+>fiQ26u z1Wĝxf[;2'},t+(,hsKotP[Ddխ`'aRGD&sLMӿgZk4jW\Hl:U>e;zN1qqQ{vj!9s{ ]΍TyZ.qsߤǝrw96ݓVHpeKNsXr㬁sj6vͽtN94VtNTjjn=BN< ֩i<e RL )Ð.8;S~re#6|ġd"SЎp&jVͧ fӨ'[8m2k{lXvVжAjΔ/::nN2KjJWrDIד+-odAhK֓JYp_5){;"-UE- EBux ~UsY;ZQ.B\%*}G&Z KzmϴzRZ\ҷys<Wô'3GҁU)9TŒj^trOצzBMic+ݎnZZ]1I* ZURnhq3o6wYZKO6EȌpVTKXet5NsG-K掕+դJWSM̮rkeF5v웿;8˥7rJ'G%J *իԧo2U:u穻%;%2B.~[88ax"Lπ5_[8Nnno8-,-˥IOd T;,[}Xڊu񛌯I/%t8(lmfG{▽' 'dTYΧW>Fs�;ak#\$9&g;!=!9 DsCC楚;?,HW0M4fҵeASW8|)90A#0~3q9gkdvCۡp&$gӥ)s")3)2"q G1Q2"D&rcC".dNPȋ91͈fɍvj5�LhPLhPLh[!ld-�RpRPm)%iAd�P�d*Q|mN�ھ-,SM\۸Zp!jDTS C$9$p詒)޽W:m#97 \Ql*tuᜳZ:toWbrnrӌ%|l ⮣L/**ʵ8EZdҟ:"qJ93qy?1_&T)$jC$VpYЊSs utKX*2RtM'1g 8mkAO!ΜjSOsUXcag%Rnʒݭkau|n2r|87v %u鵠L#. du\qn$uimd"R/NGIpbEV%8L"^>nîr"dL.#v@p哥g-w zη8%letւJ ^,*swװ _B9t3soYӔs^f(Zۣ"-W Qخq|%'m {V1;!-Knm×}vpNG:i=ʝŶ3,9:N1G:o1-=u,1To]{ӚIigE%}/sjjJk&mlї㭒`P8-Owg N7iN9g�z'Eɒr~v*ƺz>㬒أΦc'H�Z0�˔pٽpg${[4՘0ZX'XʊEr,#@�QhgS]CX,+tGYh| $ԩ8椒= : s9 :t(yBG$΂D.z: sB磇$t瞅@(.z(.zsg fr979 Fg!s�@ppn@pmŸC..p1𛃺8!H4KYO󄲇%FRBTwUbʽOqɝ gzL-ee\[JGKqvIf"|H;ƧFS:ąVNM;S9Ҋ=+#Mͺ-w'}Sj<(BM®њh򚩝'{5AYѓI[%] 9I7f$TPAsvN9Jkz^xƛh+V'4rO|qj% 4Wpd�{ċqn W}·cc4u<jEruBB:RtHM;BdZ/i-#T+9lJdaiM7 WIMIYÑXOA>}7`ols`'nuO㳆 %šJy%v[YޝѤnu'3$s)]ZX累1&AW+Ȯ埘Pt:78ӈCnkr\{g ;%lF-;׉MmNd7h2ģVo(pq5"PwgJ'*!/!wYY< ^;-UV5GG^'QɶޑItݦߠk7` MuR 9^Ox*F,Zrw<t.TB{!YvSP g s�2CiG3"e >B9 v@ZthzDRk@t认,@Ӣ1C9s:Iզl3XRgd[3d̑S fs̐(LfH\Lfl͐N ǀ.k(sXų(1lbYf(rbY�Yf(pK1lS`8�K10B8 10:pQ@P8( tXF[{&&gQF'!*8gR6bD9x:\å!I֓yWt Ƽ %(^.A%?vJR178pMK[S>up\".pԱ[�|%8W-+' ]\VY$q\'#VLEb&#�Wy&8+<GJ1U>|jw$0:CY!>q"4/}VqIٜDA#Ѻrs>c=/gULux -jΧYlbW=i$-$Z9s-TZNgv5Ԏ˴z{N̜)Йu><qԉk Tw.E|>wԆwԉk9$v+'CžK K$6W%ΎJDN&et''c9/uQ<SB: <+8*cp4 DQ� F(#FD gN#C ` endstream endobj 249 0 obj [/ICCBased 246 0 R] endobj 250 0 obj <</Filter/FlateDecode/Length 343>>stream H\͊0y9m҂ŶaXwƱ+1D{w)]XA ?2Tv=,oS֎]UD4UBA&XKCn%DЍCA 5zk!Ds٪ie|N!k},P1=z2*49QrS > /$ݬg@l QH%Gsow;f->z/g>sY4iqf)' 8v0[-1cLja#>}8K=|IIZ{{Tů`�\ endstream endobj 251 0 obj <</Ascent 882/CapHeight 674/CharSet(/space/comma/hyphen/semicolon/A/B/I/M/R/S/U/a/c/d/e/h/i/k/l/m/n/o/r/s/t/v/y)/Descent -250/Flags 96/FontBBox[-103 -250 1165 882]/FontFamily(Myriad Pro Light)/FontFile3 253 0 R/FontName/KGORFR+MyriadPro-SemiboldIt/FontStretch/Normal/FontWeight 700/ItalicAngle -11/StemV 116/Type/FontDescriptor/XHeight 487>> endobj 252 0 obj <</Filter/FlateDecode/Length 356>>stream H\n0 yCBh+qhb:@l\u"Q>'_Deu\7= I \;'oƋy\;<.SX-XWYeT߼$cYB4AFKڦMs,-17B G!3Bi|7A r\Ȼ1<.9aNbM)7cMFlǼ#Θ3d{*SFF)1բ3\E*ȧ(b?̌5SM4{A+fEb"߫IW!4YG<^,į�� endstream endobj 253 0 obj <</Filter/FlateDecode/Length 2365/Subtype/Type1C>>stream H|T{TI!l!* f0u*VnApy.,*H $ XAE,O "XP|V{5"uWz~x3ܹ{c quNVE}Tq 1Qps9Z9Xӳ"$1l eNy`Un p<-xgkd-_~([P #vK*>N%OP/WjJHDe*(>N %j,.a? 9^)s+#0 0l&Űǜ0l5Qy/0,xM0o2K8 $Ap𱅫E?yD,N֖[-;,ߋ9"EϺ(o| $5|^H:h0-Љ3@Mri -1:&A/l@sadW;T Z(tFGq O=#AN)6DoRD7bY�^!7 2@ 4D$2L& h4O˲ %sHܠ܈R^&cK[n*LZ ۪W3'Eg'Owܗ< U*5ZRݺ\riGS1qEpf= m<RSݼK@2y)}qyDMvN2+F!1'-~ B;Df"6YcT sQ$CP>ר׉2ɬ;[- xjp|_lKWtxI2xioӍN]un"*> %ģu?Ž![6J;R]dw]rSD`Mk2 ^\,2Z>{{DreGsDNuēګ̾YF2V{-b=H)g$Jkk10zH&Nx% iHV֫AYs^rJEԹӯ QQW9 vc9y<&LXR.gn@ƫǧљQ鳵`-aar"]ǥ pfwj5yUo!~pM4ր l|CUS�} _T2HDh r}fO݋6HR CxV(T5'\a?TAY>C+VaX3@,h:jw()oa/`Q_V֚CIÛ3 Z= 7 bnع Rk{ڹz341yj)hrE5Qw,CR;2POA(WoQ4>[$T Yq~>Ƀ<W r trdخ�fSdQ}/<],LS$O_# -As'=\-l�"YgKg+Yr %9jqn X ,(ʜ-P4$6U@U垬׺n VH]N !)Z0 0nK1ϜnfUce" 2xmfqA*h~GMEߢŻRSR#9<@�BC '*im}tJ ] oJn\(lH2h`A.^;8y!1S~* xENU%AR֊.gFdG{HM3b6WF/} Df6 < W $@\fH;S!H=yT2ز{g(꧂Aⳉ }^LԄ�kH75UGkf;a4 K~!_`pe'L~uz +<ږF^F&;v 3@i{ XI55OEɌrCb? =`/**#GVYI76HGK%gwKğGdcA@j,ۦ*Ho PRS st#I\Drï@*x]&͑V:~βK',=;#ܧ}d[K .v.Z(s}_ JhzA`mF}G[ GsIcyPۛ?Mݭ-9Y%%%VN<3>:ؔgxMRI5Li6w:L?O9yDvtܴi*@I@]O[4?S=<|ߛD~D�!n endstream endobj 254 0 obj <</Filter/FlateDecode/Length 353>>stream H\͊0yCښ Bk[`+1D{w)]؀/dfGTҵCo*e:`­ubHۚq052}0I.RDoBhM.j)tF<LR׺a{;N+<dY- 6jwxr]h6"K9 6 rʜ)nĊYj=3dW8J3kxϼ'f 4(֬H:1g 36$ӬYfaMz4ѤGs-jѬAu&?IƭY{8yuZE`� endstream endobj 255 0 obj <</Ascent 897/CapHeight 674/CharSet(/space/hyphen/colon/E/F/N/P/S/T/U/a/c/e/f/i/l/m/n/o/p/r/s/t/y)/Descent -250/Flags 32/FontBBox[-92 -250 1256 897]/FontFamily(Myriad Pro)/FontFile3 257 0 R/FontName/KGORFR+MyriadPro-Bold/FontStretch/Normal/FontWeight 700/ItalicAngle 0/StemV 152/Type/FontDescriptor/XHeight 489>> endobj 256 0 obj <</Filter/FlateDecode/Length 341>>stream H\͊0y9bk5R<8v5hdJ6 g&QY*8C4޽A⭷jCۛq 34NE\/ӌCeQ9Df؎W\ͷ{{WY!v-Qƽ6B6UK~^688w"ƌ-N1{Co_ m'vwUsvK$|"Qy&C5YLJ{=s"0K]`29^K1Ks/}>0%h֬YzEg-cmY,3Kߌf3c"|LGI{ZQxa7e~�n endstream endobj 257 0 obj <</Filter/FlateDecode/Length 2018/Subtype/Type1C>>stream H|TkPWffz B+4MpZ{} qQA�F@\PE"*A- 1٨f)cYf=MXMj֭u=w=$!!Ht YS <W25ӞGJri=j'fs C0τ/caCWrԴ<qq#htIZ1rgn6+W\yZ)FLsmVo6n1 ;O z9lMzv׻\1QkSqDV#5ڬD}Kټ"vhFB$>MA7A,%6*$ &P9iClJl$cRFF6l|l2l&Ⱦ'rP+)j+Fi ^0,KSJ jM0I!]aaZWZ2DXvZBM#5 Yc4Ph{ ?<jeb4{Tp͔)9Eii͈`Fٙz&mӷ,ܑLJ<YdAp.{'#3E_i5@{w56T,T 7ECfA0BqAA $(i~/ήOa>>|**{HjWƏ_%`< <dc.X*pO&]^W'}{r^KZKkM716&飼sn+Gntް�=OD3 Nt3'V0-Q+9d9RyHדJH^ښ1LB b0aX�f ,Mi`˜)?jD۟X4'Dne-բ`ӻ?9u) γ}Nw,Q\94}۔C(W�D!F`o~~s KbТl,Чp;Zo~Pa^aJx<]y*viC쉲 s!#a2ݑ<_,ߛObPLz셅~#!kWCDEShlu7T&%mCLس{o Mh4S�r|싍N߁].3+KGVi䙉:S0 ;m7Nےw8N7tPɼx ]`OLY\lmp[Z S[(n(hmu3N<˜} |ہc]č:AʆZ iik; K al?1Ԭ>`a[E]6p+fJ*W IިA?EV3%RS&szr<w jàJXeyqkr5" K/"\y AxK|n'jdCҾ;nGe)a3~).G(*[NxLT;GfS [Xs}`S+4QҺ U06!8TShRS_?y;PUՀw(W25EeBnKxjb:P 6ZE'vdYcߍ?r `cv$񘪭|+EsVLSSD?mlޭW rHb1^AN>wc/8ePS\G6?~C- b/8qK529"aLIYT:kbgrUyeiE)_CYܒ==ٵC# ]6ߺ)+.M-˙]IvrW2׀b,2\oͭ}(Ϊ ٯ|玷ebo+ס ܝuv|5%M{f{❥l? �1 endstream endobj xref 0 1 0000000000 65535 f 4 1 0000457678 00000 n 41 1 0000457998 00000 n 53 1 0000458108 00000 n 127 1 0000458178 00000 n 134 1 0000458564 00000 n 164 1 0000459006 00000 n 169 1 0000459454 00000 n 178 1 0000459854 00000 n 210 48 0000460298 00000 n 0000460363 00000 n 0000460870 00000 n 0000461179 00000 n 0000461584 00000 n 0000462109 00000 n 0000462526 00000 n 0000462952 00000 n 0000466481 00000 n 0000472403 00000 n 0000478159 00000 n 0000478572 00000 n 0000479469 00000 n 0000479861 00000 n 0000492515 00000 n 0000492629 00000 n 0000492743 00000 n 0000492780 00000 n 0000492876 00000 n 0000492903 00000 n 0000493317 00000 n 0000495968 00000 n 0000496005 00000 n 0000496101 00000 n 0000496128 00000 n 0000496542 00000 n 0000496581 00000 n 0000533507 00000 n 0000533889 00000 n 0000534247 00000 n 0000537162 00000 n 0000537610 00000 n 0000538110 00000 n 0000542086 00000 n 0000542198 00000 n 0000542312 00000 n 0000542349 00000 n 0000545018 00000 n 0000568304 00000 n 0000590849 00000 n 0000590886 00000 n 0000591299 00000 n 0000591658 00000 n 0000592084 00000 n 0000594535 00000 n 0000594958 00000 n 0000595288 00000 n 0000595699 00000 n trailer <</Size 258/Root 210 0 R/Info 4 0 R/ID[<38F81177F239849D69C477E8E6C3BB41><1E176FDB5E6C4E6F93264F099BE4DA4D>]/Prev 453291>> startxref 597803 %%EOF �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.18.2/doc/html/example_2cuse_8c_source.html���������������������������������������������������0000644�0001750�0001750�00000165776�15156613442�020657� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US"> <head> <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/> <meta http-equiv="X-UA-Compatible" content="IE=11"/> <meta name="generator" content="Doxygen 1.9.8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>libfuse: example/cuse.c Source File
libfuse
cuse.c
Go to the documentation of this file.
1/*
2 CUSE example: Character device in Userspace
3 Copyright (C) 2008-2009 SUSE Linux Products GmbH
4 Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8
9*/
10
34#define FUSE_USE_VERSION 31
35
36#include <cuse_lowlevel.h>
37#include <fuse_opt.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43#include <errno.h>
44
45#include "ioctl.h"
46
47static void *cusexmp_buf;
48static size_t cusexmp_size;
49
50static const char *usage =
51"usage: cusexmp [options]\n"
52"\n"
53"options:\n"
54" --help|-h print this help message\n"
55" --maj=MAJ|-M MAJ device major number\n"
56" --min=MIN|-m MIN device minor number\n"
57" --name=NAME|-n NAME device name (mandatory)\n"
58" -d -o debug enable debug output (implies -f)\n"
59" -f foreground operation\n"
60" -s disable multi-threaded operation\n"
61"\n";
62
63static int cusexmp_resize(size_t new_size)
64{
65 void *new_buf;
66
67 if (new_size == cusexmp_size)
68 return 0;
69
70 new_buf = realloc(cusexmp_buf, new_size);
71 if (!new_buf && new_size)
72 return -ENOMEM;
73
74 if (new_size > cusexmp_size)
75 memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
76
77 cusexmp_buf = new_buf;
78 cusexmp_size = new_size;
79
80 return 0;
81}
82
83static int cusexmp_expand(size_t new_size)
84{
85 if (new_size > cusexmp_size)
86 return cusexmp_resize(new_size);
87 return 0;
88}
89
90static void cusexmp_init(void *userdata, struct fuse_conn_info *conn)
91{
92 (void)userdata;
93
94 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
95 conn->no_interrupt = 1;
96}
97
98static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
99{
100 fuse_reply_open(req, fi);
101}
102
103static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
104 struct fuse_file_info *fi)
105{
106 (void)fi;
107
108 if (off >= cusexmp_size)
109 off = cusexmp_size;
110 if (size > cusexmp_size - off)
111 size = cusexmp_size - off;
112
113 fuse_reply_buf(req, cusexmp_buf + off, size);
114}
115
116static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
117 off_t off, struct fuse_file_info *fi)
118{
119 (void)fi;
120
121 if (cusexmp_expand(off + size)) {
122 fuse_reply_err(req, ENOMEM);
123 return;
124 }
125
126 memcpy(cusexmp_buf + off, buf, size);
127 fuse_reply_write(req, size);
128}
129
130static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
131 size_t in_bufsz, size_t out_bufsz, int is_read)
132{
133 const struct fioc_rw_arg *arg;
134 struct iovec in_iov[2], out_iov[3], iov[3];
135 size_t cur_size;
136
137 /* read in arg */
138 in_iov[0].iov_base = addr;
139 in_iov[0].iov_len = sizeof(*arg);
140 if (!in_bufsz) {
141 fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
142 return;
143 }
144 arg = in_buf;
145 in_buf += sizeof(*arg);
146 in_bufsz -= sizeof(*arg);
147
148 /* prepare size outputs */
149 out_iov[0].iov_base =
150 addr + offsetof(struct fioc_rw_arg, prev_size);
151 out_iov[0].iov_len = sizeof(arg->prev_size);
152
153 out_iov[1].iov_base =
154 addr + offsetof(struct fioc_rw_arg, new_size);
155 out_iov[1].iov_len = sizeof(arg->new_size);
156
157 /* prepare client buf */
158 if (is_read) {
159 out_iov[2].iov_base = arg->buf;
160 out_iov[2].iov_len = arg->size;
161 if (!out_bufsz) {
162 fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
163 return;
164 }
165 } else {
166 in_iov[1].iov_base = arg->buf;
167 in_iov[1].iov_len = arg->size;
168 if (arg->size && !in_bufsz) {
169 fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
170 return;
171 }
172 }
173
174 /* we're all set */
175 cur_size = cusexmp_size;
176 iov[0].iov_base = &cur_size;
177 iov[0].iov_len = sizeof(cur_size);
178
179 iov[1].iov_base = &cusexmp_size;
180 iov[1].iov_len = sizeof(cusexmp_size);
181
182 if (is_read) {
183 size_t off = arg->offset;
184 size_t size = arg->size;
185
186 if (off >= cusexmp_size)
187 off = cusexmp_size;
188 if (size > cusexmp_size - off)
189 size = cusexmp_size - off;
190
191 iov[2].iov_base = cusexmp_buf + off;
192 iov[2].iov_len = size;
193 fuse_reply_ioctl_iov(req, size, iov, 3);
194 } else {
195 if (cusexmp_expand(arg->offset + in_bufsz)) {
196 fuse_reply_err(req, ENOMEM);
197 return;
198 }
199
200 memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
201 fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
202 }
203}
204
205static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
206 struct fuse_file_info *fi, unsigned flags,
207 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
208{
209 int is_read = 0;
210
211 (void)fi;
212
213 if (flags & FUSE_IOCTL_COMPAT) {
214 fuse_reply_err(req, ENOSYS);
215 return;
216 }
217
218 switch (cmd) {
219 case FIOC_GET_SIZE:
220 if (!out_bufsz) {
221 struct iovec iov = { arg, sizeof(size_t) };
222
223 fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
224 } else
225 fuse_reply_ioctl(req, 0, &cusexmp_size,
226 sizeof(cusexmp_size));
227 break;
228
229 case FIOC_SET_SIZE:
230 if (!in_bufsz) {
231 struct iovec iov = { arg, sizeof(size_t) };
232
233 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
234 } else {
235 cusexmp_resize(*(size_t *)in_buf);
236 fuse_reply_ioctl(req, 0, NULL, 0);
237 }
238 break;
239
240 case FIOC_READ:
241 is_read = 1;
242 /* fall through */
243 case FIOC_WRITE:
244 fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
245 break;
246
247 default:
248 fuse_reply_err(req, EINVAL);
249 }
250}
251
252struct cusexmp_param {
253 unsigned major;
254 unsigned minor;
255 char *dev_name;
256 int is_help;
257};
258
259#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
260
261static const struct fuse_opt cusexmp_opts[] = {
262 CUSEXMP_OPT("-M %u", major),
263 CUSEXMP_OPT("--maj=%u", major),
264 CUSEXMP_OPT("-m %u", minor),
265 CUSEXMP_OPT("--min=%u", minor),
266 CUSEXMP_OPT("-n %s", dev_name),
267 CUSEXMP_OPT("--name=%s", dev_name),
268 FUSE_OPT_KEY("-h", 0),
269 FUSE_OPT_KEY("--help", 0),
271};
272
273static int cusexmp_process_arg(void *data, const char *arg, int key,
274 struct fuse_args *outargs)
275{
276 struct cusexmp_param *param = data;
277
278 (void)outargs;
279 (void)arg;
280
281 switch (key) {
282 case 0:
283 param->is_help = 1;
284 fprintf(stderr, "%s", usage);
285 return fuse_opt_add_arg(outargs, "-ho");
286 default:
287 return 1;
288 }
289}
290
291static const struct cuse_lowlevel_ops cusexmp_clop = {
292 .init = cusexmp_init,
293 .open = cusexmp_open,
294 .read = cusexmp_read,
295 .write = cusexmp_write,
296 .ioctl = cusexmp_ioctl,
297};
298
299int main(int argc, char **argv)
300{
301 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
302 struct cusexmp_param param = { 0, 0, NULL, 0 };
303 char dev_name[128] = "DEVNAME=";
304 const char *dev_info_argv[] = { dev_name };
305 struct cuse_info ci;
306 int ret = 1;
307
308 if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
309 printf("failed to parse option\n");
310 free(param.dev_name);
311 goto out;
312 }
313
314 if (!param.is_help) {
315 if (!param.dev_name) {
316 fprintf(stderr, "Error: device name missing\n");
317 goto out;
318 }
319 strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME="));
320 free(param.dev_name);
321 }
322
323 memset(&ci, 0, sizeof(ci));
324 ci.dev_major = param.major;
325 ci.dev_minor = param.minor;
326 ci.dev_info_argc = 1;
327 ci.dev_info_argv = dev_info_argv;
328 ci.flags = CUSE_UNRESTRICTED_IOCTL;
329
330 ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL);
331
332out:
333 fuse_opt_free_args(&args);
334 return ret;
335}
#define FUSE_IOCTL_COMPAT
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2cuse_8c_source.html0000644000175000017500000016620015156613442023012 0ustar berndbernd libfuse: fuse-3.18.1/example/cuse.c Source File
libfuse
cuse.c
Go to the documentation of this file.
1/*
2 CUSE example: Character device in Userspace
3 Copyright (C) 2008-2009 SUSE Linux Products GmbH
4 Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8
9*/
10
34#define FUSE_USE_VERSION 31
35
36#include <cuse_lowlevel.h>
37#include <fuse_opt.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43#include <errno.h>
44
45#include "ioctl.h"
46
47static void *cusexmp_buf;
48static size_t cusexmp_size;
49
50static const char *usage =
51"usage: cusexmp [options]\n"
52"\n"
53"options:\n"
54" --help|-h print this help message\n"
55" --maj=MAJ|-M MAJ device major number\n"
56" --min=MIN|-m MIN device minor number\n"
57" --name=NAME|-n NAME device name (mandatory)\n"
58" -d -o debug enable debug output (implies -f)\n"
59" -f foreground operation\n"
60" -s disable multi-threaded operation\n"
61"\n";
62
63static int cusexmp_resize(size_t new_size)
64{
65 void *new_buf;
66
67 if (new_size == cusexmp_size)
68 return 0;
69
70 new_buf = realloc(cusexmp_buf, new_size);
71 if (!new_buf && new_size)
72 return -ENOMEM;
73
74 if (new_size > cusexmp_size)
75 memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
76
77 cusexmp_buf = new_buf;
78 cusexmp_size = new_size;
79
80 return 0;
81}
82
83static int cusexmp_expand(size_t new_size)
84{
85 if (new_size > cusexmp_size)
86 return cusexmp_resize(new_size);
87 return 0;
88}
89
90static void cusexmp_init(void *userdata, struct fuse_conn_info *conn)
91{
92 (void)userdata;
93
94 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
95 conn->no_interrupt = 1;
96}
97
98static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
99{
100 fuse_reply_open(req, fi);
101}
102
103static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
104 struct fuse_file_info *fi)
105{
106 (void)fi;
107
108 if (off >= cusexmp_size)
109 off = cusexmp_size;
110 if (size > cusexmp_size - off)
111 size = cusexmp_size - off;
112
113 fuse_reply_buf(req, cusexmp_buf + off, size);
114}
115
116static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
117 off_t off, struct fuse_file_info *fi)
118{
119 (void)fi;
120
121 if (cusexmp_expand(off + size)) {
122 fuse_reply_err(req, ENOMEM);
123 return;
124 }
125
126 memcpy(cusexmp_buf + off, buf, size);
127 fuse_reply_write(req, size);
128}
129
130static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
131 size_t in_bufsz, size_t out_bufsz, int is_read)
132{
133 const struct fioc_rw_arg *arg;
134 struct iovec in_iov[2], out_iov[3], iov[3];
135 size_t cur_size;
136
137 /* read in arg */
138 in_iov[0].iov_base = addr;
139 in_iov[0].iov_len = sizeof(*arg);
140 if (!in_bufsz) {
141 fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
142 return;
143 }
144 arg = in_buf;
145 in_buf += sizeof(*arg);
146 in_bufsz -= sizeof(*arg);
147
148 /* prepare size outputs */
149 out_iov[0].iov_base =
150 addr + offsetof(struct fioc_rw_arg, prev_size);
151 out_iov[0].iov_len = sizeof(arg->prev_size);
152
153 out_iov[1].iov_base =
154 addr + offsetof(struct fioc_rw_arg, new_size);
155 out_iov[1].iov_len = sizeof(arg->new_size);
156
157 /* prepare client buf */
158 if (is_read) {
159 out_iov[2].iov_base = arg->buf;
160 out_iov[2].iov_len = arg->size;
161 if (!out_bufsz) {
162 fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
163 return;
164 }
165 } else {
166 in_iov[1].iov_base = arg->buf;
167 in_iov[1].iov_len = arg->size;
168 if (arg->size && !in_bufsz) {
169 fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
170 return;
171 }
172 }
173
174 /* we're all set */
175 cur_size = cusexmp_size;
176 iov[0].iov_base = &cur_size;
177 iov[0].iov_len = sizeof(cur_size);
178
179 iov[1].iov_base = &cusexmp_size;
180 iov[1].iov_len = sizeof(cusexmp_size);
181
182 if (is_read) {
183 size_t off = arg->offset;
184 size_t size = arg->size;
185
186 if (off >= cusexmp_size)
187 off = cusexmp_size;
188 if (size > cusexmp_size - off)
189 size = cusexmp_size - off;
190
191 iov[2].iov_base = cusexmp_buf + off;
192 iov[2].iov_len = size;
193 fuse_reply_ioctl_iov(req, size, iov, 3);
194 } else {
195 if (cusexmp_expand(arg->offset + in_bufsz)) {
196 fuse_reply_err(req, ENOMEM);
197 return;
198 }
199
200 memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
201 fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
202 }
203}
204
205static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
206 struct fuse_file_info *fi, unsigned flags,
207 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
208{
209 int is_read = 0;
210
211 (void)fi;
212
213 if (flags & FUSE_IOCTL_COMPAT) {
214 fuse_reply_err(req, ENOSYS);
215 return;
216 }
217
218 switch (cmd) {
219 case FIOC_GET_SIZE:
220 if (!out_bufsz) {
221 struct iovec iov = { arg, sizeof(size_t) };
222
223 fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
224 } else
225 fuse_reply_ioctl(req, 0, &cusexmp_size,
226 sizeof(cusexmp_size));
227 break;
228
229 case FIOC_SET_SIZE:
230 if (!in_bufsz) {
231 struct iovec iov = { arg, sizeof(size_t) };
232
233 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
234 } else {
235 cusexmp_resize(*(size_t *)in_buf);
236 fuse_reply_ioctl(req, 0, NULL, 0);
237 }
238 break;
239
240 case FIOC_READ:
241 is_read = 1;
242 /* fall through */
243 case FIOC_WRITE:
244 fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
245 break;
246
247 default:
248 fuse_reply_err(req, EINVAL);
249 }
250}
251
252struct cusexmp_param {
253 unsigned major;
254 unsigned minor;
255 char *dev_name;
256 int is_help;
257};
258
259#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
260
261static const struct fuse_opt cusexmp_opts[] = {
262 CUSEXMP_OPT("-M %u", major),
263 CUSEXMP_OPT("--maj=%u", major),
264 CUSEXMP_OPT("-m %u", minor),
265 CUSEXMP_OPT("--min=%u", minor),
266 CUSEXMP_OPT("-n %s", dev_name),
267 CUSEXMP_OPT("--name=%s", dev_name),
268 FUSE_OPT_KEY("-h", 0),
269 FUSE_OPT_KEY("--help", 0),
271};
272
273static int cusexmp_process_arg(void *data, const char *arg, int key,
274 struct fuse_args *outargs)
275{
276 struct cusexmp_param *param = data;
277
278 (void)outargs;
279 (void)arg;
280
281 switch (key) {
282 case 0:
283 param->is_help = 1;
284 fprintf(stderr, "%s", usage);
285 return fuse_opt_add_arg(outargs, "-ho");
286 default:
287 return 1;
288 }
289}
290
291static const struct cuse_lowlevel_ops cusexmp_clop = {
292 .init = cusexmp_init,
293 .open = cusexmp_open,
294 .read = cusexmp_read,
295 .write = cusexmp_write,
296 .ioctl = cusexmp_ioctl,
297};
298
299int main(int argc, char **argv)
300{
301 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
302 struct cusexmp_param param = { 0, 0, NULL, 0 };
303 char dev_name[128] = "DEVNAME=";
304 const char *dev_info_argv[] = { dev_name };
305 struct cuse_info ci;
306 int ret = 1;
307
308 if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
309 printf("failed to parse option\n");
310 free(param.dev_name);
311 goto out;
312 }
313
314 if (!param.is_help) {
315 if (!param.dev_name) {
316 fprintf(stderr, "Error: device name missing\n");
317 goto out;
318 }
319 strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME="));
320 free(param.dev_name);
321 }
322
323 memset(&ci, 0, sizeof(ci));
324 ci.dev_major = param.major;
325 ci.dev_minor = param.minor;
326 ci.dev_info_argc = 1;
327 ci.dev_info_argv = dev_info_argv;
328 ci.flags = CUSE_UNRESTRICTED_IOCTL;
329
330 ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL);
331
332out:
333 fuse_opt_free_args(&args);
334 return ret;
335}
#define FUSE_IOCTL_COMPAT
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse-3.18.2/doc/html/example_2cuse__client_8c_source.html0000644000175000017500000005040015156613442022325 0ustar berndbernd libfuse: example/cuse_client.c Source File
libfuse
cuse_client.c
Go to the documentation of this file.
1/*
2 FUSE fioclient: FUSE ioctl example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
40#include <sys/types.h>
41#include <fcntl.h>
42#include <sys/stat.h>
43#include <sys/ioctl.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <ctype.h>
47#include <errno.h>
48#include <unistd.h>
49#include "ioctl.h"
50
51const char *usage =
52"Usage: cuse_client FIOC_FILE COMMAND\n"
53"\n"
54"COMMANDS\n"
55" s [SIZE] : get size if SIZE is omitted, set size otherwise\n"
56" r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n"
57" w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n"
58"\n";
59
60static int do_rw(int fd, int is_read, size_t size, off_t offset,
61 size_t *prev_size, size_t *new_size)
62{
63 struct fioc_rw_arg arg = { .offset = offset };
64 ssize_t ret;
65
66 arg.buf = calloc(1, size);
67 if (!arg.buf) {
68 fprintf(stderr, "failed to allocated %zu bytes\n", size);
69 return -1;
70 }
71
72 if (is_read) {
73 arg.size = size;
74 ret = ioctl(fd, FIOC_READ, &arg);
75 if (ret >= 0)
76 fwrite(arg.buf, 1, ret, stdout);
77 } else {
78 arg.size = fread(arg.buf, 1, size, stdin);
79 fprintf(stderr, "Writing %zu bytes\n", arg.size);
80 ret = ioctl(fd, FIOC_WRITE, &arg);
81 }
82
83 if (ret >= 0) {
84 *prev_size = arg.prev_size;
85 *new_size = arg.new_size;
86 } else
87 perror("ioctl");
88
89 free(arg.buf);
90 return ret;
91}
92
93int main(int argc, char **argv)
94{
95 size_t param[2] = { };
96 size_t size, prev_size = 0, new_size = 0;
97 char cmd;
98 int fd, i, rc;
99
100 if (argc < 3)
101 goto usage;
102
103 fd = open(argv[1], O_RDWR);
104 if (fd < 0) {
105 perror("open");
106 return 1;
107 }
108
109 cmd = tolower(argv[2][0]);
110 argc -= 3;
111 argv += 3;
112
113 for (i = 0; i < argc; i++) {
114 char *endp;
115 param[i] = strtoul(argv[i], &endp, 0);
116 if (endp == argv[i] || *endp != '\0')
117 goto usage;
118 }
119
120 switch (cmd) {
121 case 's':
122 if (!argc) {
123 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
124 perror("ioctl");
125 goto error;
126 }
127 printf("%zu\n", size);
128 } else {
129 size = param[0];
130 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
131 perror("ioctl");
132 goto error;
133 }
134 }
135 close(fd);
136 return 0;
137
138 case 'r':
139 case 'w':
140 rc = do_rw(fd, cmd == 'r', param[0], param[1],
141 &prev_size, &new_size);
142 if (rc < 0)
143 goto error;
144 fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n",
145 rc, prev_size, new_size);
146 close(fd);
147 return 0;
148 }
149
150usage:
151 fprintf(stderr, "%s", usage);
152 return 1;
153
154error:
155 close(fd);
156 return 1;
157}
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2cuse__client_8c_source.html0000644000175000017500000005060215156613442024505 0ustar berndbernd libfuse: fuse-3.18.1/example/cuse_client.c Source File
libfuse
cuse_client.c
Go to the documentation of this file.
1/*
2 FUSE fioclient: FUSE ioctl example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
40#include <sys/types.h>
41#include <fcntl.h>
42#include <sys/stat.h>
43#include <sys/ioctl.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <ctype.h>
47#include <errno.h>
48#include <unistd.h>
49#include "ioctl.h"
50
51const char *usage =
52"Usage: cuse_client FIOC_FILE COMMAND\n"
53"\n"
54"COMMANDS\n"
55" s [SIZE] : get size if SIZE is omitted, set size otherwise\n"
56" r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n"
57" w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n"
58"\n";
59
60static int do_rw(int fd, int is_read, size_t size, off_t offset,
61 size_t *prev_size, size_t *new_size)
62{
63 struct fioc_rw_arg arg = { .offset = offset };
64 ssize_t ret;
65
66 arg.buf = calloc(1, size);
67 if (!arg.buf) {
68 fprintf(stderr, "failed to allocated %zu bytes\n", size);
69 return -1;
70 }
71
72 if (is_read) {
73 arg.size = size;
74 ret = ioctl(fd, FIOC_READ, &arg);
75 if (ret >= 0)
76 fwrite(arg.buf, 1, ret, stdout);
77 } else {
78 arg.size = fread(arg.buf, 1, size, stdin);
79 fprintf(stderr, "Writing %zu bytes\n", arg.size);
80 ret = ioctl(fd, FIOC_WRITE, &arg);
81 }
82
83 if (ret >= 0) {
84 *prev_size = arg.prev_size;
85 *new_size = arg.new_size;
86 } else
87 perror("ioctl");
88
89 free(arg.buf);
90 return ret;
91}
92
93int main(int argc, char **argv)
94{
95 size_t param[2] = { };
96 size_t size, prev_size = 0, new_size = 0;
97 char cmd;
98 int fd, i, rc;
99
100 if (argc < 3)
101 goto usage;
102
103 fd = open(argv[1], O_RDWR);
104 if (fd < 0) {
105 perror("open");
106 return 1;
107 }
108
109 cmd = tolower(argv[2][0]);
110 argc -= 3;
111 argv += 3;
112
113 for (i = 0; i < argc; i++) {
114 char *endp;
115 param[i] = strtoul(argv[i], &endp, 0);
116 if (endp == argv[i] || *endp != '\0')
117 goto usage;
118 }
119
120 switch (cmd) {
121 case 's':
122 if (!argc) {
123 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
124 perror("ioctl");
125 goto error;
126 }
127 printf("%zu\n", size);
128 } else {
129 size = param[0];
130 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
131 perror("ioctl");
132 goto error;
133 }
134 }
135 close(fd);
136 return 0;
137
138 case 'r':
139 case 'w':
140 rc = do_rw(fd, cmd == 'r', param[0], param[1],
141 &prev_size, &new_size);
142 if (rc < 0)
143 goto error;
144 fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n",
145 rc, prev_size, new_size);
146 close(fd);
147 return 0;
148 }
149
150usage:
151 fprintf(stderr, "%s", usage);
152 return 1;
153
154error:
155 close(fd);
156 return 1;
157}
fuse-3.18.2/doc/html/example_2hello_8c_source.html0000644000175000017500000011600515156613442021000 0ustar berndbernd libfuse: example/hello.c Source File
libfuse
hello.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
22#define FUSE_USE_VERSION 31
23
24#include <fuse.h>
25#include <stdio.h>
26#include <string.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <stddef.h>
30#include <assert.h>
31
32/*
33 * Command line options
34 *
35 * We can't set default values for the char* fields here because
36 * fuse_opt_parse would attempt to free() them when the user specifies
37 * different values on the command line.
38 */
39static struct options {
40 const char *filename;
41 const char *contents;
42 int show_help;
43} options;
44
45#define OPTION(t, p) \
46 { t, offsetof(struct options, p), 1 }
47static const struct fuse_opt option_spec[] = {
48 OPTION("--name=%s", filename),
49 OPTION("--contents=%s", contents),
50 OPTION("-h", show_help),
51 OPTION("--help", show_help),
53};
54
55static void *hello_init(struct fuse_conn_info *conn,
56 struct fuse_config *cfg)
57{
58 (void) conn;
59 cfg->kernel_cache = 1;
60
61 /* Test setting flags the old way */
64
65 return NULL;
66}
67
68static int hello_getattr(const char *path, struct stat *stbuf,
69 struct fuse_file_info *fi)
70{
71 (void) fi;
72 int res = 0;
73
74 memset(stbuf, 0, sizeof(struct stat));
75 if (strcmp(path, "/") == 0) {
76 stbuf->st_mode = S_IFDIR | 0755;
77 stbuf->st_nlink = 2;
78 } else if (strcmp(path+1, options.filename) == 0) {
79 stbuf->st_mode = S_IFREG | 0444;
80 stbuf->st_nlink = 1;
81 stbuf->st_size = strlen(options.contents);
82 } else
83 res = -ENOENT;
84
85 return res;
86}
87
88static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
89 off_t offset, struct fuse_file_info *fi,
90 enum fuse_readdir_flags flags)
91{
92 (void) offset;
93 (void) fi;
94 (void) flags;
95
96 if (strcmp(path, "/") != 0)
97 return -ENOENT;
98
99 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
100 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
101 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
102
103 return 0;
104}
105
106static int hello_open(const char *path, struct fuse_file_info *fi)
107{
108 if (strcmp(path+1, options.filename) != 0)
109 return -ENOENT;
110
111 if ((fi->flags & O_ACCMODE) != O_RDONLY)
112 return -EACCES;
113
114 return 0;
115}
116
117static int hello_read(const char *path, char *buf, size_t size, off_t offset,
118 struct fuse_file_info *fi)
119{
120 size_t len;
121 (void) fi;
122 if(strcmp(path+1, options.filename) != 0)
123 return -ENOENT;
124
125 len = strlen(options.contents);
126 if (offset < len) {
127 if (offset + size > len)
128 size = len - offset;
129 memcpy(buf, options.contents + offset, size);
130 } else
131 size = 0;
132
133 return size;
134}
135
136static const struct fuse_operations hello_oper = {
137 .init = hello_init,
138 .getattr = hello_getattr,
139 .readdir = hello_readdir,
140 .open = hello_open,
141 .read = hello_read,
142};
143
144static void show_help(const char *progname)
145{
146 printf("usage: %s [options] <mountpoint>\n\n", progname);
147 printf("File-system specific options:\n"
148 " --name=<s> Name of the \"hello\" file\n"
149 " (default: \"hello\")\n"
150 " --contents=<s> Contents \"hello\" file\n"
151 " (default \"Hello, World!\\n\")\n"
152 "\n");
153}
154
155int main(int argc, char *argv[])
156{
157 int ret;
158 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
159
160 /* Set defaults -- we have to use strdup so that
161 fuse_opt_parse can free the defaults if other
162 values are specified */
163 options.filename = strdup("hello");
164 options.contents = strdup("Hello World!\n");
165
166 /* Parse options */
167 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
168 return 1;
169
170 /* When --help is specified, first print our own file-system
171 specific help text, then signal fuse_main to show
172 additional help (by adding `--help` to the options again)
173 without usage: line (by setting argv[0] to the empty
174 string) */
175 if (options.show_help) {
176 show_help(argv[0]);
177 assert(fuse_opt_add_arg(&args, "--help") == 0);
178 args.argv[0][0] = '\0';
179 }
180
181 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
182 fuse_opt_free_args(&args);
183 return ret;
184}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2hello_8c_source.html0000644000175000017500000011407215156613442022504 0ustar berndbernd libfuse: fuse-3.17.4/test/hello.c Source File
libfuse
hello.c
Go to the documentation of this file.
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 *
5 * This program can be distributed under the terms of the GNU GPLv2.
6 * See the file COPYING.
7 */
8
21#define FUSE_USE_VERSION 31
22
23#include <fuse.h>
24#include <stdio.h>
25#include <string.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <stddef.h>
29#include <assert.h>
30
31/*
32 * Command line options
33 *
34 * We can't set default values for the char* fields here because
35 * fuse_opt_parse would attempt to free() them when the user specifies
36 * different values on the command line.
37 */
38static struct options {
39 const char *filename;
40 const char *contents;
41 int show_help;
42} options;
43
44#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
45static const struct fuse_opt option_spec[] = {
46 OPTION("--name=%s", filename), OPTION("--contents=%s", contents),
47 OPTION("-h", show_help), OPTION("--help", show_help), FUSE_OPT_END
48};
49
50static void *hello_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
51{
52 (void)conn;
53 cfg->kernel_cache = 1;
54
55 /* Test setting flags the old way */
57 conn->want &= ~FUSE_CAP_ASYNC_READ;
58
59 return NULL;
60}
61
62static int hello_getattr(const char *path, struct stat *stbuf,
63 struct fuse_file_info *fi)
64{
65 (void)fi;
66 int res = 0;
67
68 memset(stbuf, 0, sizeof(struct stat));
69 if (strcmp(path, "/") == 0) {
70 stbuf->st_mode = S_IFDIR | 0755;
71 stbuf->st_nlink = 2;
72 } else if (strcmp(path + 1, options.filename) == 0) {
73 stbuf->st_mode = S_IFREG | 0444;
74 stbuf->st_nlink = 1;
75 stbuf->st_size = strlen(options.contents);
76 } else
77 res = -ENOENT;
78
79 return res;
80}
81
82static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
83 off_t offset, struct fuse_file_info *fi,
84 enum fuse_readdir_flags flags)
85{
86 (void)offset;
87 (void)fi;
88 (void)flags;
89
90 if (strcmp(path, "/") != 0)
91 return -ENOENT;
92
93 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
94 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
95 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
96
97 return 0;
98}
99
100static int hello_open(const char *path, struct fuse_file_info *fi)
101{
102 if (strcmp(path + 1, options.filename) != 0)
103 return -ENOENT;
104
105 if ((fi->flags & O_ACCMODE) != O_RDONLY)
106 return -EACCES;
107
108 return 0;
109}
110
111static int hello_read(const char *path, char *buf, size_t size, off_t offset,
112 struct fuse_file_info *fi)
113{
114 size_t len;
115 (void)fi;
116 if (strcmp(path + 1, options.filename) != 0)
117 return -ENOENT;
118
119 len = strlen(options.contents);
120 if (offset < len) {
121 if (offset + size > len)
122 size = len - offset;
123 memcpy(buf, options.contents + offset, size);
124 } else
125 size = 0;
126
127 return size;
128}
129
130static const struct fuse_operations hello_oper = {
131 .init = hello_init,
132 .getattr = hello_getattr,
133 .readdir = hello_readdir,
134 .open = hello_open,
135 .read = hello_read,
136};
137
138static void show_help(const char *progname)
139{
140 printf("usage: %s [options] <mountpoint>\n\n", progname);
141 printf("File-system specific options:\n"
142 " --name=<s> Name of the \"hello\" file\n"
143 " (default: \"hello\")\n"
144 " --contents=<s> Contents \"hello\" file\n"
145 " (default \"Hello, World!\\n\")\n"
146 "\n");
147}
148
149int main(int argc, char *argv[])
150{
151 int ret;
152 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
153
154 /* Set defaults -- we have to use strdup so that
155 * fuse_opt_parse can free the defaults if other
156 * values are specified
157 */
158 options.filename = strdup("hello");
159 options.contents = strdup("Hello World!\n");
160
161 /* Parse options */
162 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
163 return 1;
164
165 /* When --help is specified, first print our own file-system
166 * specific help text, then signal fuse_main to show
167 * additional help (by adding `--help` to the options again)
168 * without usage: line (by setting argv[0] to the empty
169 * string)
170 */
171 if (options.show_help) {
172 show_help(argv[0]);
173 assert(fuse_opt_add_arg(&args, "--help") == 0);
174 args.argv[0][0] = '\0';
175 }
176
177 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
178 fuse_opt_free_args(&args);
179 return ret;
180}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2hello_8c_source.html0000644000175000017500000011620715156613442023160 0ustar berndbernd libfuse: fuse-3.18.1/example/hello.c Source File
libfuse
hello.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
22#define FUSE_USE_VERSION 31
23
24#include <fuse.h>
25#include <stdio.h>
26#include <string.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <stddef.h>
30#include <assert.h>
31
32/*
33 * Command line options
34 *
35 * We can't set default values for the char* fields here because
36 * fuse_opt_parse would attempt to free() them when the user specifies
37 * different values on the command line.
38 */
39static struct options {
40 const char *filename;
41 const char *contents;
42 int show_help;
43} options;
44
45#define OPTION(t, p) \
46 { t, offsetof(struct options, p), 1 }
47static const struct fuse_opt option_spec[] = {
48 OPTION("--name=%s", filename),
49 OPTION("--contents=%s", contents),
50 OPTION("-h", show_help),
51 OPTION("--help", show_help),
53};
54
55static void *hello_init(struct fuse_conn_info *conn,
56 struct fuse_config *cfg)
57{
58 (void) conn;
59 cfg->kernel_cache = 1;
60
61 /* Test setting flags the old way */
64
65 return NULL;
66}
67
68static int hello_getattr(const char *path, struct stat *stbuf,
69 struct fuse_file_info *fi)
70{
71 (void) fi;
72 int res = 0;
73
74 memset(stbuf, 0, sizeof(struct stat));
75 if (strcmp(path, "/") == 0) {
76 stbuf->st_mode = S_IFDIR | 0755;
77 stbuf->st_nlink = 2;
78 } else if (strcmp(path+1, options.filename) == 0) {
79 stbuf->st_mode = S_IFREG | 0444;
80 stbuf->st_nlink = 1;
81 stbuf->st_size = strlen(options.contents);
82 } else
83 res = -ENOENT;
84
85 return res;
86}
87
88static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
89 off_t offset, struct fuse_file_info *fi,
90 enum fuse_readdir_flags flags)
91{
92 (void) offset;
93 (void) fi;
94 (void) flags;
95
96 if (strcmp(path, "/") != 0)
97 return -ENOENT;
98
99 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
100 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
101 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
102
103 return 0;
104}
105
106static int hello_open(const char *path, struct fuse_file_info *fi)
107{
108 if (strcmp(path+1, options.filename) != 0)
109 return -ENOENT;
110
111 if ((fi->flags & O_ACCMODE) != O_RDONLY)
112 return -EACCES;
113
114 return 0;
115}
116
117static int hello_read(const char *path, char *buf, size_t size, off_t offset,
118 struct fuse_file_info *fi)
119{
120 size_t len;
121 (void) fi;
122 if(strcmp(path+1, options.filename) != 0)
123 return -ENOENT;
124
125 len = strlen(options.contents);
126 if (offset < len) {
127 if (offset + size > len)
128 size = len - offset;
129 memcpy(buf, options.contents + offset, size);
130 } else
131 size = 0;
132
133 return size;
134}
135
136static const struct fuse_operations hello_oper = {
137 .init = hello_init,
138 .getattr = hello_getattr,
139 .readdir = hello_readdir,
140 .open = hello_open,
141 .read = hello_read,
142};
143
144static void show_help(const char *progname)
145{
146 printf("usage: %s [options] <mountpoint>\n\n", progname);
147 printf("File-system specific options:\n"
148 " --name=<s> Name of the \"hello\" file\n"
149 " (default: \"hello\")\n"
150 " --contents=<s> Contents \"hello\" file\n"
151 " (default \"Hello, World!\\n\")\n"
152 "\n");
153}
154
155int main(int argc, char *argv[])
156{
157 int ret;
158 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
159
160 /* Set defaults -- we have to use strdup so that
161 fuse_opt_parse can free the defaults if other
162 values are specified */
163 options.filename = strdup("hello");
164 options.contents = strdup("Hello World!\n");
165
166 /* Parse options */
167 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
168 return 1;
169
170 /* When --help is specified, first print our own file-system
171 specific help text, then signal fuse_main to show
172 additional help (by adding `--help` to the options again)
173 without usage: line (by setting argv[0] to the empty
174 string) */
175 if (options.show_help) {
176 show_help(argv[0]);
177 assert(fuse_opt_add_arg(&args, "--help") == 0);
178 args.argv[0][0] = '\0';
179 }
180
181 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
182 fuse_opt_free_args(&args);
183 return ret;
184}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2hello_8c_source.html0000644000175000017500000011407315156613442022503 0ustar berndbernd libfuse: fuse-3.18.1/test/hello.c Source File
libfuse
hello.c
Go to the documentation of this file.
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 *
5 * This program can be distributed under the terms of the GNU GPLv2.
6 * See the file GPL2.txt.
7 */
8
21#define FUSE_USE_VERSION 31
22
23#include <fuse.h>
24#include <stdio.h>
25#include <string.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <stddef.h>
29#include <assert.h>
30
31/*
32 * Command line options
33 *
34 * We can't set default values for the char* fields here because
35 * fuse_opt_parse would attempt to free() them when the user specifies
36 * different values on the command line.
37 */
38static struct options {
39 const char *filename;
40 const char *contents;
41 int show_help;
42} options;
43
44#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
45static const struct fuse_opt option_spec[] = {
46 OPTION("--name=%s", filename), OPTION("--contents=%s", contents),
47 OPTION("-h", show_help), OPTION("--help", show_help), FUSE_OPT_END
48};
49
50static void *hello_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
51{
52 (void)conn;
53 cfg->kernel_cache = 1;
54
55 /* Test setting flags the old way */
57 conn->want &= ~FUSE_CAP_ASYNC_READ;
58
59 return NULL;
60}
61
62static int hello_getattr(const char *path, struct stat *stbuf,
63 struct fuse_file_info *fi)
64{
65 (void)fi;
66 int res = 0;
67
68 memset(stbuf, 0, sizeof(struct stat));
69 if (strcmp(path, "/") == 0) {
70 stbuf->st_mode = S_IFDIR | 0755;
71 stbuf->st_nlink = 2;
72 } else if (strcmp(path + 1, options.filename) == 0) {
73 stbuf->st_mode = S_IFREG | 0444;
74 stbuf->st_nlink = 1;
75 stbuf->st_size = strlen(options.contents);
76 } else
77 res = -ENOENT;
78
79 return res;
80}
81
82static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
83 off_t offset, struct fuse_file_info *fi,
84 enum fuse_readdir_flags flags)
85{
86 (void)offset;
87 (void)fi;
88 (void)flags;
89
90 if (strcmp(path, "/") != 0)
91 return -ENOENT;
92
93 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
94 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
95 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
96
97 return 0;
98}
99
100static int hello_open(const char *path, struct fuse_file_info *fi)
101{
102 if (strcmp(path + 1, options.filename) != 0)
103 return -ENOENT;
104
105 if ((fi->flags & O_ACCMODE) != O_RDONLY)
106 return -EACCES;
107
108 return 0;
109}
110
111static int hello_read(const char *path, char *buf, size_t size, off_t offset,
112 struct fuse_file_info *fi)
113{
114 size_t len;
115 (void)fi;
116 if (strcmp(path + 1, options.filename) != 0)
117 return -ENOENT;
118
119 len = strlen(options.contents);
120 if (offset < len) {
121 if (offset + size > len)
122 size = len - offset;
123 memcpy(buf, options.contents + offset, size);
124 } else
125 size = 0;
126
127 return size;
128}
129
130static const struct fuse_operations hello_oper = {
131 .init = hello_init,
132 .getattr = hello_getattr,
133 .readdir = hello_readdir,
134 .open = hello_open,
135 .read = hello_read,
136};
137
138static void show_help(const char *progname)
139{
140 printf("usage: %s [options] <mountpoint>\n\n", progname);
141 printf("File-system specific options:\n"
142 " --name=<s> Name of the \"hello\" file\n"
143 " (default: \"hello\")\n"
144 " --contents=<s> Contents \"hello\" file\n"
145 " (default \"Hello, World!\\n\")\n"
146 "\n");
147}
148
149int main(int argc, char *argv[])
150{
151 int ret;
152 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
153
154 /* Set defaults -- we have to use strdup so that
155 * fuse_opt_parse can free the defaults if other
156 * values are specified
157 */
158 options.filename = strdup("hello");
159 options.contents = strdup("Hello World!\n");
160
161 /* Parse options */
162 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
163 return 1;
164
165 /* When --help is specified, first print our own file-system
166 * specific help text, then signal fuse_main to show
167 * additional help (by adding `--help` to the options again)
168 * without usage: line (by setting argv[0] to the empty
169 * string)
170 */
171 if (options.show_help) {
172 show_help(argv[0]);
173 assert(fuse_opt_add_arg(&args, "--help") == 0);
174 args.argv[0][0] = '\0';
175 }
176
177 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
178 fuse_opt_free_args(&args);
179 return ret;
180}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/test_2hello_8c_source.html0000644000175000017500000011367115156613442020332 0ustar berndbernd libfuse: test/hello.c Source File
libfuse
hello.c
Go to the documentation of this file.
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 *
5 * This program can be distributed under the terms of the GNU GPLv2.
6 * See the file GPL2.txt.
7 */
8
21#define FUSE_USE_VERSION 31
22
23#include <fuse.h>
24#include <stdio.h>
25#include <string.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <stddef.h>
29#include <assert.h>
30
31/*
32 * Command line options
33 *
34 * We can't set default values for the char* fields here because
35 * fuse_opt_parse would attempt to free() them when the user specifies
36 * different values on the command line.
37 */
38static struct options {
39 const char *filename;
40 const char *contents;
41 int show_help;
42} options;
43
44#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
45static const struct fuse_opt option_spec[] = {
46 OPTION("--name=%s", filename), OPTION("--contents=%s", contents),
47 OPTION("-h", show_help), OPTION("--help", show_help), FUSE_OPT_END
48};
49
50static void *hello_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
51{
52 (void)conn;
53 cfg->kernel_cache = 1;
54
55 /* Test setting flags the old way */
57 conn->want &= ~FUSE_CAP_ASYNC_READ;
58
59 return NULL;
60}
61
62static int hello_getattr(const char *path, struct stat *stbuf,
63 struct fuse_file_info *fi)
64{
65 (void)fi;
66 int res = 0;
67
68 memset(stbuf, 0, sizeof(struct stat));
69 if (strcmp(path, "/") == 0) {
70 stbuf->st_mode = S_IFDIR | 0755;
71 stbuf->st_nlink = 2;
72 } else if (strcmp(path + 1, options.filename) == 0) {
73 stbuf->st_mode = S_IFREG | 0444;
74 stbuf->st_nlink = 1;
75 stbuf->st_size = strlen(options.contents);
76 } else
77 res = -ENOENT;
78
79 return res;
80}
81
82static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
83 off_t offset, struct fuse_file_info *fi,
84 enum fuse_readdir_flags flags)
85{
86 (void)offset;
87 (void)fi;
88 (void)flags;
89
90 if (strcmp(path, "/") != 0)
91 return -ENOENT;
92
93 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
94 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
95 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
96
97 return 0;
98}
99
100static int hello_open(const char *path, struct fuse_file_info *fi)
101{
102 if (strcmp(path + 1, options.filename) != 0)
103 return -ENOENT;
104
105 if ((fi->flags & O_ACCMODE) != O_RDONLY)
106 return -EACCES;
107
108 return 0;
109}
110
111static int hello_read(const char *path, char *buf, size_t size, off_t offset,
112 struct fuse_file_info *fi)
113{
114 size_t len;
115 (void)fi;
116 if (strcmp(path + 1, options.filename) != 0)
117 return -ENOENT;
118
119 len = strlen(options.contents);
120 if (offset < len) {
121 if (offset + size > len)
122 size = len - offset;
123 memcpy(buf, options.contents + offset, size);
124 } else
125 size = 0;
126
127 return size;
128}
129
130static const struct fuse_operations hello_oper = {
131 .init = hello_init,
132 .getattr = hello_getattr,
133 .readdir = hello_readdir,
134 .open = hello_open,
135 .read = hello_read,
136};
137
138static void show_help(const char *progname)
139{
140 printf("usage: %s [options] <mountpoint>\n\n", progname);
141 printf("File-system specific options:\n"
142 " --name=<s> Name of the \"hello\" file\n"
143 " (default: \"hello\")\n"
144 " --contents=<s> Contents \"hello\" file\n"
145 " (default \"Hello, World!\\n\")\n"
146 "\n");
147}
148
149int main(int argc, char *argv[])
150{
151 int ret;
152 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
153
154 /* Set defaults -- we have to use strdup so that
155 * fuse_opt_parse can free the defaults if other
156 * values are specified
157 */
158 options.filename = strdup("hello");
159 options.contents = strdup("Hello World!\n");
160
161 /* Parse options */
162 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
163 return 1;
164
165 /* When --help is specified, first print our own file-system
166 * specific help text, then signal fuse_main to show
167 * additional help (by adding `--help` to the options again)
168 * without usage: line (by setting argv[0] to the empty
169 * string)
170 */
171 if (options.show_help) {
172 show_help(argv[0]);
173 assert(fuse_opt_add_arg(&args, "--help") == 0);
174 args.argv[0][0] = '\0';
175 }
176
177 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
178 fuse_opt_free_args(&args);
179 return ret;
180}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/example_2hello__ll_8c_source.html0000644000175000017500000017346615156613442021644 0ustar berndbernd libfuse: example/hello_ll.c Source File
libfuse
hello_ll.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
25#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
26
27#include <fuse_lowlevel.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <unistd.h>
34#include <assert.h>
35
36static const char *hello_str = "Hello World!\n";
37static const char *hello_name = "hello";
38
39static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
40{
41 stbuf->st_ino = ino;
42 switch (ino) {
43 case 1:
44 stbuf->st_mode = S_IFDIR | 0755;
45 stbuf->st_nlink = 2;
46 break;
47
48 case 2:
49 stbuf->st_mode = S_IFREG | 0444;
50 stbuf->st_nlink = 1;
51 stbuf->st_size = strlen(hello_str);
52 break;
53
54 default:
55 return -1;
56 }
57 return 0;
58}
59
60static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
61{
62 (void)userdata;
63
64 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
65 conn->no_interrupt = 1;
66
67 /* Test setting flags the old way */
69 conn->want &= ~FUSE_CAP_ASYNC_READ;
70}
71
72static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
73 struct fuse_file_info *fi)
74{
75 struct stat stbuf;
76
77 (void) fi;
78
79 memset(&stbuf, 0, sizeof(stbuf));
80 if (hello_stat(ino, &stbuf) == -1)
81 fuse_reply_err(req, ENOENT);
82 else
83 fuse_reply_attr(req, &stbuf, 1.0);
84}
85
86static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
87{
88 struct fuse_entry_param e;
89
90 if (parent != 1 || strcmp(name, hello_name) != 0)
91 fuse_reply_err(req, ENOENT);
92 else {
93 memset(&e, 0, sizeof(e));
94 e.ino = 2;
95 e.attr_timeout = 1.0;
96 e.entry_timeout = 1.0;
97 hello_stat(e.ino, &e.attr);
98
99 fuse_reply_entry(req, &e);
100 }
101}
102
103struct dirbuf {
104 char *p;
105 size_t size;
106};
107
108static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
109 fuse_ino_t ino)
110{
111 struct stat stbuf;
112 size_t oldsize = b->size;
113 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
114 b->p = (char *) realloc(b->p, b->size);
115 memset(&stbuf, 0, sizeof(stbuf));
116 stbuf.st_ino = ino;
117 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
118 b->size);
119}
120
121#define min(x, y) ((x) < (y) ? (x) : (y))
122
123static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
124 off_t off, size_t maxsize)
125{
126 if (off < bufsize)
127 return fuse_reply_buf(req, buf + off,
128 min(bufsize - off, maxsize));
129 else
130 return fuse_reply_buf(req, NULL, 0);
131}
132
133static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
134 off_t off, struct fuse_file_info *fi)
135{
136 (void) fi;
137
138 if (ino != 1)
139 fuse_reply_err(req, ENOTDIR);
140 else {
141 struct dirbuf b;
142
143 memset(&b, 0, sizeof(b));
144 dirbuf_add(req, &b, ".", 1);
145 dirbuf_add(req, &b, "..", 1);
146 dirbuf_add(req, &b, hello_name, 2);
147 reply_buf_limited(req, b.p, b.size, off, size);
148 free(b.p);
149 }
150}
151
152static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
153 struct fuse_file_info *fi)
154{
155 if (ino != 2)
156 fuse_reply_err(req, EISDIR);
157 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
158 fuse_reply_err(req, EACCES);
159 else
160 fuse_reply_open(req, fi);
161}
162
163static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
164 off_t off, struct fuse_file_info *fi)
165{
166 (void) fi;
167
168 assert(ino == 2);
169 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
170}
171
172static void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
173 size_t size)
174{
175 (void)size;
176 assert(ino == 1 || ino == 2);
177 if (strcmp(name, "hello_ll_getxattr_name") == 0)
178 {
179 const char *buf = "hello_ll_getxattr_value";
180 fuse_reply_buf(req, buf, strlen(buf));
181 }
182 else
183 {
184 fuse_reply_err(req, ENOTSUP);
185 }
186}
187
188static void hello_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
189 const char *value, size_t size, int flags)
190{
191 (void)flags;
192 (void)size;
193 assert(ino == 1 || ino == 2);
194 const char* exp_val = "hello_ll_setxattr_value";
195 if (strcmp(name, "hello_ll_setxattr_name") == 0 &&
196 strlen(exp_val) == size &&
197 strncmp(value, exp_val, size) == 0)
198 {
199 fuse_reply_err(req, 0);
200 }
201 else
202 {
203 fuse_reply_err(req, ENOTSUP);
204 }
205}
206
207static void hello_ll_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
208{
209 assert(ino == 1 || ino == 2);
210 if (strcmp(name, "hello_ll_removexattr_name") == 0)
211 {
212 fuse_reply_err(req, 0);
213 }
214 else
215 {
216 fuse_reply_err(req, ENOTSUP);
217 }
218}
219
220static const struct fuse_lowlevel_ops hello_ll_oper = {
221 .init = hello_ll_init,
222 .lookup = hello_ll_lookup,
223 .getattr = hello_ll_getattr,
224 .readdir = hello_ll_readdir,
225 .open = hello_ll_open,
226 .read = hello_ll_read,
227 .setxattr = hello_ll_setxattr,
228 .getxattr = hello_ll_getxattr,
229 .removexattr = hello_ll_removexattr,
230};
231
232int main(int argc, char *argv[])
233{
234 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
235 struct fuse_session *se;
236 struct fuse_cmdline_opts opts;
237 struct fuse_loop_config *config;
238 int ret = -1;
239
240 if (fuse_parse_cmdline(&args, &opts) != 0)
241 return 1;
242 if (opts.show_help) {
243 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
246 ret = 0;
247 goto err_out1;
248 } else if (opts.show_version) {
249 printf("FUSE library version %s\n", fuse_pkgversion());
251 ret = 0;
252 goto err_out1;
253 }
254
255 if(opts.mountpoint == NULL) {
256 printf("usage: %s [options] <mountpoint>\n", argv[0]);
257 printf(" %s --help\n", argv[0]);
258 ret = 1;
259 goto err_out1;
260 }
261
262 se = fuse_session_new(&args, &hello_ll_oper,
263 sizeof(hello_ll_oper), NULL);
264 if (se == NULL)
265 goto err_out1;
266
267 if (fuse_set_signal_handlers(se) != 0)
268 goto err_out2;
269
270 if (fuse_session_mount(se, opts.mountpoint) != 0)
271 goto err_out3;
272
273 fuse_daemonize(opts.foreground);
274
275 /* Block until ctrl+c or fusermount -u */
276 if (opts.singlethread)
277 ret = fuse_session_loop(se);
278 else {
279 config = fuse_loop_cfg_create();
280 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
281 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
282 ret = fuse_session_loop_mt(se, config);
283 fuse_loop_cfg_destroy(config);
284 config = NULL;
285 }
286
288err_out3:
290err_out2:
292err_out1:
293 free(opts.mountpoint);
294 fuse_opt_free_args(&args);
295
296 return ret ? 1 : 0;
297}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2hello__ll_8c_source.html0000644000175000017500000017367015156613442024015 0ustar berndbernd libfuse: fuse-3.18.1/example/hello_ll.c Source File
libfuse
hello_ll.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
25#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
26
27#include <fuse_lowlevel.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <unistd.h>
34#include <assert.h>
35
36static const char *hello_str = "Hello World!\n";
37static const char *hello_name = "hello";
38
39static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
40{
41 stbuf->st_ino = ino;
42 switch (ino) {
43 case 1:
44 stbuf->st_mode = S_IFDIR | 0755;
45 stbuf->st_nlink = 2;
46 break;
47
48 case 2:
49 stbuf->st_mode = S_IFREG | 0444;
50 stbuf->st_nlink = 1;
51 stbuf->st_size = strlen(hello_str);
52 break;
53
54 default:
55 return -1;
56 }
57 return 0;
58}
59
60static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
61{
62 (void)userdata;
63
64 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
65 conn->no_interrupt = 1;
66
67 /* Test setting flags the old way */
69 conn->want &= ~FUSE_CAP_ASYNC_READ;
70}
71
72static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
73 struct fuse_file_info *fi)
74{
75 struct stat stbuf;
76
77 (void) fi;
78
79 memset(&stbuf, 0, sizeof(stbuf));
80 if (hello_stat(ino, &stbuf) == -1)
81 fuse_reply_err(req, ENOENT);
82 else
83 fuse_reply_attr(req, &stbuf, 1.0);
84}
85
86static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
87{
88 struct fuse_entry_param e;
89
90 if (parent != 1 || strcmp(name, hello_name) != 0)
91 fuse_reply_err(req, ENOENT);
92 else {
93 memset(&e, 0, sizeof(e));
94 e.ino = 2;
95 e.attr_timeout = 1.0;
96 e.entry_timeout = 1.0;
97 hello_stat(e.ino, &e.attr);
98
99 fuse_reply_entry(req, &e);
100 }
101}
102
103struct dirbuf {
104 char *p;
105 size_t size;
106};
107
108static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
109 fuse_ino_t ino)
110{
111 struct stat stbuf;
112 size_t oldsize = b->size;
113 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
114 b->p = (char *) realloc(b->p, b->size);
115 memset(&stbuf, 0, sizeof(stbuf));
116 stbuf.st_ino = ino;
117 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
118 b->size);
119}
120
121#define min(x, y) ((x) < (y) ? (x) : (y))
122
123static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
124 off_t off, size_t maxsize)
125{
126 if (off < bufsize)
127 return fuse_reply_buf(req, buf + off,
128 min(bufsize - off, maxsize));
129 else
130 return fuse_reply_buf(req, NULL, 0);
131}
132
133static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
134 off_t off, struct fuse_file_info *fi)
135{
136 (void) fi;
137
138 if (ino != 1)
139 fuse_reply_err(req, ENOTDIR);
140 else {
141 struct dirbuf b;
142
143 memset(&b, 0, sizeof(b));
144 dirbuf_add(req, &b, ".", 1);
145 dirbuf_add(req, &b, "..", 1);
146 dirbuf_add(req, &b, hello_name, 2);
147 reply_buf_limited(req, b.p, b.size, off, size);
148 free(b.p);
149 }
150}
151
152static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
153 struct fuse_file_info *fi)
154{
155 if (ino != 2)
156 fuse_reply_err(req, EISDIR);
157 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
158 fuse_reply_err(req, EACCES);
159 else
160 fuse_reply_open(req, fi);
161}
162
163static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
164 off_t off, struct fuse_file_info *fi)
165{
166 (void) fi;
167
168 assert(ino == 2);
169 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
170}
171
172static void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
173 size_t size)
174{
175 (void)size;
176 assert(ino == 1 || ino == 2);
177 if (strcmp(name, "hello_ll_getxattr_name") == 0)
178 {
179 const char *buf = "hello_ll_getxattr_value";
180 fuse_reply_buf(req, buf, strlen(buf));
181 }
182 else
183 {
184 fuse_reply_err(req, ENOTSUP);
185 }
186}
187
188static void hello_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
189 const char *value, size_t size, int flags)
190{
191 (void)flags;
192 (void)size;
193 assert(ino == 1 || ino == 2);
194 const char* exp_val = "hello_ll_setxattr_value";
195 if (strcmp(name, "hello_ll_setxattr_name") == 0 &&
196 strlen(exp_val) == size &&
197 strncmp(value, exp_val, size) == 0)
198 {
199 fuse_reply_err(req, 0);
200 }
201 else
202 {
203 fuse_reply_err(req, ENOTSUP);
204 }
205}
206
207static void hello_ll_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
208{
209 assert(ino == 1 || ino == 2);
210 if (strcmp(name, "hello_ll_removexattr_name") == 0)
211 {
212 fuse_reply_err(req, 0);
213 }
214 else
215 {
216 fuse_reply_err(req, ENOTSUP);
217 }
218}
219
220static const struct fuse_lowlevel_ops hello_ll_oper = {
221 .init = hello_ll_init,
222 .lookup = hello_ll_lookup,
223 .getattr = hello_ll_getattr,
224 .readdir = hello_ll_readdir,
225 .open = hello_ll_open,
226 .read = hello_ll_read,
227 .setxattr = hello_ll_setxattr,
228 .getxattr = hello_ll_getxattr,
229 .removexattr = hello_ll_removexattr,
230};
231
232int main(int argc, char *argv[])
233{
234 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
235 struct fuse_session *se;
236 struct fuse_cmdline_opts opts;
237 struct fuse_loop_config *config;
238 int ret = -1;
239
240 if (fuse_parse_cmdline(&args, &opts) != 0)
241 return 1;
242 if (opts.show_help) {
243 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
246 ret = 0;
247 goto err_out1;
248 } else if (opts.show_version) {
249 printf("FUSE library version %s\n", fuse_pkgversion());
251 ret = 0;
252 goto err_out1;
253 }
254
255 if(opts.mountpoint == NULL) {
256 printf("usage: %s [options] <mountpoint>\n", argv[0]);
257 printf(" %s --help\n", argv[0]);
258 ret = 1;
259 goto err_out1;
260 }
261
262 se = fuse_session_new(&args, &hello_ll_oper,
263 sizeof(hello_ll_oper), NULL);
264 if (se == NULL)
265 goto err_out1;
266
267 if (fuse_set_signal_handlers(se) != 0)
268 goto err_out2;
269
270 if (fuse_session_mount(se, opts.mountpoint) != 0)
271 goto err_out3;
272
273 fuse_daemonize(opts.foreground);
274
275 /* Block until ctrl+c or fusermount -u */
276 if (opts.singlethread)
277 ret = fuse_session_loop(se);
278 else {
279 config = fuse_loop_cfg_create();
280 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
281 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
282 ret = fuse_session_loop_mt(se, config);
283 fuse_loop_cfg_destroy(config);
284 config = NULL;
285 }
286
288err_out3:
290err_out2:
292err_out1:
293 free(opts.mountpoint);
294 fuse_opt_free_args(&args);
295
296 return ret ? 1 : 0;
297}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/example_2hello__ll__uds_8c_source.html0000644000175000017500000020730115156613442022640 0ustar berndbernd libfuse: example/hello_ll_uds.c Source File
libfuse
hello_ll_uds.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2022 Tofik Sonono <tofik.sonono@intel.com>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
23#define FUSE_USE_VERSION 34
24
25
26#ifndef _GNU_SOURCE
27#define _GNU_SOURCE
28#endif
29
30#include <fuse_lowlevel.h>
31#include <fuse_kernel.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <unistd.h>
38#include <assert.h>
39#include <sys/socket.h>
40#include <sys/un.h>
41
42static const char *hello_str = "Hello World!\n";
43static const char *hello_name = "hello";
44
45static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
46{
47 stbuf->st_ino = ino;
48 switch (ino) {
49 case 1:
50 stbuf->st_mode = S_IFDIR | 0755;
51 stbuf->st_nlink = 2;
52 break;
53
54 case 2:
55 stbuf->st_mode = S_IFREG | 0444;
56 stbuf->st_nlink = 1;
57 stbuf->st_size = strlen(hello_str);
58 break;
59
60 default:
61 return -1;
62 }
63 return 0;
64}
65
66static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
67 struct fuse_file_info *fi)
68{
69 struct stat stbuf;
70
71 (void) fi;
72
73 memset(&stbuf, 0, sizeof(stbuf));
74 if (hello_stat(ino, &stbuf) == -1)
75 fuse_reply_err(req, ENOENT);
76 else
77 fuse_reply_attr(req, &stbuf, 1.0);
78}
79
80static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
81{
82 (void)userdata;
83
84 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
85 conn->no_interrupt = 1;
86}
87
88static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
89{
90 struct fuse_entry_param e;
91
92 if (parent != 1 || strcmp(name, hello_name) != 0)
93 fuse_reply_err(req, ENOENT);
94 else {
95 memset(&e, 0, sizeof(e));
96 e.ino = 2;
97 e.attr_timeout = 1.0;
98 e.entry_timeout = 1.0;
99 hello_stat(e.ino, &e.attr);
100
101 fuse_reply_entry(req, &e);
102 }
103}
104
105struct dirbuf {
106 char *p;
107 size_t size;
108};
109
110static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
111 fuse_ino_t ino)
112{
113 struct stat stbuf;
114 size_t oldsize = b->size;
115 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
116 b->p = (char *) realloc(b->p, b->size);
117 memset(&stbuf, 0, sizeof(stbuf));
118 stbuf.st_ino = ino;
119 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
120 b->size);
121}
122
123#define min(x, y) ((x) < (y) ? (x) : (y))
124
125static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
126 off_t off, size_t maxsize)
127{
128 if (off < bufsize)
129 return fuse_reply_buf(req, buf + off,
130 min(bufsize - off, maxsize));
131 else
132 return fuse_reply_buf(req, NULL, 0);
133}
134
135static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
136 off_t off, struct fuse_file_info *fi)
137{
138 (void) fi;
139
140 if (ino != 1)
141 fuse_reply_err(req, ENOTDIR);
142 else {
143 struct dirbuf b;
144
145 memset(&b, 0, sizeof(b));
146 dirbuf_add(req, &b, ".", 1);
147 dirbuf_add(req, &b, "..", 1);
148 dirbuf_add(req, &b, hello_name, 2);
149 reply_buf_limited(req, b.p, b.size, off, size);
150 free(b.p);
151 }
152}
153
154static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
155 struct fuse_file_info *fi)
156{
157 if (ino != 2)
158 fuse_reply_err(req, EISDIR);
159 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
160 fuse_reply_err(req, EACCES);
161 else
162 fuse_reply_open(req, fi);
163}
164
165static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
166 off_t off, struct fuse_file_info *fi)
167{
168 (void) fi;
169
170 assert(ino == 2);
171 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
172}
173
174static const struct fuse_lowlevel_ops hello_ll_oper = {
175 .init = hello_ll_init,
176 .lookup = hello_ll_lookup,
177 .getattr = hello_ll_getattr,
178 .readdir = hello_ll_readdir,
179 .open = hello_ll_open,
180 .read = hello_ll_read,
181};
182
183static int create_socket(const char *socket_path) {
184 struct sockaddr_un addr;
185
186 if (strnlen(socket_path, sizeof(addr.sun_path)) >=
187 sizeof(addr.sun_path)) {
188 printf("Socket path may not be longer than %zu characters\n",
189 sizeof(addr.sun_path) - 1);
190 return -1;
191 }
192
193 if (remove(socket_path) == -1 && errno != ENOENT) {
194 printf("Could not delete previous socket file entry at %s. Error: "
195 "%s\n", socket_path, strerror(errno));
196 return -1;
197 }
198
199 memset(&addr, 0, sizeof(struct sockaddr_un));
200 strcpy(addr.sun_path, socket_path);
201
202 int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
203 if (sfd == -1) {
204 printf("Could not create socket. Error: %s\n", strerror(errno));
205 return -1;
206 }
207
208 addr.sun_family = AF_UNIX;
209 if (bind(sfd, (struct sockaddr *) &addr,
210 sizeof(struct sockaddr_un)) == -1) {
211 printf("Could not bind socket. Error: %s\n", strerror(errno));
212 return -1;
213 }
214
215 if (listen(sfd, 1) == -1)
216 return -1;
217
218 printf("Awaiting connection on socket at %s...\n", socket_path);
219 int cfd = accept(sfd, NULL, NULL);
220 if (cfd == -1) {
221 printf("Could not accept connection. Error: %s\n",
222 strerror(errno));
223 return -1;
224 } else {
225 printf("Accepted connection!\n");
226 }
227 return cfd;
228}
229
230static ssize_t stream_writev(int fd, struct iovec *iov, int count,
231 void *userdata) {
232 (void)userdata;
233
234 ssize_t written = 0;
235 int cur = 0;
236 for (;;) {
237 written = writev(fd, iov+cur, count-cur);
238 if (written < 0)
239 return written;
240
241 while (cur < count && written >= iov[cur].iov_len)
242 written -= iov[cur++].iov_len;
243 if (cur == count)
244 break;
245
246 iov[cur].iov_base = (char *)iov[cur].iov_base + written;
247 iov[cur].iov_len -= written;
248 }
249 return written;
250}
251
252
253static ssize_t readall(int fd, void *buf, size_t len) {
254 size_t count = 0;
255
256 while (count < len) {
257 int i = read(fd, (char *)buf + count, len - count);
258 if (!i)
259 break;
260
261 if (i < 0)
262 return i;
263
264 count += i;
265 }
266 return count;
267}
268
269static ssize_t stream_read(int fd, void *buf, size_t buf_len, void *userdata) {
270 (void)userdata;
271
272 int res = readall(fd, buf, sizeof(struct fuse_in_header));
273 if (res == -1)
274 return res;
275
276
277 uint32_t packet_len = ((struct fuse_in_header *)buf)->len;
278 if (packet_len > buf_len)
279 return -1;
280
281 int prev_res = res;
282
283 res = readall(fd, (char *)buf + sizeof(struct fuse_in_header),
284 packet_len - sizeof(struct fuse_in_header));
285
286 return (res == -1) ? res : (res + prev_res);
287}
288
289static ssize_t stream_splice_send(int fdin, off_t *offin, int fdout,
290 off_t *offout, size_t len,
291 unsigned int flags, void *userdata) {
292 (void)userdata;
293
294 size_t count = 0;
295 while (count < len) {
296 int i = splice(fdin, offin, fdout, offout, len - count, flags);
297 if (i < 1)
298 return i;
299
300 count += i;
301 }
302 return count;
303}
304
305static void fuse_cmdline_help_uds(void)
306{
307 printf(" -h --help print help\n"
308 " -V --version print version\n"
309 " -d -o debug enable debug output (implies -f)\n");
310}
311
312int main(int argc, char *argv[])
313{
314 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
315 struct fuse_session *se;
316 struct fuse_cmdline_opts opts;
317 const struct fuse_custom_io io = {
318 .writev = stream_writev,
319 .read = stream_read,
320 .splice_receive = NULL,
321 .splice_send = stream_splice_send,
322 };
323 int cfd = -1;
324 int ret = -1;
325
326 if (fuse_parse_cmdline(&args, &opts) != 0)
327 return 1;
328 if (opts.show_help) {
329 printf("usage: %s [options]\n\n", argv[0]);
330 fuse_cmdline_help_uds();
332 ret = 0;
333 goto err_out1;
334 } else if (opts.show_version) {
335 printf("FUSE library version %s\n", fuse_pkgversion());
337 ret = 0;
338 goto err_out1;
339 }
340
341 se = fuse_session_new(&args, &hello_ll_oper,
342 sizeof(hello_ll_oper), NULL);
343 if (se == NULL)
344 goto err_out1;
345
346 if (fuse_set_signal_handlers(se) != 0)
347 goto err_out2;
348
349 cfd = create_socket("/tmp/libfuse-hello-ll.sock");
350 if (cfd == -1)
351 goto err_out3;
352
353 if (fuse_session_custom_io(se, &io, cfd) != 0)
354 goto err_out3;
355
356 /* Block until ctrl+c */
357 ret = fuse_session_loop(se);
358err_out3:
360err_out2:
362err_out1:
363 free(opts.mountpoint);
364 fuse_opt_free_args(&args);
365
366 return ret ? 1 : 0;
367}
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_lowlevel_help(void)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2hello__ll__uds_8c_source.html0000644000175000017500000020750315156613442025020 0ustar berndbernd libfuse: fuse-3.18.1/example/hello_ll_uds.c Source File
libfuse
hello_ll_uds.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2022 Tofik Sonono <tofik.sonono@intel.com>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
23#define FUSE_USE_VERSION 34
24
25
26#ifndef _GNU_SOURCE
27#define _GNU_SOURCE
28#endif
29
30#include <fuse_lowlevel.h>
31#include <fuse_kernel.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <unistd.h>
38#include <assert.h>
39#include <sys/socket.h>
40#include <sys/un.h>
41
42static const char *hello_str = "Hello World!\n";
43static const char *hello_name = "hello";
44
45static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
46{
47 stbuf->st_ino = ino;
48 switch (ino) {
49 case 1:
50 stbuf->st_mode = S_IFDIR | 0755;
51 stbuf->st_nlink = 2;
52 break;
53
54 case 2:
55 stbuf->st_mode = S_IFREG | 0444;
56 stbuf->st_nlink = 1;
57 stbuf->st_size = strlen(hello_str);
58 break;
59
60 default:
61 return -1;
62 }
63 return 0;
64}
65
66static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
67 struct fuse_file_info *fi)
68{
69 struct stat stbuf;
70
71 (void) fi;
72
73 memset(&stbuf, 0, sizeof(stbuf));
74 if (hello_stat(ino, &stbuf) == -1)
75 fuse_reply_err(req, ENOENT);
76 else
77 fuse_reply_attr(req, &stbuf, 1.0);
78}
79
80static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
81{
82 (void)userdata;
83
84 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
85 conn->no_interrupt = 1;
86}
87
88static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
89{
90 struct fuse_entry_param e;
91
92 if (parent != 1 || strcmp(name, hello_name) != 0)
93 fuse_reply_err(req, ENOENT);
94 else {
95 memset(&e, 0, sizeof(e));
96 e.ino = 2;
97 e.attr_timeout = 1.0;
98 e.entry_timeout = 1.0;
99 hello_stat(e.ino, &e.attr);
100
101 fuse_reply_entry(req, &e);
102 }
103}
104
105struct dirbuf {
106 char *p;
107 size_t size;
108};
109
110static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
111 fuse_ino_t ino)
112{
113 struct stat stbuf;
114 size_t oldsize = b->size;
115 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
116 b->p = (char *) realloc(b->p, b->size);
117 memset(&stbuf, 0, sizeof(stbuf));
118 stbuf.st_ino = ino;
119 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
120 b->size);
121}
122
123#define min(x, y) ((x) < (y) ? (x) : (y))
124
125static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
126 off_t off, size_t maxsize)
127{
128 if (off < bufsize)
129 return fuse_reply_buf(req, buf + off,
130 min(bufsize - off, maxsize));
131 else
132 return fuse_reply_buf(req, NULL, 0);
133}
134
135static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
136 off_t off, struct fuse_file_info *fi)
137{
138 (void) fi;
139
140 if (ino != 1)
141 fuse_reply_err(req, ENOTDIR);
142 else {
143 struct dirbuf b;
144
145 memset(&b, 0, sizeof(b));
146 dirbuf_add(req, &b, ".", 1);
147 dirbuf_add(req, &b, "..", 1);
148 dirbuf_add(req, &b, hello_name, 2);
149 reply_buf_limited(req, b.p, b.size, off, size);
150 free(b.p);
151 }
152}
153
154static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
155 struct fuse_file_info *fi)
156{
157 if (ino != 2)
158 fuse_reply_err(req, EISDIR);
159 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
160 fuse_reply_err(req, EACCES);
161 else
162 fuse_reply_open(req, fi);
163}
164
165static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
166 off_t off, struct fuse_file_info *fi)
167{
168 (void) fi;
169
170 assert(ino == 2);
171 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
172}
173
174static const struct fuse_lowlevel_ops hello_ll_oper = {
175 .init = hello_ll_init,
176 .lookup = hello_ll_lookup,
177 .getattr = hello_ll_getattr,
178 .readdir = hello_ll_readdir,
179 .open = hello_ll_open,
180 .read = hello_ll_read,
181};
182
183static int create_socket(const char *socket_path) {
184 struct sockaddr_un addr;
185
186 if (strnlen(socket_path, sizeof(addr.sun_path)) >=
187 sizeof(addr.sun_path)) {
188 printf("Socket path may not be longer than %zu characters\n",
189 sizeof(addr.sun_path) - 1);
190 return -1;
191 }
192
193 if (remove(socket_path) == -1 && errno != ENOENT) {
194 printf("Could not delete previous socket file entry at %s. Error: "
195 "%s\n", socket_path, strerror(errno));
196 return -1;
197 }
198
199 memset(&addr, 0, sizeof(struct sockaddr_un));
200 strcpy(addr.sun_path, socket_path);
201
202 int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
203 if (sfd == -1) {
204 printf("Could not create socket. Error: %s\n", strerror(errno));
205 return -1;
206 }
207
208 addr.sun_family = AF_UNIX;
209 if (bind(sfd, (struct sockaddr *) &addr,
210 sizeof(struct sockaddr_un)) == -1) {
211 printf("Could not bind socket. Error: %s\n", strerror(errno));
212 return -1;
213 }
214
215 if (listen(sfd, 1) == -1)
216 return -1;
217
218 printf("Awaiting connection on socket at %s...\n", socket_path);
219 int cfd = accept(sfd, NULL, NULL);
220 if (cfd == -1) {
221 printf("Could not accept connection. Error: %s\n",
222 strerror(errno));
223 return -1;
224 } else {
225 printf("Accepted connection!\n");
226 }
227 return cfd;
228}
229
230static ssize_t stream_writev(int fd, struct iovec *iov, int count,
231 void *userdata) {
232 (void)userdata;
233
234 ssize_t written = 0;
235 int cur = 0;
236 for (;;) {
237 written = writev(fd, iov+cur, count-cur);
238 if (written < 0)
239 return written;
240
241 while (cur < count && written >= iov[cur].iov_len)
242 written -= iov[cur++].iov_len;
243 if (cur == count)
244 break;
245
246 iov[cur].iov_base = (char *)iov[cur].iov_base + written;
247 iov[cur].iov_len -= written;
248 }
249 return written;
250}
251
252
253static ssize_t readall(int fd, void *buf, size_t len) {
254 size_t count = 0;
255
256 while (count < len) {
257 int i = read(fd, (char *)buf + count, len - count);
258 if (!i)
259 break;
260
261 if (i < 0)
262 return i;
263
264 count += i;
265 }
266 return count;
267}
268
269static ssize_t stream_read(int fd, void *buf, size_t buf_len, void *userdata) {
270 (void)userdata;
271
272 int res = readall(fd, buf, sizeof(struct fuse_in_header));
273 if (res == -1)
274 return res;
275
276
277 uint32_t packet_len = ((struct fuse_in_header *)buf)->len;
278 if (packet_len > buf_len)
279 return -1;
280
281 int prev_res = res;
282
283 res = readall(fd, (char *)buf + sizeof(struct fuse_in_header),
284 packet_len - sizeof(struct fuse_in_header));
285
286 return (res == -1) ? res : (res + prev_res);
287}
288
289static ssize_t stream_splice_send(int fdin, off_t *offin, int fdout,
290 off_t *offout, size_t len,
291 unsigned int flags, void *userdata) {
292 (void)userdata;
293
294 size_t count = 0;
295 while (count < len) {
296 int i = splice(fdin, offin, fdout, offout, len - count, flags);
297 if (i < 1)
298 return i;
299
300 count += i;
301 }
302 return count;
303}
304
305static void fuse_cmdline_help_uds(void)
306{
307 printf(" -h --help print help\n"
308 " -V --version print version\n"
309 " -d -o debug enable debug output (implies -f)\n");
310}
311
312int main(int argc, char *argv[])
313{
314 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
315 struct fuse_session *se;
316 struct fuse_cmdline_opts opts;
317 const struct fuse_custom_io io = {
318 .writev = stream_writev,
319 .read = stream_read,
320 .splice_receive = NULL,
321 .splice_send = stream_splice_send,
322 };
323 int cfd = -1;
324 int ret = -1;
325
326 if (fuse_parse_cmdline(&args, &opts) != 0)
327 return 1;
328 if (opts.show_help) {
329 printf("usage: %s [options]\n\n", argv[0]);
330 fuse_cmdline_help_uds();
332 ret = 0;
333 goto err_out1;
334 } else if (opts.show_version) {
335 printf("FUSE library version %s\n", fuse_pkgversion());
337 ret = 0;
338 goto err_out1;
339 }
340
341 se = fuse_session_new(&args, &hello_ll_oper,
342 sizeof(hello_ll_oper), NULL);
343 if (se == NULL)
344 goto err_out1;
345
346 if (fuse_set_signal_handlers(se) != 0)
347 goto err_out2;
348
349 cfd = create_socket("/tmp/libfuse-hello-ll.sock");
350 if (cfd == -1)
351 goto err_out3;
352
353 if (fuse_session_custom_io(se, &io, cfd) != 0)
354 goto err_out3;
355
356 /* Block until ctrl+c */
357 ret = fuse_session_loop(se);
358err_out3:
360err_out2:
362err_out1:
363 free(opts.mountpoint);
364 fuse_opt_free_args(&args);
365
366 return ret ? 1 : 0;
367}
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_lowlevel_help(void)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/example_2invalidate__path_8c_source.html0000644000175000017500000016464215156613442023202 0ustar berndbernd libfuse: example/invalidate_path.c Source File
libfuse
invalidate_path.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4 (C) 2017 EditShare LLC <slawek.rudnicki@editshare.com>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8 */
9
28#define FUSE_USE_VERSION 34
29
30#include <fuse.h>
31#include <fuse_lowlevel.h> /* for fuse_cmdline_opts */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <assert.h>
39#include <stddef.h>
40#include <unistd.h>
41#include <pthread.h>
42
43/* We can't actually tell the kernel that there is no
44 timeout, so we just send a big value */
45#define NO_TIMEOUT 500000
46
47#define MAX_STR_LEN 128
48#define TIME_FILE_NAME "current_time"
49#define TIME_FILE_INO 2
50#define GROW_FILE_NAME "growing"
51#define GROW_FILE_INO 3
52
53static char time_file_contents[MAX_STR_LEN];
54static size_t grow_file_size;
55
56/* Command line parsing */
57struct options {
58 int no_notify;
59 int update_interval;
60};
61static struct options options = {
62 .no_notify = 0,
63 .update_interval = 1,
64};
65
66#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
67static const struct fuse_opt option_spec[] = {
68 OPTION("--no-notify", no_notify),
69 OPTION("--update-interval=%d", update_interval),
71};
72
73static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
74{
75 (void) conn;
76 cfg->entry_timeout = NO_TIMEOUT;
77 cfg->attr_timeout = NO_TIMEOUT;
78 cfg->negative_timeout = 0;
79
80 return NULL;
81}
82
83static int xmp_getattr(const char *path,
84 struct stat *stbuf, struct fuse_file_info* fi) {
85 (void) fi;
86 if (strcmp(path, "/") == 0) {
87 stbuf->st_ino = 1;
88 stbuf->st_mode = S_IFDIR | 0755;
89 stbuf->st_nlink = 1;
90 } else if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
91 stbuf->st_ino = TIME_FILE_INO;
92 stbuf->st_mode = S_IFREG | 0444;
93 stbuf->st_nlink = 1;
94 stbuf->st_size = strlen(time_file_contents);
95 } else if (strcmp(path, "/" GROW_FILE_NAME) == 0) {
96 stbuf->st_ino = GROW_FILE_INO;
97 stbuf->st_mode = S_IFREG | 0444;
98 stbuf->st_nlink = 1;
99 stbuf->st_size = grow_file_size;
100 } else {
101 return -ENOENT;
102 }
103
104 return 0;
105}
106
107static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
108 off_t offset, struct fuse_file_info *fi,
109 enum fuse_readdir_flags flags) {
110 (void) fi;
111 (void) offset;
112 (void) flags;
113 if (strcmp(path, "/") != 0) {
114 return -ENOTDIR;
115 } else {
116 (void) filler;
117 (void) buf;
118 struct stat file_stat;
119 xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL);
120 filler(buf, TIME_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
121 xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL);
122 filler(buf, GROW_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
123 return 0;
124 }
125}
126
127static int xmp_open(const char *path, struct fuse_file_info *fi) {
128 (void) path;
129 /* Make cache persistent even if file is closed,
130 this makes it easier to see the effects */
131 fi->keep_cache = 1;
132 return 0;
133}
134
135static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
136 struct fuse_file_info *fi) {
137 (void) fi;
138 (void) offset;
139 if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
140 int file_length = strlen(time_file_contents);
141 int to_copy = offset + size <= file_length
142 ? size
143 : file_length - offset;
144 memcpy(buf, time_file_contents, to_copy);
145 return to_copy;
146 } else {
147 assert(strcmp(path, "/" GROW_FILE_NAME) == 0);
148 int to_copy = offset + size <= grow_file_size
149 ? size
150 : grow_file_size - offset;
151 memset(buf, 'x', to_copy);
152 return to_copy;
153 }
154}
155
156static const struct fuse_operations xmp_oper = {
157 .init = xmp_init,
158 .getattr = xmp_getattr,
159 .readdir = xmp_readdir,
160 .open = xmp_open,
161 .read = xmp_read,
162};
163
164static void update_fs(void) {
165 static int count = 0;
166 struct tm *now;
167 time_t t;
168 t = time(NULL);
169 now = localtime(&t);
170 assert(now != NULL);
171
172 int time_file_size = strftime(time_file_contents, MAX_STR_LEN,
173 "The current time is %H:%M:%S\n", now);
174 assert(time_file_size != 0);
175
176 grow_file_size = count++;
177}
178
179static int invalidate(struct fuse *fuse, const char *path) {
180 int status = fuse_invalidate_path(fuse, path);
181 if (status == -ENOENT) {
182 return 0;
183 } else {
184 return status;
185 }
186}
187
188static void* update_fs_loop(void *data) {
189 struct fuse *fuse = (struct fuse*) data;
190
191 while (1) {
192 update_fs();
193 if (!options.no_notify) {
194 assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0);
195 assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0);
196 }
197 sleep(options.update_interval);
198 }
199 return NULL;
200}
201
202static void show_help(const char *progname)
203{
204 printf("usage: %s [options] <mountpoint>\n\n", progname);
205 printf("File-system specific options:\n"
206 " --update-interval=<secs> Update-rate of file system contents\n"
207 " --no-notify Disable kernel notifications\n"
208 "\n");
209}
210
211int main(int argc, char *argv[]) {
212 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
213 struct fuse *fuse;
214 struct fuse_cmdline_opts opts;
215 struct fuse_loop_config config;
216 int res;
217
218 /* Initialize the files */
219 update_fs();
220
221 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
222 return 1;
223
224 if (fuse_parse_cmdline(&args, &opts) != 0)
225 return 1;
226
227 if (opts.show_version) {
228 printf("FUSE library version %s\n", fuse_pkgversion());
230 res = 0;
231 goto out1;
232 } else if (opts.show_help) {
233 show_help(argv[0]);
235 fuse_lib_help(&args);
236 res = 0;
237 goto out1;
238 } else if (!opts.mountpoint) {
239 fprintf(stderr, "error: no mountpoint specified\n");
240 res = 1;
241 goto out1;
242 }
243
244 fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL);
245 if (fuse == NULL) {
246 res = 1;
247 goto out1;
248 }
249
250 if (fuse_mount(fuse,opts.mountpoint) != 0) {
251 res = 1;
252 goto out2;
253 }
254
255 if (fuse_daemonize(opts.foreground) != 0) {
256 res = 1;
257 goto out3;
258 }
259
260 pthread_t updater; /* Start thread to update file contents */
261 int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse);
262 if (ret != 0) {
263 fprintf(stderr, "pthread_create failed with %s\n", strerror(ret));
264 return 1;
265 };
266
267 struct fuse_session *se = fuse_get_session(fuse);
268 if (fuse_set_signal_handlers(se) != 0) {
269 res = 1;
270 goto out3;
271 }
272
273 if (opts.singlethread)
274 res = fuse_loop(fuse);
275 else {
276 config.clone_fd = opts.clone_fd;
277 config.max_idle_threads = opts.max_idle_threads;
278 res = fuse_loop_mt(fuse, &config);
279 }
280 if (res)
281 res = 1;
282
284out3:
285 fuse_unmount(fuse);
286out2:
287 fuse_destroy(fuse);
288out1:
289 free(opts.mountpoint);
290 fuse_opt_free_args(&args);
291 return res;
292}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t keep_cache
Definition fuse_common.h:69
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2invalidate__path_8c_source.html0000644000175000017500000016504415156613442025353 0ustar berndbernd libfuse: fuse-3.18.1/example/invalidate_path.c Source File
libfuse
invalidate_path.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4 (C) 2017 EditShare LLC <slawek.rudnicki@editshare.com>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8 */
9
28#define FUSE_USE_VERSION 34
29
30#include <fuse.h>
31#include <fuse_lowlevel.h> /* for fuse_cmdline_opts */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <assert.h>
39#include <stddef.h>
40#include <unistd.h>
41#include <pthread.h>
42
43/* We can't actually tell the kernel that there is no
44 timeout, so we just send a big value */
45#define NO_TIMEOUT 500000
46
47#define MAX_STR_LEN 128
48#define TIME_FILE_NAME "current_time"
49#define TIME_FILE_INO 2
50#define GROW_FILE_NAME "growing"
51#define GROW_FILE_INO 3
52
53static char time_file_contents[MAX_STR_LEN];
54static size_t grow_file_size;
55
56/* Command line parsing */
57struct options {
58 int no_notify;
59 int update_interval;
60};
61static struct options options = {
62 .no_notify = 0,
63 .update_interval = 1,
64};
65
66#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
67static const struct fuse_opt option_spec[] = {
68 OPTION("--no-notify", no_notify),
69 OPTION("--update-interval=%d", update_interval),
71};
72
73static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
74{
75 (void) conn;
76 cfg->entry_timeout = NO_TIMEOUT;
77 cfg->attr_timeout = NO_TIMEOUT;
78 cfg->negative_timeout = 0;
79
80 return NULL;
81}
82
83static int xmp_getattr(const char *path,
84 struct stat *stbuf, struct fuse_file_info* fi) {
85 (void) fi;
86 if (strcmp(path, "/") == 0) {
87 stbuf->st_ino = 1;
88 stbuf->st_mode = S_IFDIR | 0755;
89 stbuf->st_nlink = 1;
90 } else if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
91 stbuf->st_ino = TIME_FILE_INO;
92 stbuf->st_mode = S_IFREG | 0444;
93 stbuf->st_nlink = 1;
94 stbuf->st_size = strlen(time_file_contents);
95 } else if (strcmp(path, "/" GROW_FILE_NAME) == 0) {
96 stbuf->st_ino = GROW_FILE_INO;
97 stbuf->st_mode = S_IFREG | 0444;
98 stbuf->st_nlink = 1;
99 stbuf->st_size = grow_file_size;
100 } else {
101 return -ENOENT;
102 }
103
104 return 0;
105}
106
107static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
108 off_t offset, struct fuse_file_info *fi,
109 enum fuse_readdir_flags flags) {
110 (void) fi;
111 (void) offset;
112 (void) flags;
113 if (strcmp(path, "/") != 0) {
114 return -ENOTDIR;
115 } else {
116 (void) filler;
117 (void) buf;
118 struct stat file_stat;
119 xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL);
120 filler(buf, TIME_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
121 xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL);
122 filler(buf, GROW_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
123 return 0;
124 }
125}
126
127static int xmp_open(const char *path, struct fuse_file_info *fi) {
128 (void) path;
129 /* Make cache persistent even if file is closed,
130 this makes it easier to see the effects */
131 fi->keep_cache = 1;
132 return 0;
133}
134
135static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
136 struct fuse_file_info *fi) {
137 (void) fi;
138 (void) offset;
139 if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
140 int file_length = strlen(time_file_contents);
141 int to_copy = offset + size <= file_length
142 ? size
143 : file_length - offset;
144 memcpy(buf, time_file_contents, to_copy);
145 return to_copy;
146 } else {
147 assert(strcmp(path, "/" GROW_FILE_NAME) == 0);
148 int to_copy = offset + size <= grow_file_size
149 ? size
150 : grow_file_size - offset;
151 memset(buf, 'x', to_copy);
152 return to_copy;
153 }
154}
155
156static const struct fuse_operations xmp_oper = {
157 .init = xmp_init,
158 .getattr = xmp_getattr,
159 .readdir = xmp_readdir,
160 .open = xmp_open,
161 .read = xmp_read,
162};
163
164static void update_fs(void) {
165 static int count = 0;
166 struct tm *now;
167 time_t t;
168 t = time(NULL);
169 now = localtime(&t);
170 assert(now != NULL);
171
172 int time_file_size = strftime(time_file_contents, MAX_STR_LEN,
173 "The current time is %H:%M:%S\n", now);
174 assert(time_file_size != 0);
175
176 grow_file_size = count++;
177}
178
179static int invalidate(struct fuse *fuse, const char *path) {
180 int status = fuse_invalidate_path(fuse, path);
181 if (status == -ENOENT) {
182 return 0;
183 } else {
184 return status;
185 }
186}
187
188static void* update_fs_loop(void *data) {
189 struct fuse *fuse = (struct fuse*) data;
190
191 while (1) {
192 update_fs();
193 if (!options.no_notify) {
194 assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0);
195 assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0);
196 }
197 sleep(options.update_interval);
198 }
199 return NULL;
200}
201
202static void show_help(const char *progname)
203{
204 printf("usage: %s [options] <mountpoint>\n\n", progname);
205 printf("File-system specific options:\n"
206 " --update-interval=<secs> Update-rate of file system contents\n"
207 " --no-notify Disable kernel notifications\n"
208 "\n");
209}
210
211int main(int argc, char *argv[]) {
212 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
213 struct fuse *fuse;
214 struct fuse_cmdline_opts opts;
215 struct fuse_loop_config config;
216 int res;
217
218 /* Initialize the files */
219 update_fs();
220
221 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
222 return 1;
223
224 if (fuse_parse_cmdline(&args, &opts) != 0)
225 return 1;
226
227 if (opts.show_version) {
228 printf("FUSE library version %s\n", fuse_pkgversion());
230 res = 0;
231 goto out1;
232 } else if (opts.show_help) {
233 show_help(argv[0]);
235 fuse_lib_help(&args);
236 res = 0;
237 goto out1;
238 } else if (!opts.mountpoint) {
239 fprintf(stderr, "error: no mountpoint specified\n");
240 res = 1;
241 goto out1;
242 }
243
244 fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL);
245 if (fuse == NULL) {
246 res = 1;
247 goto out1;
248 }
249
250 if (fuse_mount(fuse,opts.mountpoint) != 0) {
251 res = 1;
252 goto out2;
253 }
254
255 if (fuse_daemonize(opts.foreground) != 0) {
256 res = 1;
257 goto out3;
258 }
259
260 pthread_t updater; /* Start thread to update file contents */
261 int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse);
262 if (ret != 0) {
263 fprintf(stderr, "pthread_create failed with %s\n", strerror(ret));
264 return 1;
265 };
266
267 struct fuse_session *se = fuse_get_session(fuse);
268 if (fuse_set_signal_handlers(se) != 0) {
269 res = 1;
270 goto out3;
271 }
272
273 if (opts.singlethread)
274 res = fuse_loop(fuse);
275 else {
276 config.clone_fd = opts.clone_fd;
277 config.max_idle_threads = opts.max_idle_threads;
278 res = fuse_loop_mt(fuse, &config);
279 }
280 if (res)
281 res = 1;
282
284out3:
285 fuse_unmount(fuse);
286out2:
287 fuse_destroy(fuse);
288out1:
289 free(opts.mountpoint);
290 fuse_opt_free_args(&args);
291 return res;
292}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t keep_cache
Definition fuse_common.h:69
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/example_2ioctl_8c_source.html0000644000175000017500000010722515156613442021013 0ustar berndbernd libfuse: example/ioctl.c Source File
libfuse
ioctl.c
Go to the documentation of this file.
1/*
2 FUSE fioc: FUSE ioctl example
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
25#define FUSE_USE_VERSION 35
26
27#include <fuse.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31#include <unistd.h>
32#include <time.h>
33#include <errno.h>
34
35#include "ioctl.h"
36
37#define FIOC_NAME "fioc"
38
39enum {
40 FIOC_NONE,
41 FIOC_ROOT,
42 FIOC_FILE,
43};
44
45static void *fioc_buf;
46static size_t fioc_size;
47
48static int fioc_resize(size_t new_size)
49{
50 void *new_buf;
51
52 if (new_size == fioc_size)
53 return 0;
54
55 new_buf = realloc(fioc_buf, new_size);
56 if (!new_buf && new_size)
57 return -ENOMEM;
58
59 if (new_size > fioc_size)
60 memset(new_buf + fioc_size, 0, new_size - fioc_size);
61
62 fioc_buf = new_buf;
63 fioc_size = new_size;
64
65 return 0;
66}
67
68static int fioc_expand(size_t new_size)
69{
70 if (new_size > fioc_size)
71 return fioc_resize(new_size);
72 return 0;
73}
74
75static int fioc_file_type(const char *path)
76{
77 if (strcmp(path, "/") == 0)
78 return FIOC_ROOT;
79 if (strcmp(path, "/" FIOC_NAME) == 0)
80 return FIOC_FILE;
81 return FIOC_NONE;
82}
83
84static int fioc_getattr(const char *path, struct stat *stbuf,
85 struct fuse_file_info *fi)
86{
87 (void) fi;
88 stbuf->st_uid = getuid();
89 stbuf->st_gid = getgid();
90 stbuf->st_atime = stbuf->st_mtime = time(NULL);
91
92 switch (fioc_file_type(path)) {
93 case FIOC_ROOT:
94 stbuf->st_mode = S_IFDIR | 0755;
95 stbuf->st_nlink = 2;
96 break;
97 case FIOC_FILE:
98 stbuf->st_mode = S_IFREG | 0644;
99 stbuf->st_nlink = 1;
100 stbuf->st_size = fioc_size;
101 break;
102 case FIOC_NONE:
103 return -ENOENT;
104 }
105
106 return 0;
107}
108
109static int fioc_open(const char *path, struct fuse_file_info *fi)
110{
111 (void) fi;
112
113 if (fioc_file_type(path) != FIOC_NONE)
114 return 0;
115 return -ENOENT;
116}
117
118static int fioc_do_read(char *buf, size_t size, off_t offset)
119{
120 if (offset >= fioc_size)
121 return 0;
122
123 if (size > fioc_size - offset)
124 size = fioc_size - offset;
125
126 memcpy(buf, fioc_buf + offset, size);
127
128 return size;
129}
130
131static int fioc_read(const char *path, char *buf, size_t size,
132 off_t offset, struct fuse_file_info *fi)
133{
134 (void) fi;
135
136 if (fioc_file_type(path) != FIOC_FILE)
137 return -EINVAL;
138
139 return fioc_do_read(buf, size, offset);
140}
141
142static int fioc_do_write(const char *buf, size_t size, off_t offset)
143{
144 if (fioc_expand(offset + size))
145 return -ENOMEM;
146
147 memcpy(fioc_buf + offset, buf, size);
148
149 return size;
150}
151
152static int fioc_write(const char *path, const char *buf, size_t size,
153 off_t offset, struct fuse_file_info *fi)
154{
155 (void) fi;
156
157 if (fioc_file_type(path) != FIOC_FILE)
158 return -EINVAL;
159
160 return fioc_do_write(buf, size, offset);
161}
162
163static int fioc_truncate(const char *path, off_t size,
164 struct fuse_file_info *fi)
165{
166 (void) fi;
167 if (fioc_file_type(path) != FIOC_FILE)
168 return -EINVAL;
169
170 return fioc_resize(size);
171}
172
173static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
174 off_t offset, struct fuse_file_info *fi,
175 enum fuse_readdir_flags flags)
176{
177 (void) fi;
178 (void) offset;
179 (void) flags;
180
181 if (fioc_file_type(path) != FIOC_ROOT)
182 return -ENOENT;
183
184 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
185 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
186 filler(buf, FIOC_NAME, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
187
188 return 0;
189}
190
191static int fioc_ioctl(const char *path, unsigned int cmd, void *arg,
192 struct fuse_file_info *fi, unsigned int flags, void *data)
193{
194 (void) arg;
195 (void) fi;
196 (void) flags;
197
198 if (fioc_file_type(path) != FIOC_FILE)
199 return -EINVAL;
200
201 if (flags & FUSE_IOCTL_COMPAT)
202 return -ENOSYS;
203
204 switch (cmd) {
205 case FIOC_GET_SIZE:
206 *(size_t *)data = fioc_size;
207 return 0;
208
209 case FIOC_SET_SIZE:
210 fioc_resize(*(size_t *)data);
211 return 0;
212 }
213
214 return -EINVAL;
215}
216
217static const struct fuse_operations fioc_oper = {
218 .getattr = fioc_getattr,
219 .readdir = fioc_readdir,
220 .truncate = fioc_truncate,
221 .open = fioc_open,
222 .read = fioc_read,
223 .write = fioc_write,
224 .ioctl = fioc_ioctl,
225};
226
227int main(int argc, char *argv[])
228{
229 return fuse_main(argc, argv, &fioc_oper, NULL);
230}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_IOCTL_COMPAT
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2ioctl_8c_source.html0000644000175000017500000010742715156613442023173 0ustar berndbernd libfuse: fuse-3.18.1/example/ioctl.c Source File
libfuse
ioctl.c
Go to the documentation of this file.
1/*
2 FUSE fioc: FUSE ioctl example
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
25#define FUSE_USE_VERSION 35
26
27#include <fuse.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31#include <unistd.h>
32#include <time.h>
33#include <errno.h>
34
35#include "ioctl.h"
36
37#define FIOC_NAME "fioc"
38
39enum {
40 FIOC_NONE,
41 FIOC_ROOT,
42 FIOC_FILE,
43};
44
45static void *fioc_buf;
46static size_t fioc_size;
47
48static int fioc_resize(size_t new_size)
49{
50 void *new_buf;
51
52 if (new_size == fioc_size)
53 return 0;
54
55 new_buf = realloc(fioc_buf, new_size);
56 if (!new_buf && new_size)
57 return -ENOMEM;
58
59 if (new_size > fioc_size)
60 memset(new_buf + fioc_size, 0, new_size - fioc_size);
61
62 fioc_buf = new_buf;
63 fioc_size = new_size;
64
65 return 0;
66}
67
68static int fioc_expand(size_t new_size)
69{
70 if (new_size > fioc_size)
71 return fioc_resize(new_size);
72 return 0;
73}
74
75static int fioc_file_type(const char *path)
76{
77 if (strcmp(path, "/") == 0)
78 return FIOC_ROOT;
79 if (strcmp(path, "/" FIOC_NAME) == 0)
80 return FIOC_FILE;
81 return FIOC_NONE;
82}
83
84static int fioc_getattr(const char *path, struct stat *stbuf,
85 struct fuse_file_info *fi)
86{
87 (void) fi;
88 stbuf->st_uid = getuid();
89 stbuf->st_gid = getgid();
90 stbuf->st_atime = stbuf->st_mtime = time(NULL);
91
92 switch (fioc_file_type(path)) {
93 case FIOC_ROOT:
94 stbuf->st_mode = S_IFDIR | 0755;
95 stbuf->st_nlink = 2;
96 break;
97 case FIOC_FILE:
98 stbuf->st_mode = S_IFREG | 0644;
99 stbuf->st_nlink = 1;
100 stbuf->st_size = fioc_size;
101 break;
102 case FIOC_NONE:
103 return -ENOENT;
104 }
105
106 return 0;
107}
108
109static int fioc_open(const char *path, struct fuse_file_info *fi)
110{
111 (void) fi;
112
113 if (fioc_file_type(path) != FIOC_NONE)
114 return 0;
115 return -ENOENT;
116}
117
118static int fioc_do_read(char *buf, size_t size, off_t offset)
119{
120 if (offset >= fioc_size)
121 return 0;
122
123 if (size > fioc_size - offset)
124 size = fioc_size - offset;
125
126 memcpy(buf, fioc_buf + offset, size);
127
128 return size;
129}
130
131static int fioc_read(const char *path, char *buf, size_t size,
132 off_t offset, struct fuse_file_info *fi)
133{
134 (void) fi;
135
136 if (fioc_file_type(path) != FIOC_FILE)
137 return -EINVAL;
138
139 return fioc_do_read(buf, size, offset);
140}
141
142static int fioc_do_write(const char *buf, size_t size, off_t offset)
143{
144 if (fioc_expand(offset + size))
145 return -ENOMEM;
146
147 memcpy(fioc_buf + offset, buf, size);
148
149 return size;
150}
151
152static int fioc_write(const char *path, const char *buf, size_t size,
153 off_t offset, struct fuse_file_info *fi)
154{
155 (void) fi;
156
157 if (fioc_file_type(path) != FIOC_FILE)
158 return -EINVAL;
159
160 return fioc_do_write(buf, size, offset);
161}
162
163static int fioc_truncate(const char *path, off_t size,
164 struct fuse_file_info *fi)
165{
166 (void) fi;
167 if (fioc_file_type(path) != FIOC_FILE)
168 return -EINVAL;
169
170 return fioc_resize(size);
171}
172
173static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
174 off_t offset, struct fuse_file_info *fi,
175 enum fuse_readdir_flags flags)
176{
177 (void) fi;
178 (void) offset;
179 (void) flags;
180
181 if (fioc_file_type(path) != FIOC_ROOT)
182 return -ENOENT;
183
184 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
185 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
186 filler(buf, FIOC_NAME, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
187
188 return 0;
189}
190
191static int fioc_ioctl(const char *path, unsigned int cmd, void *arg,
192 struct fuse_file_info *fi, unsigned int flags, void *data)
193{
194 (void) arg;
195 (void) fi;
196 (void) flags;
197
198 if (fioc_file_type(path) != FIOC_FILE)
199 return -EINVAL;
200
201 if (flags & FUSE_IOCTL_COMPAT)
202 return -ENOSYS;
203
204 switch (cmd) {
205 case FIOC_GET_SIZE:
206 *(size_t *)data = fioc_size;
207 return 0;
208
209 case FIOC_SET_SIZE:
210 fioc_resize(*(size_t *)data);
211 return 0;
212 }
213
214 return -EINVAL;
215}
216
217static const struct fuse_operations fioc_oper = {
218 .getattr = fioc_getattr,
219 .readdir = fioc_readdir,
220 .truncate = fioc_truncate,
221 .open = fioc_open,
222 .read = fioc_read,
223 .write = fioc_write,
224 .ioctl = fioc_ioctl,
225};
226
227int main(int argc, char *argv[])
228{
229 return fuse_main(argc, argv, &fioc_oper, NULL);
230}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_IOCTL_COMPAT
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
fuse-3.18.2/doc/html/example_2ioctl_8h_source.html0000644000175000017500000001570515156613442021021 0ustar berndbernd libfuse: example/ioctl.h Source File
libfuse
ioctl.h
Go to the documentation of this file.
1/*
2 FUSE-ioctl: ioctl support for FUSE
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
20#include <sys/types.h>
21#include <sys/uio.h>
22#include <sys/ioctl.h>
23
24enum {
25 FIOC_GET_SIZE = _IOR('E', 0, size_t),
26 FIOC_SET_SIZE = _IOW('E', 1, size_t),
27
28 /*
29 * The following two ioctls don't follow usual encoding rules
30 * and transfer variable amount of data.
31 */
32 FIOC_READ = _IO('E', 2),
33 FIOC_WRITE = _IO('E', 3),
34};
35
36struct fioc_rw_arg {
37 off_t offset;
38 void *buf;
39 size_t size;
40 size_t prev_size; /* out param for previous total size */
41 size_t new_size; /* out param for new total size */
42};
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2ioctl_8h_source.html0000644000175000017500000001610715156613442023172 0ustar berndbernd libfuse: fuse-3.18.1/example/ioctl.h Source File
libfuse
ioctl.h
Go to the documentation of this file.
1/*
2 FUSE-ioctl: ioctl support for FUSE
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
20#include <sys/types.h>
21#include <sys/uio.h>
22#include <sys/ioctl.h>
23
24enum {
25 FIOC_GET_SIZE = _IOR('E', 0, size_t),
26 FIOC_SET_SIZE = _IOW('E', 1, size_t),
27
28 /*
29 * The following two ioctls don't follow usual encoding rules
30 * and transfer variable amount of data.
31 */
32 FIOC_READ = _IO('E', 2),
33 FIOC_WRITE = _IO('E', 3),
34};
35
36struct fioc_rw_arg {
37 off_t offset;
38 void *buf;
39 size_t size;
40 size_t prev_size; /* out param for previous total size */
41 size_t new_size; /* out param for new total size */
42};
fuse-3.18.2/doc/html/example_2ioctl__client_8c_source.html0000644000175000017500000002610115156613442022501 0ustar berndbernd libfuse: example/ioctl_client.c Source File
libfuse
ioctl_client.c
Go to the documentation of this file.
1/*
2 FUSE fioclient: FUSE ioctl example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
22#include <sys/types.h>
23#include <fcntl.h>
24#include <sys/stat.h>
25#include <sys/ioctl.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <ctype.h>
29#include <errno.h>
30#include <unistd.h>
31#include "ioctl.h"
32
33const char *usage =
34"Usage: fioclient FIOC_FILE [size]\n"
35"\n"
36"Get size if <size> is omitted, set size otherwise\n"
37"\n";
38
39int main(int argc, char **argv)
40{
41 size_t size;
42 int fd;
43 int ret = 0;
44
45 if (argc < 2) {
46 fprintf(stderr, "%s", usage);
47 return 1;
48 }
49
50 fd = open(argv[1], O_RDWR);
51 if (fd < 0) {
52 perror("open");
53 return 1;
54 }
55
56 if (argc == 2) {
57 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
58 perror("ioctl");
59 ret = 1;
60 goto out;
61 }
62 printf("%zu\n", size);
63 } else {
64 size = strtoul(argv[2], NULL, 0);
65 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
66 perror("ioctl");
67 ret = 1;
68 goto out;
69 }
70 }
71out:
72 close(fd);
73 return ret;
74}
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2ioctl__client_8c_source.html0000644000175000017500000002630315156613442024661 0ustar berndbernd libfuse: fuse-3.18.1/example/ioctl_client.c Source File
libfuse
ioctl_client.c
Go to the documentation of this file.
1/*
2 FUSE fioclient: FUSE ioctl example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
22#include <sys/types.h>
23#include <fcntl.h>
24#include <sys/stat.h>
25#include <sys/ioctl.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <ctype.h>
29#include <errno.h>
30#include <unistd.h>
31#include "ioctl.h"
32
33const char *usage =
34"Usage: fioclient FIOC_FILE [size]\n"
35"\n"
36"Get size if <size> is omitted, set size otherwise\n"
37"\n";
38
39int main(int argc, char **argv)
40{
41 size_t size;
42 int fd;
43 int ret = 0;
44
45 if (argc < 2) {
46 fprintf(stderr, "%s", usage);
47 return 1;
48 }
49
50 fd = open(argv[1], O_RDWR);
51 if (fd < 0) {
52 perror("open");
53 return 1;
54 }
55
56 if (argc == 2) {
57 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
58 perror("ioctl");
59 ret = 1;
60 goto out;
61 }
62 printf("%zu\n", size);
63 } else {
64 size = strtoul(argv[2], NULL, 0);
65 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
66 perror("ioctl");
67 ret = 1;
68 goto out;
69 }
70 }
71out:
72 close(fd);
73 return ret;
74}
fuse-3.18.2/doc/html/example_2notify__inval__entry_8c_source.html0000644000175000017500000022032715156613442024120 0ustar berndbernd libfuse: example/notify_inval_entry.c Source File
libfuse
notify_inval_entry.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
85#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
86
87#include <fuse_lowlevel.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <string.h>
91#include <errno.h>
92#include <fcntl.h>
93#include <assert.h>
94#include <signal.h>
95#include <stddef.h>
96#include <sys/stat.h>
97#include <unistd.h>
98#include <pthread.h>
99
100#define MAX_STR_LEN 128
101static char file_name[MAX_STR_LEN];
102static fuse_ino_t file_ino = 2;
103static int lookup_cnt = 0;
104static pthread_t main_thread;
105
106/* Command line parsing */
107struct options {
108 int no_notify;
109 float timeout;
110 int update_interval;
111 int only_expire;
112 int inc_epoch;
113};
114static struct options options = {
115 .timeout = 5,
116 .no_notify = 0,
117 .update_interval = 1,
118 .only_expire = 0,
119 .inc_epoch = 0,
120};
121
122#define OPTION(t, p) \
123 { t, offsetof(struct options, p), 1 }
124static const struct fuse_opt option_spec[] = {
125 OPTION("--no-notify", no_notify),
126 OPTION("--update-interval=%d", update_interval),
127 OPTION("--timeout=%f", timeout),
128 OPTION("--only-expire", only_expire),
129 OPTION("--inc-epoch", inc_epoch),
131};
132
133static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
134 stbuf->st_ino = ino;
135 if (ino == FUSE_ROOT_ID) {
136 stbuf->st_mode = S_IFDIR | 0755;
137 stbuf->st_nlink = 1;
138 }
139
140 else if (ino == file_ino) {
141 stbuf->st_mode = S_IFREG | 0000;
142 stbuf->st_nlink = 1;
143 stbuf->st_size = 0;
144 }
145
146 else
147 return -1;
148
149 return 0;
150}
151
152static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
153 (void)userdata;
154
155 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
156 conn->no_interrupt = 1;
157}
158
159static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
160 const char *name) {
161 struct fuse_entry_param e;
162 memset(&e, 0, sizeof(e));
163
164 if (parent != FUSE_ROOT_ID)
165 goto err_out;
166 else if (strcmp(name, file_name) == 0) {
167 e.ino = file_ino;
168 lookup_cnt++;
169 } else
170 goto err_out;
171
172 e.attr_timeout = options.timeout;
173 e.entry_timeout = options.timeout;
174 if (tfs_stat(e.ino, &e.attr) != 0)
175 goto err_out;
176 fuse_reply_entry(req, &e);
177 return;
178
179err_out:
180 fuse_reply_err(req, ENOENT);
181}
182
183static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
184 uint64_t nlookup) {
185 (void) req;
186 if(ino == file_ino)
187 lookup_cnt -= nlookup;
188 else
189 assert(ino == FUSE_ROOT_ID);
190 fuse_reply_none(req);
191}
192
193static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
194 struct fuse_file_info *fi) {
195 struct stat stbuf;
196
197 (void) fi;
198
199 memset(&stbuf, 0, sizeof(stbuf));
200 if (tfs_stat(ino, &stbuf) != 0)
201 fuse_reply_err(req, ENOENT);
202 else
203 fuse_reply_attr(req, &stbuf, options.timeout);
204}
205
206struct dirbuf {
207 char *p;
208 size_t size;
209};
210
211static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
212 fuse_ino_t ino) {
213 struct stat stbuf;
214 size_t oldsize = b->size;
215 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
216 b->p = (char *) realloc(b->p, b->size);
217 memset(&stbuf, 0, sizeof(stbuf));
218 stbuf.st_ino = ino;
219 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
220 b->size);
221}
222
223#define min(x, y) ((x) < (y) ? (x) : (y))
224
225static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
226 off_t off, size_t maxsize) {
227 if (off < bufsize)
228 return fuse_reply_buf(req, buf + off,
229 min(bufsize - off, maxsize));
230 else
231 return fuse_reply_buf(req, NULL, 0);
232}
233
234static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
235 off_t off, struct fuse_file_info *fi) {
236 (void) fi;
237
238 if (ino != FUSE_ROOT_ID)
239 fuse_reply_err(req, ENOTDIR);
240 else {
241 struct dirbuf b;
242
243 memset(&b, 0, sizeof(b));
244 dirbuf_add(req, &b, file_name, file_ino);
245 reply_buf_limited(req, b.p, b.size, off, size);
246 free(b.p);
247 }
248}
249
250static const struct fuse_lowlevel_ops tfs_oper = {
251 .init = tfs_init,
252 .lookup = tfs_lookup,
253 .getattr = tfs_getattr,
254 .readdir = tfs_readdir,
255 .forget = tfs_forget,
256};
257
258static void update_fs(void) {
259 time_t t;
260 struct tm *now;
261 ssize_t ret;
262
263 t = time(NULL);
264 now = localtime(&t);
265 assert(now != NULL);
266
267 ret = strftime(file_name, MAX_STR_LEN,
268 "Time_is_%Hh_%Mm_%Ss", now);
269 assert(ret != 0);
270}
271
272static void* update_fs_loop(void *data) {
273 struct fuse_session *se = (struct fuse_session*) data;
274 char *old_name;
275 int ret = 0;
276
277 while(!fuse_session_exited(se)) {
278 old_name = strdup(file_name);
279 update_fs();
280
281 if (!options.no_notify && lookup_cnt) {
282 if(options.only_expire) { // expire entry
284 (se, FUSE_ROOT_ID, old_name, strlen(old_name));
285
286 // no kernel support
287 if (ret == -ENOSYS) {
288 printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
289 break;
290 }
291
292 // 1) ret == 0: successful expire of an existing entry
293 // 2) ret == -ENOENT: kernel has already expired the entry /
294 // entry does not exist anymore in the kernel
295 assert(ret == 0 || ret == -ENOENT);
296 } else if (options.inc_epoch) { // increment epoch
298
299 if (ret == -ENOSYS) {
300 printf("fuse_lowlevel_notify_increment_epoch not supported by kernel\n");
301 break;
302 }
303 assert(ret == 0);
304 } else { // invalidate entry
306 (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
307 }
308 }
309 free(old_name);
310 sleep(options.update_interval);
311 }
312
313 if (ret == -ENOSYS) {
314 printf("Exiting...\n");
315
317 // Make sure to exit now, rather than on next request from userspace
318 pthread_kill(main_thread, SIGPIPE);
319 }
320
321 return NULL;
322}
323
324static void show_help(const char *progname)
325{
326 printf("usage: %s [options] <mountpoint>\n\n", progname);
327 printf("File-system specific options:\n"
328 " --timeout=<secs> Timeout for kernel caches\n"
329 " --update-interval=<secs> Update-rate of file system contents\n"
330 " --no-notify Disable kernel notifications\n"
331 " --only-expire Expire entries instead of invalidating them\n"
332 " --inc-epoch Increment epoch, invalidating all dentries\n"
333 "\n");
334}
335
336int main(int argc, char *argv[]) {
337 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
338 struct fuse_session *se;
339 struct fuse_cmdline_opts opts;
340 struct fuse_loop_config *config;
341 pthread_t updater;
342 int ret = -1;
343
344 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
345 return 1;
346
347 if (fuse_parse_cmdline(&args, &opts) != 0)
348 return 1;
349 if (opts.show_help) {
350 show_help(argv[0]);
353 ret = 0;
354 goto err_out1;
355 } else if (opts.show_version) {
356 printf("FUSE library version %s\n", fuse_pkgversion());
358 ret = 0;
359 goto err_out1;
360 }
361 if (options.only_expire && options.inc_epoch) {
362 printf("'only-expire' and 'inc-epoch' options are exclusive\n");
363 ret = 0;
364 goto err_out1;
365 }
366
367 /* Initial contents */
368 update_fs();
369
370 se = fuse_session_new(&args, &tfs_oper,
371 sizeof(tfs_oper), &se);
372 if (se == NULL)
373 goto err_out1;
374
375 if (fuse_set_signal_handlers(se) != 0)
376 goto err_out2;
377
378 if (fuse_session_mount(se, opts.mountpoint) != 0)
379 goto err_out3;
380
381 fuse_daemonize(opts.foreground);
382
383 // Needed to ensure that the main thread continues/restarts processing as soon
384 // as the fuse session ends (immediately after calling fuse_session_exit() )
385 // and not only on the next request from userspace
386 main_thread = pthread_self();
387
388 /* Start thread to update file contents */
389 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
390 if (ret != 0) {
391 fprintf(stderr, "pthread_create failed with %s\n",
392 strerror(ret));
393 goto err_out3;
394 }
395
396 /* Block until ctrl+c or fusermount -u */
397 if (opts.singlethread) {
398 ret = fuse_session_loop(se);
399 } else {
400 config = fuse_loop_cfg_create();
401 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
402 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
403 ret = fuse_session_loop_mt(se, config);
404 fuse_loop_cfg_destroy(config);
405 config = NULL;
406 }
407
409err_out3:
411err_out2:
413err_out1:
414 free(opts.mountpoint);
415 fuse_opt_free_args(&args);
416
417 return ret ? 1 : 0;
418}
419
420
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2notify__inval__entry_8c_source.html0000644000175000017500000022053115156613442026271 0ustar berndbernd libfuse: fuse-3.18.1/example/notify_inval_entry.c Source File
libfuse
notify_inval_entry.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
85#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
86
87#include <fuse_lowlevel.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <string.h>
91#include <errno.h>
92#include <fcntl.h>
93#include <assert.h>
94#include <signal.h>
95#include <stddef.h>
96#include <sys/stat.h>
97#include <unistd.h>
98#include <pthread.h>
99
100#define MAX_STR_LEN 128
101static char file_name[MAX_STR_LEN];
102static fuse_ino_t file_ino = 2;
103static int lookup_cnt = 0;
104static pthread_t main_thread;
105
106/* Command line parsing */
107struct options {
108 int no_notify;
109 float timeout;
110 int update_interval;
111 int only_expire;
112 int inc_epoch;
113};
114static struct options options = {
115 .timeout = 5,
116 .no_notify = 0,
117 .update_interval = 1,
118 .only_expire = 0,
119 .inc_epoch = 0,
120};
121
122#define OPTION(t, p) \
123 { t, offsetof(struct options, p), 1 }
124static const struct fuse_opt option_spec[] = {
125 OPTION("--no-notify", no_notify),
126 OPTION("--update-interval=%d", update_interval),
127 OPTION("--timeout=%f", timeout),
128 OPTION("--only-expire", only_expire),
129 OPTION("--inc-epoch", inc_epoch),
131};
132
133static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
134 stbuf->st_ino = ino;
135 if (ino == FUSE_ROOT_ID) {
136 stbuf->st_mode = S_IFDIR | 0755;
137 stbuf->st_nlink = 1;
138 }
139
140 else if (ino == file_ino) {
141 stbuf->st_mode = S_IFREG | 0000;
142 stbuf->st_nlink = 1;
143 stbuf->st_size = 0;
144 }
145
146 else
147 return -1;
148
149 return 0;
150}
151
152static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
153 (void)userdata;
154
155 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
156 conn->no_interrupt = 1;
157}
158
159static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
160 const char *name) {
161 struct fuse_entry_param e;
162 memset(&e, 0, sizeof(e));
163
164 if (parent != FUSE_ROOT_ID)
165 goto err_out;
166 else if (strcmp(name, file_name) == 0) {
167 e.ino = file_ino;
168 lookup_cnt++;
169 } else
170 goto err_out;
171
172 e.attr_timeout = options.timeout;
173 e.entry_timeout = options.timeout;
174 if (tfs_stat(e.ino, &e.attr) != 0)
175 goto err_out;
176 fuse_reply_entry(req, &e);
177 return;
178
179err_out:
180 fuse_reply_err(req, ENOENT);
181}
182
183static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
184 uint64_t nlookup) {
185 (void) req;
186 if(ino == file_ino)
187 lookup_cnt -= nlookup;
188 else
189 assert(ino == FUSE_ROOT_ID);
190 fuse_reply_none(req);
191}
192
193static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
194 struct fuse_file_info *fi) {
195 struct stat stbuf;
196
197 (void) fi;
198
199 memset(&stbuf, 0, sizeof(stbuf));
200 if (tfs_stat(ino, &stbuf) != 0)
201 fuse_reply_err(req, ENOENT);
202 else
203 fuse_reply_attr(req, &stbuf, options.timeout);
204}
205
206struct dirbuf {
207 char *p;
208 size_t size;
209};
210
211static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
212 fuse_ino_t ino) {
213 struct stat stbuf;
214 size_t oldsize = b->size;
215 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
216 b->p = (char *) realloc(b->p, b->size);
217 memset(&stbuf, 0, sizeof(stbuf));
218 stbuf.st_ino = ino;
219 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
220 b->size);
221}
222
223#define min(x, y) ((x) < (y) ? (x) : (y))
224
225static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
226 off_t off, size_t maxsize) {
227 if (off < bufsize)
228 return fuse_reply_buf(req, buf + off,
229 min(bufsize - off, maxsize));
230 else
231 return fuse_reply_buf(req, NULL, 0);
232}
233
234static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
235 off_t off, struct fuse_file_info *fi) {
236 (void) fi;
237
238 if (ino != FUSE_ROOT_ID)
239 fuse_reply_err(req, ENOTDIR);
240 else {
241 struct dirbuf b;
242
243 memset(&b, 0, sizeof(b));
244 dirbuf_add(req, &b, file_name, file_ino);
245 reply_buf_limited(req, b.p, b.size, off, size);
246 free(b.p);
247 }
248}
249
250static const struct fuse_lowlevel_ops tfs_oper = {
251 .init = tfs_init,
252 .lookup = tfs_lookup,
253 .getattr = tfs_getattr,
254 .readdir = tfs_readdir,
255 .forget = tfs_forget,
256};
257
258static void update_fs(void) {
259 time_t t;
260 struct tm *now;
261 ssize_t ret;
262
263 t = time(NULL);
264 now = localtime(&t);
265 assert(now != NULL);
266
267 ret = strftime(file_name, MAX_STR_LEN,
268 "Time_is_%Hh_%Mm_%Ss", now);
269 assert(ret != 0);
270}
271
272static void* update_fs_loop(void *data) {
273 struct fuse_session *se = (struct fuse_session*) data;
274 char *old_name;
275 int ret = 0;
276
277 while(!fuse_session_exited(se)) {
278 old_name = strdup(file_name);
279 update_fs();
280
281 if (!options.no_notify && lookup_cnt) {
282 if(options.only_expire) { // expire entry
284 (se, FUSE_ROOT_ID, old_name, strlen(old_name));
285
286 // no kernel support
287 if (ret == -ENOSYS) {
288 printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
289 break;
290 }
291
292 // 1) ret == 0: successful expire of an existing entry
293 // 2) ret == -ENOENT: kernel has already expired the entry /
294 // entry does not exist anymore in the kernel
295 assert(ret == 0 || ret == -ENOENT);
296 } else if (options.inc_epoch) { // increment epoch
298
299 if (ret == -ENOSYS) {
300 printf("fuse_lowlevel_notify_increment_epoch not supported by kernel\n");
301 break;
302 }
303 assert(ret == 0);
304 } else { // invalidate entry
306 (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
307 }
308 }
309 free(old_name);
310 sleep(options.update_interval);
311 }
312
313 if (ret == -ENOSYS) {
314 printf("Exiting...\n");
315
317 // Make sure to exit now, rather than on next request from userspace
318 pthread_kill(main_thread, SIGPIPE);
319 }
320
321 return NULL;
322}
323
324static void show_help(const char *progname)
325{
326 printf("usage: %s [options] <mountpoint>\n\n", progname);
327 printf("File-system specific options:\n"
328 " --timeout=<secs> Timeout for kernel caches\n"
329 " --update-interval=<secs> Update-rate of file system contents\n"
330 " --no-notify Disable kernel notifications\n"
331 " --only-expire Expire entries instead of invalidating them\n"
332 " --inc-epoch Increment epoch, invalidating all dentries\n"
333 "\n");
334}
335
336int main(int argc, char *argv[]) {
337 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
338 struct fuse_session *se;
339 struct fuse_cmdline_opts opts;
340 struct fuse_loop_config *config;
341 pthread_t updater;
342 int ret = -1;
343
344 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
345 return 1;
346
347 if (fuse_parse_cmdline(&args, &opts) != 0)
348 return 1;
349 if (opts.show_help) {
350 show_help(argv[0]);
353 ret = 0;
354 goto err_out1;
355 } else if (opts.show_version) {
356 printf("FUSE library version %s\n", fuse_pkgversion());
358 ret = 0;
359 goto err_out1;
360 }
361 if (options.only_expire && options.inc_epoch) {
362 printf("'only-expire' and 'inc-epoch' options are exclusive\n");
363 ret = 0;
364 goto err_out1;
365 }
366
367 /* Initial contents */
368 update_fs();
369
370 se = fuse_session_new(&args, &tfs_oper,
371 sizeof(tfs_oper), &se);
372 if (se == NULL)
373 goto err_out1;
374
375 if (fuse_set_signal_handlers(se) != 0)
376 goto err_out2;
377
378 if (fuse_session_mount(se, opts.mountpoint) != 0)
379 goto err_out3;
380
381 fuse_daemonize(opts.foreground);
382
383 // Needed to ensure that the main thread continues/restarts processing as soon
384 // as the fuse session ends (immediately after calling fuse_session_exit() )
385 // and not only on the next request from userspace
386 main_thread = pthread_self();
387
388 /* Start thread to update file contents */
389 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
390 if (ret != 0) {
391 fprintf(stderr, "pthread_create failed with %s\n",
392 strerror(ret));
393 goto err_out3;
394 }
395
396 /* Block until ctrl+c or fusermount -u */
397 if (opts.singlethread) {
398 ret = fuse_session_loop(se);
399 } else {
400 config = fuse_loop_cfg_create();
401 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
402 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
403 ret = fuse_session_loop_mt(se, config);
404 fuse_loop_cfg_destroy(config);
405 config = NULL;
406 }
407
409err_out3:
411err_out2:
413err_out1:
414 free(opts.mountpoint);
415 fuse_opt_free_args(&args);
416
417 return ret ? 1 : 0;
418}
419
420
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/example_2notify__inval__inode_8c_source.html0000644000175000017500000021724315156613442024060 0ustar berndbernd libfuse: example/notify_inval_inode.c Source File
libfuse
notify_inval_inode.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
62#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
63
64#include <fuse_lowlevel.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <errno.h>
69#include <fcntl.h>
70#include <assert.h>
71#include <stddef.h>
72#include <unistd.h>
73#include <pthread.h>
74#include <stdbool.h>
75#include <stdatomic.h>
76
77/* We can't actually tell the kernel that there is no
78 timeout, so we just send a big value */
79#define NO_TIMEOUT 500000
80
81#define MAX_STR_LEN 128
82#define FILE_INO 2
83#define FILE_NAME "current_time"
84static char file_contents[MAX_STR_LEN];
85static int lookup_cnt = 0;
86static size_t file_size;
87static _Atomic bool is_stop = false;
88
89/* Command line parsing */
90struct options {
91 int no_notify;
92 int update_interval;
93};
94static struct options options = {
95 .no_notify = 0,
96 .update_interval = 1,
97};
98
99#define OPTION(t, p) \
100 { t, offsetof(struct options, p), 1 }
101static const struct fuse_opt option_spec[] = {
102 OPTION("--no-notify", no_notify),
103 OPTION("--update-interval=%d", update_interval),
105};
106
107static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
108 stbuf->st_ino = ino;
109 if (ino == FUSE_ROOT_ID) {
110 stbuf->st_mode = S_IFDIR | 0755;
111 stbuf->st_nlink = 1;
112 }
113
114 else if (ino == FILE_INO) {
115 stbuf->st_mode = S_IFREG | 0444;
116 stbuf->st_nlink = 1;
117 stbuf->st_size = file_size;
118 }
119
120 else
121 return -1;
122
123 return 0;
124}
125
126static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
127 (void)userdata;
128
129 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
130 conn->no_interrupt = 1;
131}
132
133static void tfs_destroy(void *userarg)
134{
135 (void)userarg;
136
137 is_stop = true;
138}
139
140static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
141 const char *name) {
142 struct fuse_entry_param e;
143 memset(&e, 0, sizeof(e));
144
145 if (parent != FUSE_ROOT_ID)
146 goto err_out;
147 else if (strcmp(name, FILE_NAME) == 0) {
148 e.ino = FILE_INO;
149 lookup_cnt++;
150 } else
151 goto err_out;
152
153 e.attr_timeout = NO_TIMEOUT;
154 e.entry_timeout = NO_TIMEOUT;
155 if (tfs_stat(e.ino, &e.attr) != 0)
156 goto err_out;
157 fuse_reply_entry(req, &e);
158 return;
159
160err_out:
161 fuse_reply_err(req, ENOENT);
162}
163
164static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
165 uint64_t nlookup) {
166 (void) req;
167 if(ino == FILE_INO)
168 lookup_cnt -= nlookup;
169 else
170 assert(ino == FUSE_ROOT_ID);
171 fuse_reply_none(req);
172}
173
174static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
175 struct fuse_file_info *fi) {
176 struct stat stbuf;
177
178 (void) fi;
179
180 memset(&stbuf, 0, sizeof(stbuf));
181 if (tfs_stat(ino, &stbuf) != 0)
182 fuse_reply_err(req, ENOENT);
183 else
184 fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
185}
186
187struct dirbuf {
188 char *p;
189 size_t size;
190};
191
192static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
193 fuse_ino_t ino) {
194 struct stat stbuf;
195 size_t oldsize = b->size;
196 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
197 b->p = (char *) realloc(b->p, b->size);
198 memset(&stbuf, 0, sizeof(stbuf));
199 stbuf.st_ino = ino;
200 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
201 b->size);
202}
203
204#define min(x, y) ((x) < (y) ? (x) : (y))
205
206static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
207 off_t off, size_t maxsize) {
208 if (off < bufsize)
209 return fuse_reply_buf(req, buf + off,
210 min(bufsize - off, maxsize));
211 else
212 return fuse_reply_buf(req, NULL, 0);
213}
214
215static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
216 off_t off, struct fuse_file_info *fi) {
217 (void) fi;
218
219 if (ino != FUSE_ROOT_ID)
220 fuse_reply_err(req, ENOTDIR);
221 else {
222 struct dirbuf b;
223
224 memset(&b, 0, sizeof(b));
225 dirbuf_add(req, &b, FILE_NAME, FILE_INO);
226 reply_buf_limited(req, b.p, b.size, off, size);
227 free(b.p);
228 }
229}
230
231static void tfs_open(fuse_req_t req, fuse_ino_t ino,
232 struct fuse_file_info *fi) {
233
234 /* Make cache persistent even if file is closed,
235 this makes it easier to see the effects */
236 fi->keep_cache = 1;
237
238 if (ino == FUSE_ROOT_ID)
239 fuse_reply_err(req, EISDIR);
240 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
241 fuse_reply_err(req, EACCES);
242 else if (ino == FILE_INO)
243 fuse_reply_open(req, fi);
244 else {
245 // This should not happen
246 fprintf(stderr, "Got open for non-existing inode!\n");
247 fuse_reply_err(req, ENOENT);
248 }
249}
250
251static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
252 off_t off, struct fuse_file_info *fi) {
253 (void) fi;
254
255 assert(ino == FILE_INO);
256 reply_buf_limited(req, file_contents, file_size, off, size);
257}
258
259static const struct fuse_lowlevel_ops tfs_oper = {
260 .init = tfs_init,
261 .destroy = tfs_destroy,
262 .lookup = tfs_lookup,
263 .getattr = tfs_getattr,
264 .readdir = tfs_readdir,
265 .open = tfs_open,
266 .read = tfs_read,
267 .forget = tfs_forget,
268};
269
270static void update_fs(void) {
271 struct tm *now;
272 time_t t;
273 t = time(NULL);
274 now = localtime(&t);
275 assert(now != NULL);
276
277 file_size = strftime(file_contents, MAX_STR_LEN,
278 "The current time is %H:%M:%S\n", now);
279 assert(file_size != 0);
280}
281
282static void* update_fs_loop(void *data) {
283 struct fuse_session *se = (struct fuse_session*) data;
284
285 while(!is_stop) {
286 update_fs();
287 if (!options.no_notify && lookup_cnt) {
288 /* Only send notification if the kernel is aware of the inode */
289
290 /* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
291 * might come up during umount, when kernel side already releases
292 * all inodes, but does not send FUSE_DESTROY yet.
293 */
294 int ret =
295 fuse_lowlevel_notify_inval_inode(se, FILE_INO, 0, 0);
296 if ((ret != 0 && !is_stop) &&
297 ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
298 fprintf(stderr,
299 "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
300 strerror(-ret), -ret);
301 abort();
302 }
303 }
304 sleep(options.update_interval);
305 }
306 return NULL;
307}
308
309static void show_help(const char *progname)
310{
311 printf("usage: %s [options] <mountpoint>\n\n", progname);
312 printf("File-system specific options:\n"
313 " --update-interval=<secs> Update-rate of file system contents\n"
314 " --no-notify Disable kernel notifications\n"
315 "\n");
316}
317
318int main(int argc, char *argv[]) {
319 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
320 struct fuse_session *se;
321 struct fuse_cmdline_opts opts;
322 struct fuse_loop_config *config;
323 pthread_t updater;
324 int ret = -1;
325
326 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
327 return 1;
328
329 if (fuse_parse_cmdline(&args, &opts) != 0) {
330 ret = 1;
331 goto err_out1;
332 }
333
334 if (opts.show_help) {
335 show_help(argv[0]);
338 ret = 0;
339 goto err_out1;
340 } else if (opts.show_version) {
341 printf("FUSE library version %s\n", fuse_pkgversion());
343 ret = 0;
344 goto err_out1;
345 }
346
347 /* Initial contents */
348 update_fs();
349
350 se = fuse_session_new(&args, &tfs_oper,
351 sizeof(tfs_oper), NULL);
352 if (se == NULL)
353 goto err_out1;
354
355 if (fuse_set_signal_handlers(se) != 0)
356 goto err_out2;
357
358 if (fuse_session_mount(se, opts.mountpoint) != 0)
359 goto err_out3;
360
361 fuse_daemonize(opts.foreground);
362
363 /* Start thread to update file contents */
364 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
365 if (ret != 0) {
366 fprintf(stderr, "pthread_create failed with %s\n",
367 strerror(ret));
368 goto err_out3;
369 }
370
371 /* Block until ctrl+c or fusermount -u */
372 if (opts.singlethread)
373 ret = fuse_session_loop(se);
374 else {
375 config = fuse_loop_cfg_create();
376 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
377 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
378 ret = fuse_session_loop_mt(se, config);
379 fuse_loop_cfg_destroy(config);
380 config = NULL;
381 }
382
384err_out3:
386err_out2:
388err_out1:
389 fuse_opt_free_args(&args);
390 free(opts.mountpoint);
391
392 return ret ? 1 : 0;
393}
394
395
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2notify__inval__inode_8c_source.html0000644000175000017500000021744515156613442026240 0ustar berndbernd libfuse: fuse-3.18.1/example/notify_inval_inode.c Source File
libfuse
notify_inval_inode.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
62#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
63
64#include <fuse_lowlevel.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <errno.h>
69#include <fcntl.h>
70#include <assert.h>
71#include <stddef.h>
72#include <unistd.h>
73#include <pthread.h>
74#include <stdbool.h>
75#include <stdatomic.h>
76
77/* We can't actually tell the kernel that there is no
78 timeout, so we just send a big value */
79#define NO_TIMEOUT 500000
80
81#define MAX_STR_LEN 128
82#define FILE_INO 2
83#define FILE_NAME "current_time"
84static char file_contents[MAX_STR_LEN];
85static int lookup_cnt = 0;
86static size_t file_size;
87static _Atomic bool is_stop = false;
88
89/* Command line parsing */
90struct options {
91 int no_notify;
92 int update_interval;
93};
94static struct options options = {
95 .no_notify = 0,
96 .update_interval = 1,
97};
98
99#define OPTION(t, p) \
100 { t, offsetof(struct options, p), 1 }
101static const struct fuse_opt option_spec[] = {
102 OPTION("--no-notify", no_notify),
103 OPTION("--update-interval=%d", update_interval),
105};
106
107static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
108 stbuf->st_ino = ino;
109 if (ino == FUSE_ROOT_ID) {
110 stbuf->st_mode = S_IFDIR | 0755;
111 stbuf->st_nlink = 1;
112 }
113
114 else if (ino == FILE_INO) {
115 stbuf->st_mode = S_IFREG | 0444;
116 stbuf->st_nlink = 1;
117 stbuf->st_size = file_size;
118 }
119
120 else
121 return -1;
122
123 return 0;
124}
125
126static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
127 (void)userdata;
128
129 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
130 conn->no_interrupt = 1;
131}
132
133static void tfs_destroy(void *userarg)
134{
135 (void)userarg;
136
137 is_stop = true;
138}
139
140static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
141 const char *name) {
142 struct fuse_entry_param e;
143 memset(&e, 0, sizeof(e));
144
145 if (parent != FUSE_ROOT_ID)
146 goto err_out;
147 else if (strcmp(name, FILE_NAME) == 0) {
148 e.ino = FILE_INO;
149 lookup_cnt++;
150 } else
151 goto err_out;
152
153 e.attr_timeout = NO_TIMEOUT;
154 e.entry_timeout = NO_TIMEOUT;
155 if (tfs_stat(e.ino, &e.attr) != 0)
156 goto err_out;
157 fuse_reply_entry(req, &e);
158 return;
159
160err_out:
161 fuse_reply_err(req, ENOENT);
162}
163
164static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
165 uint64_t nlookup) {
166 (void) req;
167 if(ino == FILE_INO)
168 lookup_cnt -= nlookup;
169 else
170 assert(ino == FUSE_ROOT_ID);
171 fuse_reply_none(req);
172}
173
174static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
175 struct fuse_file_info *fi) {
176 struct stat stbuf;
177
178 (void) fi;
179
180 memset(&stbuf, 0, sizeof(stbuf));
181 if (tfs_stat(ino, &stbuf) != 0)
182 fuse_reply_err(req, ENOENT);
183 else
184 fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
185}
186
187struct dirbuf {
188 char *p;
189 size_t size;
190};
191
192static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
193 fuse_ino_t ino) {
194 struct stat stbuf;
195 size_t oldsize = b->size;
196 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
197 b->p = (char *) realloc(b->p, b->size);
198 memset(&stbuf, 0, sizeof(stbuf));
199 stbuf.st_ino = ino;
200 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
201 b->size);
202}
203
204#define min(x, y) ((x) < (y) ? (x) : (y))
205
206static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
207 off_t off, size_t maxsize) {
208 if (off < bufsize)
209 return fuse_reply_buf(req, buf + off,
210 min(bufsize - off, maxsize));
211 else
212 return fuse_reply_buf(req, NULL, 0);
213}
214
215static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
216 off_t off, struct fuse_file_info *fi) {
217 (void) fi;
218
219 if (ino != FUSE_ROOT_ID)
220 fuse_reply_err(req, ENOTDIR);
221 else {
222 struct dirbuf b;
223
224 memset(&b, 0, sizeof(b));
225 dirbuf_add(req, &b, FILE_NAME, FILE_INO);
226 reply_buf_limited(req, b.p, b.size, off, size);
227 free(b.p);
228 }
229}
230
231static void tfs_open(fuse_req_t req, fuse_ino_t ino,
232 struct fuse_file_info *fi) {
233
234 /* Make cache persistent even if file is closed,
235 this makes it easier to see the effects */
236 fi->keep_cache = 1;
237
238 if (ino == FUSE_ROOT_ID)
239 fuse_reply_err(req, EISDIR);
240 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
241 fuse_reply_err(req, EACCES);
242 else if (ino == FILE_INO)
243 fuse_reply_open(req, fi);
244 else {
245 // This should not happen
246 fprintf(stderr, "Got open for non-existing inode!\n");
247 fuse_reply_err(req, ENOENT);
248 }
249}
250
251static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
252 off_t off, struct fuse_file_info *fi) {
253 (void) fi;
254
255 assert(ino == FILE_INO);
256 reply_buf_limited(req, file_contents, file_size, off, size);
257}
258
259static const struct fuse_lowlevel_ops tfs_oper = {
260 .init = tfs_init,
261 .destroy = tfs_destroy,
262 .lookup = tfs_lookup,
263 .getattr = tfs_getattr,
264 .readdir = tfs_readdir,
265 .open = tfs_open,
266 .read = tfs_read,
267 .forget = tfs_forget,
268};
269
270static void update_fs(void) {
271 struct tm *now;
272 time_t t;
273 t = time(NULL);
274 now = localtime(&t);
275 assert(now != NULL);
276
277 file_size = strftime(file_contents, MAX_STR_LEN,
278 "The current time is %H:%M:%S\n", now);
279 assert(file_size != 0);
280}
281
282static void* update_fs_loop(void *data) {
283 struct fuse_session *se = (struct fuse_session*) data;
284
285 while(!is_stop) {
286 update_fs();
287 if (!options.no_notify && lookup_cnt) {
288 /* Only send notification if the kernel is aware of the inode */
289
290 /* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
291 * might come up during umount, when kernel side already releases
292 * all inodes, but does not send FUSE_DESTROY yet.
293 */
294 int ret =
295 fuse_lowlevel_notify_inval_inode(se, FILE_INO, 0, 0);
296 if ((ret != 0 && !is_stop) &&
297 ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
298 fprintf(stderr,
299 "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
300 strerror(-ret), -ret);
301 abort();
302 }
303 }
304 sleep(options.update_interval);
305 }
306 return NULL;
307}
308
309static void show_help(const char *progname)
310{
311 printf("usage: %s [options] <mountpoint>\n\n", progname);
312 printf("File-system specific options:\n"
313 " --update-interval=<secs> Update-rate of file system contents\n"
314 " --no-notify Disable kernel notifications\n"
315 "\n");
316}
317
318int main(int argc, char *argv[]) {
319 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
320 struct fuse_session *se;
321 struct fuse_cmdline_opts opts;
322 struct fuse_loop_config *config;
323 pthread_t updater;
324 int ret = -1;
325
326 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
327 return 1;
328
329 if (fuse_parse_cmdline(&args, &opts) != 0) {
330 ret = 1;
331 goto err_out1;
332 }
333
334 if (opts.show_help) {
335 show_help(argv[0]);
338 ret = 0;
339 goto err_out1;
340 } else if (opts.show_version) {
341 printf("FUSE library version %s\n", fuse_pkgversion());
343 ret = 0;
344 goto err_out1;
345 }
346
347 /* Initial contents */
348 update_fs();
349
350 se = fuse_session_new(&args, &tfs_oper,
351 sizeof(tfs_oper), NULL);
352 if (se == NULL)
353 goto err_out1;
354
355 if (fuse_set_signal_handlers(se) != 0)
356 goto err_out2;
357
358 if (fuse_session_mount(se, opts.mountpoint) != 0)
359 goto err_out3;
360
361 fuse_daemonize(opts.foreground);
362
363 /* Start thread to update file contents */
364 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
365 if (ret != 0) {
366 fprintf(stderr, "pthread_create failed with %s\n",
367 strerror(ret));
368 goto err_out3;
369 }
370
371 /* Block until ctrl+c or fusermount -u */
372 if (opts.singlethread)
373 ret = fuse_session_loop(se);
374 else {
375 config = fuse_loop_cfg_create();
376 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
377 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
378 ret = fuse_session_loop_mt(se, config);
379 fuse_loop_cfg_destroy(config);
380 config = NULL;
381 }
382
384err_out3:
386err_out2:
388err_out1:
389 fuse_opt_free_args(&args);
390 free(opts.mountpoint);
391
392 return ret ? 1 : 0;
393}
394
395
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/example_2notify__store__retrieve_8c_source.html0000644000175000017500000025071715156613442024635 0ustar berndbernd libfuse: example/notify_store_retrieve.c Source File
libfuse
notify_store_retrieve.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
61#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
62
63#include <fuse_lowlevel.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <errno.h>
68#include <fcntl.h>
69#include <assert.h>
70#include <stddef.h>
71#include <unistd.h>
72#include <pthread.h>
73#include <stdbool.h>
74
75/* We can't actually tell the kernel that there is no
76 timeout, so we just send a big value */
77#define NO_TIMEOUT 500000
78
79#define MAX_STR_LEN 128
80#define FILE_INO 2
81#define FILE_NAME "current_time"
82static char file_contents[MAX_STR_LEN];
83static int lookup_cnt = 0;
84static int open_cnt = 0;
85static size_t file_size;
86static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
87
88/* Keep track if we ever stored data (==1), and
89 received it back correctly (==2) */
90static int retrieve_status = 0;
91
92static bool is_umount = false;
93
94/* updater thread tid */
95static pthread_t updater;
96
97
98/* Command line parsing */
99struct options {
100 int no_notify;
101 int update_interval;
102};
103static struct options options = {
104 .no_notify = 0,
105 .update_interval = 1,
106};
107
108#define OPTION(t, p) \
109 { t, offsetof(struct options, p), 1 }
110static const struct fuse_opt option_spec[] = {
111 OPTION("--no-notify", no_notify),
112 OPTION("--update-interval=%d", update_interval),
114};
115
116static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
117 stbuf->st_ino = ino;
118 if (ino == FUSE_ROOT_ID) {
119 stbuf->st_mode = S_IFDIR | 0755;
120 stbuf->st_nlink = 1;
121 }
122
123 else if (ino == FILE_INO) {
124 stbuf->st_mode = S_IFREG | 0444;
125 stbuf->st_nlink = 1;
126 stbuf->st_size = file_size;
127 }
128
129 else
130 return -1;
131
132 return 0;
133}
134
135static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
136 (void)userdata;
137
138 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
139 conn->no_interrupt = 1;
140}
141
142static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
143 const char *name) {
144 struct fuse_entry_param e;
145 memset(&e, 0, sizeof(e));
146
147 if (parent != FUSE_ROOT_ID)
148 goto err_out;
149 else if (strcmp(name, FILE_NAME) == 0) {
150 e.ino = FILE_INO;
151 } else
152 goto err_out;
153
154 e.attr_timeout = NO_TIMEOUT;
155 e.entry_timeout = NO_TIMEOUT;
156 if (tfs_stat(e.ino, &e.attr) != 0)
157 goto err_out;
158 fuse_reply_entry(req, &e);
159
160 /*
161 * must only be set when the kernel knows about the entry,
162 * otherwise update_fs_loop() might see a positive count, but kernel
163 * would not have the entry yet
164 */
165 if (e.ino == FILE_INO) {
166 pthread_mutex_lock(&lock);
167 lookup_cnt++;
168 pthread_mutex_unlock(&lock);
169 }
170
171 return;
172
173err_out:
174 fuse_reply_err(req, ENOENT);
175}
176
177static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
178 uint64_t nlookup) {
179 (void) req;
180 if(ino == FILE_INO) {
181 pthread_mutex_lock(&lock);
182 lookup_cnt -= nlookup;
183 pthread_mutex_unlock(&lock);
184 } else
185 assert(ino == FUSE_ROOT_ID);
186 fuse_reply_none(req);
187}
188
189static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
190 struct fuse_file_info *fi) {
191 struct stat stbuf;
192
193 (void) fi;
194
195 memset(&stbuf, 0, sizeof(stbuf));
196 if (tfs_stat(ino, &stbuf) != 0)
197 fuse_reply_err(req, ENOENT);
198 else
199 fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
200}
201
202struct dirbuf {
203 char *p;
204 size_t size;
205};
206
207static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
208 fuse_ino_t ino) {
209 struct stat stbuf;
210 size_t oldsize = b->size;
211 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
212 b->p = (char *) realloc(b->p, b->size);
213 memset(&stbuf, 0, sizeof(stbuf));
214 stbuf.st_ino = ino;
215 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
216 b->size);
217}
218
219#define min(x, y) ((x) < (y) ? (x) : (y))
220
221static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
222 off_t off, size_t maxsize) {
223 if (off < bufsize)
224 return fuse_reply_buf(req, buf + off,
225 min(bufsize - off, maxsize));
226 else
227 return fuse_reply_buf(req, NULL, 0);
228}
229
230static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
231 off_t off, struct fuse_file_info *fi) {
232 (void) fi;
233
234 if (ino != FUSE_ROOT_ID)
235 fuse_reply_err(req, ENOTDIR);
236 else {
237 struct dirbuf b;
238
239 memset(&b, 0, sizeof(b));
240 dirbuf_add(req, &b, FILE_NAME, FILE_INO);
241 reply_buf_limited(req, b.p, b.size, off, size);
242 free(b.p);
243 }
244}
245
246static void tfs_open(fuse_req_t req, fuse_ino_t ino,
247 struct fuse_file_info *fi) {
248
249 /* Make cache persistent even if file is closed,
250 this makes it easier to see the effects */
251 fi->keep_cache = 1;
252
253 if (ino == FUSE_ROOT_ID)
254 fuse_reply_err(req, EISDIR);
255 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
256 fuse_reply_err(req, EACCES);
257 else if (ino == FILE_INO) {
258 fuse_reply_open(req, fi);
259 pthread_mutex_lock(&lock);
260 open_cnt++;
261 pthread_mutex_unlock(&lock);
262 } else {
263 // This should not happen
264 fprintf(stderr, "Got open for non-existing inode!\n");
265 fuse_reply_err(req, ENOENT);
266 }
267}
268
269static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
270 off_t off, struct fuse_file_info *fi) {
271 (void) fi;
272
273 assert(ino == FILE_INO);
274 reply_buf_limited(req, file_contents, file_size, off, size);
275}
276
277static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
278 off_t offset, struct fuse_bufvec *data) {
279 struct fuse_bufvec bufv;
280 char buf[MAX_STR_LEN];
281 char *expected;
282 ssize_t ret;
283
284 assert(ino == FILE_INO);
285 assert(offset == 0);
286 expected = (char*) cookie;
287
288 bufv.count = 1;
289 bufv.idx = 0;
290 bufv.off = 0;
291 bufv.buf[0].size = MAX_STR_LEN;
292 bufv.buf[0].mem = buf;
293 bufv.buf[0].flags = 0;
294
295 ret = fuse_buf_copy(&bufv, data, 0);
296 assert(ret > 0);
297 assert(strncmp(buf, expected, ret) == 0);
298 free(expected);
299 retrieve_status = 2;
300 fuse_reply_none(req);
301}
302
303static void tfs_destroy(void *userdata)
304{
305 (void)userdata;
306
307 is_umount = true;
308
309 pthread_join(updater, NULL);
310}
311
312
313static const struct fuse_lowlevel_ops tfs_oper = {
314 .init = tfs_init,
315 .lookup = tfs_lookup,
316 .getattr = tfs_getattr,
317 .readdir = tfs_readdir,
318 .open = tfs_open,
319 .read = tfs_read,
320 .forget = tfs_forget,
321 .retrieve_reply = tfs_retrieve_reply,
322 .destroy = tfs_destroy,
323};
324
325static void update_fs(void) {
326 struct tm *now;
327 time_t t;
328 t = time(NULL);
329 now = localtime(&t);
330 assert(now != NULL);
331
332 file_size = strftime(file_contents, MAX_STR_LEN,
333 "The current time is %H:%M:%S\n", now);
334 assert(file_size != 0);
335}
336
337static void* update_fs_loop(void *data) {
338 struct fuse_session *se = (struct fuse_session*) data;
339 struct fuse_bufvec bufv;
340 int ret;
341
342 while(!is_umount) {
343 update_fs();
344 pthread_mutex_lock(&lock);
345 if (!options.no_notify && open_cnt && lookup_cnt) {
346 /* Only send notification if the kernel
347 is aware of the inode */
348 bufv.count = 1;
349 bufv.idx = 0;
350 bufv.off = 0;
351 bufv.buf[0].size = file_size;
352 bufv.buf[0].mem = file_contents;
353 bufv.buf[0].flags = 0;
354
355 /*
356 * Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
357 * might come up during umount, when kernel side already releases
358 * all inodes, but does not send FUSE_DESTROY yet.
359 */
360
361 ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
362 if ((ret != 0 && !is_umount) &&
363 ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
364 fprintf(stderr,
365 "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
366 strerror(-ret), -ret);
367 abort();
368 }
369
370 /* To make sure that everything worked correctly, ask the
371 kernel to send us back the stored data */
372 ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
373 0, (void*) strdup(file_contents));
374 assert((ret == 0 || is_umount) || ret == -ENOENT || ret == -EBADF ||
375 ret != -ENODEV);
376 if(retrieve_status == 0)
377 retrieve_status = 1;
378 }
379 pthread_mutex_unlock(&lock);
380 sleep(options.update_interval);
381 }
382 return NULL;
383}
384
385static void show_help(const char *progname)
386{
387 printf("usage: %s [options] <mountpoint>\n\n", progname);
388 printf("File-system specific options:\n"
389 " --update-interval=<secs> Update-rate of file system contents\n"
390 " --no-notify Disable kernel notifications\n"
391 "\n");
392}
393
394int main(int argc, char *argv[]) {
395 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
396 struct fuse_session *se;
397 struct fuse_cmdline_opts opts;
398 struct fuse_loop_config *config;
399 int ret = -1;
400
401 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
402 return 1;
403
404 if (fuse_parse_cmdline(&args, &opts) != 0)
405 return 1;
406 if (opts.show_help) {
407 show_help(argv[0]);
410 ret = 0;
411 goto err_out1;
412 } else if (opts.show_version) {
413 printf("FUSE library version %s\n", fuse_pkgversion());
415 ret = 0;
416 goto err_out1;
417 }
418
419 /* Initial contents */
420 update_fs();
421
422 se = fuse_session_new(&args, &tfs_oper,
423 sizeof(tfs_oper), NULL);
424 if (se == NULL)
425 goto err_out1;
426
427 if (fuse_set_signal_handlers(se) != 0)
428 goto err_out2;
429
430 if (fuse_session_mount(se, opts.mountpoint) != 0)
431 goto err_out3;
432
433 fuse_daemonize(opts.foreground);
434
435 /* Start thread to update file contents */
436 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
437 if (ret != 0) {
438 fprintf(stderr, "pthread_create failed with %s\n",
439 strerror(ret));
440 goto err_out3;
441 }
442
443 /* Block until ctrl+c or fusermount -u */
444 if (opts.singlethread)
445 ret = fuse_session_loop(se);
446 else {
447 config = fuse_loop_cfg_create();
448 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
449 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
450 ret = fuse_session_loop_mt(se, config);
451 fuse_loop_cfg_destroy(config);
452 config = NULL;
453 }
454
455 assert(retrieve_status != 1);
457err_out3:
459err_out2:
461err_out1:
462 free(opts.mountpoint);
463 fuse_opt_free_args(&args);
464
465 return ret ? 1 : 0;
466}
467
468
int fuse_set_signal_handlers(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
struct fuse_buf buf[1]
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_817_84_2example_2notify__store__retrieve_8c_source.html0000644000175000017500000025112015156613442027000 0ustar berndbernd libfuse: fuse-3.17.4/example/notify_store_retrieve.c Source File
libfuse
notify_store_retrieve.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file COPYING.
7*/
8
61#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
62
63#include <fuse_lowlevel.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <errno.h>
68#include <fcntl.h>
69#include <assert.h>
70#include <stddef.h>
71#include <unistd.h>
72#include <pthread.h>
73#include <stdbool.h>
74
75/* We can't actually tell the kernel that there is no
76 timeout, so we just send a big value */
77#define NO_TIMEOUT 500000
78
79#define MAX_STR_LEN 128
80#define FILE_INO 2
81#define FILE_NAME "current_time"
82static char file_contents[MAX_STR_LEN];
83static int lookup_cnt = 0;
84static int open_cnt = 0;
85static size_t file_size;
86static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
87
88/* Keep track if we ever stored data (==1), and
89 received it back correctly (==2) */
90static int retrieve_status = 0;
91
92static bool is_umount = false;
93
94/* updater thread tid */
95static pthread_t updater;
96
97
98/* Command line parsing */
99struct options {
100 int no_notify;
101 int update_interval;
102};
103static struct options options = {
104 .no_notify = 0,
105 .update_interval = 1,
106};
107
108#define OPTION(t, p) \
109 { t, offsetof(struct options, p), 1 }
110static const struct fuse_opt option_spec[] = {
111 OPTION("--no-notify", no_notify),
112 OPTION("--update-interval=%d", update_interval),
114};
115
116static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
117 stbuf->st_ino = ino;
118 if (ino == FUSE_ROOT_ID) {
119 stbuf->st_mode = S_IFDIR | 0755;
120 stbuf->st_nlink = 1;
121 }
122
123 else if (ino == FILE_INO) {
124 stbuf->st_mode = S_IFREG | 0444;
125 stbuf->st_nlink = 1;
126 stbuf->st_size = file_size;
127 }
128
129 else
130 return -1;
131
132 return 0;
133}
134
135static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
136 (void)userdata;
137
138 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
139 conn->no_interrupt = 1;
140}
141
142static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
143 const char *name) {
144 struct fuse_entry_param e;
145 memset(&e, 0, sizeof(e));
146
147 if (parent != FUSE_ROOT_ID)
148 goto err_out;
149 else if (strcmp(name, FILE_NAME) == 0) {
150 e.ino = FILE_INO;
151 } else
152 goto err_out;
153
154 e.attr_timeout = NO_TIMEOUT;
155 e.entry_timeout = NO_TIMEOUT;
156 if (tfs_stat(e.ino, &e.attr) != 0)
157 goto err_out;
158 fuse_reply_entry(req, &e);
159
160 /*
161 * must only be set when the kernel knows about the entry,
162 * otherwise update_fs_loop() might see a positive count, but kernel
163 * would not have the entry yet
164 */
165 if (e.ino == FILE_INO) {
166 pthread_mutex_lock(&lock);
167 lookup_cnt++;
168 pthread_mutex_unlock(&lock);
169 }
170
171 return;
172
173err_out:
174 fuse_reply_err(req, ENOENT);
175}
176
177static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
178 uint64_t nlookup) {
179 (void) req;
180 if(ino == FILE_INO) {
181 pthread_mutex_lock(&lock);
182 lookup_cnt -= nlookup;
183 pthread_mutex_unlock(&lock);
184 } else
185 assert(ino == FUSE_ROOT_ID);
186 fuse_reply_none(req);
187}
188
189static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
190 struct fuse_file_info *fi) {
191 struct stat stbuf;
192
193 (void) fi;
194
195 memset(&stbuf, 0, sizeof(stbuf));
196 if (tfs_stat(ino, &stbuf) != 0)
197 fuse_reply_err(req, ENOENT);
198 else
199 fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
200}
201
202struct dirbuf {
203 char *p;
204 size_t size;
205};
206
207static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
208 fuse_ino_t ino) {
209 struct stat stbuf;
210 size_t oldsize = b->size;
211 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
212 b->p = (char *) realloc(b->p, b->size);
213 memset(&stbuf, 0, sizeof(stbuf));
214 stbuf.st_ino = ino;
215 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
216 b->size);
217}
218
219#define min(x, y) ((x) < (y) ? (x) : (y))
220
221static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
222 off_t off, size_t maxsize) {
223 if (off < bufsize)
224 return fuse_reply_buf(req, buf + off,
225 min(bufsize - off, maxsize));
226 else
227 return fuse_reply_buf(req, NULL, 0);
228}
229
230static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
231 off_t off, struct fuse_file_info *fi) {
232 (void) fi;
233
234 if (ino != FUSE_ROOT_ID)
235 fuse_reply_err(req, ENOTDIR);
236 else {
237 struct dirbuf b;
238
239 memset(&b, 0, sizeof(b));
240 dirbuf_add(req, &b, FILE_NAME, FILE_INO);
241 reply_buf_limited(req, b.p, b.size, off, size);
242 free(b.p);
243 }
244}
245
246static void tfs_open(fuse_req_t req, fuse_ino_t ino,
247 struct fuse_file_info *fi) {
248
249 /* Make cache persistent even if file is closed,
250 this makes it easier to see the effects */
251 fi->keep_cache = 1;
252
253 if (ino == FUSE_ROOT_ID)
254 fuse_reply_err(req, EISDIR);
255 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
256 fuse_reply_err(req, EACCES);
257 else if (ino == FILE_INO) {
258 fuse_reply_open(req, fi);
259 pthread_mutex_lock(&lock);
260 open_cnt++;
261 pthread_mutex_unlock(&lock);
262 } else {
263 // This should not happen
264 fprintf(stderr, "Got open for non-existing inode!\n");
265 fuse_reply_err(req, ENOENT);
266 }
267}
268
269static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
270 off_t off, struct fuse_file_info *fi) {
271 (void) fi;
272
273 assert(ino == FILE_INO);
274 reply_buf_limited(req, file_contents, file_size, off, size);
275}
276
277static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
278 off_t offset, struct fuse_bufvec *data) {
279 struct fuse_bufvec bufv;
280 char buf[MAX_STR_LEN];
281 char *expected;
282 ssize_t ret;
283
284 assert(ino == FILE_INO);
285 assert(offset == 0);
286 expected = (char*) cookie;
287
288 bufv.count = 1;
289 bufv.idx = 0;
290 bufv.off = 0;
291 bufv.buf[0].size = MAX_STR_LEN;
292 bufv.buf[0].mem = buf;
293 bufv.buf[0].flags = 0;
294
295 ret = fuse_buf_copy(&bufv, data, 0);
296 assert(ret > 0);
297 assert(strncmp(buf, expected, ret) == 0);
298 free(expected);
299 retrieve_status = 2;
300 fuse_reply_none(req);
301}
302
303static void tfs_destroy(void *userdata)
304{
305 (void)userdata;
306
307 is_umount = true;
308
309 pthread_join(updater, NULL);
310}
311
312
313static const struct fuse_lowlevel_ops tfs_oper = {
314 .init = tfs_init,
315 .lookup = tfs_lookup,
316 .getattr = tfs_getattr,
317 .readdir = tfs_readdir,
318 .open = tfs_open,
319 .read = tfs_read,
320 .forget = tfs_forget,
321 .retrieve_reply = tfs_retrieve_reply,
322 .destroy = tfs_destroy,
323};
324
325static void update_fs(void) {
326 struct tm *now;
327 time_t t;
328 t = time(NULL);
329 now = localtime(&t);
330 assert(now != NULL);
331
332 file_size = strftime(file_contents, MAX_STR_LEN,
333 "The current time is %H:%M:%S\n", now);
334 assert(file_size != 0);
335}
336
337static void* update_fs_loop(void *data) {
338 struct fuse_session *se = (struct fuse_session*) data;
339 struct fuse_bufvec bufv;
340 int ret;
341
342 while(!is_umount) {
343 update_fs();
344 pthread_mutex_lock(&lock);
345 if (!options.no_notify && open_cnt && lookup_cnt) {
346 /* Only send notification if the kernel
347 is aware of the inode */
348 bufv.count = 1;
349 bufv.idx = 0;
350 bufv.off = 0;
351 bufv.buf[0].size = file_size;
352 bufv.buf[0].mem = file_contents;
353 bufv.buf[0].flags = 0;
354
355 /*
356 * Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
357 * might come up during umount, when kernel side already releases
358 * all inodes, but does not send FUSE_DESTROY yet.
359 */
360
361 ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
362 if ((ret != 0 && !is_umount) &&
363 ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
364 fprintf(stderr,
365 "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
366 strerror(-ret), -ret);
367 abort();
368 }
369
370 /* To make sure that everything worked correctly, ask the
371 kernel to send us back the stored data */
372 ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
373 0, (void*) strdup(file_contents));
374 assert((ret == 0 || is_umount) || ret == -ENOENT || ret == -EBADF ||
375 ret != -ENODEV);
376 if(retrieve_status == 0)
377 retrieve_status = 1;
378 }
379 pthread_mutex_unlock(&lock);
380 sleep(options.update_interval);
381 }
382 return NULL;
383}
384
385static void show_help(const char *progname)
386{
387 printf("usage: %s [options] <mountpoint>\n\n", progname);
388 printf("File-system specific options:\n"
389 " --update-interval=<secs> Update-rate of file system contents\n"
390 " --no-notify Disable kernel notifications\n"
391 "\n");
392}
393
394int main(int argc, char *argv[]) {
395 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
396 struct fuse_session *se;
397 struct fuse_cmdline_opts opts;
398 struct fuse_loop_config *config;
399 int ret = -1;
400
401 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
402 return 1;
403
404 if (fuse_parse_cmdline(&args, &opts) != 0)
405 return 1;
406 if (opts.show_help) {
407 show_help(argv[0]);
410 ret = 0;
411 goto err_out1;
412 } else if (opts.show_version) {
413 printf("FUSE library version %s\n", fuse_pkgversion());
415 ret = 0;
416 goto err_out1;
417 }
418
419 /* Initial contents */
420 update_fs();
421
422 se = fuse_session_new(&args, &tfs_oper,
423 sizeof(tfs_oper), NULL);
424 if (se == NULL)
425 goto err_out1;
426
427 if (fuse_set_signal_handlers(se) != 0)
428 goto err_out2;
429
430 if (fuse_session_mount(se, opts.mountpoint) != 0)
431 goto err_out3;
432
433 fuse_daemonize(opts.foreground);
434
435 /* Start thread to update file contents */
436 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
437 if (ret != 0) {
438 fprintf(stderr, "pthread_create failed with %s\n",
439 strerror(ret));
440 goto err_out3;
441 }
442
443 /* Block until ctrl+c or fusermount -u */
444 if (opts.singlethread)
445 ret = fuse_session_loop(se);
446 else {
447 config = fuse_loop_cfg_create();
448 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
449 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
450 ret = fuse_session_loop_mt(se, config);
451 fuse_loop_cfg_destroy(config);
452 config = NULL;
453 }
454
455 assert(retrieve_status != 1);
457err_out3:
459err_out2:
461err_out1:
462 free(opts.mountpoint);
463 fuse_opt_free_args(&args);
464
465 return ret ? 1 : 0;
466}
467
468
int fuse_set_signal_handlers(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
struct fuse_buf buf[1]
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2notify__store__retrieve_8c_source.html0000644000175000017500000025112115156613442026777 0ustar berndbernd libfuse: fuse-3.18.1/example/notify_store_retrieve.c Source File
libfuse
notify_store_retrieve.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
61#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
62
63#include <fuse_lowlevel.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <errno.h>
68#include <fcntl.h>
69#include <assert.h>
70#include <stddef.h>
71#include <unistd.h>
72#include <pthread.h>
73#include <stdbool.h>
74
75/* We can't actually tell the kernel that there is no
76 timeout, so we just send a big value */
77#define NO_TIMEOUT 500000
78
79#define MAX_STR_LEN 128
80#define FILE_INO 2
81#define FILE_NAME "current_time"
82static char file_contents[MAX_STR_LEN];
83static int lookup_cnt = 0;
84static int open_cnt = 0;
85static size_t file_size;
86static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
87
88/* Keep track if we ever stored data (==1), and
89 received it back correctly (==2) */
90static int retrieve_status = 0;
91
92static bool is_umount = false;
93
94/* updater thread tid */
95static pthread_t updater;
96
97
98/* Command line parsing */
99struct options {
100 int no_notify;
101 int update_interval;
102};
103static struct options options = {
104 .no_notify = 0,
105 .update_interval = 1,
106};
107
108#define OPTION(t, p) \
109 { t, offsetof(struct options, p), 1 }
110static const struct fuse_opt option_spec[] = {
111 OPTION("--no-notify", no_notify),
112 OPTION("--update-interval=%d", update_interval),
114};
115
116static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
117 stbuf->st_ino = ino;
118 if (ino == FUSE_ROOT_ID) {
119 stbuf->st_mode = S_IFDIR | 0755;
120 stbuf->st_nlink = 1;
121 }
122
123 else if (ino == FILE_INO) {
124 stbuf->st_mode = S_IFREG | 0444;
125 stbuf->st_nlink = 1;
126 stbuf->st_size = file_size;
127 }
128
129 else
130 return -1;
131
132 return 0;
133}
134
135static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
136 (void)userdata;
137
138 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
139 conn->no_interrupt = 1;
140}
141
142static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
143 const char *name) {
144 struct fuse_entry_param e;
145 memset(&e, 0, sizeof(e));
146
147 if (parent != FUSE_ROOT_ID)
148 goto err_out;
149 else if (strcmp(name, FILE_NAME) == 0) {
150 e.ino = FILE_INO;
151 } else
152 goto err_out;
153
154 e.attr_timeout = NO_TIMEOUT;
155 e.entry_timeout = NO_TIMEOUT;
156 if (tfs_stat(e.ino, &e.attr) != 0)
157 goto err_out;
158 fuse_reply_entry(req, &e);
159
160 /*
161 * must only be set when the kernel knows about the entry,
162 * otherwise update_fs_loop() might see a positive count, but kernel
163 * would not have the entry yet
164 */
165 if (e.ino == FILE_INO) {
166 pthread_mutex_lock(&lock);
167 lookup_cnt++;
168 pthread_mutex_unlock(&lock);
169 }
170
171 return;
172
173err_out:
174 fuse_reply_err(req, ENOENT);
175}
176
177static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
178 uint64_t nlookup) {
179 (void) req;
180 if(ino == FILE_INO) {
181 pthread_mutex_lock(&lock);
182 lookup_cnt -= nlookup;
183 pthread_mutex_unlock(&lock);
184 } else
185 assert(ino == FUSE_ROOT_ID);
186 fuse_reply_none(req);
187}
188
189static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
190 struct fuse_file_info *fi) {
191 struct stat stbuf;
192
193 (void) fi;
194
195 memset(&stbuf, 0, sizeof(stbuf));
196 if (tfs_stat(ino, &stbuf) != 0)
197 fuse_reply_err(req, ENOENT);
198 else
199 fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
200}
201
202struct dirbuf {
203 char *p;
204 size_t size;
205};
206
207static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
208 fuse_ino_t ino) {
209 struct stat stbuf;
210 size_t oldsize = b->size;
211 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
212 b->p = (char *) realloc(b->p, b->size);
213 memset(&stbuf, 0, sizeof(stbuf));
214 stbuf.st_ino = ino;
215 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
216 b->size);
217}
218
219#define min(x, y) ((x) < (y) ? (x) : (y))
220
221static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
222 off_t off, size_t maxsize) {
223 if (off < bufsize)
224 return fuse_reply_buf(req, buf + off,
225 min(bufsize - off, maxsize));
226 else
227 return fuse_reply_buf(req, NULL, 0);
228}
229
230static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
231 off_t off, struct fuse_file_info *fi) {
232 (void) fi;
233
234 if (ino != FUSE_ROOT_ID)
235 fuse_reply_err(req, ENOTDIR);
236 else {
237 struct dirbuf b;
238
239 memset(&b, 0, sizeof(b));
240 dirbuf_add(req, &b, FILE_NAME, FILE_INO);
241 reply_buf_limited(req, b.p, b.size, off, size);
242 free(b.p);
243 }
244}
245
246static void tfs_open(fuse_req_t req, fuse_ino_t ino,
247 struct fuse_file_info *fi) {
248
249 /* Make cache persistent even if file is closed,
250 this makes it easier to see the effects */
251 fi->keep_cache = 1;
252
253 if (ino == FUSE_ROOT_ID)
254 fuse_reply_err(req, EISDIR);
255 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
256 fuse_reply_err(req, EACCES);
257 else if (ino == FILE_INO) {
258 fuse_reply_open(req, fi);
259 pthread_mutex_lock(&lock);
260 open_cnt++;
261 pthread_mutex_unlock(&lock);
262 } else {
263 // This should not happen
264 fprintf(stderr, "Got open for non-existing inode!\n");
265 fuse_reply_err(req, ENOENT);
266 }
267}
268
269static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
270 off_t off, struct fuse_file_info *fi) {
271 (void) fi;
272
273 assert(ino == FILE_INO);
274 reply_buf_limited(req, file_contents, file_size, off, size);
275}
276
277static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
278 off_t offset, struct fuse_bufvec *data) {
279 struct fuse_bufvec bufv;
280 char buf[MAX_STR_LEN];
281 char *expected;
282 ssize_t ret;
283
284 assert(ino == FILE_INO);
285 assert(offset == 0);
286 expected = (char*) cookie;
287
288 bufv.count = 1;
289 bufv.idx = 0;
290 bufv.off = 0;
291 bufv.buf[0].size = MAX_STR_LEN;
292 bufv.buf[0].mem = buf;
293 bufv.buf[0].flags = 0;
294
295 ret = fuse_buf_copy(&bufv, data, 0);
296 assert(ret > 0);
297 assert(strncmp(buf, expected, ret) == 0);
298 free(expected);
299 retrieve_status = 2;
300 fuse_reply_none(req);
301}
302
303static void tfs_destroy(void *userdata)
304{
305 (void)userdata;
306
307 is_umount = true;
308
309 pthread_join(updater, NULL);
310}
311
312
313static const struct fuse_lowlevel_ops tfs_oper = {
314 .init = tfs_init,
315 .lookup = tfs_lookup,
316 .getattr = tfs_getattr,
317 .readdir = tfs_readdir,
318 .open = tfs_open,
319 .read = tfs_read,
320 .forget = tfs_forget,
321 .retrieve_reply = tfs_retrieve_reply,
322 .destroy = tfs_destroy,
323};
324
325static void update_fs(void) {
326 struct tm *now;
327 time_t t;
328 t = time(NULL);
329 now = localtime(&t);
330 assert(now != NULL);
331
332 file_size = strftime(file_contents, MAX_STR_LEN,
333 "The current time is %H:%M:%S\n", now);
334 assert(file_size != 0);
335}
336
337static void* update_fs_loop(void *data) {
338 struct fuse_session *se = (struct fuse_session*) data;
339 struct fuse_bufvec bufv;
340 int ret;
341
342 while(!is_umount) {
343 update_fs();
344 pthread_mutex_lock(&lock);
345 if (!options.no_notify && open_cnt && lookup_cnt) {
346 /* Only send notification if the kernel
347 is aware of the inode */
348 bufv.count = 1;
349 bufv.idx = 0;
350 bufv.off = 0;
351 bufv.buf[0].size = file_size;
352 bufv.buf[0].mem = file_contents;
353 bufv.buf[0].flags = 0;
354
355 /*
356 * Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
357 * might come up during umount, when kernel side already releases
358 * all inodes, but does not send FUSE_DESTROY yet.
359 */
360
361 ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
362 if ((ret != 0 && !is_umount) &&
363 ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
364 fprintf(stderr,
365 "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
366 strerror(-ret), -ret);
367 abort();
368 }
369
370 /* To make sure that everything worked correctly, ask the
371 kernel to send us back the stored data */
372 ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
373 0, (void*) strdup(file_contents));
374 assert((ret == 0 || is_umount) || ret == -ENOENT || ret == -EBADF ||
375 ret != -ENODEV);
376 if(retrieve_status == 0)
377 retrieve_status = 1;
378 }
379 pthread_mutex_unlock(&lock);
380 sleep(options.update_interval);
381 }
382 return NULL;
383}
384
385static void show_help(const char *progname)
386{
387 printf("usage: %s [options] <mountpoint>\n\n", progname);
388 printf("File-system specific options:\n"
389 " --update-interval=<secs> Update-rate of file system contents\n"
390 " --no-notify Disable kernel notifications\n"
391 "\n");
392}
393
394int main(int argc, char *argv[]) {
395 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
396 struct fuse_session *se;
397 struct fuse_cmdline_opts opts;
398 struct fuse_loop_config *config;
399 int ret = -1;
400
401 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
402 return 1;
403
404 if (fuse_parse_cmdline(&args, &opts) != 0)
405 return 1;
406 if (opts.show_help) {
407 show_help(argv[0]);
410 ret = 0;
411 goto err_out1;
412 } else if (opts.show_version) {
413 printf("FUSE library version %s\n", fuse_pkgversion());
415 ret = 0;
416 goto err_out1;
417 }
418
419 /* Initial contents */
420 update_fs();
421
422 se = fuse_session_new(&args, &tfs_oper,
423 sizeof(tfs_oper), NULL);
424 if (se == NULL)
425 goto err_out1;
426
427 if (fuse_set_signal_handlers(se) != 0)
428 goto err_out2;
429
430 if (fuse_session_mount(se, opts.mountpoint) != 0)
431 goto err_out3;
432
433 fuse_daemonize(opts.foreground);
434
435 /* Start thread to update file contents */
436 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
437 if (ret != 0) {
438 fprintf(stderr, "pthread_create failed with %s\n",
439 strerror(ret));
440 goto err_out3;
441 }
442
443 /* Block until ctrl+c or fusermount -u */
444 if (opts.singlethread)
445 ret = fuse_session_loop(se);
446 else {
447 config = fuse_loop_cfg_create();
448 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
449 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
450 ret = fuse_session_loop_mt(se, config);
451 fuse_loop_cfg_destroy(config);
452 config = NULL;
453 }
454
455 assert(retrieve_status != 1);
457err_out3:
459err_out2:
461err_out1:
462 free(opts.mountpoint);
463 fuse_opt_free_args(&args);
464
465 return ret ? 1 : 0;
466}
467
468
int fuse_set_signal_handlers(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
struct fuse_buf buf[1]
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/example_2null_8c_source.html0000644000175000017500000005713615156613442020660 0ustar berndbernd libfuse: example/null.c Source File
libfuse
null.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
25#define FUSE_USE_VERSION 31
26
27#include <fuse.h>
28#include <fuse_lowlevel.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <time.h>
34#include <errno.h>
35
36static int null_getattr(const char *path, struct stat *stbuf,
37 struct fuse_file_info *fi)
38{
39 (void) fi;
40
41 if(strcmp(path, "/") != 0)
42 return -ENOENT;
43
44 stbuf->st_mode = S_IFREG | 0644;
45 stbuf->st_nlink = 1;
46 stbuf->st_uid = getuid();
47 stbuf->st_gid = getgid();
48 stbuf->st_size = (1ULL << 32); /* 4G */
49 stbuf->st_blocks = 0;
50 stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
51
52 return 0;
53}
54
55static int null_truncate(const char *path, off_t size,
56 struct fuse_file_info *fi)
57{
58 (void) size;
59 (void) fi;
60
61 if(strcmp(path, "/") != 0)
62 return -ENOENT;
63
64 return 0;
65}
66
67static int null_open(const char *path, struct fuse_file_info *fi)
68{
69 (void) fi;
70
71 if(strcmp(path, "/") != 0)
72 return -ENOENT;
73
74 return 0;
75}
76
77static int null_read(const char *path, char *buf, size_t size,
78 off_t offset, struct fuse_file_info *fi)
79{
80 (void) buf;
81 (void) offset;
82 (void) fi;
83
84 if(strcmp(path, "/") != 0)
85 return -ENOENT;
86
87 if (offset >= (1ULL << 32))
88 return 0;
89
90 memset(buf, 0, size);
91 return size;
92}
93
94static int null_write(const char *path, const char *buf, size_t size,
95 off_t offset, struct fuse_file_info *fi)
96{
97 (void) buf;
98 (void) offset;
99 (void) fi;
100
101 if(strcmp(path, "/") != 0)
102 return -ENOENT;
103
104 return size;
105}
106
107static const struct fuse_operations null_oper = {
108 .getattr = null_getattr,
109 .truncate = null_truncate,
110 .open = null_open,
111 .read = null_read,
112 .write = null_write,
113};
114
115int main(int argc, char *argv[])
116{
117 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
118 struct fuse_cmdline_opts opts;
119 struct stat stbuf;
120
121 if (fuse_parse_cmdline(&args, &opts) != 0)
122 return 1;
123 fuse_opt_free_args(&args);
124
125 if (!opts.mountpoint) {
126 fprintf(stderr, "missing mountpoint parameter\n");
127 return 1;
128 }
129
130 if (stat(opts.mountpoint, &stbuf) == -1) {
131 fprintf(stderr ,"failed to access mountpoint %s: %s\n",
132 opts.mountpoint, strerror(errno));
133 free(opts.mountpoint);
134 return 1;
135 }
136 free(opts.mountpoint);
137 if (!S_ISREG(stbuf.st_mode)) {
138 fprintf(stderr, "mountpoint is not a regular file\n");
139 return 1;
140 }
141
142 return fuse_main(argc, argv, &null_oper, NULL);
143}
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
fuse-3.18.2/doc/html/fuse-3_817_84_2example_2null_8c_source.html0000644000175000017500000005733715156613442023041 0ustar berndbernd libfuse: fuse-3.17.4/example/null.c Source File
libfuse
null.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file COPYING.
7*/
8
25#define FUSE_USE_VERSION 31
26
27#include <fuse.h>
28#include <fuse_lowlevel.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <time.h>
34#include <errno.h>
35
36static int null_getattr(const char *path, struct stat *stbuf,
37 struct fuse_file_info *fi)
38{
39 (void) fi;
40
41 if(strcmp(path, "/") != 0)
42 return -ENOENT;
43
44 stbuf->st_mode = S_IFREG | 0644;
45 stbuf->st_nlink = 1;
46 stbuf->st_uid = getuid();
47 stbuf->st_gid = getgid();
48 stbuf->st_size = (1ULL << 32); /* 4G */
49 stbuf->st_blocks = 0;
50 stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
51
52 return 0;
53}
54
55static int null_truncate(const char *path, off_t size,
56 struct fuse_file_info *fi)
57{
58 (void) size;
59 (void) fi;
60
61 if(strcmp(path, "/") != 0)
62 return -ENOENT;
63
64 return 0;
65}
66
67static int null_open(const char *path, struct fuse_file_info *fi)
68{
69 (void) fi;
70
71 if(strcmp(path, "/") != 0)
72 return -ENOENT;
73
74 return 0;
75}
76
77static int null_read(const char *path, char *buf, size_t size,
78 off_t offset, struct fuse_file_info *fi)
79{
80 (void) buf;
81 (void) offset;
82 (void) fi;
83
84 if(strcmp(path, "/") != 0)
85 return -ENOENT;
86
87 if (offset >= (1ULL << 32))
88 return 0;
89
90 memset(buf, 0, size);
91 return size;
92}
93
94static int null_write(const char *path, const char *buf, size_t size,
95 off_t offset, struct fuse_file_info *fi)
96{
97 (void) buf;
98 (void) offset;
99 (void) fi;
100
101 if(strcmp(path, "/") != 0)
102 return -ENOENT;
103
104 return size;
105}
106
107static const struct fuse_operations null_oper = {
108 .getattr = null_getattr,
109 .truncate = null_truncate,
110 .open = null_open,
111 .read = null_read,
112 .write = null_write,
113};
114
115int main(int argc, char *argv[])
116{
117 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
118 struct fuse_cmdline_opts opts;
119 struct stat stbuf;
120
121 if (fuse_parse_cmdline(&args, &opts) != 0)
122 return 1;
123 fuse_opt_free_args(&args);
124
125 if (!opts.mountpoint) {
126 fprintf(stderr, "missing mountpoint parameter\n");
127 return 1;
128 }
129
130 if (stat(opts.mountpoint, &stbuf) == -1) {
131 fprintf(stderr ,"failed to access mountpoint %s: %s\n",
132 opts.mountpoint, strerror(errno));
133 free(opts.mountpoint);
134 return 1;
135 }
136 free(opts.mountpoint);
137 if (!S_ISREG(stbuf.st_mode)) {
138 fprintf(stderr, "mountpoint is not a regular file\n");
139 return 1;
140 }
141
142 return fuse_main(argc, argv, &null_oper, NULL);
143}
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2null_8c_source.html0000644000175000017500000005734015156613442023031 0ustar berndbernd libfuse: fuse-3.18.1/example/null.c Source File
libfuse
null.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
25#define FUSE_USE_VERSION 31
26
27#include <fuse.h>
28#include <fuse_lowlevel.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <time.h>
34#include <errno.h>
35
36static int null_getattr(const char *path, struct stat *stbuf,
37 struct fuse_file_info *fi)
38{
39 (void) fi;
40
41 if(strcmp(path, "/") != 0)
42 return -ENOENT;
43
44 stbuf->st_mode = S_IFREG | 0644;
45 stbuf->st_nlink = 1;
46 stbuf->st_uid = getuid();
47 stbuf->st_gid = getgid();
48 stbuf->st_size = (1ULL << 32); /* 4G */
49 stbuf->st_blocks = 0;
50 stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
51
52 return 0;
53}
54
55static int null_truncate(const char *path, off_t size,
56 struct fuse_file_info *fi)
57{
58 (void) size;
59 (void) fi;
60
61 if(strcmp(path, "/") != 0)
62 return -ENOENT;
63
64 return 0;
65}
66
67static int null_open(const char *path, struct fuse_file_info *fi)
68{
69 (void) fi;
70
71 if(strcmp(path, "/") != 0)
72 return -ENOENT;
73
74 return 0;
75}
76
77static int null_read(const char *path, char *buf, size_t size,
78 off_t offset, struct fuse_file_info *fi)
79{
80 (void) buf;
81 (void) offset;
82 (void) fi;
83
84 if(strcmp(path, "/") != 0)
85 return -ENOENT;
86
87 if (offset >= (1ULL << 32))
88 return 0;
89
90 memset(buf, 0, size);
91 return size;
92}
93
94static int null_write(const char *path, const char *buf, size_t size,
95 off_t offset, struct fuse_file_info *fi)
96{
97 (void) buf;
98 (void) offset;
99 (void) fi;
100
101 if(strcmp(path, "/") != 0)
102 return -ENOENT;
103
104 return size;
105}
106
107static const struct fuse_operations null_oper = {
108 .getattr = null_getattr,
109 .truncate = null_truncate,
110 .open = null_open,
111 .read = null_read,
112 .write = null_write,
113};
114
115int main(int argc, char *argv[])
116{
117 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
118 struct fuse_cmdline_opts opts;
119 struct stat stbuf;
120
121 if (fuse_parse_cmdline(&args, &opts) != 0)
122 return 1;
123 fuse_opt_free_args(&args);
124
125 if (!opts.mountpoint) {
126 fprintf(stderr, "missing mountpoint parameter\n");
127 return 1;
128 }
129
130 if (stat(opts.mountpoint, &stbuf) == -1) {
131 fprintf(stderr ,"failed to access mountpoint %s: %s\n",
132 opts.mountpoint, strerror(errno));
133 free(opts.mountpoint);
134 return 1;
135 }
136 free(opts.mountpoint);
137 if (!S_ISREG(stbuf.st_mode)) {
138 fprintf(stderr, "mountpoint is not a regular file\n");
139 return 1;
140 }
141
142 return fuse_main(argc, argv, &null_oper, NULL);
143}
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
fuse-3.18.2/doc/html/example_2passthrough_8c_source.html0000644000175000017500000026674415156613442022264 0ustar berndbernd libfuse: example/passthrough.c Source File
libfuse
passthrough.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
26#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
27
28#define _GNU_SOURCE
29
30#ifdef linux
31/* For pread()/pwrite()/utimensat() */
32#define _XOPEN_SOURCE 700
33#endif
34
35#include <fuse.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39#include <fcntl.h>
40#include <sys/stat.h>
41#include <dirent.h>
42#include <errno.h>
43#include <sys/time.h>
44#ifdef HAVE_SETXATTR
45#include <sys/xattr.h>
46#endif
47
48#include "passthrough_helpers.h"
49
50static int fill_dir_plus = 0;
51static int readdir_zero_ino;
52
53static void *xmp_init(struct fuse_conn_info *conn,
54 struct fuse_config *cfg)
55{
56 (void) conn;
57 cfg->use_ino = !readdir_zero_ino;
58
59 /* parallel_direct_writes feature depends on direct_io features.
60 To make parallel_direct_writes valid, need either set cfg->direct_io
61 in current function (recommended in high level API) or set fi->direct_io
62 in xmp_create() or xmp_open(). */
63 // cfg->direct_io = 1;
65
66 /* Pick up changes from lower filesystem right away. This is
67 also necessary for better hardlink support. When the kernel
68 calls the unlink() handler, it does not know the inode of
69 the to-be-removed entry and can therefore not invalidate
70 the cache of the associated inode - resulting in an
71 incorrect st_nlink value being reported for any remaining
72 hardlinks to this inode. */
73 if (!cfg->auto_cache) {
74 cfg->entry_timeout = 0;
75 cfg->attr_timeout = 0;
76 cfg->negative_timeout = 0;
77 }
78
79 return NULL;
80}
81
82static int xmp_getattr(const char *path, struct stat *stbuf,
83 struct fuse_file_info *fi)
84{
85 (void) fi;
86 int res;
87
88 res = lstat(path, stbuf);
89 if (res == -1)
90 return -errno;
91
92 return 0;
93}
94
95static int xmp_access(const char *path, int mask)
96{
97 int res;
98
99 res = access(path, mask);
100 if (res == -1)
101 return -errno;
102
103 return 0;
104}
105
106static int xmp_readlink(const char *path, char *buf, size_t size)
107{
108 int res;
109
110 res = readlink(path, buf, size - 1);
111 if (res == -1)
112 return -errno;
113
114 buf[res] = '\0';
115 return 0;
116}
117
118
119static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
120 off_t offset, struct fuse_file_info *fi,
121 enum fuse_readdir_flags flags)
122{
123 DIR *dp;
124 struct dirent *de;
125
126 (void) offset;
127 (void) fi;
128 (void) flags;
129
130 dp = opendir(path);
131 if (dp == NULL)
132 return -errno;
133
134 while ((de = readdir(dp)) != NULL) {
135 struct stat st;
136 if (fill_dir_plus) {
137 fstatat(dirfd(dp), de->d_name, &st,
138 AT_SYMLINK_NOFOLLOW);
139 } else {
140 memset(&st, 0, sizeof(st));
141 st.st_ino = de->d_ino;
142 st.st_mode = de->d_type << 12;
143 }
144 if (readdir_zero_ino)
145 st.st_ino = 0;
146 if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
147 break;
148 }
149
150 closedir(dp);
151 return 0;
152}
153
154static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
155{
156 int res;
157
158 res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
159 if (res == -1)
160 return -errno;
161
162 return 0;
163}
164
165static int xmp_mkdir(const char *path, mode_t mode)
166{
167 int res;
168
169 res = mkdir(path, mode);
170 if (res == -1)
171 return -errno;
172
173 return 0;
174}
175
176static int xmp_unlink(const char *path)
177{
178 int res;
179
180 res = unlink(path);
181 if (res == -1)
182 return -errno;
183
184 return 0;
185}
186
187static int xmp_rmdir(const char *path)
188{
189 int res;
190
191 res = rmdir(path);
192 if (res == -1)
193 return -errno;
194
195 return 0;
196}
197
198static int xmp_symlink(const char *from, const char *to)
199{
200 int res;
201
202 res = symlink(from, to);
203 if (res == -1)
204 return -errno;
205
206 return 0;
207}
208
209static int xmp_rename(const char *from, const char *to, unsigned int flags)
210{
211 int res;
212
213 if (flags)
214 return -EINVAL;
215
216 res = rename(from, to);
217 if (res == -1)
218 return -errno;
219
220 return 0;
221}
222
223static int xmp_link(const char *from, const char *to)
224{
225 int res;
226
227 res = link(from, to);
228 if (res == -1)
229 return -errno;
230
231 return 0;
232}
233
234static int xmp_chmod(const char *path, mode_t mode,
235 struct fuse_file_info *fi)
236{
237 (void) fi;
238 int res;
239
240 res = chmod(path, mode);
241 if (res == -1)
242 return -errno;
243
244 return 0;
245}
246
247static int xmp_chown(const char *path, uid_t uid, gid_t gid,
248 struct fuse_file_info *fi)
249{
250 (void) fi;
251 int res;
252
253 res = lchown(path, uid, gid);
254 if (res == -1)
255 return -errno;
256
257 return 0;
258}
259
260static int xmp_truncate(const char *path, off_t size,
261 struct fuse_file_info *fi)
262{
263 int res;
264
265 if (fi != NULL)
266 res = ftruncate(fi->fh, size);
267 else
268 res = truncate(path, size);
269 if (res == -1)
270 return -errno;
271
272 return 0;
273}
274
275#ifdef HAVE_UTIMENSAT
276static int xmp_utimens(const char *path, const struct timespec ts[2],
277 struct fuse_file_info *fi)
278{
279 (void) fi;
280 int res;
281
282 /* don't use utime/utimes since they follow symlinks */
283 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
284 if (res == -1)
285 return -errno;
286
287 return 0;
288}
289#endif
290
291static int xmp_create(const char *path, mode_t mode,
292 struct fuse_file_info *fi)
293{
294 int res;
295
296 res = open(path, fi->flags, mode);
297 if (res == -1)
298 return -errno;
299
300 fi->fh = res;
301 return 0;
302}
303
304static int xmp_open(const char *path, struct fuse_file_info *fi)
305{
306 int res;
307
308 res = open(path, fi->flags);
309 if (res == -1)
310 return -errno;
311
312 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
313 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
314 for writes to the same file). */
315 if (fi->flags & O_DIRECT) {
316 fi->direct_io = 1;
318 }
319
320 fi->fh = res;
321 return 0;
322}
323
324static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
325 struct fuse_file_info *fi)
326{
327 int fd;
328 int res;
329
330 if(fi == NULL)
331 fd = open(path, O_RDONLY);
332 else
333 fd = fi->fh;
334
335 if (fd == -1)
336 return -errno;
337
338 res = pread(fd, buf, size, offset);
339 if (res == -1)
340 res = -errno;
341
342 if(fi == NULL)
343 close(fd);
344 return res;
345}
346
347static int xmp_write(const char *path, const char *buf, size_t size,
348 off_t offset, struct fuse_file_info *fi)
349{
350 int fd;
351 int res;
352
353 (void) fi;
354 if(fi == NULL)
355 fd = open(path, O_WRONLY);
356 else
357 fd = fi->fh;
358
359 if (fd == -1)
360 return -errno;
361
362 res = pwrite(fd, buf, size, offset);
363 if (res == -1)
364 res = -errno;
365
366 if(fi == NULL)
367 close(fd);
368 return res;
369}
370
371static int xmp_statfs(const char *path, struct statvfs *stbuf)
372{
373 int res;
374
375 res = statvfs(path, stbuf);
376 if (res == -1)
377 return -errno;
378
379 return 0;
380}
381
382static int xmp_release(const char *path, struct fuse_file_info *fi)
383{
384 (void) path;
385 close(fi->fh);
386 return 0;
387}
388
389static int xmp_fsync(const char *path, int isdatasync,
390 struct fuse_file_info *fi)
391{
392 /* Just a stub. This method is optional and can safely be left
393 unimplemented */
394
395 (void) path;
396 (void) isdatasync;
397 (void) fi;
398 return 0;
399}
400
401static int xmp_fallocate(const char *path, int mode,
402 off_t offset, off_t length, struct fuse_file_info *fi)
403{
404 int fd;
405 int res;
406
407 (void) fi;
408
409 if(fi == NULL)
410 fd = open(path, O_WRONLY);
411 else
412 fd = fi->fh;
413
414 if (fd == -1)
415 return -errno;
416
417 res = do_fallocate(fd, mode, offset, length);
418
419 if(fi == NULL)
420 close(fd);
421 return res;
422}
423
424#ifdef HAVE_SETXATTR
425/* xattr operations are optional and can safely be left unimplemented */
426static int xmp_setxattr(const char *path, const char *name, const char *value,
427 size_t size, int flags)
428{
429 int res = lsetxattr(path, name, value, size, flags);
430 if (res == -1)
431 return -errno;
432 return 0;
433}
434
435static int xmp_getxattr(const char *path, const char *name, char *value,
436 size_t size)
437{
438 int res = lgetxattr(path, name, value, size);
439 if (res == -1)
440 return -errno;
441 return res;
442}
443
444static int xmp_listxattr(const char *path, char *list, size_t size)
445{
446 int res = llistxattr(path, list, size);
447 if (res == -1)
448 return -errno;
449 return res;
450}
451
452static int xmp_removexattr(const char *path, const char *name)
453{
454 int res = lremovexattr(path, name);
455 if (res == -1)
456 return -errno;
457 return 0;
458}
459#endif /* HAVE_SETXATTR */
460
461#ifdef HAVE_COPY_FILE_RANGE
462static ssize_t xmp_copy_file_range(const char *path_in,
463 struct fuse_file_info *fi_in,
464 off_t offset_in, const char *path_out,
465 struct fuse_file_info *fi_out,
466 off_t offset_out, size_t len, int flags)
467{
468 int fd_in, fd_out;
469 ssize_t res;
470
471 if(fi_in == NULL)
472 fd_in = open(path_in, O_RDONLY);
473 else
474 fd_in = fi_in->fh;
475
476 if (fd_in == -1)
477 return -errno;
478
479 if(fi_out == NULL)
480 fd_out = open(path_out, O_WRONLY);
481 else
482 fd_out = fi_out->fh;
483
484 if (fd_out == -1) {
485 close(fd_in);
486 return -errno;
487 }
488
489 res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
490 flags);
491 if (res == -1)
492 res = -errno;
493
494 if (fi_out == NULL)
495 close(fd_out);
496 if (fi_in == NULL)
497 close(fd_in);
498
499 return res;
500}
501#endif
502
503static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
504{
505 int fd;
506 off_t res;
507
508 if (fi == NULL)
509 fd = open(path, O_RDONLY);
510 else
511 fd = fi->fh;
512
513 if (fd == -1)
514 return -errno;
515
516 res = lseek(fd, off, whence);
517 if (res == -1)
518 res = -errno;
519
520 if (fi == NULL)
521 close(fd);
522 return res;
523}
524
525#ifdef HAVE_STATX
526static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
527 struct fuse_file_info *fi)
528{
529 int fd = -1;
530 int res;
531
532 if (fi)
533 fd = fi->fh;
534
535 res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
536 if (res == -1)
537 return -errno;
538
539 return 0;
540}
541#endif
542
543static const struct fuse_operations xmp_oper = {
544 .init = xmp_init,
545 .getattr = xmp_getattr,
546 .access = xmp_access,
547 .readlink = xmp_readlink,
548 .readdir = xmp_readdir,
549 .mknod = xmp_mknod,
550 .mkdir = xmp_mkdir,
551 .symlink = xmp_symlink,
552 .unlink = xmp_unlink,
553 .rmdir = xmp_rmdir,
554 .rename = xmp_rename,
555 .link = xmp_link,
556 .chmod = xmp_chmod,
557 .chown = xmp_chown,
558 .truncate = xmp_truncate,
559#ifdef HAVE_UTIMENSAT
560 .utimens = xmp_utimens,
561#endif
562 .open = xmp_open,
563 .create = xmp_create,
564 .read = xmp_read,
565 .write = xmp_write,
566 .statfs = xmp_statfs,
567 .release = xmp_release,
568 .fsync = xmp_fsync,
569 .fallocate = xmp_fallocate,
570#ifdef HAVE_SETXATTR
571 .setxattr = xmp_setxattr,
572 .getxattr = xmp_getxattr,
573 .listxattr = xmp_listxattr,
574 .removexattr = xmp_removexattr,
575#endif
576#ifdef HAVE_COPY_FILE_RANGE
577 .copy_file_range = xmp_copy_file_range,
578#endif
579 .lseek = xmp_lseek,
580#ifdef HAVE_STATX
581 .statx = xmp_statx,
582#endif
583};
584
585int main(int argc, char *argv[])
586{
587 enum { MAX_ARGS = 10 };
588 int i,new_argc;
589 char *new_argv[MAX_ARGS];
590
591 umask(0);
592 /* Process the "--plus" option apart */
593 for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
594 if (!strcmp(argv[i], "--plus")) {
595 fill_dir_plus = FUSE_FILL_DIR_PLUS;
596 } else if (!strcmp(argv[i], "--readdir-zero-inodes")) {
597 // Return zero inodes from readdir
598 readdir_zero_ino = 1;
599 } else {
600 new_argv[new_argc++] = argv[i];
601 }
602 }
603 return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
604}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_readdir_flags
Definition fuse.h:42
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t auto_cache
Definition fuse.h:253
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/fuse-3_817_84_2example_2passthrough_8c_source.html0000644000175000017500000026200015156613442024417 0ustar berndbernd libfuse: fuse-3.17.4/example/passthrough.c Source File
libfuse
passthrough.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file COPYING.
8*/
9
26#define FUSE_USE_VERSION 31
27
28#define _GNU_SOURCE
29
30#ifdef linux
31/* For pread()/pwrite()/utimensat() */
32#define _XOPEN_SOURCE 700
33#endif
34
35#include <fuse.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39#include <fcntl.h>
40#include <sys/stat.h>
41#include <dirent.h>
42#include <errno.h>
43#ifdef __FreeBSD__
44#include <sys/socket.h>
45#include <sys/un.h>
46#endif
47#include <sys/time.h>
48#ifdef HAVE_SETXATTR
49#include <sys/xattr.h>
50#endif
51
52#include "passthrough_helpers.h"
53
54static int fill_dir_plus = 0;
55
56static void *xmp_init(struct fuse_conn_info *conn,
57 struct fuse_config *cfg)
58{
59 (void) conn;
60 cfg->use_ino = 1;
61
62 /* parallel_direct_writes feature depends on direct_io features.
63 To make parallel_direct_writes valid, need either set cfg->direct_io
64 in current function (recommended in high level API) or set fi->direct_io
65 in xmp_create() or xmp_open(). */
66 // cfg->direct_io = 1;
68
69 /* Pick up changes from lower filesystem right away. This is
70 also necessary for better hardlink support. When the kernel
71 calls the unlink() handler, it does not know the inode of
72 the to-be-removed entry and can therefore not invalidate
73 the cache of the associated inode - resulting in an
74 incorrect st_nlink value being reported for any remaining
75 hardlinks to this inode. */
76 if (!cfg->auto_cache) {
77 cfg->entry_timeout = 0;
78 cfg->attr_timeout = 0;
79 cfg->negative_timeout = 0;
80 }
81
82 return NULL;
83}
84
85static int xmp_getattr(const char *path, struct stat *stbuf,
86 struct fuse_file_info *fi)
87{
88 (void) fi;
89 int res;
90
91 res = lstat(path, stbuf);
92 if (res == -1)
93 return -errno;
94
95 return 0;
96}
97
98static int xmp_access(const char *path, int mask)
99{
100 int res;
101
102 res = access(path, mask);
103 if (res == -1)
104 return -errno;
105
106 return 0;
107}
108
109static int xmp_readlink(const char *path, char *buf, size_t size)
110{
111 int res;
112
113 res = readlink(path, buf, size - 1);
114 if (res == -1)
115 return -errno;
116
117 buf[res] = '\0';
118 return 0;
119}
120
121
122static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
123 off_t offset, struct fuse_file_info *fi,
124 enum fuse_readdir_flags flags)
125{
126 DIR *dp;
127 struct dirent *de;
128
129 (void) offset;
130 (void) fi;
131 (void) flags;
132
133 dp = opendir(path);
134 if (dp == NULL)
135 return -errno;
136
137 while ((de = readdir(dp)) != NULL) {
138 struct stat st;
139 if (fill_dir_plus) {
140 fstatat(dirfd(dp), de->d_name, &st,
141 AT_SYMLINK_NOFOLLOW);
142 } else {
143 memset(&st, 0, sizeof(st));
144 st.st_ino = de->d_ino;
145 st.st_mode = de->d_type << 12;
146 }
147 if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
148 break;
149 }
150
151 closedir(dp);
152 return 0;
153}
154
155static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
156{
157 int res;
158
159 res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
160 if (res == -1)
161 return -errno;
162
163 return 0;
164}
165
166static int xmp_mkdir(const char *path, mode_t mode)
167{
168 int res;
169
170 res = mkdir(path, mode);
171 if (res == -1)
172 return -errno;
173
174 return 0;
175}
176
177static int xmp_unlink(const char *path)
178{
179 int res;
180
181 res = unlink(path);
182 if (res == -1)
183 return -errno;
184
185 return 0;
186}
187
188static int xmp_rmdir(const char *path)
189{
190 int res;
191
192 res = rmdir(path);
193 if (res == -1)
194 return -errno;
195
196 return 0;
197}
198
199static int xmp_symlink(const char *from, const char *to)
200{
201 int res;
202
203 res = symlink(from, to);
204 if (res == -1)
205 return -errno;
206
207 return 0;
208}
209
210static int xmp_rename(const char *from, const char *to, unsigned int flags)
211{
212 int res;
213
214 if (flags)
215 return -EINVAL;
216
217 res = rename(from, to);
218 if (res == -1)
219 return -errno;
220
221 return 0;
222}
223
224static int xmp_link(const char *from, const char *to)
225{
226 int res;
227
228 res = link(from, to);
229 if (res == -1)
230 return -errno;
231
232 return 0;
233}
234
235static int xmp_chmod(const char *path, mode_t mode,
236 struct fuse_file_info *fi)
237{
238 (void) fi;
239 int res;
240
241 res = chmod(path, mode);
242 if (res == -1)
243 return -errno;
244
245 return 0;
246}
247
248static int xmp_chown(const char *path, uid_t uid, gid_t gid,
249 struct fuse_file_info *fi)
250{
251 (void) fi;
252 int res;
253
254 res = lchown(path, uid, gid);
255 if (res == -1)
256 return -errno;
257
258 return 0;
259}
260
261static int xmp_truncate(const char *path, off_t size,
262 struct fuse_file_info *fi)
263{
264 int res;
265
266 if (fi != NULL)
267 res = ftruncate(fi->fh, size);
268 else
269 res = truncate(path, size);
270 if (res == -1)
271 return -errno;
272
273 return 0;
274}
275
276#ifdef HAVE_UTIMENSAT
277static int xmp_utimens(const char *path, const struct timespec ts[2],
278 struct fuse_file_info *fi)
279{
280 (void) fi;
281 int res;
282
283 /* don't use utime/utimes since they follow symlinks */
284 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
285 if (res == -1)
286 return -errno;
287
288 return 0;
289}
290#endif
291
292static int xmp_create(const char *path, mode_t mode,
293 struct fuse_file_info *fi)
294{
295 int res;
296
297 res = open(path, fi->flags, mode);
298 if (res == -1)
299 return -errno;
300
301 fi->fh = res;
302 return 0;
303}
304
305static int xmp_open(const char *path, struct fuse_file_info *fi)
306{
307 int res;
308
309 res = open(path, fi->flags);
310 if (res == -1)
311 return -errno;
312
313 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
314 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
315 for writes to the same file). */
316 if (fi->flags & O_DIRECT) {
317 fi->direct_io = 1;
319 }
320
321 fi->fh = res;
322 return 0;
323}
324
325static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
326 struct fuse_file_info *fi)
327{
328 int fd;
329 int res;
330
331 if(fi == NULL)
332 fd = open(path, O_RDONLY);
333 else
334 fd = fi->fh;
335
336 if (fd == -1)
337 return -errno;
338
339 res = pread(fd, buf, size, offset);
340 if (res == -1)
341 res = -errno;
342
343 if(fi == NULL)
344 close(fd);
345 return res;
346}
347
348static int xmp_write(const char *path, const char *buf, size_t size,
349 off_t offset, struct fuse_file_info *fi)
350{
351 int fd;
352 int res;
353
354 (void) fi;
355 if(fi == NULL)
356 fd = open(path, O_WRONLY);
357 else
358 fd = fi->fh;
359
360 if (fd == -1)
361 return -errno;
362
363 res = pwrite(fd, buf, size, offset);
364 if (res == -1)
365 res = -errno;
366
367 if(fi == NULL)
368 close(fd);
369 return res;
370}
371
372static int xmp_statfs(const char *path, struct statvfs *stbuf)
373{
374 int res;
375
376 res = statvfs(path, stbuf);
377 if (res == -1)
378 return -errno;
379
380 return 0;
381}
382
383static int xmp_release(const char *path, struct fuse_file_info *fi)
384{
385 (void) path;
386 close(fi->fh);
387 return 0;
388}
389
390static int xmp_fsync(const char *path, int isdatasync,
391 struct fuse_file_info *fi)
392{
393 /* Just a stub. This method is optional and can safely be left
394 unimplemented */
395
396 (void) path;
397 (void) isdatasync;
398 (void) fi;
399 return 0;
400}
401
402#ifdef HAVE_POSIX_FALLOCATE
403static int xmp_fallocate(const char *path, int mode,
404 off_t offset, off_t length, struct fuse_file_info *fi)
405{
406 int fd;
407 int res;
408
409 (void) fi;
410
411 if (mode)
412 return -EOPNOTSUPP;
413
414 if(fi == NULL)
415 fd = open(path, O_WRONLY);
416 else
417 fd = fi->fh;
418
419 if (fd == -1)
420 return -errno;
421
422 res = -posix_fallocate(fd, offset, length);
423
424 if(fi == NULL)
425 close(fd);
426 return res;
427}
428#endif
429
430#ifdef HAVE_SETXATTR
431/* xattr operations are optional and can safely be left unimplemented */
432static int xmp_setxattr(const char *path, const char *name, const char *value,
433 size_t size, int flags)
434{
435 int res = lsetxattr(path, name, value, size, flags);
436 if (res == -1)
437 return -errno;
438 return 0;
439}
440
441static int xmp_getxattr(const char *path, const char *name, char *value,
442 size_t size)
443{
444 int res = lgetxattr(path, name, value, size);
445 if (res == -1)
446 return -errno;
447 return res;
448}
449
450static int xmp_listxattr(const char *path, char *list, size_t size)
451{
452 int res = llistxattr(path, list, size);
453 if (res == -1)
454 return -errno;
455 return res;
456}
457
458static int xmp_removexattr(const char *path, const char *name)
459{
460 int res = lremovexattr(path, name);
461 if (res == -1)
462 return -errno;
463 return 0;
464}
465#endif /* HAVE_SETXATTR */
466
467#ifdef HAVE_COPY_FILE_RANGE
468static ssize_t xmp_copy_file_range(const char *path_in,
469 struct fuse_file_info *fi_in,
470 off_t offset_in, const char *path_out,
471 struct fuse_file_info *fi_out,
472 off_t offset_out, size_t len, int flags)
473{
474 int fd_in, fd_out;
475 ssize_t res;
476
477 if(fi_in == NULL)
478 fd_in = open(path_in, O_RDONLY);
479 else
480 fd_in = fi_in->fh;
481
482 if (fd_in == -1)
483 return -errno;
484
485 if(fi_out == NULL)
486 fd_out = open(path_out, O_WRONLY);
487 else
488 fd_out = fi_out->fh;
489
490 if (fd_out == -1) {
491 close(fd_in);
492 return -errno;
493 }
494
495 res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
496 flags);
497 if (res == -1)
498 res = -errno;
499
500 if (fi_out == NULL)
501 close(fd_out);
502 if (fi_in == NULL)
503 close(fd_in);
504
505 return res;
506}
507#endif
508
509static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
510{
511 int fd;
512 off_t res;
513
514 if (fi == NULL)
515 fd = open(path, O_RDONLY);
516 else
517 fd = fi->fh;
518
519 if (fd == -1)
520 return -errno;
521
522 res = lseek(fd, off, whence);
523 if (res == -1)
524 res = -errno;
525
526 if (fi == NULL)
527 close(fd);
528 return res;
529}
530
531static const struct fuse_operations xmp_oper = {
532 .init = xmp_init,
533 .getattr = xmp_getattr,
534 .access = xmp_access,
535 .readlink = xmp_readlink,
536 .readdir = xmp_readdir,
537 .mknod = xmp_mknod,
538 .mkdir = xmp_mkdir,
539 .symlink = xmp_symlink,
540 .unlink = xmp_unlink,
541 .rmdir = xmp_rmdir,
542 .rename = xmp_rename,
543 .link = xmp_link,
544 .chmod = xmp_chmod,
545 .chown = xmp_chown,
546 .truncate = xmp_truncate,
547#ifdef HAVE_UTIMENSAT
548 .utimens = xmp_utimens,
549#endif
550 .open = xmp_open,
551 .create = xmp_create,
552 .read = xmp_read,
553 .write = xmp_write,
554 .statfs = xmp_statfs,
555 .release = xmp_release,
556 .fsync = xmp_fsync,
557#ifdef HAVE_POSIX_FALLOCATE
558 .fallocate = xmp_fallocate,
559#endif
560#ifdef HAVE_SETXATTR
561 .setxattr = xmp_setxattr,
562 .getxattr = xmp_getxattr,
563 .listxattr = xmp_listxattr,
564 .removexattr = xmp_removexattr,
565#endif
566#ifdef HAVE_COPY_FILE_RANGE
567 .copy_file_range = xmp_copy_file_range,
568#endif
569 .lseek = xmp_lseek,
570};
571
572int main(int argc, char *argv[])
573{
574 enum { MAX_ARGS = 10 };
575 int i,new_argc;
576 char *new_argv[MAX_ARGS];
577
578 umask(0);
579 /* Process the "--plus" option apart */
580 for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
581 if (!strcmp(argv[i], "--plus")) {
582 fill_dir_plus = FUSE_FILL_DIR_PLUS;
583 } else {
584 new_argv[new_argc++] = argv[i];
585 }
586 }
587 return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
588}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_readdir_flags
Definition fuse.h:42
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t auto_cache
Definition fuse.h:253
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2passthrough_8c_source.html0000644000175000017500000026714615156613442024435 0ustar berndbernd libfuse: fuse-3.18.1/example/passthrough.c Source File
libfuse
passthrough.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
26#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
27
28#define _GNU_SOURCE
29
30#ifdef linux
31/* For pread()/pwrite()/utimensat() */
32#define _XOPEN_SOURCE 700
33#endif
34
35#include <fuse.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39#include <fcntl.h>
40#include <sys/stat.h>
41#include <dirent.h>
42#include <errno.h>
43#include <sys/time.h>
44#ifdef HAVE_SETXATTR
45#include <sys/xattr.h>
46#endif
47
48#include "passthrough_helpers.h"
49
50static int fill_dir_plus = 0;
51static int readdir_zero_ino;
52
53static void *xmp_init(struct fuse_conn_info *conn,
54 struct fuse_config *cfg)
55{
56 (void) conn;
57 cfg->use_ino = !readdir_zero_ino;
58
59 /* parallel_direct_writes feature depends on direct_io features.
60 To make parallel_direct_writes valid, need either set cfg->direct_io
61 in current function (recommended in high level API) or set fi->direct_io
62 in xmp_create() or xmp_open(). */
63 // cfg->direct_io = 1;
65
66 /* Pick up changes from lower filesystem right away. This is
67 also necessary for better hardlink support. When the kernel
68 calls the unlink() handler, it does not know the inode of
69 the to-be-removed entry and can therefore not invalidate
70 the cache of the associated inode - resulting in an
71 incorrect st_nlink value being reported for any remaining
72 hardlinks to this inode. */
73 if (!cfg->auto_cache) {
74 cfg->entry_timeout = 0;
75 cfg->attr_timeout = 0;
76 cfg->negative_timeout = 0;
77 }
78
79 return NULL;
80}
81
82static int xmp_getattr(const char *path, struct stat *stbuf,
83 struct fuse_file_info *fi)
84{
85 (void) fi;
86 int res;
87
88 res = lstat(path, stbuf);
89 if (res == -1)
90 return -errno;
91
92 return 0;
93}
94
95static int xmp_access(const char *path, int mask)
96{
97 int res;
98
99 res = access(path, mask);
100 if (res == -1)
101 return -errno;
102
103 return 0;
104}
105
106static int xmp_readlink(const char *path, char *buf, size_t size)
107{
108 int res;
109
110 res = readlink(path, buf, size - 1);
111 if (res == -1)
112 return -errno;
113
114 buf[res] = '\0';
115 return 0;
116}
117
118
119static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
120 off_t offset, struct fuse_file_info *fi,
121 enum fuse_readdir_flags flags)
122{
123 DIR *dp;
124 struct dirent *de;
125
126 (void) offset;
127 (void) fi;
128 (void) flags;
129
130 dp = opendir(path);
131 if (dp == NULL)
132 return -errno;
133
134 while ((de = readdir(dp)) != NULL) {
135 struct stat st;
136 if (fill_dir_plus) {
137 fstatat(dirfd(dp), de->d_name, &st,
138 AT_SYMLINK_NOFOLLOW);
139 } else {
140 memset(&st, 0, sizeof(st));
141 st.st_ino = de->d_ino;
142 st.st_mode = de->d_type << 12;
143 }
144 if (readdir_zero_ino)
145 st.st_ino = 0;
146 if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
147 break;
148 }
149
150 closedir(dp);
151 return 0;
152}
153
154static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
155{
156 int res;
157
158 res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
159 if (res == -1)
160 return -errno;
161
162 return 0;
163}
164
165static int xmp_mkdir(const char *path, mode_t mode)
166{
167 int res;
168
169 res = mkdir(path, mode);
170 if (res == -1)
171 return -errno;
172
173 return 0;
174}
175
176static int xmp_unlink(const char *path)
177{
178 int res;
179
180 res = unlink(path);
181 if (res == -1)
182 return -errno;
183
184 return 0;
185}
186
187static int xmp_rmdir(const char *path)
188{
189 int res;
190
191 res = rmdir(path);
192 if (res == -1)
193 return -errno;
194
195 return 0;
196}
197
198static int xmp_symlink(const char *from, const char *to)
199{
200 int res;
201
202 res = symlink(from, to);
203 if (res == -1)
204 return -errno;
205
206 return 0;
207}
208
209static int xmp_rename(const char *from, const char *to, unsigned int flags)
210{
211 int res;
212
213 if (flags)
214 return -EINVAL;
215
216 res = rename(from, to);
217 if (res == -1)
218 return -errno;
219
220 return 0;
221}
222
223static int xmp_link(const char *from, const char *to)
224{
225 int res;
226
227 res = link(from, to);
228 if (res == -1)
229 return -errno;
230
231 return 0;
232}
233
234static int xmp_chmod(const char *path, mode_t mode,
235 struct fuse_file_info *fi)
236{
237 (void) fi;
238 int res;
239
240 res = chmod(path, mode);
241 if (res == -1)
242 return -errno;
243
244 return 0;
245}
246
247static int xmp_chown(const char *path, uid_t uid, gid_t gid,
248 struct fuse_file_info *fi)
249{
250 (void) fi;
251 int res;
252
253 res = lchown(path, uid, gid);
254 if (res == -1)
255 return -errno;
256
257 return 0;
258}
259
260static int xmp_truncate(const char *path, off_t size,
261 struct fuse_file_info *fi)
262{
263 int res;
264
265 if (fi != NULL)
266 res = ftruncate(fi->fh, size);
267 else
268 res = truncate(path, size);
269 if (res == -1)
270 return -errno;
271
272 return 0;
273}
274
275#ifdef HAVE_UTIMENSAT
276static int xmp_utimens(const char *path, const struct timespec ts[2],
277 struct fuse_file_info *fi)
278{
279 (void) fi;
280 int res;
281
282 /* don't use utime/utimes since they follow symlinks */
283 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
284 if (res == -1)
285 return -errno;
286
287 return 0;
288}
289#endif
290
291static int xmp_create(const char *path, mode_t mode,
292 struct fuse_file_info *fi)
293{
294 int res;
295
296 res = open(path, fi->flags, mode);
297 if (res == -1)
298 return -errno;
299
300 fi->fh = res;
301 return 0;
302}
303
304static int xmp_open(const char *path, struct fuse_file_info *fi)
305{
306 int res;
307
308 res = open(path, fi->flags);
309 if (res == -1)
310 return -errno;
311
312 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
313 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
314 for writes to the same file). */
315 if (fi->flags & O_DIRECT) {
316 fi->direct_io = 1;
318 }
319
320 fi->fh = res;
321 return 0;
322}
323
324static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
325 struct fuse_file_info *fi)
326{
327 int fd;
328 int res;
329
330 if(fi == NULL)
331 fd = open(path, O_RDONLY);
332 else
333 fd = fi->fh;
334
335 if (fd == -1)
336 return -errno;
337
338 res = pread(fd, buf, size, offset);
339 if (res == -1)
340 res = -errno;
341
342 if(fi == NULL)
343 close(fd);
344 return res;
345}
346
347static int xmp_write(const char *path, const char *buf, size_t size,
348 off_t offset, struct fuse_file_info *fi)
349{
350 int fd;
351 int res;
352
353 (void) fi;
354 if(fi == NULL)
355 fd = open(path, O_WRONLY);
356 else
357 fd = fi->fh;
358
359 if (fd == -1)
360 return -errno;
361
362 res = pwrite(fd, buf, size, offset);
363 if (res == -1)
364 res = -errno;
365
366 if(fi == NULL)
367 close(fd);
368 return res;
369}
370
371static int xmp_statfs(const char *path, struct statvfs *stbuf)
372{
373 int res;
374
375 res = statvfs(path, stbuf);
376 if (res == -1)
377 return -errno;
378
379 return 0;
380}
381
382static int xmp_release(const char *path, struct fuse_file_info *fi)
383{
384 (void) path;
385 close(fi->fh);
386 return 0;
387}
388
389static int xmp_fsync(const char *path, int isdatasync,
390 struct fuse_file_info *fi)
391{
392 /* Just a stub. This method is optional and can safely be left
393 unimplemented */
394
395 (void) path;
396 (void) isdatasync;
397 (void) fi;
398 return 0;
399}
400
401static int xmp_fallocate(const char *path, int mode,
402 off_t offset, off_t length, struct fuse_file_info *fi)
403{
404 int fd;
405 int res;
406
407 (void) fi;
408
409 if(fi == NULL)
410 fd = open(path, O_WRONLY);
411 else
412 fd = fi->fh;
413
414 if (fd == -1)
415 return -errno;
416
417 res = do_fallocate(fd, mode, offset, length);
418
419 if(fi == NULL)
420 close(fd);
421 return res;
422}
423
424#ifdef HAVE_SETXATTR
425/* xattr operations are optional and can safely be left unimplemented */
426static int xmp_setxattr(const char *path, const char *name, const char *value,
427 size_t size, int flags)
428{
429 int res = lsetxattr(path, name, value, size, flags);
430 if (res == -1)
431 return -errno;
432 return 0;
433}
434
435static int xmp_getxattr(const char *path, const char *name, char *value,
436 size_t size)
437{
438 int res = lgetxattr(path, name, value, size);
439 if (res == -1)
440 return -errno;
441 return res;
442}
443
444static int xmp_listxattr(const char *path, char *list, size_t size)
445{
446 int res = llistxattr(path, list, size);
447 if (res == -1)
448 return -errno;
449 return res;
450}
451
452static int xmp_removexattr(const char *path, const char *name)
453{
454 int res = lremovexattr(path, name);
455 if (res == -1)
456 return -errno;
457 return 0;
458}
459#endif /* HAVE_SETXATTR */
460
461#ifdef HAVE_COPY_FILE_RANGE
462static ssize_t xmp_copy_file_range(const char *path_in,
463 struct fuse_file_info *fi_in,
464 off_t offset_in, const char *path_out,
465 struct fuse_file_info *fi_out,
466 off_t offset_out, size_t len, int flags)
467{
468 int fd_in, fd_out;
469 ssize_t res;
470
471 if(fi_in == NULL)
472 fd_in = open(path_in, O_RDONLY);
473 else
474 fd_in = fi_in->fh;
475
476 if (fd_in == -1)
477 return -errno;
478
479 if(fi_out == NULL)
480 fd_out = open(path_out, O_WRONLY);
481 else
482 fd_out = fi_out->fh;
483
484 if (fd_out == -1) {
485 close(fd_in);
486 return -errno;
487 }
488
489 res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
490 flags);
491 if (res == -1)
492 res = -errno;
493
494 if (fi_out == NULL)
495 close(fd_out);
496 if (fi_in == NULL)
497 close(fd_in);
498
499 return res;
500}
501#endif
502
503static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
504{
505 int fd;
506 off_t res;
507
508 if (fi == NULL)
509 fd = open(path, O_RDONLY);
510 else
511 fd = fi->fh;
512
513 if (fd == -1)
514 return -errno;
515
516 res = lseek(fd, off, whence);
517 if (res == -1)
518 res = -errno;
519
520 if (fi == NULL)
521 close(fd);
522 return res;
523}
524
525#ifdef HAVE_STATX
526static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
527 struct fuse_file_info *fi)
528{
529 int fd = -1;
530 int res;
531
532 if (fi)
533 fd = fi->fh;
534
535 res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
536 if (res == -1)
537 return -errno;
538
539 return 0;
540}
541#endif
542
543static const struct fuse_operations xmp_oper = {
544 .init = xmp_init,
545 .getattr = xmp_getattr,
546 .access = xmp_access,
547 .readlink = xmp_readlink,
548 .readdir = xmp_readdir,
549 .mknod = xmp_mknod,
550 .mkdir = xmp_mkdir,
551 .symlink = xmp_symlink,
552 .unlink = xmp_unlink,
553 .rmdir = xmp_rmdir,
554 .rename = xmp_rename,
555 .link = xmp_link,
556 .chmod = xmp_chmod,
557 .chown = xmp_chown,
558 .truncate = xmp_truncate,
559#ifdef HAVE_UTIMENSAT
560 .utimens = xmp_utimens,
561#endif
562 .open = xmp_open,
563 .create = xmp_create,
564 .read = xmp_read,
565 .write = xmp_write,
566 .statfs = xmp_statfs,
567 .release = xmp_release,
568 .fsync = xmp_fsync,
569 .fallocate = xmp_fallocate,
570#ifdef HAVE_SETXATTR
571 .setxattr = xmp_setxattr,
572 .getxattr = xmp_getxattr,
573 .listxattr = xmp_listxattr,
574 .removexattr = xmp_removexattr,
575#endif
576#ifdef HAVE_COPY_FILE_RANGE
577 .copy_file_range = xmp_copy_file_range,
578#endif
579 .lseek = xmp_lseek,
580#ifdef HAVE_STATX
581 .statx = xmp_statx,
582#endif
583};
584
585int main(int argc, char *argv[])
586{
587 enum { MAX_ARGS = 10 };
588 int i,new_argc;
589 char *new_argv[MAX_ARGS];
590
591 umask(0);
592 /* Process the "--plus" option apart */
593 for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
594 if (!strcmp(argv[i], "--plus")) {
595 fill_dir_plus = FUSE_FILL_DIR_PLUS;
596 } else if (!strcmp(argv[i], "--readdir-zero-inodes")) {
597 // Return zero inodes from readdir
598 readdir_zero_ino = 1;
599 } else {
600 new_argv[new_argc++] = argv[i];
601 }
602 }
603 return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
604}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_readdir_flags
Definition fuse.h:42
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t auto_cache
Definition fuse.h:253
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/example_2passthrough__fh_8c_source.html0000644000175000017500000034602615156613442023070 0ustar berndbernd libfuse: example/passthrough_fh.c Source File
libfuse
passthrough_fh.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
26#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
27
28#define _GNU_SOURCE
29
30#include <fuse.h>
31
32#ifdef HAVE_LIBULOCKMGR
33#include <ulockmgr.h>
34#endif
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <sys/stat.h>
42#include <dirent.h>
43#include <errno.h>
44#include <sys/time.h>
45#ifdef HAVE_SETXATTR
46#include <sys/xattr.h>
47#endif
48#include <sys/file.h> /* flock(2) */
49
50#include "passthrough_helpers.h"
51
52static void *xmp_init(struct fuse_conn_info *conn,
53 struct fuse_config *cfg)
54{
55 (void) conn;
56 cfg->use_ino = 1;
57 cfg->nullpath_ok = 1;
58
59 /* parallel_direct_writes feature depends on direct_io features.
60 To make parallel_direct_writes valid, need either set cfg->direct_io
61 in current function (recommended in high level API) or set fi->direct_io
62 in xmp_create() or xmp_open(). */
63 // cfg->direct_io = 1;
65
66 /* Pick up changes from lower filesystem right away. This is
67 also necessary for better hardlink support. When the kernel
68 calls the unlink() handler, it does not know the inode of
69 the to-be-removed entry and can therefore not invalidate
70 the cache of the associated inode - resulting in an
71 incorrect st_nlink value being reported for any remaining
72 hardlinks to this inode. */
73 cfg->entry_timeout = 0;
74 cfg->attr_timeout = 0;
75 cfg->negative_timeout = 0;
76
77 return NULL;
78}
79
80static int xmp_getattr(const char *path, struct stat *stbuf,
81 struct fuse_file_info *fi)
82{
83 int res;
84
85 (void) path;
86
87 if(fi)
88 res = fstat(fi->fh, stbuf);
89 else
90 res = lstat(path, stbuf);
91 if (res == -1)
92 return -errno;
93
94 return 0;
95}
96
97static int xmp_access(const char *path, int mask)
98{
99 int res;
100
101 res = access(path, mask);
102 if (res == -1)
103 return -errno;
104
105 return 0;
106}
107
108static int xmp_readlink(const char *path, char *buf, size_t size)
109{
110 int res;
111
112 res = readlink(path, buf, size - 1);
113 if (res == -1)
114 return -errno;
115
116 buf[res] = '\0';
117 return 0;
118}
119
120struct xmp_dirp {
121 DIR *dp;
122 struct dirent *entry;
123 off_t offset;
124};
125
126static int xmp_opendir(const char *path, struct fuse_file_info *fi)
127{
128 int res;
129 struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
130 if (d == NULL)
131 return -ENOMEM;
132
133 d->dp = opendir(path);
134 if (d->dp == NULL) {
135 res = -errno;
136 free(d);
137 return res;
138 }
139 d->offset = 0;
140 d->entry = NULL;
141
142 fi->fh = (unsigned long) d;
143 return 0;
144}
145
146static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
147{
148 return (struct xmp_dirp *) (uintptr_t) fi->fh;
149}
150
151static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
152 off_t offset, struct fuse_file_info *fi,
153 enum fuse_readdir_flags flags)
154{
155 struct xmp_dirp *d = get_dirp(fi);
156
157 (void) path;
158 if (offset != d->offset) {
159#ifndef __FreeBSD__
160 seekdir(d->dp, offset);
161#else
162 /* Subtract the one that we add when calling
163 telldir() below */
164 seekdir(d->dp, offset-1);
165#endif
166 d->entry = NULL;
167 d->offset = offset;
168 }
169 while (1) {
170 struct stat st;
171 off_t nextoff;
173
174 if (!d->entry) {
175 d->entry = readdir(d->dp);
176 if (!d->entry)
177 break;
178 }
179#ifdef HAVE_FSTATAT
180 if (flags & FUSE_READDIR_PLUS) {
181 int res;
182
183 res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
184 AT_SYMLINK_NOFOLLOW);
185 if (res != -1)
186 fill_flags |= FUSE_FILL_DIR_PLUS;
187 }
188#endif
189 if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
190 memset(&st, 0, sizeof(st));
191 st.st_ino = d->entry->d_ino;
192 st.st_mode = d->entry->d_type << 12;
193 }
194 nextoff = telldir(d->dp);
195#ifdef __FreeBSD__
196 /* Under FreeBSD, telldir() may return 0 the first time
197 it is called. But for libfuse, an offset of zero
198 means that offsets are not supported, so we shift
199 everything by one. */
200 nextoff++;
201#endif
202 if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
203 break;
204
205 d->entry = NULL;
206 d->offset = nextoff;
207 }
208
209 return 0;
210}
211
212static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
213{
214 struct xmp_dirp *d = get_dirp(fi);
215 (void) path;
216 closedir(d->dp);
217 free(d);
218 return 0;
219}
220
221static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
222{
223 int res;
224
225 if (S_ISFIFO(mode))
226 res = mkfifo(path, mode);
227 else
228 res = mknod(path, mode, rdev);
229 if (res == -1)
230 return -errno;
231
232 return 0;
233}
234
235static int xmp_mkdir(const char *path, mode_t mode)
236{
237 int res;
238
239 res = mkdir(path, mode);
240 if (res == -1)
241 return -errno;
242
243 return 0;
244}
245
246static int xmp_unlink(const char *path)
247{
248 int res;
249
250 res = unlink(path);
251 if (res == -1)
252 return -errno;
253
254 return 0;
255}
256
257static int xmp_rmdir(const char *path)
258{
259 int res;
260
261 res = rmdir(path);
262 if (res == -1)
263 return -errno;
264
265 return 0;
266}
267
268static int xmp_symlink(const char *from, const char *to)
269{
270 int res;
271
272 res = symlink(from, to);
273 if (res == -1)
274 return -errno;
275
276 return 0;
277}
278
279static int xmp_rename(const char *from, const char *to, unsigned int flags)
280{
281 int res;
282
283 /* When we have renameat2() in libc, then we can implement flags */
284 if (flags)
285 return -EINVAL;
286
287 res = rename(from, to);
288 if (res == -1)
289 return -errno;
290
291 return 0;
292}
293
294static int xmp_link(const char *from, const char *to)
295{
296 int res;
297
298 res = link(from, to);
299 if (res == -1)
300 return -errno;
301
302 return 0;
303}
304
305static int xmp_chmod(const char *path, mode_t mode,
306 struct fuse_file_info *fi)
307{
308 int res;
309
310 if(fi)
311 res = fchmod(fi->fh, mode);
312 else
313 res = chmod(path, mode);
314 if (res == -1)
315 return -errno;
316
317 return 0;
318}
319
320static int xmp_chown(const char *path, uid_t uid, gid_t gid,
321 struct fuse_file_info *fi)
322{
323 int res;
324
325 if (fi)
326 res = fchown(fi->fh, uid, gid);
327 else
328 res = lchown(path, uid, gid);
329 if (res == -1)
330 return -errno;
331
332 return 0;
333}
334
335static int xmp_truncate(const char *path, off_t size,
336 struct fuse_file_info *fi)
337{
338 int res;
339
340 if(fi)
341 res = ftruncate(fi->fh, size);
342 else
343 res = truncate(path, size);
344
345 if (res == -1)
346 return -errno;
347
348 return 0;
349}
350
351#ifdef HAVE_UTIMENSAT
352static int xmp_utimens(const char *path, const struct timespec ts[2],
353 struct fuse_file_info *fi)
354{
355 int res;
356
357 /* don't use utime/utimes since they follow symlinks */
358 if (fi)
359 res = futimens(fi->fh, ts);
360 else
361 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
362 if (res == -1)
363 return -errno;
364
365 return 0;
366}
367#endif
368
369static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
370{
371 int fd;
372
373 fd = open(path, fi->flags, mode);
374 if (fd == -1)
375 return -errno;
376
377 fi->fh = fd;
378 return 0;
379}
380
381static int xmp_open(const char *path, struct fuse_file_info *fi)
382{
383 int fd;
384
385 fd = open(path, fi->flags);
386 if (fd == -1)
387 return -errno;
388
389 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
390 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
391 for writes to the same file). */
392 if (fi->flags & O_DIRECT) {
393 fi->direct_io = 1;
395 }
396
397 fi->fh = fd;
398 return 0;
399}
400
401static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
402 struct fuse_file_info *fi)
403{
404 int res;
405
406 (void) path;
407 res = pread(fi->fh, buf, size, offset);
408 if (res == -1)
409 res = -errno;
410
411 return res;
412}
413
414static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
415 size_t size, off_t offset, struct fuse_file_info *fi)
416{
417 struct fuse_bufvec *src;
418
419 (void) path;
420
421 src = malloc(sizeof(struct fuse_bufvec));
422 if (src == NULL)
423 return -ENOMEM;
424
425 *src = FUSE_BUFVEC_INIT(size);
426
428 src->buf[0].fd = fi->fh;
429 src->buf[0].pos = offset;
430
431 *bufp = src;
432
433 return 0;
434}
435
436static int xmp_write(const char *path, const char *buf, size_t size,
437 off_t offset, struct fuse_file_info *fi)
438{
439 int res;
440
441 (void) path;
442 res = pwrite(fi->fh, buf, size, offset);
443 if (res == -1)
444 res = -errno;
445
446 return res;
447}
448
449static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
450 off_t offset, struct fuse_file_info *fi)
451{
452 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
453
454 (void) path;
455
457 dst.buf[0].fd = fi->fh;
458 dst.buf[0].pos = offset;
459
461}
462
463static int xmp_statfs(const char *path, struct statvfs *stbuf)
464{
465 int res;
466
467 res = statvfs(path, stbuf);
468 if (res == -1)
469 return -errno;
470
471 return 0;
472}
473
474static int xmp_flush(const char *path, struct fuse_file_info *fi)
475{
476 int res;
477
478 (void) path;
479 /* This is called from every close on an open file, so call the
480 close on the underlying filesystem. But since flush may be
481 called multiple times for an open file, this must not really
482 close the file. This is important if used on a network
483 filesystem like NFS which flush the data/metadata on close() */
484 res = close(dup(fi->fh));
485 if (res == -1)
486 return -errno;
487
488 return 0;
489}
490
491static int xmp_release(const char *path, struct fuse_file_info *fi)
492{
493 (void) path;
494 close(fi->fh);
495
496 return 0;
497}
498
499static int xmp_fsync(const char *path, int isdatasync,
500 struct fuse_file_info *fi)
501{
502 int res;
503 (void) path;
504
505#ifndef HAVE_FDATASYNC
506 (void) isdatasync;
507#else
508 if (isdatasync)
509 res = fdatasync(fi->fh);
510 else
511#endif
512 res = fsync(fi->fh);
513 if (res == -1)
514 return -errno;
515
516 return 0;
517}
518
519static int xmp_fallocate(const char *path, int mode,
520 off_t offset, off_t length, struct fuse_file_info *fi)
521{
522 (void) path;
523
524 return do_fallocate(fi->fh, mode, offset, length);
525}
526
527#ifdef HAVE_SETXATTR
528/* xattr operations are optional and can safely be left unimplemented */
529static int xmp_setxattr(const char *path, const char *name, const char *value,
530 size_t size, int flags)
531{
532 int res = lsetxattr(path, name, value, size, flags);
533 if (res == -1)
534 return -errno;
535 return 0;
536}
537
538static int xmp_getxattr(const char *path, const char *name, char *value,
539 size_t size)
540{
541 int res = lgetxattr(path, name, value, size);
542 if (res == -1)
543 return -errno;
544 return res;
545}
546
547static int xmp_listxattr(const char *path, char *list, size_t size)
548{
549 int res = llistxattr(path, list, size);
550 if (res == -1)
551 return -errno;
552 return res;
553}
554
555static int xmp_removexattr(const char *path, const char *name)
556{
557 int res = lremovexattr(path, name);
558 if (res == -1)
559 return -errno;
560 return 0;
561}
562#endif /* HAVE_SETXATTR */
563
564#ifdef HAVE_LIBULOCKMGR
565static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
566 struct flock *lock)
567{
568 (void) path;
569
570 return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
571 sizeof(fi->lock_owner));
572}
573#endif
574
575static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
576{
577 int res;
578 (void) path;
579
580 res = flock(fi->fh, op);
581 if (res == -1)
582 return -errno;
583
584 return 0;
585}
586
587#ifdef HAVE_COPY_FILE_RANGE
588static ssize_t xmp_copy_file_range(const char *path_in,
589 struct fuse_file_info *fi_in,
590 off_t off_in, const char *path_out,
591 struct fuse_file_info *fi_out,
592 off_t off_out, size_t len, int flags)
593{
594 ssize_t res;
595 (void) path_in;
596 (void) path_out;
597
598 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
599 flags);
600 if (res == -1)
601 return -errno;
602
603 return res;
604}
605#endif
606
607static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
608{
609 off_t res;
610 (void) path;
611
612 res = lseek(fi->fh, off, whence);
613 if (res == -1)
614 return -errno;
615
616 return res;
617}
618
619#ifdef HAVE_STATX
620static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
621 struct fuse_file_info *fi)
622{
623 int fd = -1;
624 int res;
625
626 if (fi)
627 fd = fi->fh;
628
629 res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
630 if (res == -1)
631 return -errno;
632
633 return 0;
634}
635#endif
636
637static const struct fuse_operations xmp_oper = {
638 .init = xmp_init,
639 .getattr = xmp_getattr,
640 .access = xmp_access,
641 .readlink = xmp_readlink,
642 .opendir = xmp_opendir,
643 .readdir = xmp_readdir,
644 .releasedir = xmp_releasedir,
645 .mknod = xmp_mknod,
646 .mkdir = xmp_mkdir,
647 .symlink = xmp_symlink,
648 .unlink = xmp_unlink,
649 .rmdir = xmp_rmdir,
650 .rename = xmp_rename,
651 .link = xmp_link,
652 .chmod = xmp_chmod,
653 .chown = xmp_chown,
654 .truncate = xmp_truncate,
655#ifdef HAVE_UTIMENSAT
656 .utimens = xmp_utimens,
657#endif
658 .create = xmp_create,
659 .open = xmp_open,
660 .read = xmp_read,
661 .read_buf = xmp_read_buf,
662 .write = xmp_write,
663 .write_buf = xmp_write_buf,
664 .statfs = xmp_statfs,
665 .flush = xmp_flush,
666 .release = xmp_release,
667 .fsync = xmp_fsync,
668 .fallocate = xmp_fallocate,
669#ifdef HAVE_SETXATTR
670 .setxattr = xmp_setxattr,
671 .getxattr = xmp_getxattr,
672 .listxattr = xmp_listxattr,
673 .removexattr = xmp_removexattr,
674#endif
675#ifdef HAVE_LIBULOCKMGR
676 .lock = xmp_lock,
677#endif
678 .flock = xmp_flock,
679#ifdef HAVE_COPY_FILE_RANGE
680 .copy_file_range = xmp_copy_file_range,
681#endif
682 .lseek = xmp_lseek,
683#ifdef HAVE_STATX
684 .statx = xmp_statx,
685#endif
686};
687
688int main(int argc, char *argv[])
689{
690 umask(0);
691 return fuse_main(argc, argv, &xmp_oper, NULL);
692}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/fuse-3_817_84_2example_2passthrough__fh_8c_source.html0000644000175000017500000034143715156613442025247 0ustar berndbernd libfuse: fuse-3.17.4/example/passthrough_fh.c Source File
libfuse
passthrough_fh.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file COPYING.
8*/
9
26#define FUSE_USE_VERSION 31
27
28#define _GNU_SOURCE
29
30#include <fuse.h>
31
32#ifdef HAVE_LIBULOCKMGR
33#include <ulockmgr.h>
34#endif
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <sys/stat.h>
42#include <dirent.h>
43#include <errno.h>
44#include <sys/time.h>
45#ifdef HAVE_SETXATTR
46#include <sys/xattr.h>
47#endif
48#include <sys/file.h> /* flock(2) */
49
50static void *xmp_init(struct fuse_conn_info *conn,
51 struct fuse_config *cfg)
52{
53 (void) conn;
54 cfg->use_ino = 1;
55 cfg->nullpath_ok = 1;
56
57 /* parallel_direct_writes feature depends on direct_io features.
58 To make parallel_direct_writes valid, need either set cfg->direct_io
59 in current function (recommended in high level API) or set fi->direct_io
60 in xmp_create() or xmp_open(). */
61 // cfg->direct_io = 1;
63
64 /* Pick up changes from lower filesystem right away. This is
65 also necessary for better hardlink support. When the kernel
66 calls the unlink() handler, it does not know the inode of
67 the to-be-removed entry and can therefore not invalidate
68 the cache of the associated inode - resulting in an
69 incorrect st_nlink value being reported for any remaining
70 hardlinks to this inode. */
71 cfg->entry_timeout = 0;
72 cfg->attr_timeout = 0;
73 cfg->negative_timeout = 0;
74
75 return NULL;
76}
77
78static int xmp_getattr(const char *path, struct stat *stbuf,
79 struct fuse_file_info *fi)
80{
81 int res;
82
83 (void) path;
84
85 if(fi)
86 res = fstat(fi->fh, stbuf);
87 else
88 res = lstat(path, stbuf);
89 if (res == -1)
90 return -errno;
91
92 return 0;
93}
94
95static int xmp_access(const char *path, int mask)
96{
97 int res;
98
99 res = access(path, mask);
100 if (res == -1)
101 return -errno;
102
103 return 0;
104}
105
106static int xmp_readlink(const char *path, char *buf, size_t size)
107{
108 int res;
109
110 res = readlink(path, buf, size - 1);
111 if (res == -1)
112 return -errno;
113
114 buf[res] = '\0';
115 return 0;
116}
117
118struct xmp_dirp {
119 DIR *dp;
120 struct dirent *entry;
121 off_t offset;
122};
123
124static int xmp_opendir(const char *path, struct fuse_file_info *fi)
125{
126 int res;
127 struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
128 if (d == NULL)
129 return -ENOMEM;
130
131 d->dp = opendir(path);
132 if (d->dp == NULL) {
133 res = -errno;
134 free(d);
135 return res;
136 }
137 d->offset = 0;
138 d->entry = NULL;
139
140 fi->fh = (unsigned long) d;
141 return 0;
142}
143
144static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
145{
146 return (struct xmp_dirp *) (uintptr_t) fi->fh;
147}
148
149static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
150 off_t offset, struct fuse_file_info *fi,
151 enum fuse_readdir_flags flags)
152{
153 struct xmp_dirp *d = get_dirp(fi);
154
155 (void) path;
156 if (offset != d->offset) {
157#ifndef __FreeBSD__
158 seekdir(d->dp, offset);
159#else
160 /* Subtract the one that we add when calling
161 telldir() below */
162 seekdir(d->dp, offset-1);
163#endif
164 d->entry = NULL;
165 d->offset = offset;
166 }
167 while (1) {
168 struct stat st;
169 off_t nextoff;
171
172 if (!d->entry) {
173 d->entry = readdir(d->dp);
174 if (!d->entry)
175 break;
176 }
177#ifdef HAVE_FSTATAT
178 if (flags & FUSE_READDIR_PLUS) {
179 int res;
180
181 res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
182 AT_SYMLINK_NOFOLLOW);
183 if (res != -1)
184 fill_flags |= FUSE_FILL_DIR_PLUS;
185 }
186#endif
187 if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
188 memset(&st, 0, sizeof(st));
189 st.st_ino = d->entry->d_ino;
190 st.st_mode = d->entry->d_type << 12;
191 }
192 nextoff = telldir(d->dp);
193#ifdef __FreeBSD__
194 /* Under FreeBSD, telldir() may return 0 the first time
195 it is called. But for libfuse, an offset of zero
196 means that offsets are not supported, so we shift
197 everything by one. */
198 nextoff++;
199#endif
200 if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
201 break;
202
203 d->entry = NULL;
204 d->offset = nextoff;
205 }
206
207 return 0;
208}
209
210static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
211{
212 struct xmp_dirp *d = get_dirp(fi);
213 (void) path;
214 closedir(d->dp);
215 free(d);
216 return 0;
217}
218
219static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
220{
221 int res;
222
223 if (S_ISFIFO(mode))
224 res = mkfifo(path, mode);
225 else
226 res = mknod(path, mode, rdev);
227 if (res == -1)
228 return -errno;
229
230 return 0;
231}
232
233static int xmp_mkdir(const char *path, mode_t mode)
234{
235 int res;
236
237 res = mkdir(path, mode);
238 if (res == -1)
239 return -errno;
240
241 return 0;
242}
243
244static int xmp_unlink(const char *path)
245{
246 int res;
247
248 res = unlink(path);
249 if (res == -1)
250 return -errno;
251
252 return 0;
253}
254
255static int xmp_rmdir(const char *path)
256{
257 int res;
258
259 res = rmdir(path);
260 if (res == -1)
261 return -errno;
262
263 return 0;
264}
265
266static int xmp_symlink(const char *from, const char *to)
267{
268 int res;
269
270 res = symlink(from, to);
271 if (res == -1)
272 return -errno;
273
274 return 0;
275}
276
277static int xmp_rename(const char *from, const char *to, unsigned int flags)
278{
279 int res;
280
281 /* When we have renameat2() in libc, then we can implement flags */
282 if (flags)
283 return -EINVAL;
284
285 res = rename(from, to);
286 if (res == -1)
287 return -errno;
288
289 return 0;
290}
291
292static int xmp_link(const char *from, const char *to)
293{
294 int res;
295
296 res = link(from, to);
297 if (res == -1)
298 return -errno;
299
300 return 0;
301}
302
303static int xmp_chmod(const char *path, mode_t mode,
304 struct fuse_file_info *fi)
305{
306 int res;
307
308 if(fi)
309 res = fchmod(fi->fh, mode);
310 else
311 res = chmod(path, mode);
312 if (res == -1)
313 return -errno;
314
315 return 0;
316}
317
318static int xmp_chown(const char *path, uid_t uid, gid_t gid,
319 struct fuse_file_info *fi)
320{
321 int res;
322
323 if (fi)
324 res = fchown(fi->fh, uid, gid);
325 else
326 res = lchown(path, uid, gid);
327 if (res == -1)
328 return -errno;
329
330 return 0;
331}
332
333static int xmp_truncate(const char *path, off_t size,
334 struct fuse_file_info *fi)
335{
336 int res;
337
338 if(fi)
339 res = ftruncate(fi->fh, size);
340 else
341 res = truncate(path, size);
342
343 if (res == -1)
344 return -errno;
345
346 return 0;
347}
348
349#ifdef HAVE_UTIMENSAT
350static int xmp_utimens(const char *path, const struct timespec ts[2],
351 struct fuse_file_info *fi)
352{
353 int res;
354
355 /* don't use utime/utimes since they follow symlinks */
356 if (fi)
357 res = futimens(fi->fh, ts);
358 else
359 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
360 if (res == -1)
361 return -errno;
362
363 return 0;
364}
365#endif
366
367static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
368{
369 int fd;
370
371 fd = open(path, fi->flags, mode);
372 if (fd == -1)
373 return -errno;
374
375 fi->fh = fd;
376 return 0;
377}
378
379static int xmp_open(const char *path, struct fuse_file_info *fi)
380{
381 int fd;
382
383 fd = open(path, fi->flags);
384 if (fd == -1)
385 return -errno;
386
387 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
388 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
389 for writes to the same file). */
390 if (fi->flags & O_DIRECT) {
391 fi->direct_io = 1;
393 }
394
395 fi->fh = fd;
396 return 0;
397}
398
399static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
400 struct fuse_file_info *fi)
401{
402 int res;
403
404 (void) path;
405 res = pread(fi->fh, buf, size, offset);
406 if (res == -1)
407 res = -errno;
408
409 return res;
410}
411
412static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
413 size_t size, off_t offset, struct fuse_file_info *fi)
414{
415 struct fuse_bufvec *src;
416
417 (void) path;
418
419 src = malloc(sizeof(struct fuse_bufvec));
420 if (src == NULL)
421 return -ENOMEM;
422
423 *src = FUSE_BUFVEC_INIT(size);
424
426 src->buf[0].fd = fi->fh;
427 src->buf[0].pos = offset;
428
429 *bufp = src;
430
431 return 0;
432}
433
434static int xmp_write(const char *path, const char *buf, size_t size,
435 off_t offset, struct fuse_file_info *fi)
436{
437 int res;
438
439 (void) path;
440 res = pwrite(fi->fh, buf, size, offset);
441 if (res == -1)
442 res = -errno;
443
444 return res;
445}
446
447static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
448 off_t offset, struct fuse_file_info *fi)
449{
450 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
451
452 (void) path;
453
455 dst.buf[0].fd = fi->fh;
456 dst.buf[0].pos = offset;
457
459}
460
461static int xmp_statfs(const char *path, struct statvfs *stbuf)
462{
463 int res;
464
465 res = statvfs(path, stbuf);
466 if (res == -1)
467 return -errno;
468
469 return 0;
470}
471
472static int xmp_flush(const char *path, struct fuse_file_info *fi)
473{
474 int res;
475
476 (void) path;
477 /* This is called from every close on an open file, so call the
478 close on the underlying filesystem. But since flush may be
479 called multiple times for an open file, this must not really
480 close the file. This is important if used on a network
481 filesystem like NFS which flush the data/metadata on close() */
482 res = close(dup(fi->fh));
483 if (res == -1)
484 return -errno;
485
486 return 0;
487}
488
489static int xmp_release(const char *path, struct fuse_file_info *fi)
490{
491 (void) path;
492 close(fi->fh);
493
494 return 0;
495}
496
497static int xmp_fsync(const char *path, int isdatasync,
498 struct fuse_file_info *fi)
499{
500 int res;
501 (void) path;
502
503#ifndef HAVE_FDATASYNC
504 (void) isdatasync;
505#else
506 if (isdatasync)
507 res = fdatasync(fi->fh);
508 else
509#endif
510 res = fsync(fi->fh);
511 if (res == -1)
512 return -errno;
513
514 return 0;
515}
516
517#ifdef HAVE_POSIX_FALLOCATE
518static int xmp_fallocate(const char *path, int mode,
519 off_t offset, off_t length, struct fuse_file_info *fi)
520{
521 (void) path;
522
523 if (mode)
524 return -EOPNOTSUPP;
525
526 return -posix_fallocate(fi->fh, offset, length);
527}
528#endif
529
530#ifdef HAVE_SETXATTR
531/* xattr operations are optional and can safely be left unimplemented */
532static int xmp_setxattr(const char *path, const char *name, const char *value,
533 size_t size, int flags)
534{
535 int res = lsetxattr(path, name, value, size, flags);
536 if (res == -1)
537 return -errno;
538 return 0;
539}
540
541static int xmp_getxattr(const char *path, const char *name, char *value,
542 size_t size)
543{
544 int res = lgetxattr(path, name, value, size);
545 if (res == -1)
546 return -errno;
547 return res;
548}
549
550static int xmp_listxattr(const char *path, char *list, size_t size)
551{
552 int res = llistxattr(path, list, size);
553 if (res == -1)
554 return -errno;
555 return res;
556}
557
558static int xmp_removexattr(const char *path, const char *name)
559{
560 int res = lremovexattr(path, name);
561 if (res == -1)
562 return -errno;
563 return 0;
564}
565#endif /* HAVE_SETXATTR */
566
567#ifdef HAVE_LIBULOCKMGR
568static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
569 struct flock *lock)
570{
571 (void) path;
572
573 return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
574 sizeof(fi->lock_owner));
575}
576#endif
577
578static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
579{
580 int res;
581 (void) path;
582
583 res = flock(fi->fh, op);
584 if (res == -1)
585 return -errno;
586
587 return 0;
588}
589
590#ifdef HAVE_COPY_FILE_RANGE
591static ssize_t xmp_copy_file_range(const char *path_in,
592 struct fuse_file_info *fi_in,
593 off_t off_in, const char *path_out,
594 struct fuse_file_info *fi_out,
595 off_t off_out, size_t len, int flags)
596{
597 ssize_t res;
598 (void) path_in;
599 (void) path_out;
600
601 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
602 flags);
603 if (res == -1)
604 return -errno;
605
606 return res;
607}
608#endif
609
610static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
611{
612 off_t res;
613 (void) path;
614
615 res = lseek(fi->fh, off, whence);
616 if (res == -1)
617 return -errno;
618
619 return res;
620}
621
622static const struct fuse_operations xmp_oper = {
623 .init = xmp_init,
624 .getattr = xmp_getattr,
625 .access = xmp_access,
626 .readlink = xmp_readlink,
627 .opendir = xmp_opendir,
628 .readdir = xmp_readdir,
629 .releasedir = xmp_releasedir,
630 .mknod = xmp_mknod,
631 .mkdir = xmp_mkdir,
632 .symlink = xmp_symlink,
633 .unlink = xmp_unlink,
634 .rmdir = xmp_rmdir,
635 .rename = xmp_rename,
636 .link = xmp_link,
637 .chmod = xmp_chmod,
638 .chown = xmp_chown,
639 .truncate = xmp_truncate,
640#ifdef HAVE_UTIMENSAT
641 .utimens = xmp_utimens,
642#endif
643 .create = xmp_create,
644 .open = xmp_open,
645 .read = xmp_read,
646 .read_buf = xmp_read_buf,
647 .write = xmp_write,
648 .write_buf = xmp_write_buf,
649 .statfs = xmp_statfs,
650 .flush = xmp_flush,
651 .release = xmp_release,
652 .fsync = xmp_fsync,
653#ifdef HAVE_POSIX_FALLOCATE
654 .fallocate = xmp_fallocate,
655#endif
656#ifdef HAVE_SETXATTR
657 .setxattr = xmp_setxattr,
658 .getxattr = xmp_getxattr,
659 .listxattr = xmp_listxattr,
660 .removexattr = xmp_removexattr,
661#endif
662#ifdef HAVE_LIBULOCKMGR
663 .lock = xmp_lock,
664#endif
665 .flock = xmp_flock,
666#ifdef HAVE_COPY_FILE_RANGE
667 .copy_file_range = xmp_copy_file_range,
668#endif
669 .lseek = xmp_lseek,
670};
671
672int main(int argc, char *argv[])
673{
674 umask(0);
675 return fuse_main(argc, argv, &xmp_oper, NULL);
676}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2passthrough__fh_8c_source.html0000644000175000017500000034623015156613442025241 0ustar berndbernd libfuse: fuse-3.18.1/example/passthrough_fh.c Source File
libfuse
passthrough_fh.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
26#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
27
28#define _GNU_SOURCE
29
30#include <fuse.h>
31
32#ifdef HAVE_LIBULOCKMGR
33#include <ulockmgr.h>
34#endif
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <sys/stat.h>
42#include <dirent.h>
43#include <errno.h>
44#include <sys/time.h>
45#ifdef HAVE_SETXATTR
46#include <sys/xattr.h>
47#endif
48#include <sys/file.h> /* flock(2) */
49
50#include "passthrough_helpers.h"
51
52static void *xmp_init(struct fuse_conn_info *conn,
53 struct fuse_config *cfg)
54{
55 (void) conn;
56 cfg->use_ino = 1;
57 cfg->nullpath_ok = 1;
58
59 /* parallel_direct_writes feature depends on direct_io features.
60 To make parallel_direct_writes valid, need either set cfg->direct_io
61 in current function (recommended in high level API) or set fi->direct_io
62 in xmp_create() or xmp_open(). */
63 // cfg->direct_io = 1;
65
66 /* Pick up changes from lower filesystem right away. This is
67 also necessary for better hardlink support. When the kernel
68 calls the unlink() handler, it does not know the inode of
69 the to-be-removed entry and can therefore not invalidate
70 the cache of the associated inode - resulting in an
71 incorrect st_nlink value being reported for any remaining
72 hardlinks to this inode. */
73 cfg->entry_timeout = 0;
74 cfg->attr_timeout = 0;
75 cfg->negative_timeout = 0;
76
77 return NULL;
78}
79
80static int xmp_getattr(const char *path, struct stat *stbuf,
81 struct fuse_file_info *fi)
82{
83 int res;
84
85 (void) path;
86
87 if(fi)
88 res = fstat(fi->fh, stbuf);
89 else
90 res = lstat(path, stbuf);
91 if (res == -1)
92 return -errno;
93
94 return 0;
95}
96
97static int xmp_access(const char *path, int mask)
98{
99 int res;
100
101 res = access(path, mask);
102 if (res == -1)
103 return -errno;
104
105 return 0;
106}
107
108static int xmp_readlink(const char *path, char *buf, size_t size)
109{
110 int res;
111
112 res = readlink(path, buf, size - 1);
113 if (res == -1)
114 return -errno;
115
116 buf[res] = '\0';
117 return 0;
118}
119
120struct xmp_dirp {
121 DIR *dp;
122 struct dirent *entry;
123 off_t offset;
124};
125
126static int xmp_opendir(const char *path, struct fuse_file_info *fi)
127{
128 int res;
129 struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
130 if (d == NULL)
131 return -ENOMEM;
132
133 d->dp = opendir(path);
134 if (d->dp == NULL) {
135 res = -errno;
136 free(d);
137 return res;
138 }
139 d->offset = 0;
140 d->entry = NULL;
141
142 fi->fh = (unsigned long) d;
143 return 0;
144}
145
146static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
147{
148 return (struct xmp_dirp *) (uintptr_t) fi->fh;
149}
150
151static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
152 off_t offset, struct fuse_file_info *fi,
153 enum fuse_readdir_flags flags)
154{
155 struct xmp_dirp *d = get_dirp(fi);
156
157 (void) path;
158 if (offset != d->offset) {
159#ifndef __FreeBSD__
160 seekdir(d->dp, offset);
161#else
162 /* Subtract the one that we add when calling
163 telldir() below */
164 seekdir(d->dp, offset-1);
165#endif
166 d->entry = NULL;
167 d->offset = offset;
168 }
169 while (1) {
170 struct stat st;
171 off_t nextoff;
173
174 if (!d->entry) {
175 d->entry = readdir(d->dp);
176 if (!d->entry)
177 break;
178 }
179#ifdef HAVE_FSTATAT
180 if (flags & FUSE_READDIR_PLUS) {
181 int res;
182
183 res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
184 AT_SYMLINK_NOFOLLOW);
185 if (res != -1)
186 fill_flags |= FUSE_FILL_DIR_PLUS;
187 }
188#endif
189 if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
190 memset(&st, 0, sizeof(st));
191 st.st_ino = d->entry->d_ino;
192 st.st_mode = d->entry->d_type << 12;
193 }
194 nextoff = telldir(d->dp);
195#ifdef __FreeBSD__
196 /* Under FreeBSD, telldir() may return 0 the first time
197 it is called. But for libfuse, an offset of zero
198 means that offsets are not supported, so we shift
199 everything by one. */
200 nextoff++;
201#endif
202 if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
203 break;
204
205 d->entry = NULL;
206 d->offset = nextoff;
207 }
208
209 return 0;
210}
211
212static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
213{
214 struct xmp_dirp *d = get_dirp(fi);
215 (void) path;
216 closedir(d->dp);
217 free(d);
218 return 0;
219}
220
221static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
222{
223 int res;
224
225 if (S_ISFIFO(mode))
226 res = mkfifo(path, mode);
227 else
228 res = mknod(path, mode, rdev);
229 if (res == -1)
230 return -errno;
231
232 return 0;
233}
234
235static int xmp_mkdir(const char *path, mode_t mode)
236{
237 int res;
238
239 res = mkdir(path, mode);
240 if (res == -1)
241 return -errno;
242
243 return 0;
244}
245
246static int xmp_unlink(const char *path)
247{
248 int res;
249
250 res = unlink(path);
251 if (res == -1)
252 return -errno;
253
254 return 0;
255}
256
257static int xmp_rmdir(const char *path)
258{
259 int res;
260
261 res = rmdir(path);
262 if (res == -1)
263 return -errno;
264
265 return 0;
266}
267
268static int xmp_symlink(const char *from, const char *to)
269{
270 int res;
271
272 res = symlink(from, to);
273 if (res == -1)
274 return -errno;
275
276 return 0;
277}
278
279static int xmp_rename(const char *from, const char *to, unsigned int flags)
280{
281 int res;
282
283 /* When we have renameat2() in libc, then we can implement flags */
284 if (flags)
285 return -EINVAL;
286
287 res = rename(from, to);
288 if (res == -1)
289 return -errno;
290
291 return 0;
292}
293
294static int xmp_link(const char *from, const char *to)
295{
296 int res;
297
298 res = link(from, to);
299 if (res == -1)
300 return -errno;
301
302 return 0;
303}
304
305static int xmp_chmod(const char *path, mode_t mode,
306 struct fuse_file_info *fi)
307{
308 int res;
309
310 if(fi)
311 res = fchmod(fi->fh, mode);
312 else
313 res = chmod(path, mode);
314 if (res == -1)
315 return -errno;
316
317 return 0;
318}
319
320static int xmp_chown(const char *path, uid_t uid, gid_t gid,
321 struct fuse_file_info *fi)
322{
323 int res;
324
325 if (fi)
326 res = fchown(fi->fh, uid, gid);
327 else
328 res = lchown(path, uid, gid);
329 if (res == -1)
330 return -errno;
331
332 return 0;
333}
334
335static int xmp_truncate(const char *path, off_t size,
336 struct fuse_file_info *fi)
337{
338 int res;
339
340 if(fi)
341 res = ftruncate(fi->fh, size);
342 else
343 res = truncate(path, size);
344
345 if (res == -1)
346 return -errno;
347
348 return 0;
349}
350
351#ifdef HAVE_UTIMENSAT
352static int xmp_utimens(const char *path, const struct timespec ts[2],
353 struct fuse_file_info *fi)
354{
355 int res;
356
357 /* don't use utime/utimes since they follow symlinks */
358 if (fi)
359 res = futimens(fi->fh, ts);
360 else
361 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
362 if (res == -1)
363 return -errno;
364
365 return 0;
366}
367#endif
368
369static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
370{
371 int fd;
372
373 fd = open(path, fi->flags, mode);
374 if (fd == -1)
375 return -errno;
376
377 fi->fh = fd;
378 return 0;
379}
380
381static int xmp_open(const char *path, struct fuse_file_info *fi)
382{
383 int fd;
384
385 fd = open(path, fi->flags);
386 if (fd == -1)
387 return -errno;
388
389 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
390 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
391 for writes to the same file). */
392 if (fi->flags & O_DIRECT) {
393 fi->direct_io = 1;
395 }
396
397 fi->fh = fd;
398 return 0;
399}
400
401static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
402 struct fuse_file_info *fi)
403{
404 int res;
405
406 (void) path;
407 res = pread(fi->fh, buf, size, offset);
408 if (res == -1)
409 res = -errno;
410
411 return res;
412}
413
414static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
415 size_t size, off_t offset, struct fuse_file_info *fi)
416{
417 struct fuse_bufvec *src;
418
419 (void) path;
420
421 src = malloc(sizeof(struct fuse_bufvec));
422 if (src == NULL)
423 return -ENOMEM;
424
425 *src = FUSE_BUFVEC_INIT(size);
426
428 src->buf[0].fd = fi->fh;
429 src->buf[0].pos = offset;
430
431 *bufp = src;
432
433 return 0;
434}
435
436static int xmp_write(const char *path, const char *buf, size_t size,
437 off_t offset, struct fuse_file_info *fi)
438{
439 int res;
440
441 (void) path;
442 res = pwrite(fi->fh, buf, size, offset);
443 if (res == -1)
444 res = -errno;
445
446 return res;
447}
448
449static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
450 off_t offset, struct fuse_file_info *fi)
451{
452 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
453
454 (void) path;
455
457 dst.buf[0].fd = fi->fh;
458 dst.buf[0].pos = offset;
459
461}
462
463static int xmp_statfs(const char *path, struct statvfs *stbuf)
464{
465 int res;
466
467 res = statvfs(path, stbuf);
468 if (res == -1)
469 return -errno;
470
471 return 0;
472}
473
474static int xmp_flush(const char *path, struct fuse_file_info *fi)
475{
476 int res;
477
478 (void) path;
479 /* This is called from every close on an open file, so call the
480 close on the underlying filesystem. But since flush may be
481 called multiple times for an open file, this must not really
482 close the file. This is important if used on a network
483 filesystem like NFS which flush the data/metadata on close() */
484 res = close(dup(fi->fh));
485 if (res == -1)
486 return -errno;
487
488 return 0;
489}
490
491static int xmp_release(const char *path, struct fuse_file_info *fi)
492{
493 (void) path;
494 close(fi->fh);
495
496 return 0;
497}
498
499static int xmp_fsync(const char *path, int isdatasync,
500 struct fuse_file_info *fi)
501{
502 int res;
503 (void) path;
504
505#ifndef HAVE_FDATASYNC
506 (void) isdatasync;
507#else
508 if (isdatasync)
509 res = fdatasync(fi->fh);
510 else
511#endif
512 res = fsync(fi->fh);
513 if (res == -1)
514 return -errno;
515
516 return 0;
517}
518
519static int xmp_fallocate(const char *path, int mode,
520 off_t offset, off_t length, struct fuse_file_info *fi)
521{
522 (void) path;
523
524 return do_fallocate(fi->fh, mode, offset, length);
525}
526
527#ifdef HAVE_SETXATTR
528/* xattr operations are optional and can safely be left unimplemented */
529static int xmp_setxattr(const char *path, const char *name, const char *value,
530 size_t size, int flags)
531{
532 int res = lsetxattr(path, name, value, size, flags);
533 if (res == -1)
534 return -errno;
535 return 0;
536}
537
538static int xmp_getxattr(const char *path, const char *name, char *value,
539 size_t size)
540{
541 int res = lgetxattr(path, name, value, size);
542 if (res == -1)
543 return -errno;
544 return res;
545}
546
547static int xmp_listxattr(const char *path, char *list, size_t size)
548{
549 int res = llistxattr(path, list, size);
550 if (res == -1)
551 return -errno;
552 return res;
553}
554
555static int xmp_removexattr(const char *path, const char *name)
556{
557 int res = lremovexattr(path, name);
558 if (res == -1)
559 return -errno;
560 return 0;
561}
562#endif /* HAVE_SETXATTR */
563
564#ifdef HAVE_LIBULOCKMGR
565static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
566 struct flock *lock)
567{
568 (void) path;
569
570 return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
571 sizeof(fi->lock_owner));
572}
573#endif
574
575static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
576{
577 int res;
578 (void) path;
579
580 res = flock(fi->fh, op);
581 if (res == -1)
582 return -errno;
583
584 return 0;
585}
586
587#ifdef HAVE_COPY_FILE_RANGE
588static ssize_t xmp_copy_file_range(const char *path_in,
589 struct fuse_file_info *fi_in,
590 off_t off_in, const char *path_out,
591 struct fuse_file_info *fi_out,
592 off_t off_out, size_t len, int flags)
593{
594 ssize_t res;
595 (void) path_in;
596 (void) path_out;
597
598 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
599 flags);
600 if (res == -1)
601 return -errno;
602
603 return res;
604}
605#endif
606
607static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
608{
609 off_t res;
610 (void) path;
611
612 res = lseek(fi->fh, off, whence);
613 if (res == -1)
614 return -errno;
615
616 return res;
617}
618
619#ifdef HAVE_STATX
620static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
621 struct fuse_file_info *fi)
622{
623 int fd = -1;
624 int res;
625
626 if (fi)
627 fd = fi->fh;
628
629 res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
630 if (res == -1)
631 return -errno;
632
633 return 0;
634}
635#endif
636
637static const struct fuse_operations xmp_oper = {
638 .init = xmp_init,
639 .getattr = xmp_getattr,
640 .access = xmp_access,
641 .readlink = xmp_readlink,
642 .opendir = xmp_opendir,
643 .readdir = xmp_readdir,
644 .releasedir = xmp_releasedir,
645 .mknod = xmp_mknod,
646 .mkdir = xmp_mkdir,
647 .symlink = xmp_symlink,
648 .unlink = xmp_unlink,
649 .rmdir = xmp_rmdir,
650 .rename = xmp_rename,
651 .link = xmp_link,
652 .chmod = xmp_chmod,
653 .chown = xmp_chown,
654 .truncate = xmp_truncate,
655#ifdef HAVE_UTIMENSAT
656 .utimens = xmp_utimens,
657#endif
658 .create = xmp_create,
659 .open = xmp_open,
660 .read = xmp_read,
661 .read_buf = xmp_read_buf,
662 .write = xmp_write,
663 .write_buf = xmp_write_buf,
664 .statfs = xmp_statfs,
665 .flush = xmp_flush,
666 .release = xmp_release,
667 .fsync = xmp_fsync,
668 .fallocate = xmp_fallocate,
669#ifdef HAVE_SETXATTR
670 .setxattr = xmp_setxattr,
671 .getxattr = xmp_getxattr,
672 .listxattr = xmp_listxattr,
673 .removexattr = xmp_removexattr,
674#endif
675#ifdef HAVE_LIBULOCKMGR
676 .lock = xmp_lock,
677#endif
678 .flock = xmp_flock,
679#ifdef HAVE_COPY_FILE_RANGE
680 .copy_file_range = xmp_copy_file_range,
681#endif
682 .lseek = xmp_lseek,
683#ifdef HAVE_STATX
684 .statx = xmp_statx,
685#endif
686};
687
688int main(int argc, char *argv[])
689{
690 umask(0);
691 return fuse_main(argc, argv, &xmp_oper, NULL);
692}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/example_2passthrough__helpers_8h_source.html0000644000175000017500000005075615156613442024144 0ustar berndbernd libfuse: example/passthrough_helpers.h Source File
libfuse
passthrough_helpers.h
1/*
2 * FUSE: Filesystem in Userspace
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE
24 */
25
26#ifndef FUSE_EXAMPLE_PASSTHROUGH_HELPERS_H_
27#define FUSE_EXAMPLE_PASSTHROUGH_HELPERS_H_
28
29#include <errno.h>
30#include <fcntl.h>
31#include <string.h>
32#include <sys/stat.h>
33#include <unistd.h>
34
35#ifdef __FreeBSD__
36#include <sys/socket.h>
37#include <sys/un.h>
38#endif
39
40static inline int do_fallocate(int fd, int mode, off_t offset, off_t length)
41{
42#ifdef HAVE_FALLOCATE
43 if (fallocate(fd, mode, offset, length) == -1)
44 return -errno;
45 return 0;
46#else // HAVE_FALLOCATE
47
48#ifdef HAVE_POSIX_FALLOCATE
49 if (mode == 0)
50 return -posix_fallocate(fd, offset, length);
51#endif
52
53#ifdef HAVE_FSPACECTL
54 // 0x3 == FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE
55 if (mode == 0x3) {
56 struct spacectl_range sr;
57
58 sr.r_offset = offset;
59 sr.r_len = length;
60 if (fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, NULL) == -1)
61 return -errno;
62 return 0;
63 }
64#endif
65
66 return -EOPNOTSUPP;
67#endif // HAVE_FALLOCATE
68}
69
70/*
71 * Creates files on the underlying file system in response to a FUSE_MKNOD
72 * operation
73 */
74static inline int mknod_wrapper(int dirfd, const char *path, const char *link,
75 int mode, dev_t rdev)
76{
77 int res;
78
79 if (S_ISREG(mode)) {
80 res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode);
81 if (res >= 0)
82 res = close(res);
83 } else if (S_ISDIR(mode)) {
84 res = mkdirat(dirfd, path, mode);
85 } else if (S_ISLNK(mode) && link != NULL) {
86 res = symlinkat(link, dirfd, path);
87 } else if (S_ISFIFO(mode)) {
88 res = mkfifoat(dirfd, path, mode);
89#ifdef __FreeBSD__
90 } else if (S_ISSOCK(mode)) {
91 struct sockaddr_un su;
92 int fd;
93
94 if (strlen(path) >= sizeof(su.sun_path)) {
95 errno = ENAMETOOLONG;
96 return -1;
97 }
98 fd = socket(AF_UNIX, SOCK_STREAM, 0);
99 if (fd >= 0) {
100 /*
101 * We must bind the socket to the underlying file
102 * system to create the socket file, even though
103 * we'll never listen on this socket.
104 */
105 su.sun_family = AF_UNIX;
106 strncpy(su.sun_path, path, sizeof(su.sun_path));
107 res = bindat(dirfd, fd, (struct sockaddr*)&su,
108 sizeof(su));
109 if (res == 0)
110 close(fd);
111 } else {
112 res = -1;
113 }
114#endif
115 } else {
116 res = mknodat(dirfd, path, mode, rdev);
117 }
118
119 return res;
120}
121
122#endif // FUSE_PASSTHROUGH_HELPERS_H_
fuse-3.18.2/doc/html/fuse-3_817_84_2example_2passthrough__helpers_8h_source.html0000644000175000017500000003423115156613442026310 0ustar berndbernd libfuse: fuse-3.17.4/example/passthrough_helpers.h Source File
libfuse
passthrough_helpers.h
1/*
2 * FUSE: Filesystem in Userspace
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE
24 */
25
26/*
27 * Creates files on the underlying file system in response to a FUSE_MKNOD
28 * operation
29 */
30static int mknod_wrapper(int dirfd, const char *path, const char *link,
31 int mode, dev_t rdev)
32{
33 int res;
34
35 if (S_ISREG(mode)) {
36 res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode);
37 if (res >= 0)
38 res = close(res);
39 } else if (S_ISDIR(mode)) {
40 res = mkdirat(dirfd, path, mode);
41 } else if (S_ISLNK(mode) && link != NULL) {
42 res = symlinkat(link, dirfd, path);
43 } else if (S_ISFIFO(mode)) {
44 res = mkfifoat(dirfd, path, mode);
45#ifdef __FreeBSD__
46 } else if (S_ISSOCK(mode)) {
47 struct sockaddr_un su;
48 int fd;
49
50 if (strlen(path) >= sizeof(su.sun_path)) {
51 errno = ENAMETOOLONG;
52 return -1;
53 }
54 fd = socket(AF_UNIX, SOCK_STREAM, 0);
55 if (fd >= 0) {
56 /*
57 * We must bind the socket to the underlying file
58 * system to create the socket file, even though
59 * we'll never listen on this socket.
60 */
61 su.sun_family = AF_UNIX;
62 strncpy(su.sun_path, path, sizeof(su.sun_path));
63 res = bindat(dirfd, fd, (struct sockaddr*)&su,
64 sizeof(su));
65 if (res == 0)
66 close(fd);
67 } else {
68 res = -1;
69 }
70#endif
71 } else {
72 res = mknodat(dirfd, path, mode, rdev);
73 }
74
75 return res;
76}
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2passthrough__helpers_8h_source.html0000644000175000017500000005114115156613442026305 0ustar berndbernd libfuse: fuse-3.18.1/example/passthrough_helpers.h Source File
libfuse
passthrough_helpers.h
1/*
2 * FUSE: Filesystem in Userspace
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE
24 */
25
26#ifndef FUSE_EXAMPLE_PASSTHROUGH_HELPERS_H_
27#define FUSE_EXAMPLE_PASSTHROUGH_HELPERS_H_
28
29#include <errno.h>
30#include <fcntl.h>
31#include <string.h>
32#include <sys/stat.h>
33#include <unistd.h>
34
35#ifdef __FreeBSD__
36#include <sys/socket.h>
37#include <sys/un.h>
38#endif
39
40static inline int do_fallocate(int fd, int mode, off_t offset, off_t length)
41{
42#ifdef HAVE_FALLOCATE
43 if (fallocate(fd, mode, offset, length) == -1)
44 return -errno;
45 return 0;
46#else // HAVE_FALLOCATE
47
48#ifdef HAVE_POSIX_FALLOCATE
49 if (mode == 0)
50 return -posix_fallocate(fd, offset, length);
51#endif
52
53#ifdef HAVE_FSPACECTL
54 // 0x3 == FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE
55 if (mode == 0x3) {
56 struct spacectl_range sr;
57
58 sr.r_offset = offset;
59 sr.r_len = length;
60 if (fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, NULL) == -1)
61 return -errno;
62 return 0;
63 }
64#endif
65
66 return -EOPNOTSUPP;
67#endif // HAVE_FALLOCATE
68}
69
70/*
71 * Creates files on the underlying file system in response to a FUSE_MKNOD
72 * operation
73 */
74static inline int mknod_wrapper(int dirfd, const char *path, const char *link,
75 int mode, dev_t rdev)
76{
77 int res;
78
79 if (S_ISREG(mode)) {
80 res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode);
81 if (res >= 0)
82 res = close(res);
83 } else if (S_ISDIR(mode)) {
84 res = mkdirat(dirfd, path, mode);
85 } else if (S_ISLNK(mode) && link != NULL) {
86 res = symlinkat(link, dirfd, path);
87 } else if (S_ISFIFO(mode)) {
88 res = mkfifoat(dirfd, path, mode);
89#ifdef __FreeBSD__
90 } else if (S_ISSOCK(mode)) {
91 struct sockaddr_un su;
92 int fd;
93
94 if (strlen(path) >= sizeof(su.sun_path)) {
95 errno = ENAMETOOLONG;
96 return -1;
97 }
98 fd = socket(AF_UNIX, SOCK_STREAM, 0);
99 if (fd >= 0) {
100 /*
101 * We must bind the socket to the underlying file
102 * system to create the socket file, even though
103 * we'll never listen on this socket.
104 */
105 su.sun_family = AF_UNIX;
106 strncpy(su.sun_path, path, sizeof(su.sun_path));
107 res = bindat(dirfd, fd, (struct sockaddr*)&su,
108 sizeof(su));
109 if (res == 0)
110 close(fd);
111 } else {
112 res = -1;
113 }
114#endif
115 } else {
116 res = mknodat(dirfd, path, mode, rdev);
117 }
118
119 return res;
120}
121
122#endif // FUSE_PASSTHROUGH_HELPERS_H_
fuse-3.18.2/doc/html/example_2passthrough__ll_8c_source.html0000644000175000017500000100606515156613442023077 0ustar berndbernd libfuse: example/passthrough_ll.c Source File
libfuse
passthrough_ll.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
33#define _GNU_SOURCE
34#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
35
36#include <fuse_lowlevel.h>
37#include <unistd.h>
38#include <stdlib.h>
39#include <stdio.h>
40#include <stddef.h>
41#include <stdbool.h>
42#include <string.h>
43#include <limits.h>
44#include <dirent.h>
45#include <assert.h>
46#include <errno.h>
47#include <inttypes.h>
48#include <pthread.h>
49#include <sys/file.h>
50#include <sys/xattr.h>
51
52#include "passthrough_helpers.h"
53
54/* We are re-using pointers to our `struct lo_inode` and `struct
55 lo_dirp` elements as inodes. This means that we must be able to
56 store uintptr_t values in a fuse_ino_t variable. The following
57 incantation checks this condition at compile time. */
58#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
59_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
60 "fuse_ino_t too small to hold uintptr_t values!");
61#else
62struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
63 { unsigned _uintptr_to_must_hold_fuse_ino_t:
64 ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
65#endif
66
67struct lo_inode {
68 struct lo_inode *next; /* protected by lo->mutex */
69 struct lo_inode *prev; /* protected by lo->mutex */
70 int fd;
71 ino_t ino;
72 dev_t dev;
73 uint64_t refcount; /* protected by lo->mutex */
74};
75
76enum {
77 CACHE_NEVER,
78 CACHE_NORMAL,
79 CACHE_ALWAYS,
80};
81
82struct lo_data {
83 pthread_mutex_t mutex;
84 int debug;
85 int writeback;
86 int flock;
87 int xattr;
88 char *source;
89 double timeout;
90 int cache;
91 int timeout_set;
92 struct lo_inode root; /* protected by lo->mutex */
93};
94
95static const struct fuse_opt lo_opts[] = {
96 { "writeback",
97 offsetof(struct lo_data, writeback), 1 },
98 { "no_writeback",
99 offsetof(struct lo_data, writeback), 0 },
100 { "source=%s",
101 offsetof(struct lo_data, source), 0 },
102 { "flock",
103 offsetof(struct lo_data, flock), 1 },
104 { "no_flock",
105 offsetof(struct lo_data, flock), 0 },
106 { "xattr",
107 offsetof(struct lo_data, xattr), 1 },
108 { "no_xattr",
109 offsetof(struct lo_data, xattr), 0 },
110 { "timeout=%lf",
111 offsetof(struct lo_data, timeout), 0 },
112 { "timeout=",
113 offsetof(struct lo_data, timeout_set), 1 },
114 { "cache=never",
115 offsetof(struct lo_data, cache), CACHE_NEVER },
116 { "cache=auto",
117 offsetof(struct lo_data, cache), CACHE_NORMAL },
118 { "cache=always",
119 offsetof(struct lo_data, cache), CACHE_ALWAYS },
120
122};
123
124static void passthrough_ll_help(void)
125{
126 printf(
127" -o writeback Enable writeback\n"
128" -o no_writeback Disable write back\n"
129" -o source=/home/dir Source directory to be mounted\n"
130" -o flock Enable flock\n"
131" -o no_flock Disable flock\n"
132" -o xattr Enable xattr\n"
133" -o no_xattr Disable xattr\n"
134" -o timeout=1.0 Caching timeout\n"
135" -o timeout=0/1 Timeout is set\n"
136" -o cache=never Disable cache\n"
137" -o cache=auto Auto enable cache\n"
138" -o cache=always Cache always\n");
139}
140
141static struct lo_data *lo_data(fuse_req_t req)
142{
143 return (struct lo_data *) fuse_req_userdata(req);
144}
145
146static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
147{
148 if (ino == FUSE_ROOT_ID)
149 return &lo_data(req)->root;
150 else
151 return (struct lo_inode *) (uintptr_t) ino;
152}
153
154static int lo_fd(fuse_req_t req, fuse_ino_t ino)
155{
156 return lo_inode(req, ino)->fd;
157}
158
159static bool lo_debug(fuse_req_t req)
160{
161 return lo_data(req)->debug != 0;
162}
163
164static void lo_init(void *userdata,
165 struct fuse_conn_info *conn)
166{
167 struct lo_data *lo = (struct lo_data *)userdata;
168 bool has_flag;
169
170 if (lo->writeback) {
172 if (lo->debug && has_flag)
173 fuse_log(FUSE_LOG_DEBUG,
174 "lo_init: activating writeback\n");
175 }
176 if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
178 if (lo->debug && has_flag)
179 fuse_log(FUSE_LOG_DEBUG,
180 "lo_init: activating flock locks\n");
181 }
182
183 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
184 conn->no_interrupt = 1;
185}
186
187static void lo_destroy(void *userdata)
188{
189 struct lo_data *lo = (struct lo_data*) userdata;
190
191 while (lo->root.next != &lo->root) {
192 struct lo_inode* next = lo->root.next;
193 lo->root.next = next->next;
194 close(next->fd);
195 free(next);
196 }
197}
198
199static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
200 struct fuse_file_info *fi)
201{
202 int res;
203 struct stat buf;
204 struct lo_data *lo = lo_data(req);
205 int fd = fi ? fi->fh : lo_fd(req, ino);
206
207 (void) fi;
208
209 res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
210 if (res == -1)
211 return (void) fuse_reply_err(req, errno);
212
213 fuse_reply_attr(req, &buf, lo->timeout);
214}
215
216static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
217 int valid, struct fuse_file_info *fi)
218{
219 int saverr;
220 char procname[64];
221 struct lo_inode *inode = lo_inode(req, ino);
222 int ifd = inode->fd;
223 int res;
224
225 if (valid & FUSE_SET_ATTR_MODE) {
226 if (fi) {
227 res = fchmod(fi->fh, attr->st_mode);
228 } else {
229 sprintf(procname, "/proc/self/fd/%i", ifd);
230 res = chmod(procname, attr->st_mode);
231 }
232 if (res == -1)
233 goto out_err;
234 }
235 if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
236 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
237 attr->st_uid : (uid_t) -1;
238 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
239 attr->st_gid : (gid_t) -1;
240
241 res = fchownat(ifd, "", uid, gid,
242 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
243 if (res == -1)
244 goto out_err;
245 }
246 if (valid & FUSE_SET_ATTR_SIZE) {
247 if (fi) {
248 res = ftruncate(fi->fh, attr->st_size);
249 } else {
250 sprintf(procname, "/proc/self/fd/%i", ifd);
251 res = truncate(procname, attr->st_size);
252 }
253 if (res == -1)
254 goto out_err;
255 }
256 if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
257 struct timespec tv[2];
258
259 tv[0].tv_sec = 0;
260 tv[1].tv_sec = 0;
261 tv[0].tv_nsec = UTIME_OMIT;
262 tv[1].tv_nsec = UTIME_OMIT;
263
264 if (valid & FUSE_SET_ATTR_ATIME_NOW)
265 tv[0].tv_nsec = UTIME_NOW;
266 else if (valid & FUSE_SET_ATTR_ATIME)
267 tv[0] = attr->st_atim;
268
269 if (valid & FUSE_SET_ATTR_MTIME_NOW)
270 tv[1].tv_nsec = UTIME_NOW;
271 else if (valid & FUSE_SET_ATTR_MTIME)
272 tv[1] = attr->st_mtim;
273
274 if (fi)
275 res = futimens(fi->fh, tv);
276 else {
277 sprintf(procname, "/proc/self/fd/%i", ifd);
278 res = utimensat(AT_FDCWD, procname, tv, 0);
279 }
280 if (res == -1)
281 goto out_err;
282 }
283
284 return lo_getattr(req, ino, fi);
285
286out_err:
287 saverr = errno;
288 fuse_reply_err(req, saverr);
289}
290
291static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
292{
293 struct lo_inode *p;
294 struct lo_inode *ret = NULL;
295
296 pthread_mutex_lock(&lo->mutex);
297 for (p = lo->root.next; p != &lo->root; p = p->next) {
298 if (p->ino == st->st_ino && p->dev == st->st_dev) {
299 assert(p->refcount > 0);
300 ret = p;
301 ret->refcount++;
302 break;
303 }
304 }
305 pthread_mutex_unlock(&lo->mutex);
306 return ret;
307}
308
309
310static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo)
311{
312 struct lo_inode *inode = NULL;
313 struct lo_inode *prev, *next;
314
315 inode = calloc(1, sizeof(struct lo_inode));
316 if (!inode)
317 return NULL;
318
319 inode->refcount = 1;
320 inode->fd = fd;
321 inode->ino = e->attr.st_ino;
322 inode->dev = e->attr.st_dev;
323
324 pthread_mutex_lock(&lo->mutex);
325 prev = &lo->root;
326 next = prev->next;
327 next->prev = inode;
328 inode->next = next;
329 inode->prev = prev;
330 prev->next = inode;
331 pthread_mutex_unlock(&lo->mutex);
332 return inode;
333}
334
335static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e)
336{
337 int res;
338 struct lo_data *lo = lo_data(req);
339
340 memset(e, 0, sizeof(*e));
341 e->attr_timeout = lo->timeout;
342 e->entry_timeout = lo->timeout;
343
344 res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
345 if (res == -1)
346 return errno;
347
348 e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo);
349
350 if (lo_debug(req))
351 fuse_log(FUSE_LOG_DEBUG, " %lli/%d -> %lli\n",
352 (unsigned long long) parent, fd, (unsigned long long) e->ino);
353
354 return 0;
355
356}
357
358static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
359 struct fuse_entry_param *e)
360{
361 int newfd;
362 int res;
363 int saverr;
364 struct lo_data *lo = lo_data(req);
365 struct lo_inode *inode;
366
367 memset(e, 0, sizeof(*e));
368 e->attr_timeout = lo->timeout;
369 e->entry_timeout = lo->timeout;
370
371 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
372 if (newfd == -1)
373 goto out_err;
374
375 res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
376 if (res == -1)
377 goto out_err;
378
379 inode = lo_find(lo_data(req), &e->attr);
380 if (inode) {
381 close(newfd);
382 newfd = -1;
383 } else {
384 inode = create_new_inode(newfd, e, lo);
385 if (!inode)
386 goto out_err;
387 }
388 e->ino = (uintptr_t) inode;
389
390 if (lo_debug(req))
391 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
392 (unsigned long long) parent, name, (unsigned long long) e->ino);
393
394 return 0;
395
396out_err:
397 saverr = errno;
398 if (newfd != -1)
399 close(newfd);
400 return saverr;
401}
402
403static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
404{
405 struct fuse_entry_param e;
406 int err;
407
408 if (lo_debug(req))
409 fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
410 parent, name);
411
412 err = lo_do_lookup(req, parent, name, &e);
413 if (err)
414 fuse_reply_err(req, err);
415 else
416 fuse_reply_entry(req, &e);
417}
418
419static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
420 const char *name, mode_t mode, dev_t rdev,
421 const char *link)
422{
423 int res;
424 int saverr;
425 struct lo_inode *dir = lo_inode(req, parent);
426 struct fuse_entry_param e;
427
428 res = mknod_wrapper(dir->fd, name, link, mode, rdev);
429
430 saverr = errno;
431 if (res == -1)
432 goto out;
433
434 saverr = lo_do_lookup(req, parent, name, &e);
435 if (saverr)
436 goto out;
437
438 if (lo_debug(req))
439 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
440 (unsigned long long) parent, name, (unsigned long long) e.ino);
441
442 fuse_reply_entry(req, &e);
443 return;
444
445out:
446 fuse_reply_err(req, saverr);
447}
448
449static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
450 const char *name, mode_t mode, dev_t rdev)
451{
452 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
453}
454
455static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
456 mode_t mode)
457{
458 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
459}
460
461static void lo_symlink(fuse_req_t req, const char *link,
462 fuse_ino_t parent, const char *name)
463{
464 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
465}
466
467static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
468 const char *name)
469{
470 int res;
471 struct lo_data *lo = lo_data(req);
472 struct lo_inode *inode = lo_inode(req, ino);
473 struct fuse_entry_param e;
474 char procname[64];
475 int saverr;
476
477 memset(&e, 0, sizeof(struct fuse_entry_param));
478 e.attr_timeout = lo->timeout;
479 e.entry_timeout = lo->timeout;
480
481 sprintf(procname, "/proc/self/fd/%i", inode->fd);
482 res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
483 AT_SYMLINK_FOLLOW);
484 if (res == -1)
485 goto out_err;
486
487 res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
488 if (res == -1)
489 goto out_err;
490
491 pthread_mutex_lock(&lo->mutex);
492 inode->refcount++;
493 pthread_mutex_unlock(&lo->mutex);
494 e.ino = (uintptr_t) inode;
495
496 if (lo_debug(req))
497 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
498 (unsigned long long) parent, name,
499 (unsigned long long) e.ino);
500
501 fuse_reply_entry(req, &e);
502 return;
503
504out_err:
505 saverr = errno;
506 fuse_reply_err(req, saverr);
507}
508
509static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
510{
511 int res;
512
513 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
514
515 fuse_reply_err(req, res == -1 ? errno : 0);
516}
517
518static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
519 fuse_ino_t newparent, const char *newname,
520 unsigned int flags)
521{
522 int res;
523
524 if (flags) {
525 fuse_reply_err(req, EINVAL);
526 return;
527 }
528
529 res = renameat(lo_fd(req, parent), name,
530 lo_fd(req, newparent), newname);
531
532 fuse_reply_err(req, res == -1 ? errno : 0);
533}
534
535static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
536{
537 int res;
538
539 res = unlinkat(lo_fd(req, parent), name, 0);
540
541 fuse_reply_err(req, res == -1 ? errno : 0);
542}
543
544static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
545{
546 if (!inode)
547 return;
548
549 pthread_mutex_lock(&lo->mutex);
550 assert(inode->refcount >= n);
551 inode->refcount -= n;
552 if (!inode->refcount) {
553 struct lo_inode *prev, *next;
554
555 prev = inode->prev;
556 next = inode->next;
557 next->prev = prev;
558 prev->next = next;
559
560 pthread_mutex_unlock(&lo->mutex);
561 close(inode->fd);
562 free(inode);
563
564 } else {
565 pthread_mutex_unlock(&lo->mutex);
566 }
567}
568
569static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
570{
571 struct lo_data *lo = lo_data(req);
572 struct lo_inode *inode = lo_inode(req, ino);
573
574 if (lo_debug(req)) {
575 fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
576 (unsigned long long) ino,
577 (unsigned long long) inode->refcount,
578 (unsigned long long) nlookup);
579 }
580
581 unref_inode(lo, inode, nlookup);
582}
583
584static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
585{
586 lo_forget_one(req, ino, nlookup);
587 fuse_reply_none(req);
588}
589
590static void lo_forget_multi(fuse_req_t req, size_t count,
591 struct fuse_forget_data *forgets)
592{
593 int i;
594
595 for (i = 0; i < count; i++)
596 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
597 fuse_reply_none(req);
598}
599
600static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
601{
602 char buf[PATH_MAX + 1];
603 int res;
604
605 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
606 if (res == -1)
607 return (void) fuse_reply_err(req, errno);
608
609 if (res == sizeof(buf))
610 return (void) fuse_reply_err(req, ENAMETOOLONG);
611
612 buf[res] = '\0';
613
614 fuse_reply_readlink(req, buf);
615}
616
617struct lo_dirp {
618 DIR *dp;
619 struct dirent *entry;
620 off_t offset;
621};
622
623static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
624{
625 return (struct lo_dirp *) (uintptr_t) fi->fh;
626}
627
628static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
629{
630 int error = ENOMEM;
631 struct lo_data *lo = lo_data(req);
632 struct lo_dirp *d;
633 int fd = -1;
634
635 d = calloc(1, sizeof(struct lo_dirp));
636 if (d == NULL)
637 goto out_err;
638
639 fd = openat(lo_fd(req, ino), ".", O_RDONLY);
640 if (fd == -1)
641 goto out_errno;
642
643 d->dp = fdopendir(fd);
644 if (d->dp == NULL)
645 goto out_errno;
646
647 d->offset = 0;
648 d->entry = NULL;
649
650 fi->fh = (uintptr_t) d;
651 if (lo->cache != CACHE_NEVER)
652 fi->cache_readdir = 1;
653 if (lo->cache == CACHE_ALWAYS)
654 fi->keep_cache = 1;
655 fuse_reply_open(req, fi);
656 return;
657
658out_errno:
659 error = errno;
660out_err:
661 if (d) {
662 if (fd != -1)
663 close(fd);
664 free(d);
665 }
666 fuse_reply_err(req, error);
667}
668
669static int is_dot_or_dotdot(const char *name)
670{
671 return name[0] == '.' && (name[1] == '\0' ||
672 (name[1] == '.' && name[2] == '\0'));
673}
674
675static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
676 off_t offset, struct fuse_file_info *fi, int plus)
677{
678 struct lo_dirp *d = lo_dirp(fi);
679 char *buf;
680 char *p;
681 size_t rem = size;
682 int err;
683
684 (void) ino;
685
686 buf = calloc(1, size);
687 if (!buf) {
688 err = ENOMEM;
689 goto error;
690 }
691 p = buf;
692
693 if (offset != d->offset) {
694 seekdir(d->dp, offset);
695 d->entry = NULL;
696 d->offset = offset;
697 }
698 while (1) {
699 size_t entsize;
700 off_t nextoff;
701 const char *name;
702
703 if (!d->entry) {
704 errno = 0;
705 d->entry = readdir(d->dp);
706 if (!d->entry) {
707 if (errno) { // Error
708 err = errno;
709 goto error;
710 } else { // End of stream
711 break;
712 }
713 }
714 }
715 nextoff = d->entry->d_off;
716 name = d->entry->d_name;
717 fuse_ino_t entry_ino = 0;
718 if (plus) {
719 struct fuse_entry_param e;
720 if (is_dot_or_dotdot(name)) {
721 e = (struct fuse_entry_param) {
722 .attr.st_ino = d->entry->d_ino,
723 .attr.st_mode = d->entry->d_type << 12,
724 };
725 } else {
726 err = lo_do_lookup(req, ino, name, &e);
727 if (err)
728 goto error;
729 entry_ino = e.ino;
730 }
731
732 entsize = fuse_add_direntry_plus(req, p, rem, name,
733 &e, nextoff);
734 } else {
735 struct stat st = {
736 .st_ino = d->entry->d_ino,
737 .st_mode = d->entry->d_type << 12,
738 };
739 entsize = fuse_add_direntry(req, p, rem, name,
740 &st, nextoff);
741 }
742 if (entsize > rem) {
743 if (entry_ino != 0)
744 lo_forget_one(req, entry_ino, 1);
745 break;
746 }
747
748 p += entsize;
749 rem -= entsize;
750
751 d->entry = NULL;
752 d->offset = nextoff;
753 }
754
755 err = 0;
756error:
757 // If there's an error, we can only signal it if we haven't stored
758 // any entries yet - otherwise we'd end up with wrong lookup
759 // counts for the entries that are already in the buffer. So we
760 // return what we've collected until that point.
761 if (err && rem == size)
762 fuse_reply_err(req, err);
763 else
764 fuse_reply_buf(req, buf, size - rem);
765 free(buf);
766}
767
768static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
769 off_t offset, struct fuse_file_info *fi)
770{
771 lo_do_readdir(req, ino, size, offset, fi, 0);
772}
773
774static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
775 off_t offset, struct fuse_file_info *fi)
776{
777 lo_do_readdir(req, ino, size, offset, fi, 1);
778}
779
780static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
781{
782 struct lo_dirp *d = lo_dirp(fi);
783 (void) ino;
784 closedir(d->dp);
785 free(d);
786 fuse_reply_err(req, 0);
787}
788
789static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent,
790 mode_t mode, struct fuse_file_info *fi)
791{
792 int fd;
793 struct lo_data *lo = lo_data(req);
794 struct fuse_entry_param e;
795 int err;
796
797 if (lo_debug(req))
798 fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n",
799 parent);
800
801 fd = openat(lo_fd(req, parent), ".",
802 (fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode);
803 if (fd == -1)
804 return (void) fuse_reply_err(req, errno);
805
806 fi->fh = fd;
807 if (lo->cache == CACHE_NEVER)
808 fi->direct_io = 1;
809 else if (lo->cache == CACHE_ALWAYS)
810 fi->keep_cache = 1;
811
812 /* parallel_direct_writes feature depends on direct_io features.
813 To make parallel_direct_writes valid, need set fi->direct_io
814 in current function. */
816
817 err = fill_entry_param_new_inode(req, parent, fd, &e);
818 if (err)
819 fuse_reply_err(req, err);
820 else
821 fuse_reply_create(req, &e, fi);
822}
823
824static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
825 mode_t mode, struct fuse_file_info *fi)
826{
827 int fd;
828 struct lo_data *lo = lo_data(req);
829 struct fuse_entry_param e;
830 int err;
831
832 if (lo_debug(req))
833 fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
834 parent, name);
835
836 fd = openat(lo_fd(req, parent), name,
837 (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
838 if (fd == -1)
839 return (void) fuse_reply_err(req, errno);
840
841 fi->fh = fd;
842 if (lo->cache == CACHE_NEVER)
843 fi->direct_io = 1;
844 else if (lo->cache == CACHE_ALWAYS)
845 fi->keep_cache = 1;
846
847 /* parallel_direct_writes feature depends on direct_io features.
848 To make parallel_direct_writes valid, need set fi->direct_io
849 in current function. */
851
852 err = lo_do_lookup(req, parent, name, &e);
853 if (err)
854 fuse_reply_err(req, err);
855 else
856 fuse_reply_create(req, &e, fi);
857}
858
859static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
860 struct fuse_file_info *fi)
861{
862 int res;
863 int fd = dirfd(lo_dirp(fi)->dp);
864 (void) ino;
865 if (datasync)
866 res = fdatasync(fd);
867 else
868 res = fsync(fd);
869 fuse_reply_err(req, res == -1 ? errno : 0);
870}
871
872static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
873{
874 int fd;
875 char buf[64];
876 struct lo_data *lo = lo_data(req);
877
878 if (lo_debug(req))
879 fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
880 ino, fi->flags);
881
882 /* With writeback cache, kernel may send read requests even
883 when userspace opened write-only */
884 if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
885 fi->flags &= ~O_ACCMODE;
886 fi->flags |= O_RDWR;
887 }
888
889 /* With writeback cache, O_APPEND is handled by the kernel.
890 This breaks atomicity (since the file may change in the
891 underlying filesystem, so that the kernel's idea of the
892 end of the file isn't accurate anymore). In this example,
893 we just accept that. A more rigorous filesystem may want
894 to return an error here */
895 if (lo->writeback && (fi->flags & O_APPEND))
896 fi->flags &= ~O_APPEND;
897
898 sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
899 fd = open(buf, fi->flags & ~O_NOFOLLOW);
900 if (fd == -1)
901 return (void) fuse_reply_err(req, errno);
902
903 fi->fh = fd;
904 if (lo->cache == CACHE_NEVER)
905 fi->direct_io = 1;
906 else if (lo->cache == CACHE_ALWAYS)
907 fi->keep_cache = 1;
908
909 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
910 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
911 for writes to the same file in the kernel). */
912 if (fi->flags & O_DIRECT)
913 fi->direct_io = 1;
914
915 /* parallel_direct_writes feature depends on direct_io features.
916 To make parallel_direct_writes valid, need set fi->direct_io
917 in current function. */
919
920 fuse_reply_open(req, fi);
921}
922
923static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
924{
925 (void) ino;
926
927 close(fi->fh);
928 fuse_reply_err(req, 0);
929}
930
931static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
932{
933 int res;
934 (void) ino;
935 res = close(dup(fi->fh));
936 fuse_reply_err(req, res == -1 ? errno : 0);
937}
938
939static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
940 struct fuse_file_info *fi)
941{
942 int res;
943 (void) ino;
944 if (datasync)
945 res = fdatasync(fi->fh);
946 else
947 res = fsync(fi->fh);
948 fuse_reply_err(req, res == -1 ? errno : 0);
949}
950
951static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
952 off_t offset, struct fuse_file_info *fi)
953{
954 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
955
956 if (lo_debug(req))
957 fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
958 "off=%lu)\n", ino, size, (unsigned long) offset);
959
961 buf.buf[0].fd = fi->fh;
962 buf.buf[0].pos = offset;
963
965}
966
967static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
968 struct fuse_bufvec *in_buf, off_t off,
969 struct fuse_file_info *fi)
970{
971 (void) ino;
972 ssize_t res;
973 struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
974
976 out_buf.buf[0].fd = fi->fh;
977 out_buf.buf[0].pos = off;
978
979 if (lo_debug(req))
980 fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%jd)\n",
981 ino, out_buf.buf[0].size, (intmax_t) off);
982
983 res = fuse_buf_copy(&out_buf, in_buf, 0);
984 if(res < 0)
985 fuse_reply_err(req, -res);
986 else
987 fuse_reply_write(req, (size_t) res);
988}
989
990static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
991{
992 int res;
993 struct statvfs stbuf;
994
995 res = fstatvfs(lo_fd(req, ino), &stbuf);
996 if (res == -1)
997 fuse_reply_err(req, errno);
998 else
999 fuse_reply_statfs(req, &stbuf);
1000}
1001
1002static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
1003 off_t offset, off_t length, struct fuse_file_info *fi)
1004{
1005 int err;
1006 (void) ino;
1007
1008 err = -do_fallocate(fi->fh, mode, offset, length);
1009
1010 fuse_reply_err(req, err);
1011}
1012
1013static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
1014 int op)
1015{
1016 int res;
1017 (void) ino;
1018
1019 res = flock(fi->fh, op);
1020
1021 fuse_reply_err(req, res == -1 ? errno : 0);
1022}
1023
1024static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1025 size_t size)
1026{
1027 char *value = NULL;
1028 char procname[64];
1029 struct lo_inode *inode = lo_inode(req, ino);
1030 ssize_t ret;
1031 int saverr;
1032
1033 saverr = ENOSYS;
1034 if (!lo_data(req)->xattr)
1035 goto out;
1036
1037 if (lo_debug(req)) {
1038 fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
1039 ino, name, size);
1040 }
1041
1042 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1043
1044 if (size) {
1045 value = malloc(size);
1046 if (!value)
1047 goto out_err;
1048
1049 ret = getxattr(procname, name, value, size);
1050 if (ret == -1)
1051 goto out_err;
1052 saverr = 0;
1053 if (ret == 0)
1054 goto out;
1055
1056 fuse_reply_buf(req, value, ret);
1057 } else {
1058 ret = getxattr(procname, name, NULL, 0);
1059 if (ret == -1)
1060 goto out_err;
1061
1062 fuse_reply_xattr(req, ret);
1063 }
1064out_free:
1065 free(value);
1066 return;
1067
1068out_err:
1069 saverr = errno;
1070out:
1071 fuse_reply_err(req, saverr);
1072 goto out_free;
1073}
1074
1075static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
1076{
1077 char *value = NULL;
1078 char procname[64];
1079 struct lo_inode *inode = lo_inode(req, ino);
1080 ssize_t ret;
1081 int saverr;
1082
1083 saverr = ENOSYS;
1084 if (!lo_data(req)->xattr)
1085 goto out;
1086
1087 if (lo_debug(req)) {
1088 fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
1089 ino, size);
1090 }
1091
1092 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1093
1094 if (size) {
1095 value = malloc(size);
1096 if (!value)
1097 goto out_err;
1098
1099 ret = listxattr(procname, value, size);
1100 if (ret == -1)
1101 goto out_err;
1102 saverr = 0;
1103 if (ret == 0)
1104 goto out;
1105
1106 fuse_reply_buf(req, value, ret);
1107 } else {
1108 ret = listxattr(procname, NULL, 0);
1109 if (ret == -1)
1110 goto out_err;
1111
1112 fuse_reply_xattr(req, ret);
1113 }
1114out_free:
1115 free(value);
1116 return;
1117
1118out_err:
1119 saverr = errno;
1120out:
1121 fuse_reply_err(req, saverr);
1122 goto out_free;
1123}
1124
1125static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1126 const char *value, size_t size, int flags)
1127{
1128 char procname[64];
1129 struct lo_inode *inode = lo_inode(req, ino);
1130 ssize_t ret;
1131 int saverr;
1132
1133 saverr = ENOSYS;
1134 if (!lo_data(req)->xattr)
1135 goto out;
1136
1137 if (lo_debug(req)) {
1138 fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
1139 ino, name, value, size);
1140 }
1141
1142 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1143
1144 ret = setxattr(procname, name, value, size, flags);
1145 saverr = ret == -1 ? errno : 0;
1146
1147out:
1148 fuse_reply_err(req, saverr);
1149}
1150
1151static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
1152{
1153 char procname[64];
1154 struct lo_inode *inode = lo_inode(req, ino);
1155 ssize_t ret;
1156 int saverr;
1157
1158 saverr = ENOSYS;
1159 if (!lo_data(req)->xattr)
1160 goto out;
1161
1162 if (lo_debug(req)) {
1163 fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
1164 ino, name);
1165 }
1166
1167 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1168
1169 ret = removexattr(procname, name);
1170 saverr = ret == -1 ? errno : 0;
1171
1172out:
1173 fuse_reply_err(req, saverr);
1174}
1175
1176#ifdef HAVE_COPY_FILE_RANGE
1177static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
1178 struct fuse_file_info *fi_in,
1179 fuse_ino_t ino_out, off_t off_out,
1180 struct fuse_file_info *fi_out, size_t len,
1181 int flags)
1182{
1183 ssize_t res;
1184
1185 if (lo_debug(req))
1186 fuse_log(FUSE_LOG_DEBUG,
1187 "%s(ino=%lld fd=%lld off=%jd ino=%lld fd=%lld off=%jd, size=%zd, flags=0x%x)\n",
1188 __func__, (unsigned long long)ino_in,
1189 (unsigned long long)fi_in->fh,
1190 (intmax_t) off_in, (unsigned long long)ino_out,
1191 (unsigned long long)fi_out->fh, (intmax_t) off_out,
1192 len, flags);
1193
1194 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
1195 flags);
1196 if (res < 0)
1197 fuse_reply_err(req, errno);
1198 else
1199 fuse_reply_write(req, res);
1200}
1201#endif
1202
1203static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1204 struct fuse_file_info *fi)
1205{
1206 off_t res;
1207
1208 (void)ino;
1209 res = lseek(fi->fh, off, whence);
1210 if (res != -1)
1211 fuse_reply_lseek(req, res);
1212 else
1213 fuse_reply_err(req, errno);
1214}
1215
1216#ifdef HAVE_STATX
1217static void lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
1218 struct fuse_file_info *fi)
1219{
1220 struct lo_data *lo = lo_data(req);
1221 struct statx buf;
1222 int res;
1223 int fd;
1224
1225 if (fi)
1226 fd = fi->fh;
1227 else
1228 fd = lo_fd(req, ino);
1229
1230 res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, &buf);
1231 if (res == -1)
1232 fuse_reply_err(req, errno);
1233 else
1234 fuse_reply_statx(req, 0, &buf, lo->timeout);
1235}
1236#endif
1237
1238static const struct fuse_lowlevel_ops lo_oper = {
1239 .init = lo_init,
1240 .destroy = lo_destroy,
1241 .lookup = lo_lookup,
1242 .mkdir = lo_mkdir,
1243 .mknod = lo_mknod,
1244 .symlink = lo_symlink,
1245 .link = lo_link,
1246 .unlink = lo_unlink,
1247 .rmdir = lo_rmdir,
1248 .rename = lo_rename,
1249 .forget = lo_forget,
1250 .forget_multi = lo_forget_multi,
1251 .getattr = lo_getattr,
1252 .setattr = lo_setattr,
1253 .readlink = lo_readlink,
1254 .opendir = lo_opendir,
1255 .readdir = lo_readdir,
1256 .readdirplus = lo_readdirplus,
1257 .releasedir = lo_releasedir,
1258 .fsyncdir = lo_fsyncdir,
1259 .create = lo_create,
1260 .tmpfile = lo_tmpfile,
1261 .open = lo_open,
1262 .release = lo_release,
1263 .flush = lo_flush,
1264 .fsync = lo_fsync,
1265 .read = lo_read,
1266 .write_buf = lo_write_buf,
1267 .statfs = lo_statfs,
1268 .fallocate = lo_fallocate,
1269 .flock = lo_flock,
1270 .getxattr = lo_getxattr,
1271 .listxattr = lo_listxattr,
1272 .setxattr = lo_setxattr,
1273 .removexattr = lo_removexattr,
1274#ifdef HAVE_COPY_FILE_RANGE
1275 .copy_file_range = lo_copy_file_range,
1276#endif
1277 .lseek = lo_lseek,
1278#ifdef HAVE_STATX
1279 .statx = lo_statx,
1280#endif
1281};
1282
1283int main(int argc, char *argv[])
1284{
1285 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1286 struct fuse_session *se;
1287 struct fuse_cmdline_opts opts;
1288 struct fuse_loop_config *config;
1289 struct lo_data lo = { .debug = 0,
1290 .writeback = 0 };
1291 int ret = -1;
1292
1293 /* Don't mask creation mode, kernel already did that */
1294 umask(0);
1295
1296 pthread_mutex_init(&lo.mutex, NULL);
1297 lo.root.next = lo.root.prev = &lo.root;
1298 lo.root.fd = -1;
1299 lo.cache = CACHE_NORMAL;
1300
1301 if (fuse_parse_cmdline(&args, &opts) != 0)
1302 return 1;
1303 if (opts.show_help) {
1304 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
1307 passthrough_ll_help();
1308 ret = 0;
1309 goto err_out1;
1310 } else if (opts.show_version) {
1311 printf("FUSE library version %s\n", fuse_pkgversion());
1313 ret = 0;
1314 goto err_out1;
1315 }
1316
1317 if(opts.mountpoint == NULL) {
1318 printf("usage: %s [options] <mountpoint>\n", argv[0]);
1319 printf(" %s --help\n", argv[0]);
1320 ret = 1;
1321 goto err_out1;
1322 }
1323
1324 if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
1325 return 1;
1326
1327 lo.debug = opts.debug;
1328 lo.root.refcount = 2;
1329 if (lo.source) {
1330 struct stat stat;
1331 int res;
1332
1333 res = lstat(lo.source, &stat);
1334 if (res == -1) {
1335 fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
1336 lo.source);
1337 exit(1);
1338 }
1339 if (!S_ISDIR(stat.st_mode)) {
1340 fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
1341 exit(1);
1342 }
1343
1344 } else {
1345 lo.source = strdup("/");
1346 if(!lo.source) {
1347 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
1348 exit(1);
1349 }
1350 }
1351 if (!lo.timeout_set) {
1352 switch (lo.cache) {
1353 case CACHE_NEVER:
1354 lo.timeout = 0.0;
1355 break;
1356
1357 case CACHE_NORMAL:
1358 lo.timeout = 1.0;
1359 break;
1360
1361 case CACHE_ALWAYS:
1362 lo.timeout = 86400.0;
1363 break;
1364 }
1365 } else if (lo.timeout < 0) {
1366 fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
1367 lo.timeout);
1368 exit(1);
1369 }
1370
1371 lo.root.fd = open(lo.source, O_PATH);
1372 if (lo.root.fd == -1) {
1373 fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
1374 lo.source);
1375 exit(1);
1376 }
1377
1378 se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
1379 if (se == NULL)
1380 goto err_out1;
1381
1382 if (fuse_set_signal_handlers(se) != 0)
1383 goto err_out2;
1384
1385 if (fuse_session_mount(se, opts.mountpoint) != 0)
1386 goto err_out3;
1387
1388 fuse_daemonize(opts.foreground);
1389
1390 /* Block until ctrl+c or fusermount -u */
1391 if (opts.singlethread)
1392 ret = fuse_session_loop(se);
1393 else {
1394 config = fuse_loop_cfg_create();
1395 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
1396 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
1397 ret = fuse_session_loop_mt(se, config);
1398 fuse_loop_cfg_destroy(config);
1399 config = NULL;
1400 }
1401
1403err_out3:
1405err_out2:
1407err_out1:
1408 free(opts.mountpoint);
1409 fuse_opt_free_args(&args);
1410
1411 if (lo.root.fd >= 0)
1412 close(lo.root.fd);
1413
1414 free(lo.source);
1415 return ret ? 1 : 0;
1416}
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
off_t pos
size_t size
struct fuse_buf buf[1]
uint32_t no_interrupt
uint32_t capable
double entry_timeout
fuse_ino_t ino
double attr_timeout
struct stat attr
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_817_84_2example_2passthrough__ll_8c_source.html0000644000175000017500000100001015156613442025235 0ustar berndbernd libfuse: fuse-3.17.4/example/passthrough_ll.c Source File
libfuse
passthrough_ll.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file COPYING.
7*/
8
37#define _GNU_SOURCE
38#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
39
40#include <fuse_lowlevel.h>
41#include <unistd.h>
42#include <stdlib.h>
43#include <stdio.h>
44#include <stddef.h>
45#include <stdbool.h>
46#include <string.h>
47#include <limits.h>
48#include <dirent.h>
49#include <assert.h>
50#include <errno.h>
51#include <inttypes.h>
52#include <pthread.h>
53#include <sys/file.h>
54#include <sys/xattr.h>
55
56#include "passthrough_helpers.h"
57
58/* We are re-using pointers to our `struct lo_inode` and `struct
59 lo_dirp` elements as inodes. This means that we must be able to
60 store uintptr_t values in a fuse_ino_t variable. The following
61 incantation checks this condition at compile time. */
62#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
63_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
64 "fuse_ino_t too small to hold uintptr_t values!");
65#else
66struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
67 { unsigned _uintptr_to_must_hold_fuse_ino_t:
68 ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
69#endif
70
71struct lo_inode {
72 struct lo_inode *next; /* protected by lo->mutex */
73 struct lo_inode *prev; /* protected by lo->mutex */
74 int fd;
75 ino_t ino;
76 dev_t dev;
77 uint64_t refcount; /* protected by lo->mutex */
78};
79
80enum {
81 CACHE_NEVER,
82 CACHE_NORMAL,
83 CACHE_ALWAYS,
84};
85
86struct lo_data {
87 pthread_mutex_t mutex;
88 int debug;
89 int writeback;
90 int flock;
91 int xattr;
92 char *source;
93 double timeout;
94 int cache;
95 int timeout_set;
96 struct lo_inode root; /* protected by lo->mutex */
97};
98
99static const struct fuse_opt lo_opts[] = {
100 { "writeback",
101 offsetof(struct lo_data, writeback), 1 },
102 { "no_writeback",
103 offsetof(struct lo_data, writeback), 0 },
104 { "source=%s",
105 offsetof(struct lo_data, source), 0 },
106 { "flock",
107 offsetof(struct lo_data, flock), 1 },
108 { "no_flock",
109 offsetof(struct lo_data, flock), 0 },
110 { "xattr",
111 offsetof(struct lo_data, xattr), 1 },
112 { "no_xattr",
113 offsetof(struct lo_data, xattr), 0 },
114 { "timeout=%lf",
115 offsetof(struct lo_data, timeout), 0 },
116 { "timeout=",
117 offsetof(struct lo_data, timeout_set), 1 },
118 { "cache=never",
119 offsetof(struct lo_data, cache), CACHE_NEVER },
120 { "cache=auto",
121 offsetof(struct lo_data, cache), CACHE_NORMAL },
122 { "cache=always",
123 offsetof(struct lo_data, cache), CACHE_ALWAYS },
124
126};
127
128static void passthrough_ll_help(void)
129{
130 printf(
131" -o writeback Enable writeback\n"
132" -o no_writeback Disable write back\n"
133" -o source=/home/dir Source directory to be mounted\n"
134" -o flock Enable flock\n"
135" -o no_flock Disable flock\n"
136" -o xattr Enable xattr\n"
137" -o no_xattr Disable xattr\n"
138" -o timeout=1.0 Caching timeout\n"
139" -o timeout=0/1 Timeout is set\n"
140" -o cache=never Disable cache\n"
141" -o cache=auto Auto enable cache\n"
142" -o cache=always Cache always\n");
143}
144
145static struct lo_data *lo_data(fuse_req_t req)
146{
147 return (struct lo_data *) fuse_req_userdata(req);
148}
149
150static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
151{
152 if (ino == FUSE_ROOT_ID)
153 return &lo_data(req)->root;
154 else
155 return (struct lo_inode *) (uintptr_t) ino;
156}
157
158static int lo_fd(fuse_req_t req, fuse_ino_t ino)
159{
160 return lo_inode(req, ino)->fd;
161}
162
163static bool lo_debug(fuse_req_t req)
164{
165 return lo_data(req)->debug != 0;
166}
167
168static void lo_init(void *userdata,
169 struct fuse_conn_info *conn)
170{
171 struct lo_data *lo = (struct lo_data *)userdata;
172 bool has_flag;
173
174 if (lo->writeback) {
176 if (lo->debug && has_flag)
177 fuse_log(FUSE_LOG_DEBUG,
178 "lo_init: activating writeback\n");
179 }
180 if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
182 if (lo->debug && has_flag)
183 fuse_log(FUSE_LOG_DEBUG,
184 "lo_init: activating flock locks\n");
185 }
186
187 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
188 conn->no_interrupt = 1;
189}
190
191static void lo_destroy(void *userdata)
192{
193 struct lo_data *lo = (struct lo_data*) userdata;
194
195 while (lo->root.next != &lo->root) {
196 struct lo_inode* next = lo->root.next;
197 lo->root.next = next->next;
198 close(next->fd);
199 free(next);
200 }
201}
202
203static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
204 struct fuse_file_info *fi)
205{
206 int res;
207 struct stat buf;
208 struct lo_data *lo = lo_data(req);
209 int fd = fi ? fi->fh : lo_fd(req, ino);
210
211 (void) fi;
212
213 res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
214 if (res == -1)
215 return (void) fuse_reply_err(req, errno);
216
217 fuse_reply_attr(req, &buf, lo->timeout);
218}
219
220static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
221 int valid, struct fuse_file_info *fi)
222{
223 int saverr;
224 char procname[64];
225 struct lo_inode *inode = lo_inode(req, ino);
226 int ifd = inode->fd;
227 int res;
228
229 if (valid & FUSE_SET_ATTR_MODE) {
230 if (fi) {
231 res = fchmod(fi->fh, attr->st_mode);
232 } else {
233 sprintf(procname, "/proc/self/fd/%i", ifd);
234 res = chmod(procname, attr->st_mode);
235 }
236 if (res == -1)
237 goto out_err;
238 }
239 if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
240 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
241 attr->st_uid : (uid_t) -1;
242 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
243 attr->st_gid : (gid_t) -1;
244
245 res = fchownat(ifd, "", uid, gid,
246 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
247 if (res == -1)
248 goto out_err;
249 }
250 if (valid & FUSE_SET_ATTR_SIZE) {
251 if (fi) {
252 res = ftruncate(fi->fh, attr->st_size);
253 } else {
254 sprintf(procname, "/proc/self/fd/%i", ifd);
255 res = truncate(procname, attr->st_size);
256 }
257 if (res == -1)
258 goto out_err;
259 }
260 if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
261 struct timespec tv[2];
262
263 tv[0].tv_sec = 0;
264 tv[1].tv_sec = 0;
265 tv[0].tv_nsec = UTIME_OMIT;
266 tv[1].tv_nsec = UTIME_OMIT;
267
268 if (valid & FUSE_SET_ATTR_ATIME_NOW)
269 tv[0].tv_nsec = UTIME_NOW;
270 else if (valid & FUSE_SET_ATTR_ATIME)
271 tv[0] = attr->st_atim;
272
273 if (valid & FUSE_SET_ATTR_MTIME_NOW)
274 tv[1].tv_nsec = UTIME_NOW;
275 else if (valid & FUSE_SET_ATTR_MTIME)
276 tv[1] = attr->st_mtim;
277
278 if (fi)
279 res = futimens(fi->fh, tv);
280 else {
281 sprintf(procname, "/proc/self/fd/%i", ifd);
282 res = utimensat(AT_FDCWD, procname, tv, 0);
283 }
284 if (res == -1)
285 goto out_err;
286 }
287
288 return lo_getattr(req, ino, fi);
289
290out_err:
291 saverr = errno;
292 fuse_reply_err(req, saverr);
293}
294
295static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
296{
297 struct lo_inode *p;
298 struct lo_inode *ret = NULL;
299
300 pthread_mutex_lock(&lo->mutex);
301 for (p = lo->root.next; p != &lo->root; p = p->next) {
302 if (p->ino == st->st_ino && p->dev == st->st_dev) {
303 assert(p->refcount > 0);
304 ret = p;
305 ret->refcount++;
306 break;
307 }
308 }
309 pthread_mutex_unlock(&lo->mutex);
310 return ret;
311}
312
313
314static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo)
315{
316 struct lo_inode *inode = NULL;
317 struct lo_inode *prev, *next;
318
319 inode = calloc(1, sizeof(struct lo_inode));
320 if (!inode)
321 return NULL;
322
323 inode->refcount = 1;
324 inode->fd = fd;
325 inode->ino = e->attr.st_ino;
326 inode->dev = e->attr.st_dev;
327
328 pthread_mutex_lock(&lo->mutex);
329 prev = &lo->root;
330 next = prev->next;
331 next->prev = inode;
332 inode->next = next;
333 inode->prev = prev;
334 prev->next = inode;
335 pthread_mutex_unlock(&lo->mutex);
336 return inode;
337}
338
339static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e)
340{
341 int res;
342 struct lo_data *lo = lo_data(req);
343
344 memset(e, 0, sizeof(*e));
345 e->attr_timeout = lo->timeout;
346 e->entry_timeout = lo->timeout;
347
348 res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
349 if (res == -1)
350 return errno;
351
352 e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo);
353
354 if (lo_debug(req))
355 fuse_log(FUSE_LOG_DEBUG, " %lli/%lli -> %lli\n",
356 (unsigned long long) parent, fd, (unsigned long long) e->ino);
357
358 return 0;
359
360}
361
362static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
363 struct fuse_entry_param *e)
364{
365 int newfd;
366 int res;
367 int saverr;
368 struct lo_data *lo = lo_data(req);
369 struct lo_inode *inode;
370
371 memset(e, 0, sizeof(*e));
372 e->attr_timeout = lo->timeout;
373 e->entry_timeout = lo->timeout;
374
375 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
376 if (newfd == -1)
377 goto out_err;
378
379 res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
380 if (res == -1)
381 goto out_err;
382
383 inode = lo_find(lo_data(req), &e->attr);
384 if (inode) {
385 close(newfd);
386 newfd = -1;
387 } else {
388 inode = create_new_inode(newfd, e, lo);
389 if (!inode)
390 goto out_err;
391 }
392 e->ino = (uintptr_t) inode;
393
394 if (lo_debug(req))
395 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
396 (unsigned long long) parent, name, (unsigned long long) e->ino);
397
398 return 0;
399
400out_err:
401 saverr = errno;
402 if (newfd != -1)
403 close(newfd);
404 return saverr;
405}
406
407static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
408{
409 struct fuse_entry_param e;
410 int err;
411
412 if (lo_debug(req))
413 fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
414 parent, name);
415
416 err = lo_do_lookup(req, parent, name, &e);
417 if (err)
418 fuse_reply_err(req, err);
419 else
420 fuse_reply_entry(req, &e);
421}
422
423static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
424 const char *name, mode_t mode, dev_t rdev,
425 const char *link)
426{
427 int res;
428 int saverr;
429 struct lo_inode *dir = lo_inode(req, parent);
430 struct fuse_entry_param e;
431
432 res = mknod_wrapper(dir->fd, name, link, mode, rdev);
433
434 saverr = errno;
435 if (res == -1)
436 goto out;
437
438 saverr = lo_do_lookup(req, parent, name, &e);
439 if (saverr)
440 goto out;
441
442 if (lo_debug(req))
443 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
444 (unsigned long long) parent, name, (unsigned long long) e.ino);
445
446 fuse_reply_entry(req, &e);
447 return;
448
449out:
450 fuse_reply_err(req, saverr);
451}
452
453static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
454 const char *name, mode_t mode, dev_t rdev)
455{
456 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
457}
458
459static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
460 mode_t mode)
461{
462 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
463}
464
465static void lo_symlink(fuse_req_t req, const char *link,
466 fuse_ino_t parent, const char *name)
467{
468 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
469}
470
471static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
472 const char *name)
473{
474 int res;
475 struct lo_data *lo = lo_data(req);
476 struct lo_inode *inode = lo_inode(req, ino);
477 struct fuse_entry_param e;
478 char procname[64];
479 int saverr;
480
481 memset(&e, 0, sizeof(struct fuse_entry_param));
482 e.attr_timeout = lo->timeout;
483 e.entry_timeout = lo->timeout;
484
485 sprintf(procname, "/proc/self/fd/%i", inode->fd);
486 res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
487 AT_SYMLINK_FOLLOW);
488 if (res == -1)
489 goto out_err;
490
491 res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
492 if (res == -1)
493 goto out_err;
494
495 pthread_mutex_lock(&lo->mutex);
496 inode->refcount++;
497 pthread_mutex_unlock(&lo->mutex);
498 e.ino = (uintptr_t) inode;
499
500 if (lo_debug(req))
501 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
502 (unsigned long long) parent, name,
503 (unsigned long long) e.ino);
504
505 fuse_reply_entry(req, &e);
506 return;
507
508out_err:
509 saverr = errno;
510 fuse_reply_err(req, saverr);
511}
512
513static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
514{
515 int res;
516
517 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
518
519 fuse_reply_err(req, res == -1 ? errno : 0);
520}
521
522static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
523 fuse_ino_t newparent, const char *newname,
524 unsigned int flags)
525{
526 int res;
527
528 if (flags) {
529 fuse_reply_err(req, EINVAL);
530 return;
531 }
532
533 res = renameat(lo_fd(req, parent), name,
534 lo_fd(req, newparent), newname);
535
536 fuse_reply_err(req, res == -1 ? errno : 0);
537}
538
539static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
540{
541 int res;
542
543 res = unlinkat(lo_fd(req, parent), name, 0);
544
545 fuse_reply_err(req, res == -1 ? errno : 0);
546}
547
548static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
549{
550 if (!inode)
551 return;
552
553 pthread_mutex_lock(&lo->mutex);
554 assert(inode->refcount >= n);
555 inode->refcount -= n;
556 if (!inode->refcount) {
557 struct lo_inode *prev, *next;
558
559 prev = inode->prev;
560 next = inode->next;
561 next->prev = prev;
562 prev->next = next;
563
564 pthread_mutex_unlock(&lo->mutex);
565 close(inode->fd);
566 free(inode);
567
568 } else {
569 pthread_mutex_unlock(&lo->mutex);
570 }
571}
572
573static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
574{
575 struct lo_data *lo = lo_data(req);
576 struct lo_inode *inode = lo_inode(req, ino);
577
578 if (lo_debug(req)) {
579 fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
580 (unsigned long long) ino,
581 (unsigned long long) inode->refcount,
582 (unsigned long long) nlookup);
583 }
584
585 unref_inode(lo, inode, nlookup);
586}
587
588static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
589{
590 lo_forget_one(req, ino, nlookup);
591 fuse_reply_none(req);
592}
593
594static void lo_forget_multi(fuse_req_t req, size_t count,
595 struct fuse_forget_data *forgets)
596{
597 int i;
598
599 for (i = 0; i < count; i++)
600 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
601 fuse_reply_none(req);
602}
603
604static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
605{
606 char buf[PATH_MAX + 1];
607 int res;
608
609 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
610 if (res == -1)
611 return (void) fuse_reply_err(req, errno);
612
613 if (res == sizeof(buf))
614 return (void) fuse_reply_err(req, ENAMETOOLONG);
615
616 buf[res] = '\0';
617
618 fuse_reply_readlink(req, buf);
619}
620
621struct lo_dirp {
622 DIR *dp;
623 struct dirent *entry;
624 off_t offset;
625};
626
627static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
628{
629 return (struct lo_dirp *) (uintptr_t) fi->fh;
630}
631
632static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
633{
634 int error = ENOMEM;
635 struct lo_data *lo = lo_data(req);
636 struct lo_dirp *d;
637 int fd = -1;
638
639 d = calloc(1, sizeof(struct lo_dirp));
640 if (d == NULL)
641 goto out_err;
642
643 fd = openat(lo_fd(req, ino), ".", O_RDONLY);
644 if (fd == -1)
645 goto out_errno;
646
647 d->dp = fdopendir(fd);
648 if (d->dp == NULL)
649 goto out_errno;
650
651 d->offset = 0;
652 d->entry = NULL;
653
654 fi->fh = (uintptr_t) d;
655 if (lo->cache != CACHE_NEVER)
656 fi->cache_readdir = 1;
657 if (lo->cache == CACHE_ALWAYS)
658 fi->keep_cache = 1;
659 fuse_reply_open(req, fi);
660 return;
661
662out_errno:
663 error = errno;
664out_err:
665 if (d) {
666 if (fd != -1)
667 close(fd);
668 free(d);
669 }
670 fuse_reply_err(req, error);
671}
672
673static int is_dot_or_dotdot(const char *name)
674{
675 return name[0] == '.' && (name[1] == '\0' ||
676 (name[1] == '.' && name[2] == '\0'));
677}
678
679static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
680 off_t offset, struct fuse_file_info *fi, int plus)
681{
682 struct lo_dirp *d = lo_dirp(fi);
683 char *buf;
684 char *p;
685 size_t rem = size;
686 int err;
687
688 (void) ino;
689
690 buf = calloc(1, size);
691 if (!buf) {
692 err = ENOMEM;
693 goto error;
694 }
695 p = buf;
696
697 if (offset != d->offset) {
698 seekdir(d->dp, offset);
699 d->entry = NULL;
700 d->offset = offset;
701 }
702 while (1) {
703 size_t entsize;
704 off_t nextoff;
705 const char *name;
706
707 if (!d->entry) {
708 errno = 0;
709 d->entry = readdir(d->dp);
710 if (!d->entry) {
711 if (errno) { // Error
712 err = errno;
713 goto error;
714 } else { // End of stream
715 break;
716 }
717 }
718 }
719 nextoff = d->entry->d_off;
720 name = d->entry->d_name;
721 fuse_ino_t entry_ino = 0;
722 if (plus) {
723 struct fuse_entry_param e;
724 if (is_dot_or_dotdot(name)) {
725 e = (struct fuse_entry_param) {
726 .attr.st_ino = d->entry->d_ino,
727 .attr.st_mode = d->entry->d_type << 12,
728 };
729 } else {
730 err = lo_do_lookup(req, ino, name, &e);
731 if (err)
732 goto error;
733 entry_ino = e.ino;
734 }
735
736 entsize = fuse_add_direntry_plus(req, p, rem, name,
737 &e, nextoff);
738 } else {
739 struct stat st = {
740 .st_ino = d->entry->d_ino,
741 .st_mode = d->entry->d_type << 12,
742 };
743 entsize = fuse_add_direntry(req, p, rem, name,
744 &st, nextoff);
745 }
746 if (entsize > rem) {
747 if (entry_ino != 0)
748 lo_forget_one(req, entry_ino, 1);
749 break;
750 }
751
752 p += entsize;
753 rem -= entsize;
754
755 d->entry = NULL;
756 d->offset = nextoff;
757 }
758
759 err = 0;
760error:
761 // If there's an error, we can only signal it if we haven't stored
762 // any entries yet - otherwise we'd end up with wrong lookup
763 // counts for the entries that are already in the buffer. So we
764 // return what we've collected until that point.
765 if (err && rem == size)
766 fuse_reply_err(req, err);
767 else
768 fuse_reply_buf(req, buf, size - rem);
769 free(buf);
770}
771
772static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
773 off_t offset, struct fuse_file_info *fi)
774{
775 lo_do_readdir(req, ino, size, offset, fi, 0);
776}
777
778static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
779 off_t offset, struct fuse_file_info *fi)
780{
781 lo_do_readdir(req, ino, size, offset, fi, 1);
782}
783
784static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
785{
786 struct lo_dirp *d = lo_dirp(fi);
787 (void) ino;
788 closedir(d->dp);
789 free(d);
790 fuse_reply_err(req, 0);
791}
792
793static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent,
794 mode_t mode, struct fuse_file_info *fi)
795{
796 int fd;
797 struct lo_data *lo = lo_data(req);
798 struct fuse_entry_param e;
799 int err;
800
801 if (lo_debug(req))
802 fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n",
803 parent);
804
805 fd = openat(lo_fd(req, parent), ".",
806 (fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode);
807 if (fd == -1)
808 return (void) fuse_reply_err(req, errno);
809
810 fi->fh = fd;
811 if (lo->cache == CACHE_NEVER)
812 fi->direct_io = 1;
813 else if (lo->cache == CACHE_ALWAYS)
814 fi->keep_cache = 1;
815
816 /* parallel_direct_writes feature depends on direct_io features.
817 To make parallel_direct_writes valid, need set fi->direct_io
818 in current function. */
819 fi->parallel_direct_writes = 1;
820
821 err = fill_entry_param_new_inode(req, parent, fd, &e);
822 if (err)
823 fuse_reply_err(req, err);
824 else
825 fuse_reply_create(req, &e, fi);
826}
827
828static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
829 mode_t mode, struct fuse_file_info *fi)
830{
831 int fd;
832 struct lo_data *lo = lo_data(req);
833 struct fuse_entry_param e;
834 int err;
835
836 if (lo_debug(req))
837 fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
838 parent, name);
839
840 fd = openat(lo_fd(req, parent), name,
841 (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
842 if (fd == -1)
843 return (void) fuse_reply_err(req, errno);
844
845 fi->fh = fd;
846 if (lo->cache == CACHE_NEVER)
847 fi->direct_io = 1;
848 else if (lo->cache == CACHE_ALWAYS)
849 fi->keep_cache = 1;
850
851 /* parallel_direct_writes feature depends on direct_io features.
852 To make parallel_direct_writes valid, need set fi->direct_io
853 in current function. */
855
856 err = lo_do_lookup(req, parent, name, &e);
857 if (err)
858 fuse_reply_err(req, err);
859 else
860 fuse_reply_create(req, &e, fi);
861}
862
863static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
864 struct fuse_file_info *fi)
865{
866 int res;
867 int fd = dirfd(lo_dirp(fi)->dp);
868 (void) ino;
869 if (datasync)
870 res = fdatasync(fd);
871 else
872 res = fsync(fd);
873 fuse_reply_err(req, res == -1 ? errno : 0);
874}
875
876static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
877{
878 int fd;
879 char buf[64];
880 struct lo_data *lo = lo_data(req);
881
882 if (lo_debug(req))
883 fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
884 ino, fi->flags);
885
886 /* With writeback cache, kernel may send read requests even
887 when userspace opened write-only */
888 if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
889 fi->flags &= ~O_ACCMODE;
890 fi->flags |= O_RDWR;
891 }
892
893 /* With writeback cache, O_APPEND is handled by the kernel.
894 This breaks atomicity (since the file may change in the
895 underlying filesystem, so that the kernel's idea of the
896 end of the file isn't accurate anymore). In this example,
897 we just accept that. A more rigorous filesystem may want
898 to return an error here */
899 if (lo->writeback && (fi->flags & O_APPEND))
900 fi->flags &= ~O_APPEND;
901
902 sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
903 fd = open(buf, fi->flags & ~O_NOFOLLOW);
904 if (fd == -1)
905 return (void) fuse_reply_err(req, errno);
906
907 fi->fh = fd;
908 if (lo->cache == CACHE_NEVER)
909 fi->direct_io = 1;
910 else if (lo->cache == CACHE_ALWAYS)
911 fi->keep_cache = 1;
912
913 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
914 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
915 for writes to the same file in the kernel). */
916 if (fi->flags & O_DIRECT)
917 fi->direct_io = 1;
918
919 /* parallel_direct_writes feature depends on direct_io features.
920 To make parallel_direct_writes valid, need set fi->direct_io
921 in current function. */
923
924 fuse_reply_open(req, fi);
925}
926
927static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
928{
929 (void) ino;
930
931 close(fi->fh);
932 fuse_reply_err(req, 0);
933}
934
935static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
936{
937 int res;
938 (void) ino;
939 res = close(dup(fi->fh));
940 fuse_reply_err(req, res == -1 ? errno : 0);
941}
942
943static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
944 struct fuse_file_info *fi)
945{
946 int res;
947 (void) ino;
948 if (datasync)
949 res = fdatasync(fi->fh);
950 else
951 res = fsync(fi->fh);
952 fuse_reply_err(req, res == -1 ? errno : 0);
953}
954
955static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
956 off_t offset, struct fuse_file_info *fi)
957{
958 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
959
960 if (lo_debug(req))
961 fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
962 "off=%lu)\n", ino, size, (unsigned long) offset);
963
965 buf.buf[0].fd = fi->fh;
966 buf.buf[0].pos = offset;
967
969}
970
971static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
972 struct fuse_bufvec *in_buf, off_t off,
973 struct fuse_file_info *fi)
974{
975 (void) ino;
976 ssize_t res;
977 struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
978
980 out_buf.buf[0].fd = fi->fh;
981 out_buf.buf[0].pos = off;
982
983 if (lo_debug(req))
984 fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n",
985 ino, out_buf.buf[0].size, (unsigned long) off);
986
987 res = fuse_buf_copy(&out_buf, in_buf, 0);
988 if(res < 0)
989 fuse_reply_err(req, -res);
990 else
991 fuse_reply_write(req, (size_t) res);
992}
993
994static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
995{
996 int res;
997 struct statvfs stbuf;
998
999 res = fstatvfs(lo_fd(req, ino), &stbuf);
1000 if (res == -1)
1001 fuse_reply_err(req, errno);
1002 else
1003 fuse_reply_statfs(req, &stbuf);
1004}
1005
1006static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
1007 off_t offset, off_t length, struct fuse_file_info *fi)
1008{
1009 int err = EOPNOTSUPP;
1010 (void) ino;
1011
1012#ifdef HAVE_FALLOCATE
1013 err = fallocate(fi->fh, mode, offset, length);
1014 if (err < 0)
1015 err = errno;
1016
1017#elif defined(HAVE_POSIX_FALLOCATE)
1018 if (mode) {
1019 fuse_reply_err(req, EOPNOTSUPP);
1020 return;
1021 }
1022
1023 err = posix_fallocate(fi->fh, offset, length);
1024#endif
1025
1026 fuse_reply_err(req, err);
1027}
1028
1029static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
1030 int op)
1031{
1032 int res;
1033 (void) ino;
1034
1035 res = flock(fi->fh, op);
1036
1037 fuse_reply_err(req, res == -1 ? errno : 0);
1038}
1039
1040static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1041 size_t size)
1042{
1043 char *value = NULL;
1044 char procname[64];
1045 struct lo_inode *inode = lo_inode(req, ino);
1046 ssize_t ret;
1047 int saverr;
1048
1049 saverr = ENOSYS;
1050 if (!lo_data(req)->xattr)
1051 goto out;
1052
1053 if (lo_debug(req)) {
1054 fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
1055 ino, name, size);
1056 }
1057
1058 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1059
1060 if (size) {
1061 value = malloc(size);
1062 if (!value)
1063 goto out_err;
1064
1065 ret = getxattr(procname, name, value, size);
1066 if (ret == -1)
1067 goto out_err;
1068 saverr = 0;
1069 if (ret == 0)
1070 goto out;
1071
1072 fuse_reply_buf(req, value, ret);
1073 } else {
1074 ret = getxattr(procname, name, NULL, 0);
1075 if (ret == -1)
1076 goto out_err;
1077
1078 fuse_reply_xattr(req, ret);
1079 }
1080out_free:
1081 free(value);
1082 return;
1083
1084out_err:
1085 saverr = errno;
1086out:
1087 fuse_reply_err(req, saverr);
1088 goto out_free;
1089}
1090
1091static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
1092{
1093 char *value = NULL;
1094 char procname[64];
1095 struct lo_inode *inode = lo_inode(req, ino);
1096 ssize_t ret;
1097 int saverr;
1098
1099 saverr = ENOSYS;
1100 if (!lo_data(req)->xattr)
1101 goto out;
1102
1103 if (lo_debug(req)) {
1104 fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
1105 ino, size);
1106 }
1107
1108 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1109
1110 if (size) {
1111 value = malloc(size);
1112 if (!value)
1113 goto out_err;
1114
1115 ret = listxattr(procname, value, size);
1116 if (ret == -1)
1117 goto out_err;
1118 saverr = 0;
1119 if (ret == 0)
1120 goto out;
1121
1122 fuse_reply_buf(req, value, ret);
1123 } else {
1124 ret = listxattr(procname, NULL, 0);
1125 if (ret == -1)
1126 goto out_err;
1127
1128 fuse_reply_xattr(req, ret);
1129 }
1130out_free:
1131 free(value);
1132 return;
1133
1134out_err:
1135 saverr = errno;
1136out:
1137 fuse_reply_err(req, saverr);
1138 goto out_free;
1139}
1140
1141static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1142 const char *value, size_t size, int flags)
1143{
1144 char procname[64];
1145 struct lo_inode *inode = lo_inode(req, ino);
1146 ssize_t ret;
1147 int saverr;
1148
1149 saverr = ENOSYS;
1150 if (!lo_data(req)->xattr)
1151 goto out;
1152
1153 if (lo_debug(req)) {
1154 fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
1155 ino, name, value, size);
1156 }
1157
1158 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1159
1160 ret = setxattr(procname, name, value, size, flags);
1161 saverr = ret == -1 ? errno : 0;
1162
1163out:
1164 fuse_reply_err(req, saverr);
1165}
1166
1167static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
1168{
1169 char procname[64];
1170 struct lo_inode *inode = lo_inode(req, ino);
1171 ssize_t ret;
1172 int saverr;
1173
1174 saverr = ENOSYS;
1175 if (!lo_data(req)->xattr)
1176 goto out;
1177
1178 if (lo_debug(req)) {
1179 fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
1180 ino, name);
1181 }
1182
1183 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1184
1185 ret = removexattr(procname, name);
1186 saverr = ret == -1 ? errno : 0;
1187
1188out:
1189 fuse_reply_err(req, saverr);
1190}
1191
1192#ifdef HAVE_COPY_FILE_RANGE
1193static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
1194 struct fuse_file_info *fi_in,
1195 fuse_ino_t ino_out, off_t off_out,
1196 struct fuse_file_info *fi_out, size_t len,
1197 int flags)
1198{
1199 ssize_t res;
1200
1201 if (lo_debug(req))
1202 fuse_log(FUSE_LOG_DEBUG, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
1203 "off=%lu, ino=%" PRIu64 "/fd=%lu, "
1204 "off=%lu, size=%zd, flags=0x%x)\n",
1205 ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out,
1206 len, flags);
1207
1208 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
1209 flags);
1210 if (res < 0)
1211 fuse_reply_err(req, errno);
1212 else
1213 fuse_reply_write(req, res);
1214}
1215#endif
1216
1217static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1218 struct fuse_file_info *fi)
1219{
1220 off_t res;
1221
1222 (void)ino;
1223 res = lseek(fi->fh, off, whence);
1224 if (res != -1)
1225 fuse_reply_lseek(req, res);
1226 else
1227 fuse_reply_err(req, errno);
1228}
1229
1230static const struct fuse_lowlevel_ops lo_oper = {
1231 .init = lo_init,
1232 .destroy = lo_destroy,
1233 .lookup = lo_lookup,
1234 .mkdir = lo_mkdir,
1235 .mknod = lo_mknod,
1236 .symlink = lo_symlink,
1237 .link = lo_link,
1238 .unlink = lo_unlink,
1239 .rmdir = lo_rmdir,
1240 .rename = lo_rename,
1241 .forget = lo_forget,
1242 .forget_multi = lo_forget_multi,
1243 .getattr = lo_getattr,
1244 .setattr = lo_setattr,
1245 .readlink = lo_readlink,
1246 .opendir = lo_opendir,
1247 .readdir = lo_readdir,
1248 .readdirplus = lo_readdirplus,
1249 .releasedir = lo_releasedir,
1250 .fsyncdir = lo_fsyncdir,
1251 .create = lo_create,
1252 .tmpfile = lo_tmpfile,
1253 .open = lo_open,
1254 .release = lo_release,
1255 .flush = lo_flush,
1256 .fsync = lo_fsync,
1257 .read = lo_read,
1258 .write_buf = lo_write_buf,
1259 .statfs = lo_statfs,
1260 .fallocate = lo_fallocate,
1261 .flock = lo_flock,
1262 .getxattr = lo_getxattr,
1263 .listxattr = lo_listxattr,
1264 .setxattr = lo_setxattr,
1265 .removexattr = lo_removexattr,
1266#ifdef HAVE_COPY_FILE_RANGE
1267 .copy_file_range = lo_copy_file_range,
1268#endif
1269 .lseek = lo_lseek,
1270};
1271
1272int main(int argc, char *argv[])
1273{
1274 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1275 struct fuse_session *se;
1276 struct fuse_cmdline_opts opts;
1277 struct fuse_loop_config *config;
1278 struct lo_data lo = { .debug = 0,
1279 .writeback = 0 };
1280 int ret = -1;
1281
1282 /* Don't mask creation mode, kernel already did that */
1283 umask(0);
1284
1285 pthread_mutex_init(&lo.mutex, NULL);
1286 lo.root.next = lo.root.prev = &lo.root;
1287 lo.root.fd = -1;
1288 lo.cache = CACHE_NORMAL;
1289
1290 if (fuse_parse_cmdline(&args, &opts) != 0)
1291 return 1;
1292 if (opts.show_help) {
1293 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
1296 passthrough_ll_help();
1297 ret = 0;
1298 goto err_out1;
1299 } else if (opts.show_version) {
1300 printf("FUSE library version %s\n", fuse_pkgversion());
1302 ret = 0;
1303 goto err_out1;
1304 }
1305
1306 if(opts.mountpoint == NULL) {
1307 printf("usage: %s [options] <mountpoint>\n", argv[0]);
1308 printf(" %s --help\n", argv[0]);
1309 ret = 1;
1310 goto err_out1;
1311 }
1312
1313 if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
1314 return 1;
1315
1316 lo.debug = opts.debug;
1317 lo.root.refcount = 2;
1318 if (lo.source) {
1319 struct stat stat;
1320 int res;
1321
1322 res = lstat(lo.source, &stat);
1323 if (res == -1) {
1324 fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
1325 lo.source);
1326 exit(1);
1327 }
1328 if (!S_ISDIR(stat.st_mode)) {
1329 fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
1330 exit(1);
1331 }
1332
1333 } else {
1334 lo.source = strdup("/");
1335 if(!lo.source) {
1336 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
1337 exit(1);
1338 }
1339 }
1340 if (!lo.timeout_set) {
1341 switch (lo.cache) {
1342 case CACHE_NEVER:
1343 lo.timeout = 0.0;
1344 break;
1345
1346 case CACHE_NORMAL:
1347 lo.timeout = 1.0;
1348 break;
1349
1350 case CACHE_ALWAYS:
1351 lo.timeout = 86400.0;
1352 break;
1353 }
1354 } else if (lo.timeout < 0) {
1355 fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
1356 lo.timeout);
1357 exit(1);
1358 }
1359
1360 lo.root.fd = open(lo.source, O_PATH);
1361 if (lo.root.fd == -1) {
1362 fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
1363 lo.source);
1364 exit(1);
1365 }
1366
1367 se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
1368 if (se == NULL)
1369 goto err_out1;
1370
1371 if (fuse_set_signal_handlers(se) != 0)
1372 goto err_out2;
1373
1374 if (fuse_session_mount(se, opts.mountpoint) != 0)
1375 goto err_out3;
1376
1377 fuse_daemonize(opts.foreground);
1378
1379 /* Block until ctrl+c or fusermount -u */
1380 if (opts.singlethread)
1381 ret = fuse_session_loop(se);
1382 else {
1383 config = fuse_loop_cfg_create();
1384 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
1385 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
1386 ret = fuse_session_loop_mt(se, config);
1387 fuse_loop_cfg_destroy(config);
1388 config = NULL;
1389 }
1390
1392err_out3:
1394err_out2:
1396err_out1:
1397 free(opts.mountpoint);
1398 fuse_opt_free_args(&args);
1399
1400 if (lo.root.fd >= 0)
1401 close(lo.root.fd);
1402
1403 free(lo.source);
1404 return ret ? 1 : 0;
1405}
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
off_t pos
size_t size
struct fuse_buf buf[1]
uint32_t no_interrupt
uint32_t capable
double entry_timeout
fuse_ino_t ino
double attr_timeout
struct stat attr
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2passthrough__ll_8c_source.html0000644000175000017500000100626715156613442025257 0ustar berndbernd libfuse: fuse-3.18.1/example/passthrough_ll.c Source File
libfuse
passthrough_ll.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
33#define _GNU_SOURCE
34#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
35
36#include <fuse_lowlevel.h>
37#include <unistd.h>
38#include <stdlib.h>
39#include <stdio.h>
40#include <stddef.h>
41#include <stdbool.h>
42#include <string.h>
43#include <limits.h>
44#include <dirent.h>
45#include <assert.h>
46#include <errno.h>
47#include <inttypes.h>
48#include <pthread.h>
49#include <sys/file.h>
50#include <sys/xattr.h>
51
52#include "passthrough_helpers.h"
53
54/* We are re-using pointers to our `struct lo_inode` and `struct
55 lo_dirp` elements as inodes. This means that we must be able to
56 store uintptr_t values in a fuse_ino_t variable. The following
57 incantation checks this condition at compile time. */
58#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
59_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
60 "fuse_ino_t too small to hold uintptr_t values!");
61#else
62struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
63 { unsigned _uintptr_to_must_hold_fuse_ino_t:
64 ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
65#endif
66
67struct lo_inode {
68 struct lo_inode *next; /* protected by lo->mutex */
69 struct lo_inode *prev; /* protected by lo->mutex */
70 int fd;
71 ino_t ino;
72 dev_t dev;
73 uint64_t refcount; /* protected by lo->mutex */
74};
75
76enum {
77 CACHE_NEVER,
78 CACHE_NORMAL,
79 CACHE_ALWAYS,
80};
81
82struct lo_data {
83 pthread_mutex_t mutex;
84 int debug;
85 int writeback;
86 int flock;
87 int xattr;
88 char *source;
89 double timeout;
90 int cache;
91 int timeout_set;
92 struct lo_inode root; /* protected by lo->mutex */
93};
94
95static const struct fuse_opt lo_opts[] = {
96 { "writeback",
97 offsetof(struct lo_data, writeback), 1 },
98 { "no_writeback",
99 offsetof(struct lo_data, writeback), 0 },
100 { "source=%s",
101 offsetof(struct lo_data, source), 0 },
102 { "flock",
103 offsetof(struct lo_data, flock), 1 },
104 { "no_flock",
105 offsetof(struct lo_data, flock), 0 },
106 { "xattr",
107 offsetof(struct lo_data, xattr), 1 },
108 { "no_xattr",
109 offsetof(struct lo_data, xattr), 0 },
110 { "timeout=%lf",
111 offsetof(struct lo_data, timeout), 0 },
112 { "timeout=",
113 offsetof(struct lo_data, timeout_set), 1 },
114 { "cache=never",
115 offsetof(struct lo_data, cache), CACHE_NEVER },
116 { "cache=auto",
117 offsetof(struct lo_data, cache), CACHE_NORMAL },
118 { "cache=always",
119 offsetof(struct lo_data, cache), CACHE_ALWAYS },
120
122};
123
124static void passthrough_ll_help(void)
125{
126 printf(
127" -o writeback Enable writeback\n"
128" -o no_writeback Disable write back\n"
129" -o source=/home/dir Source directory to be mounted\n"
130" -o flock Enable flock\n"
131" -o no_flock Disable flock\n"
132" -o xattr Enable xattr\n"
133" -o no_xattr Disable xattr\n"
134" -o timeout=1.0 Caching timeout\n"
135" -o timeout=0/1 Timeout is set\n"
136" -o cache=never Disable cache\n"
137" -o cache=auto Auto enable cache\n"
138" -o cache=always Cache always\n");
139}
140
141static struct lo_data *lo_data(fuse_req_t req)
142{
143 return (struct lo_data *) fuse_req_userdata(req);
144}
145
146static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
147{
148 if (ino == FUSE_ROOT_ID)
149 return &lo_data(req)->root;
150 else
151 return (struct lo_inode *) (uintptr_t) ino;
152}
153
154static int lo_fd(fuse_req_t req, fuse_ino_t ino)
155{
156 return lo_inode(req, ino)->fd;
157}
158
159static bool lo_debug(fuse_req_t req)
160{
161 return lo_data(req)->debug != 0;
162}
163
164static void lo_init(void *userdata,
165 struct fuse_conn_info *conn)
166{
167 struct lo_data *lo = (struct lo_data *)userdata;
168 bool has_flag;
169
170 if (lo->writeback) {
172 if (lo->debug && has_flag)
173 fuse_log(FUSE_LOG_DEBUG,
174 "lo_init: activating writeback\n");
175 }
176 if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
178 if (lo->debug && has_flag)
179 fuse_log(FUSE_LOG_DEBUG,
180 "lo_init: activating flock locks\n");
181 }
182
183 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
184 conn->no_interrupt = 1;
185}
186
187static void lo_destroy(void *userdata)
188{
189 struct lo_data *lo = (struct lo_data*) userdata;
190
191 while (lo->root.next != &lo->root) {
192 struct lo_inode* next = lo->root.next;
193 lo->root.next = next->next;
194 close(next->fd);
195 free(next);
196 }
197}
198
199static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
200 struct fuse_file_info *fi)
201{
202 int res;
203 struct stat buf;
204 struct lo_data *lo = lo_data(req);
205 int fd = fi ? fi->fh : lo_fd(req, ino);
206
207 (void) fi;
208
209 res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
210 if (res == -1)
211 return (void) fuse_reply_err(req, errno);
212
213 fuse_reply_attr(req, &buf, lo->timeout);
214}
215
216static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
217 int valid, struct fuse_file_info *fi)
218{
219 int saverr;
220 char procname[64];
221 struct lo_inode *inode = lo_inode(req, ino);
222 int ifd = inode->fd;
223 int res;
224
225 if (valid & FUSE_SET_ATTR_MODE) {
226 if (fi) {
227 res = fchmod(fi->fh, attr->st_mode);
228 } else {
229 sprintf(procname, "/proc/self/fd/%i", ifd);
230 res = chmod(procname, attr->st_mode);
231 }
232 if (res == -1)
233 goto out_err;
234 }
235 if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
236 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
237 attr->st_uid : (uid_t) -1;
238 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
239 attr->st_gid : (gid_t) -1;
240
241 res = fchownat(ifd, "", uid, gid,
242 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
243 if (res == -1)
244 goto out_err;
245 }
246 if (valid & FUSE_SET_ATTR_SIZE) {
247 if (fi) {
248 res = ftruncate(fi->fh, attr->st_size);
249 } else {
250 sprintf(procname, "/proc/self/fd/%i", ifd);
251 res = truncate(procname, attr->st_size);
252 }
253 if (res == -1)
254 goto out_err;
255 }
256 if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
257 struct timespec tv[2];
258
259 tv[0].tv_sec = 0;
260 tv[1].tv_sec = 0;
261 tv[0].tv_nsec = UTIME_OMIT;
262 tv[1].tv_nsec = UTIME_OMIT;
263
264 if (valid & FUSE_SET_ATTR_ATIME_NOW)
265 tv[0].tv_nsec = UTIME_NOW;
266 else if (valid & FUSE_SET_ATTR_ATIME)
267 tv[0] = attr->st_atim;
268
269 if (valid & FUSE_SET_ATTR_MTIME_NOW)
270 tv[1].tv_nsec = UTIME_NOW;
271 else if (valid & FUSE_SET_ATTR_MTIME)
272 tv[1] = attr->st_mtim;
273
274 if (fi)
275 res = futimens(fi->fh, tv);
276 else {
277 sprintf(procname, "/proc/self/fd/%i", ifd);
278 res = utimensat(AT_FDCWD, procname, tv, 0);
279 }
280 if (res == -1)
281 goto out_err;
282 }
283
284 return lo_getattr(req, ino, fi);
285
286out_err:
287 saverr = errno;
288 fuse_reply_err(req, saverr);
289}
290
291static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
292{
293 struct lo_inode *p;
294 struct lo_inode *ret = NULL;
295
296 pthread_mutex_lock(&lo->mutex);
297 for (p = lo->root.next; p != &lo->root; p = p->next) {
298 if (p->ino == st->st_ino && p->dev == st->st_dev) {
299 assert(p->refcount > 0);
300 ret = p;
301 ret->refcount++;
302 break;
303 }
304 }
305 pthread_mutex_unlock(&lo->mutex);
306 return ret;
307}
308
309
310static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo)
311{
312 struct lo_inode *inode = NULL;
313 struct lo_inode *prev, *next;
314
315 inode = calloc(1, sizeof(struct lo_inode));
316 if (!inode)
317 return NULL;
318
319 inode->refcount = 1;
320 inode->fd = fd;
321 inode->ino = e->attr.st_ino;
322 inode->dev = e->attr.st_dev;
323
324 pthread_mutex_lock(&lo->mutex);
325 prev = &lo->root;
326 next = prev->next;
327 next->prev = inode;
328 inode->next = next;
329 inode->prev = prev;
330 prev->next = inode;
331 pthread_mutex_unlock(&lo->mutex);
332 return inode;
333}
334
335static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e)
336{
337 int res;
338 struct lo_data *lo = lo_data(req);
339
340 memset(e, 0, sizeof(*e));
341 e->attr_timeout = lo->timeout;
342 e->entry_timeout = lo->timeout;
343
344 res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
345 if (res == -1)
346 return errno;
347
348 e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo);
349
350 if (lo_debug(req))
351 fuse_log(FUSE_LOG_DEBUG, " %lli/%d -> %lli\n",
352 (unsigned long long) parent, fd, (unsigned long long) e->ino);
353
354 return 0;
355
356}
357
358static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
359 struct fuse_entry_param *e)
360{
361 int newfd;
362 int res;
363 int saverr;
364 struct lo_data *lo = lo_data(req);
365 struct lo_inode *inode;
366
367 memset(e, 0, sizeof(*e));
368 e->attr_timeout = lo->timeout;
369 e->entry_timeout = lo->timeout;
370
371 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
372 if (newfd == -1)
373 goto out_err;
374
375 res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
376 if (res == -1)
377 goto out_err;
378
379 inode = lo_find(lo_data(req), &e->attr);
380 if (inode) {
381 close(newfd);
382 newfd = -1;
383 } else {
384 inode = create_new_inode(newfd, e, lo);
385 if (!inode)
386 goto out_err;
387 }
388 e->ino = (uintptr_t) inode;
389
390 if (lo_debug(req))
391 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
392 (unsigned long long) parent, name, (unsigned long long) e->ino);
393
394 return 0;
395
396out_err:
397 saverr = errno;
398 if (newfd != -1)
399 close(newfd);
400 return saverr;
401}
402
403static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
404{
405 struct fuse_entry_param e;
406 int err;
407
408 if (lo_debug(req))
409 fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
410 parent, name);
411
412 err = lo_do_lookup(req, parent, name, &e);
413 if (err)
414 fuse_reply_err(req, err);
415 else
416 fuse_reply_entry(req, &e);
417}
418
419static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
420 const char *name, mode_t mode, dev_t rdev,
421 const char *link)
422{
423 int res;
424 int saverr;
425 struct lo_inode *dir = lo_inode(req, parent);
426 struct fuse_entry_param e;
427
428 res = mknod_wrapper(dir->fd, name, link, mode, rdev);
429
430 saverr = errno;
431 if (res == -1)
432 goto out;
433
434 saverr = lo_do_lookup(req, parent, name, &e);
435 if (saverr)
436 goto out;
437
438 if (lo_debug(req))
439 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
440 (unsigned long long) parent, name, (unsigned long long) e.ino);
441
442 fuse_reply_entry(req, &e);
443 return;
444
445out:
446 fuse_reply_err(req, saverr);
447}
448
449static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
450 const char *name, mode_t mode, dev_t rdev)
451{
452 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
453}
454
455static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
456 mode_t mode)
457{
458 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
459}
460
461static void lo_symlink(fuse_req_t req, const char *link,
462 fuse_ino_t parent, const char *name)
463{
464 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
465}
466
467static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
468 const char *name)
469{
470 int res;
471 struct lo_data *lo = lo_data(req);
472 struct lo_inode *inode = lo_inode(req, ino);
473 struct fuse_entry_param e;
474 char procname[64];
475 int saverr;
476
477 memset(&e, 0, sizeof(struct fuse_entry_param));
478 e.attr_timeout = lo->timeout;
479 e.entry_timeout = lo->timeout;
480
481 sprintf(procname, "/proc/self/fd/%i", inode->fd);
482 res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
483 AT_SYMLINK_FOLLOW);
484 if (res == -1)
485 goto out_err;
486
487 res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
488 if (res == -1)
489 goto out_err;
490
491 pthread_mutex_lock(&lo->mutex);
492 inode->refcount++;
493 pthread_mutex_unlock(&lo->mutex);
494 e.ino = (uintptr_t) inode;
495
496 if (lo_debug(req))
497 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
498 (unsigned long long) parent, name,
499 (unsigned long long) e.ino);
500
501 fuse_reply_entry(req, &e);
502 return;
503
504out_err:
505 saverr = errno;
506 fuse_reply_err(req, saverr);
507}
508
509static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
510{
511 int res;
512
513 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
514
515 fuse_reply_err(req, res == -1 ? errno : 0);
516}
517
518static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
519 fuse_ino_t newparent, const char *newname,
520 unsigned int flags)
521{
522 int res;
523
524 if (flags) {
525 fuse_reply_err(req, EINVAL);
526 return;
527 }
528
529 res = renameat(lo_fd(req, parent), name,
530 lo_fd(req, newparent), newname);
531
532 fuse_reply_err(req, res == -1 ? errno : 0);
533}
534
535static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
536{
537 int res;
538
539 res = unlinkat(lo_fd(req, parent), name, 0);
540
541 fuse_reply_err(req, res == -1 ? errno : 0);
542}
543
544static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
545{
546 if (!inode)
547 return;
548
549 pthread_mutex_lock(&lo->mutex);
550 assert(inode->refcount >= n);
551 inode->refcount -= n;
552 if (!inode->refcount) {
553 struct lo_inode *prev, *next;
554
555 prev = inode->prev;
556 next = inode->next;
557 next->prev = prev;
558 prev->next = next;
559
560 pthread_mutex_unlock(&lo->mutex);
561 close(inode->fd);
562 free(inode);
563
564 } else {
565 pthread_mutex_unlock(&lo->mutex);
566 }
567}
568
569static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
570{
571 struct lo_data *lo = lo_data(req);
572 struct lo_inode *inode = lo_inode(req, ino);
573
574 if (lo_debug(req)) {
575 fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
576 (unsigned long long) ino,
577 (unsigned long long) inode->refcount,
578 (unsigned long long) nlookup);
579 }
580
581 unref_inode(lo, inode, nlookup);
582}
583
584static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
585{
586 lo_forget_one(req, ino, nlookup);
587 fuse_reply_none(req);
588}
589
590static void lo_forget_multi(fuse_req_t req, size_t count,
591 struct fuse_forget_data *forgets)
592{
593 int i;
594
595 for (i = 0; i < count; i++)
596 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
597 fuse_reply_none(req);
598}
599
600static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
601{
602 char buf[PATH_MAX + 1];
603 int res;
604
605 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
606 if (res == -1)
607 return (void) fuse_reply_err(req, errno);
608
609 if (res == sizeof(buf))
610 return (void) fuse_reply_err(req, ENAMETOOLONG);
611
612 buf[res] = '\0';
613
614 fuse_reply_readlink(req, buf);
615}
616
617struct lo_dirp {
618 DIR *dp;
619 struct dirent *entry;
620 off_t offset;
621};
622
623static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
624{
625 return (struct lo_dirp *) (uintptr_t) fi->fh;
626}
627
628static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
629{
630 int error = ENOMEM;
631 struct lo_data *lo = lo_data(req);
632 struct lo_dirp *d;
633 int fd = -1;
634
635 d = calloc(1, sizeof(struct lo_dirp));
636 if (d == NULL)
637 goto out_err;
638
639 fd = openat(lo_fd(req, ino), ".", O_RDONLY);
640 if (fd == -1)
641 goto out_errno;
642
643 d->dp = fdopendir(fd);
644 if (d->dp == NULL)
645 goto out_errno;
646
647 d->offset = 0;
648 d->entry = NULL;
649
650 fi->fh = (uintptr_t) d;
651 if (lo->cache != CACHE_NEVER)
652 fi->cache_readdir = 1;
653 if (lo->cache == CACHE_ALWAYS)
654 fi->keep_cache = 1;
655 fuse_reply_open(req, fi);
656 return;
657
658out_errno:
659 error = errno;
660out_err:
661 if (d) {
662 if (fd != -1)
663 close(fd);
664 free(d);
665 }
666 fuse_reply_err(req, error);
667}
668
669static int is_dot_or_dotdot(const char *name)
670{
671 return name[0] == '.' && (name[1] == '\0' ||
672 (name[1] == '.' && name[2] == '\0'));
673}
674
675static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
676 off_t offset, struct fuse_file_info *fi, int plus)
677{
678 struct lo_dirp *d = lo_dirp(fi);
679 char *buf;
680 char *p;
681 size_t rem = size;
682 int err;
683
684 (void) ino;
685
686 buf = calloc(1, size);
687 if (!buf) {
688 err = ENOMEM;
689 goto error;
690 }
691 p = buf;
692
693 if (offset != d->offset) {
694 seekdir(d->dp, offset);
695 d->entry = NULL;
696 d->offset = offset;
697 }
698 while (1) {
699 size_t entsize;
700 off_t nextoff;
701 const char *name;
702
703 if (!d->entry) {
704 errno = 0;
705 d->entry = readdir(d->dp);
706 if (!d->entry) {
707 if (errno) { // Error
708 err = errno;
709 goto error;
710 } else { // End of stream
711 break;
712 }
713 }
714 }
715 nextoff = d->entry->d_off;
716 name = d->entry->d_name;
717 fuse_ino_t entry_ino = 0;
718 if (plus) {
719 struct fuse_entry_param e;
720 if (is_dot_or_dotdot(name)) {
721 e = (struct fuse_entry_param) {
722 .attr.st_ino = d->entry->d_ino,
723 .attr.st_mode = d->entry->d_type << 12,
724 };
725 } else {
726 err = lo_do_lookup(req, ino, name, &e);
727 if (err)
728 goto error;
729 entry_ino = e.ino;
730 }
731
732 entsize = fuse_add_direntry_plus(req, p, rem, name,
733 &e, nextoff);
734 } else {
735 struct stat st = {
736 .st_ino = d->entry->d_ino,
737 .st_mode = d->entry->d_type << 12,
738 };
739 entsize = fuse_add_direntry(req, p, rem, name,
740 &st, nextoff);
741 }
742 if (entsize > rem) {
743 if (entry_ino != 0)
744 lo_forget_one(req, entry_ino, 1);
745 break;
746 }
747
748 p += entsize;
749 rem -= entsize;
750
751 d->entry = NULL;
752 d->offset = nextoff;
753 }
754
755 err = 0;
756error:
757 // If there's an error, we can only signal it if we haven't stored
758 // any entries yet - otherwise we'd end up with wrong lookup
759 // counts for the entries that are already in the buffer. So we
760 // return what we've collected until that point.
761 if (err && rem == size)
762 fuse_reply_err(req, err);
763 else
764 fuse_reply_buf(req, buf, size - rem);
765 free(buf);
766}
767
768static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
769 off_t offset, struct fuse_file_info *fi)
770{
771 lo_do_readdir(req, ino, size, offset, fi, 0);
772}
773
774static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
775 off_t offset, struct fuse_file_info *fi)
776{
777 lo_do_readdir(req, ino, size, offset, fi, 1);
778}
779
780static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
781{
782 struct lo_dirp *d = lo_dirp(fi);
783 (void) ino;
784 closedir(d->dp);
785 free(d);
786 fuse_reply_err(req, 0);
787}
788
789static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent,
790 mode_t mode, struct fuse_file_info *fi)
791{
792 int fd;
793 struct lo_data *lo = lo_data(req);
794 struct fuse_entry_param e;
795 int err;
796
797 if (lo_debug(req))
798 fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n",
799 parent);
800
801 fd = openat(lo_fd(req, parent), ".",
802 (fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode);
803 if (fd == -1)
804 return (void) fuse_reply_err(req, errno);
805
806 fi->fh = fd;
807 if (lo->cache == CACHE_NEVER)
808 fi->direct_io = 1;
809 else if (lo->cache == CACHE_ALWAYS)
810 fi->keep_cache = 1;
811
812 /* parallel_direct_writes feature depends on direct_io features.
813 To make parallel_direct_writes valid, need set fi->direct_io
814 in current function. */
816
817 err = fill_entry_param_new_inode(req, parent, fd, &e);
818 if (err)
819 fuse_reply_err(req, err);
820 else
821 fuse_reply_create(req, &e, fi);
822}
823
824static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
825 mode_t mode, struct fuse_file_info *fi)
826{
827 int fd;
828 struct lo_data *lo = lo_data(req);
829 struct fuse_entry_param e;
830 int err;
831
832 if (lo_debug(req))
833 fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
834 parent, name);
835
836 fd = openat(lo_fd(req, parent), name,
837 (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
838 if (fd == -1)
839 return (void) fuse_reply_err(req, errno);
840
841 fi->fh = fd;
842 if (lo->cache == CACHE_NEVER)
843 fi->direct_io = 1;
844 else if (lo->cache == CACHE_ALWAYS)
845 fi->keep_cache = 1;
846
847 /* parallel_direct_writes feature depends on direct_io features.
848 To make parallel_direct_writes valid, need set fi->direct_io
849 in current function. */
851
852 err = lo_do_lookup(req, parent, name, &e);
853 if (err)
854 fuse_reply_err(req, err);
855 else
856 fuse_reply_create(req, &e, fi);
857}
858
859static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
860 struct fuse_file_info *fi)
861{
862 int res;
863 int fd = dirfd(lo_dirp(fi)->dp);
864 (void) ino;
865 if (datasync)
866 res = fdatasync(fd);
867 else
868 res = fsync(fd);
869 fuse_reply_err(req, res == -1 ? errno : 0);
870}
871
872static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
873{
874 int fd;
875 char buf[64];
876 struct lo_data *lo = lo_data(req);
877
878 if (lo_debug(req))
879 fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
880 ino, fi->flags);
881
882 /* With writeback cache, kernel may send read requests even
883 when userspace opened write-only */
884 if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
885 fi->flags &= ~O_ACCMODE;
886 fi->flags |= O_RDWR;
887 }
888
889 /* With writeback cache, O_APPEND is handled by the kernel.
890 This breaks atomicity (since the file may change in the
891 underlying filesystem, so that the kernel's idea of the
892 end of the file isn't accurate anymore). In this example,
893 we just accept that. A more rigorous filesystem may want
894 to return an error here */
895 if (lo->writeback && (fi->flags & O_APPEND))
896 fi->flags &= ~O_APPEND;
897
898 sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
899 fd = open(buf, fi->flags & ~O_NOFOLLOW);
900 if (fd == -1)
901 return (void) fuse_reply_err(req, errno);
902
903 fi->fh = fd;
904 if (lo->cache == CACHE_NEVER)
905 fi->direct_io = 1;
906 else if (lo->cache == CACHE_ALWAYS)
907 fi->keep_cache = 1;
908
909 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
910 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
911 for writes to the same file in the kernel). */
912 if (fi->flags & O_DIRECT)
913 fi->direct_io = 1;
914
915 /* parallel_direct_writes feature depends on direct_io features.
916 To make parallel_direct_writes valid, need set fi->direct_io
917 in current function. */
919
920 fuse_reply_open(req, fi);
921}
922
923static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
924{
925 (void) ino;
926
927 close(fi->fh);
928 fuse_reply_err(req, 0);
929}
930
931static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
932{
933 int res;
934 (void) ino;
935 res = close(dup(fi->fh));
936 fuse_reply_err(req, res == -1 ? errno : 0);
937}
938
939static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
940 struct fuse_file_info *fi)
941{
942 int res;
943 (void) ino;
944 if (datasync)
945 res = fdatasync(fi->fh);
946 else
947 res = fsync(fi->fh);
948 fuse_reply_err(req, res == -1 ? errno : 0);
949}
950
951static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
952 off_t offset, struct fuse_file_info *fi)
953{
954 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
955
956 if (lo_debug(req))
957 fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
958 "off=%lu)\n", ino, size, (unsigned long) offset);
959
961 buf.buf[0].fd = fi->fh;
962 buf.buf[0].pos = offset;
963
965}
966
967static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
968 struct fuse_bufvec *in_buf, off_t off,
969 struct fuse_file_info *fi)
970{
971 (void) ino;
972 ssize_t res;
973 struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
974
976 out_buf.buf[0].fd = fi->fh;
977 out_buf.buf[0].pos = off;
978
979 if (lo_debug(req))
980 fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%jd)\n",
981 ino, out_buf.buf[0].size, (intmax_t) off);
982
983 res = fuse_buf_copy(&out_buf, in_buf, 0);
984 if(res < 0)
985 fuse_reply_err(req, -res);
986 else
987 fuse_reply_write(req, (size_t) res);
988}
989
990static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
991{
992 int res;
993 struct statvfs stbuf;
994
995 res = fstatvfs(lo_fd(req, ino), &stbuf);
996 if (res == -1)
997 fuse_reply_err(req, errno);
998 else
999 fuse_reply_statfs(req, &stbuf);
1000}
1001
1002static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
1003 off_t offset, off_t length, struct fuse_file_info *fi)
1004{
1005 int err;
1006 (void) ino;
1007
1008 err = -do_fallocate(fi->fh, mode, offset, length);
1009
1010 fuse_reply_err(req, err);
1011}
1012
1013static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
1014 int op)
1015{
1016 int res;
1017 (void) ino;
1018
1019 res = flock(fi->fh, op);
1020
1021 fuse_reply_err(req, res == -1 ? errno : 0);
1022}
1023
1024static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1025 size_t size)
1026{
1027 char *value = NULL;
1028 char procname[64];
1029 struct lo_inode *inode = lo_inode(req, ino);
1030 ssize_t ret;
1031 int saverr;
1032
1033 saverr = ENOSYS;
1034 if (!lo_data(req)->xattr)
1035 goto out;
1036
1037 if (lo_debug(req)) {
1038 fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
1039 ino, name, size);
1040 }
1041
1042 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1043
1044 if (size) {
1045 value = malloc(size);
1046 if (!value)
1047 goto out_err;
1048
1049 ret = getxattr(procname, name, value, size);
1050 if (ret == -1)
1051 goto out_err;
1052 saverr = 0;
1053 if (ret == 0)
1054 goto out;
1055
1056 fuse_reply_buf(req, value, ret);
1057 } else {
1058 ret = getxattr(procname, name, NULL, 0);
1059 if (ret == -1)
1060 goto out_err;
1061
1062 fuse_reply_xattr(req, ret);
1063 }
1064out_free:
1065 free(value);
1066 return;
1067
1068out_err:
1069 saverr = errno;
1070out:
1071 fuse_reply_err(req, saverr);
1072 goto out_free;
1073}
1074
1075static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
1076{
1077 char *value = NULL;
1078 char procname[64];
1079 struct lo_inode *inode = lo_inode(req, ino);
1080 ssize_t ret;
1081 int saverr;
1082
1083 saverr = ENOSYS;
1084 if (!lo_data(req)->xattr)
1085 goto out;
1086
1087 if (lo_debug(req)) {
1088 fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
1089 ino, size);
1090 }
1091
1092 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1093
1094 if (size) {
1095 value = malloc(size);
1096 if (!value)
1097 goto out_err;
1098
1099 ret = listxattr(procname, value, size);
1100 if (ret == -1)
1101 goto out_err;
1102 saverr = 0;
1103 if (ret == 0)
1104 goto out;
1105
1106 fuse_reply_buf(req, value, ret);
1107 } else {
1108 ret = listxattr(procname, NULL, 0);
1109 if (ret == -1)
1110 goto out_err;
1111
1112 fuse_reply_xattr(req, ret);
1113 }
1114out_free:
1115 free(value);
1116 return;
1117
1118out_err:
1119 saverr = errno;
1120out:
1121 fuse_reply_err(req, saverr);
1122 goto out_free;
1123}
1124
1125static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1126 const char *value, size_t size, int flags)
1127{
1128 char procname[64];
1129 struct lo_inode *inode = lo_inode(req, ino);
1130 ssize_t ret;
1131 int saverr;
1132
1133 saverr = ENOSYS;
1134 if (!lo_data(req)->xattr)
1135 goto out;
1136
1137 if (lo_debug(req)) {
1138 fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
1139 ino, name, value, size);
1140 }
1141
1142 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1143
1144 ret = setxattr(procname, name, value, size, flags);
1145 saverr = ret == -1 ? errno : 0;
1146
1147out:
1148 fuse_reply_err(req, saverr);
1149}
1150
1151static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
1152{
1153 char procname[64];
1154 struct lo_inode *inode = lo_inode(req, ino);
1155 ssize_t ret;
1156 int saverr;
1157
1158 saverr = ENOSYS;
1159 if (!lo_data(req)->xattr)
1160 goto out;
1161
1162 if (lo_debug(req)) {
1163 fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
1164 ino, name);
1165 }
1166
1167 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1168
1169 ret = removexattr(procname, name);
1170 saverr = ret == -1 ? errno : 0;
1171
1172out:
1173 fuse_reply_err(req, saverr);
1174}
1175
1176#ifdef HAVE_COPY_FILE_RANGE
1177static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
1178 struct fuse_file_info *fi_in,
1179 fuse_ino_t ino_out, off_t off_out,
1180 struct fuse_file_info *fi_out, size_t len,
1181 int flags)
1182{
1183 ssize_t res;
1184
1185 if (lo_debug(req))
1186 fuse_log(FUSE_LOG_DEBUG,
1187 "%s(ino=%lld fd=%lld off=%jd ino=%lld fd=%lld off=%jd, size=%zd, flags=0x%x)\n",
1188 __func__, (unsigned long long)ino_in,
1189 (unsigned long long)fi_in->fh,
1190 (intmax_t) off_in, (unsigned long long)ino_out,
1191 (unsigned long long)fi_out->fh, (intmax_t) off_out,
1192 len, flags);
1193
1194 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
1195 flags);
1196 if (res < 0)
1197 fuse_reply_err(req, errno);
1198 else
1199 fuse_reply_write(req, res);
1200}
1201#endif
1202
1203static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1204 struct fuse_file_info *fi)
1205{
1206 off_t res;
1207
1208 (void)ino;
1209 res = lseek(fi->fh, off, whence);
1210 if (res != -1)
1211 fuse_reply_lseek(req, res);
1212 else
1213 fuse_reply_err(req, errno);
1214}
1215
1216#ifdef HAVE_STATX
1217static void lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
1218 struct fuse_file_info *fi)
1219{
1220 struct lo_data *lo = lo_data(req);
1221 struct statx buf;
1222 int res;
1223 int fd;
1224
1225 if (fi)
1226 fd = fi->fh;
1227 else
1228 fd = lo_fd(req, ino);
1229
1230 res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, &buf);
1231 if (res == -1)
1232 fuse_reply_err(req, errno);
1233 else
1234 fuse_reply_statx(req, 0, &buf, lo->timeout);
1235}
1236#endif
1237
1238static const struct fuse_lowlevel_ops lo_oper = {
1239 .init = lo_init,
1240 .destroy = lo_destroy,
1241 .lookup = lo_lookup,
1242 .mkdir = lo_mkdir,
1243 .mknod = lo_mknod,
1244 .symlink = lo_symlink,
1245 .link = lo_link,
1246 .unlink = lo_unlink,
1247 .rmdir = lo_rmdir,
1248 .rename = lo_rename,
1249 .forget = lo_forget,
1250 .forget_multi = lo_forget_multi,
1251 .getattr = lo_getattr,
1252 .setattr = lo_setattr,
1253 .readlink = lo_readlink,
1254 .opendir = lo_opendir,
1255 .readdir = lo_readdir,
1256 .readdirplus = lo_readdirplus,
1257 .releasedir = lo_releasedir,
1258 .fsyncdir = lo_fsyncdir,
1259 .create = lo_create,
1260 .tmpfile = lo_tmpfile,
1261 .open = lo_open,
1262 .release = lo_release,
1263 .flush = lo_flush,
1264 .fsync = lo_fsync,
1265 .read = lo_read,
1266 .write_buf = lo_write_buf,
1267 .statfs = lo_statfs,
1268 .fallocate = lo_fallocate,
1269 .flock = lo_flock,
1270 .getxattr = lo_getxattr,
1271 .listxattr = lo_listxattr,
1272 .setxattr = lo_setxattr,
1273 .removexattr = lo_removexattr,
1274#ifdef HAVE_COPY_FILE_RANGE
1275 .copy_file_range = lo_copy_file_range,
1276#endif
1277 .lseek = lo_lseek,
1278#ifdef HAVE_STATX
1279 .statx = lo_statx,
1280#endif
1281};
1282
1283int main(int argc, char *argv[])
1284{
1285 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1286 struct fuse_session *se;
1287 struct fuse_cmdline_opts opts;
1288 struct fuse_loop_config *config;
1289 struct lo_data lo = { .debug = 0,
1290 .writeback = 0 };
1291 int ret = -1;
1292
1293 /* Don't mask creation mode, kernel already did that */
1294 umask(0);
1295
1296 pthread_mutex_init(&lo.mutex, NULL);
1297 lo.root.next = lo.root.prev = &lo.root;
1298 lo.root.fd = -1;
1299 lo.cache = CACHE_NORMAL;
1300
1301 if (fuse_parse_cmdline(&args, &opts) != 0)
1302 return 1;
1303 if (opts.show_help) {
1304 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
1307 passthrough_ll_help();
1308 ret = 0;
1309 goto err_out1;
1310 } else if (opts.show_version) {
1311 printf("FUSE library version %s\n", fuse_pkgversion());
1313 ret = 0;
1314 goto err_out1;
1315 }
1316
1317 if(opts.mountpoint == NULL) {
1318 printf("usage: %s [options] <mountpoint>\n", argv[0]);
1319 printf(" %s --help\n", argv[0]);
1320 ret = 1;
1321 goto err_out1;
1322 }
1323
1324 if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
1325 return 1;
1326
1327 lo.debug = opts.debug;
1328 lo.root.refcount = 2;
1329 if (lo.source) {
1330 struct stat stat;
1331 int res;
1332
1333 res = lstat(lo.source, &stat);
1334 if (res == -1) {
1335 fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
1336 lo.source);
1337 exit(1);
1338 }
1339 if (!S_ISDIR(stat.st_mode)) {
1340 fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
1341 exit(1);
1342 }
1343
1344 } else {
1345 lo.source = strdup("/");
1346 if(!lo.source) {
1347 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
1348 exit(1);
1349 }
1350 }
1351 if (!lo.timeout_set) {
1352 switch (lo.cache) {
1353 case CACHE_NEVER:
1354 lo.timeout = 0.0;
1355 break;
1356
1357 case CACHE_NORMAL:
1358 lo.timeout = 1.0;
1359 break;
1360
1361 case CACHE_ALWAYS:
1362 lo.timeout = 86400.0;
1363 break;
1364 }
1365 } else if (lo.timeout < 0) {
1366 fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
1367 lo.timeout);
1368 exit(1);
1369 }
1370
1371 lo.root.fd = open(lo.source, O_PATH);
1372 if (lo.root.fd == -1) {
1373 fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
1374 lo.source);
1375 exit(1);
1376 }
1377
1378 se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
1379 if (se == NULL)
1380 goto err_out1;
1381
1382 if (fuse_set_signal_handlers(se) != 0)
1383 goto err_out2;
1384
1385 if (fuse_session_mount(se, opts.mountpoint) != 0)
1386 goto err_out3;
1387
1388 fuse_daemonize(opts.foreground);
1389
1390 /* Block until ctrl+c or fusermount -u */
1391 if (opts.singlethread)
1392 ret = fuse_session_loop(se);
1393 else {
1394 config = fuse_loop_cfg_create();
1395 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
1396 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
1397 ret = fuse_session_loop_mt(se, config);
1398 fuse_loop_cfg_destroy(config);
1399 config = NULL;
1400 }
1401
1403err_out3:
1405err_out2:
1407err_out1:
1408 free(opts.mountpoint);
1409 fuse_opt_free_args(&args);
1410
1411 if (lo.root.fd >= 0)
1412 close(lo.root.fd);
1413
1414 free(lo.source);
1415 return ret ? 1 : 0;
1416}
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
off_t pos
size_t size
struct fuse_buf buf[1]
uint32_t no_interrupt
uint32_t capable
double entry_timeout
fuse_ino_t ino
double attr_timeout
struct stat attr
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/example_2poll_8c_source.html0000644000175000017500000014323415156613442020647 0ustar berndbernd libfuse: example/poll.c Source File
libfuse
poll.c
Go to the documentation of this file.
1/*
2 FUSE fsel: FUSE select example
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
24#define FUSE_USE_VERSION 31
25
26#include <fuse.h>
27#include <unistd.h>
28#include <ctype.h>
29#include <string.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <time.h>
34#include <pthread.h>
35#include <poll.h>
36#include <stdbool.h>
37
38/*
39 * fsel_open_mask is used to limit the number of opens to 1 per file.
40 * This is to use file index (0-F) as fh as poll support requires
41 * unique fh per open file. Lifting this would require proper open
42 * file management.
43 */
44static unsigned fsel_open_mask;
45static const char fsel_hex_map[] = "0123456789ABCDEF";
46static struct fuse *fsel_fuse; /* needed for poll notification */
47
48#define FSEL_CNT_MAX 10 /* each file can store up to 10 chars */
49#define FSEL_FILES 16
50
51static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
52static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
53static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
54static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
55static _Atomic bool fsel_stop = false;
56static pthread_t fsel_producer_thread;
57
58
59static int fsel_path_index(const char *path)
60{
61 char ch = path[1];
62
63 if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
64 return -1;
65 return ch <= '9' ? ch - '0' : ch - 'A' + 10;
66}
67
68static void fsel_destroy(void *private_data)
69{
70 (void)private_data;
71
72 fsel_stop = true;
73
74 pthread_join(fsel_producer_thread, NULL);
75}
76
77static int fsel_getattr(const char *path, struct stat *stbuf,
78 struct fuse_file_info *fi)
79{
80 (void) fi;
81 int idx;
82
83 memset(stbuf, 0, sizeof(struct stat));
84
85 if (strcmp(path, "/") == 0) {
86 stbuf->st_mode = S_IFDIR | 0555;
87 stbuf->st_nlink = 2;
88 return 0;
89 }
90
91 idx = fsel_path_index(path);
92 if (idx < 0)
93 return -ENOENT;
94
95 stbuf->st_mode = S_IFREG | 0444;
96 stbuf->st_nlink = 1;
97 stbuf->st_size = fsel_cnt[idx];
98 return 0;
99}
100
101static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
102 off_t offset, struct fuse_file_info *fi,
103 enum fuse_readdir_flags flags)
104{
105 char name[2] = { };
106 int i;
107
108 (void) offset;
109 (void) fi;
110 (void) flags;
111
112 if (strcmp(path, "/") != 0)
113 return -ENOENT;
114
115 for (i = 0; i < FSEL_FILES; i++) {
116 name[0] = fsel_hex_map[i];
117 filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
118 }
119
120 return 0;
121}
122
123static int fsel_open(const char *path, struct fuse_file_info *fi)
124{
125 int idx = fsel_path_index(path);
126
127 if (idx < 0)
128 return -ENOENT;
129 if ((fi->flags & O_ACCMODE) != O_RDONLY)
130 return -EACCES;
131 if (fsel_open_mask & (1 << idx))
132 return -EBUSY;
133 fsel_open_mask |= (1 << idx);
134
135 /*
136 * fsel files are nonseekable somewhat pipe-like files which
137 * gets filled up periodically by producer thread and consumed
138 * on read. Tell FUSE as such.
139 */
140 fi->fh = idx;
141 fi->direct_io = 1;
142 fi->nonseekable = 1;
143
144 return 0;
145}
146
147static int fsel_release(const char *path, struct fuse_file_info *fi)
148{
149 int idx = fi->fh;
150
151 (void) path;
152
153 fsel_open_mask &= ~(1 << idx);
154 return 0;
155}
156
157static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
158 struct fuse_file_info *fi)
159{
160 int idx = fi->fh;
161
162 (void) path;
163 (void) offset;
164
165 pthread_mutex_lock(&fsel_mutex);
166 if (fsel_cnt[idx] < size)
167 size = fsel_cnt[idx];
168 printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
169 fsel_cnt[idx] -= size;
170 pthread_mutex_unlock(&fsel_mutex);
171
172 memset(buf, fsel_hex_map[idx], size);
173 return size;
174}
175
176static int fsel_poll(const char *path, struct fuse_file_info *fi,
177 struct fuse_pollhandle *ph, unsigned *reventsp)
178{
179 static unsigned polled_zero;
180 int idx = fi->fh;
181
182 (void) path;
183
184 /*
185 * Poll notification requires pointer to struct fuse which
186 * can't be obtained when using fuse_main(). As notification
187 * happens only after poll is called, fill it here from
188 * fuse_context.
189 */
190 if (!fsel_fuse) {
191 struct fuse_context *cxt = fuse_get_context();
192 if (cxt)
193 fsel_fuse = cxt->fuse;
194 }
195
196 pthread_mutex_lock(&fsel_mutex);
197
198 if (ph != NULL) {
199 struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
200
201 if (oldph)
203
204 fsel_poll_notify_mask |= (1 << idx);
205 fsel_poll_handle[idx] = ph;
206 }
207
208 if (fsel_cnt[idx]) {
209 *reventsp |= POLLIN;
210 printf("POLL %X cnt=%u polled_zero=%u\n",
211 idx, fsel_cnt[idx], polled_zero);
212 polled_zero = 0;
213 } else
214 polled_zero++;
215
216 pthread_mutex_unlock(&fsel_mutex);
217 return 0;
218}
219
220static const struct fuse_operations fsel_oper = {
221 .destroy = fsel_destroy,
222 .getattr = fsel_getattr,
223 .readdir = fsel_readdir,
224 .open = fsel_open,
225 .release = fsel_release,
226 .read = fsel_read,
227 .poll = fsel_poll,
228};
229
230static void *fsel_producer(void *data)
231{
232 const struct timespec interval = { 0, 250000000 };
233 unsigned idx = 0, nr = 1;
234
235 (void) data;
236
237 while (!fsel_stop) {
238 int i, t;
239
240 pthread_mutex_lock(&fsel_mutex);
241
242 /*
243 * This is the main producer loop which is executed
244 * ever 500ms. On each iteration, it fills one byte
245 * to 1, 2 or 4 files and sends poll notification if
246 * requested.
247 */
248 for (i = 0, t = idx; i < nr;
249 i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
250 if (fsel_cnt[t] == FSEL_CNT_MAX)
251 continue;
252
253 fsel_cnt[t]++;
254 if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
255 struct fuse_pollhandle *ph;
256
257 printf("NOTIFY %X\n", t);
258 ph = fsel_poll_handle[t];
259 fuse_notify_poll(ph);
261 fsel_poll_notify_mask &= ~(1 << t);
262 fsel_poll_handle[t] = NULL;
263 }
264 }
265
266 idx = (idx + 1) % FSEL_FILES;
267 if (idx == 0)
268 nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
269
270 pthread_mutex_unlock(&fsel_mutex);
271
272 nanosleep(&interval, NULL);
273 }
274
275 return NULL;
276}
277
278int main(int argc, char *argv[])
279{
280 pthread_attr_t attr;
281 int ret;
282
283 errno = pthread_mutex_init(&fsel_mutex, NULL);
284 if (errno) {
285 perror("pthread_mutex_init");
286 return 1;
287 }
288
289 errno = pthread_attr_init(&attr);
290 if (errno) {
291 perror("pthread_attr_init");
292 return 1;
293 }
294
295 errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL);
296 if (errno) {
297 perror("pthread_create");
298 return 1;
299 }
300
301 ret = fuse_main(argc, argv, &fsel_oper, NULL);
302
303 return ret;
304}
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
struct fuse * fuse
Definition fuse.h:862
uint32_t nonseekable
Definition fuse_common.h:78
uint32_t direct_io
Definition fuse_common.h:63
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/fuse-3_817_84_2example_2poll_8c_source.html0000644000175000017500000014343515156613442023030 0ustar berndbernd libfuse: fuse-3.17.4/example/poll.c Source File
libfuse
poll.c
Go to the documentation of this file.
1/*
2 FUSE fsel: FUSE select example
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file COPYING.
8*/
9
24#define FUSE_USE_VERSION 31
25
26#include <fuse.h>
27#include <unistd.h>
28#include <ctype.h>
29#include <string.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <time.h>
34#include <pthread.h>
35#include <poll.h>
36#include <stdbool.h>
37
38/*
39 * fsel_open_mask is used to limit the number of opens to 1 per file.
40 * This is to use file index (0-F) as fh as poll support requires
41 * unique fh per open file. Lifting this would require proper open
42 * file management.
43 */
44static unsigned fsel_open_mask;
45static const char fsel_hex_map[] = "0123456789ABCDEF";
46static struct fuse *fsel_fuse; /* needed for poll notification */
47
48#define FSEL_CNT_MAX 10 /* each file can store up to 10 chars */
49#define FSEL_FILES 16
50
51static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
52static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
53static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
54static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
55static _Atomic bool fsel_stop = false;
56static pthread_t fsel_producer_thread;
57
58
59static int fsel_path_index(const char *path)
60{
61 char ch = path[1];
62
63 if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
64 return -1;
65 return ch <= '9' ? ch - '0' : ch - 'A' + 10;
66}
67
68static void fsel_destroy(void *private_data)
69{
70 (void)private_data;
71
72 fsel_stop = true;
73
74 pthread_join(fsel_producer_thread, NULL);
75}
76
77static int fsel_getattr(const char *path, struct stat *stbuf,
78 struct fuse_file_info *fi)
79{
80 (void) fi;
81 int idx;
82
83 memset(stbuf, 0, sizeof(struct stat));
84
85 if (strcmp(path, "/") == 0) {
86 stbuf->st_mode = S_IFDIR | 0555;
87 stbuf->st_nlink = 2;
88 return 0;
89 }
90
91 idx = fsel_path_index(path);
92 if (idx < 0)
93 return -ENOENT;
94
95 stbuf->st_mode = S_IFREG | 0444;
96 stbuf->st_nlink = 1;
97 stbuf->st_size = fsel_cnt[idx];
98 return 0;
99}
100
101static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
102 off_t offset, struct fuse_file_info *fi,
103 enum fuse_readdir_flags flags)
104{
105 char name[2] = { };
106 int i;
107
108 (void) offset;
109 (void) fi;
110 (void) flags;
111
112 if (strcmp(path, "/") != 0)
113 return -ENOENT;
114
115 for (i = 0; i < FSEL_FILES; i++) {
116 name[0] = fsel_hex_map[i];
117 filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
118 }
119
120 return 0;
121}
122
123static int fsel_open(const char *path, struct fuse_file_info *fi)
124{
125 int idx = fsel_path_index(path);
126
127 if (idx < 0)
128 return -ENOENT;
129 if ((fi->flags & O_ACCMODE) != O_RDONLY)
130 return -EACCES;
131 if (fsel_open_mask & (1 << idx))
132 return -EBUSY;
133 fsel_open_mask |= (1 << idx);
134
135 /*
136 * fsel files are nonseekable somewhat pipe-like files which
137 * gets filled up periodically by producer thread and consumed
138 * on read. Tell FUSE as such.
139 */
140 fi->fh = idx;
141 fi->direct_io = 1;
142 fi->nonseekable = 1;
143
144 return 0;
145}
146
147static int fsel_release(const char *path, struct fuse_file_info *fi)
148{
149 int idx = fi->fh;
150
151 (void) path;
152
153 fsel_open_mask &= ~(1 << idx);
154 return 0;
155}
156
157static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
158 struct fuse_file_info *fi)
159{
160 int idx = fi->fh;
161
162 (void) path;
163 (void) offset;
164
165 pthread_mutex_lock(&fsel_mutex);
166 if (fsel_cnt[idx] < size)
167 size = fsel_cnt[idx];
168 printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
169 fsel_cnt[idx] -= size;
170 pthread_mutex_unlock(&fsel_mutex);
171
172 memset(buf, fsel_hex_map[idx], size);
173 return size;
174}
175
176static int fsel_poll(const char *path, struct fuse_file_info *fi,
177 struct fuse_pollhandle *ph, unsigned *reventsp)
178{
179 static unsigned polled_zero;
180 int idx = fi->fh;
181
182 (void) path;
183
184 /*
185 * Poll notification requires pointer to struct fuse which
186 * can't be obtained when using fuse_main(). As notification
187 * happens only after poll is called, fill it here from
188 * fuse_context.
189 */
190 if (!fsel_fuse) {
191 struct fuse_context *cxt = fuse_get_context();
192 if (cxt)
193 fsel_fuse = cxt->fuse;
194 }
195
196 pthread_mutex_lock(&fsel_mutex);
197
198 if (ph != NULL) {
199 struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
200
201 if (oldph)
203
204 fsel_poll_notify_mask |= (1 << idx);
205 fsel_poll_handle[idx] = ph;
206 }
207
208 if (fsel_cnt[idx]) {
209 *reventsp |= POLLIN;
210 printf("POLL %X cnt=%u polled_zero=%u\n",
211 idx, fsel_cnt[idx], polled_zero);
212 polled_zero = 0;
213 } else
214 polled_zero++;
215
216 pthread_mutex_unlock(&fsel_mutex);
217 return 0;
218}
219
220static const struct fuse_operations fsel_oper = {
221 .destroy = fsel_destroy,
222 .getattr = fsel_getattr,
223 .readdir = fsel_readdir,
224 .open = fsel_open,
225 .release = fsel_release,
226 .read = fsel_read,
227 .poll = fsel_poll,
228};
229
230static void *fsel_producer(void *data)
231{
232 const struct timespec interval = { 0, 250000000 };
233 unsigned idx = 0, nr = 1;
234
235 (void) data;
236
237 while (!fsel_stop) {
238 int i, t;
239
240 pthread_mutex_lock(&fsel_mutex);
241
242 /*
243 * This is the main producer loop which is executed
244 * ever 500ms. On each iteration, it fills one byte
245 * to 1, 2 or 4 files and sends poll notification if
246 * requested.
247 */
248 for (i = 0, t = idx; i < nr;
249 i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
250 if (fsel_cnt[t] == FSEL_CNT_MAX)
251 continue;
252
253 fsel_cnt[t]++;
254 if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
255 struct fuse_pollhandle *ph;
256
257 printf("NOTIFY %X\n", t);
258 ph = fsel_poll_handle[t];
259 fuse_notify_poll(ph);
261 fsel_poll_notify_mask &= ~(1 << t);
262 fsel_poll_handle[t] = NULL;
263 }
264 }
265
266 idx = (idx + 1) % FSEL_FILES;
267 if (idx == 0)
268 nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
269
270 pthread_mutex_unlock(&fsel_mutex);
271
272 nanosleep(&interval, NULL);
273 }
274
275 return NULL;
276}
277
278int main(int argc, char *argv[])
279{
280 pthread_attr_t attr;
281 int ret;
282
283 errno = pthread_mutex_init(&fsel_mutex, NULL);
284 if (errno) {
285 perror("pthread_mutex_init");
286 return 1;
287 }
288
289 errno = pthread_attr_init(&attr);
290 if (errno) {
291 perror("pthread_attr_init");
292 return 1;
293 }
294
295 errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL);
296 if (errno) {
297 perror("pthread_create");
298 return 1;
299 }
300
301 ret = fuse_main(argc, argv, &fsel_oper, NULL);
302
303 return ret;
304}
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
struct fuse * fuse
Definition fuse.h:862
uint32_t nonseekable
Definition fuse_common.h:78
uint32_t direct_io
Definition fuse_common.h:63
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2poll_8c_source.html0000644000175000017500000014343615156613442023027 0ustar berndbernd libfuse: fuse-3.18.1/example/poll.c Source File
libfuse
poll.c
Go to the documentation of this file.
1/*
2 FUSE fsel: FUSE select example
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
24#define FUSE_USE_VERSION 31
25
26#include <fuse.h>
27#include <unistd.h>
28#include <ctype.h>
29#include <string.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <time.h>
34#include <pthread.h>
35#include <poll.h>
36#include <stdbool.h>
37
38/*
39 * fsel_open_mask is used to limit the number of opens to 1 per file.
40 * This is to use file index (0-F) as fh as poll support requires
41 * unique fh per open file. Lifting this would require proper open
42 * file management.
43 */
44static unsigned fsel_open_mask;
45static const char fsel_hex_map[] = "0123456789ABCDEF";
46static struct fuse *fsel_fuse; /* needed for poll notification */
47
48#define FSEL_CNT_MAX 10 /* each file can store up to 10 chars */
49#define FSEL_FILES 16
50
51static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
52static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
53static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
54static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
55static _Atomic bool fsel_stop = false;
56static pthread_t fsel_producer_thread;
57
58
59static int fsel_path_index(const char *path)
60{
61 char ch = path[1];
62
63 if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
64 return -1;
65 return ch <= '9' ? ch - '0' : ch - 'A' + 10;
66}
67
68static void fsel_destroy(void *private_data)
69{
70 (void)private_data;
71
72 fsel_stop = true;
73
74 pthread_join(fsel_producer_thread, NULL);
75}
76
77static int fsel_getattr(const char *path, struct stat *stbuf,
78 struct fuse_file_info *fi)
79{
80 (void) fi;
81 int idx;
82
83 memset(stbuf, 0, sizeof(struct stat));
84
85 if (strcmp(path, "/") == 0) {
86 stbuf->st_mode = S_IFDIR | 0555;
87 stbuf->st_nlink = 2;
88 return 0;
89 }
90
91 idx = fsel_path_index(path);
92 if (idx < 0)
93 return -ENOENT;
94
95 stbuf->st_mode = S_IFREG | 0444;
96 stbuf->st_nlink = 1;
97 stbuf->st_size = fsel_cnt[idx];
98 return 0;
99}
100
101static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
102 off_t offset, struct fuse_file_info *fi,
103 enum fuse_readdir_flags flags)
104{
105 char name[2] = { };
106 int i;
107
108 (void) offset;
109 (void) fi;
110 (void) flags;
111
112 if (strcmp(path, "/") != 0)
113 return -ENOENT;
114
115 for (i = 0; i < FSEL_FILES; i++) {
116 name[0] = fsel_hex_map[i];
117 filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
118 }
119
120 return 0;
121}
122
123static int fsel_open(const char *path, struct fuse_file_info *fi)
124{
125 int idx = fsel_path_index(path);
126
127 if (idx < 0)
128 return -ENOENT;
129 if ((fi->flags & O_ACCMODE) != O_RDONLY)
130 return -EACCES;
131 if (fsel_open_mask & (1 << idx))
132 return -EBUSY;
133 fsel_open_mask |= (1 << idx);
134
135 /*
136 * fsel files are nonseekable somewhat pipe-like files which
137 * gets filled up periodically by producer thread and consumed
138 * on read. Tell FUSE as such.
139 */
140 fi->fh = idx;
141 fi->direct_io = 1;
142 fi->nonseekable = 1;
143
144 return 0;
145}
146
147static int fsel_release(const char *path, struct fuse_file_info *fi)
148{
149 int idx = fi->fh;
150
151 (void) path;
152
153 fsel_open_mask &= ~(1 << idx);
154 return 0;
155}
156
157static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
158 struct fuse_file_info *fi)
159{
160 int idx = fi->fh;
161
162 (void) path;
163 (void) offset;
164
165 pthread_mutex_lock(&fsel_mutex);
166 if (fsel_cnt[idx] < size)
167 size = fsel_cnt[idx];
168 printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
169 fsel_cnt[idx] -= size;
170 pthread_mutex_unlock(&fsel_mutex);
171
172 memset(buf, fsel_hex_map[idx], size);
173 return size;
174}
175
176static int fsel_poll(const char *path, struct fuse_file_info *fi,
177 struct fuse_pollhandle *ph, unsigned *reventsp)
178{
179 static unsigned polled_zero;
180 int idx = fi->fh;
181
182 (void) path;
183
184 /*
185 * Poll notification requires pointer to struct fuse which
186 * can't be obtained when using fuse_main(). As notification
187 * happens only after poll is called, fill it here from
188 * fuse_context.
189 */
190 if (!fsel_fuse) {
191 struct fuse_context *cxt = fuse_get_context();
192 if (cxt)
193 fsel_fuse = cxt->fuse;
194 }
195
196 pthread_mutex_lock(&fsel_mutex);
197
198 if (ph != NULL) {
199 struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
200
201 if (oldph)
203
204 fsel_poll_notify_mask |= (1 << idx);
205 fsel_poll_handle[idx] = ph;
206 }
207
208 if (fsel_cnt[idx]) {
209 *reventsp |= POLLIN;
210 printf("POLL %X cnt=%u polled_zero=%u\n",
211 idx, fsel_cnt[idx], polled_zero);
212 polled_zero = 0;
213 } else
214 polled_zero++;
215
216 pthread_mutex_unlock(&fsel_mutex);
217 return 0;
218}
219
220static const struct fuse_operations fsel_oper = {
221 .destroy = fsel_destroy,
222 .getattr = fsel_getattr,
223 .readdir = fsel_readdir,
224 .open = fsel_open,
225 .release = fsel_release,
226 .read = fsel_read,
227 .poll = fsel_poll,
228};
229
230static void *fsel_producer(void *data)
231{
232 const struct timespec interval = { 0, 250000000 };
233 unsigned idx = 0, nr = 1;
234
235 (void) data;
236
237 while (!fsel_stop) {
238 int i, t;
239
240 pthread_mutex_lock(&fsel_mutex);
241
242 /*
243 * This is the main producer loop which is executed
244 * ever 500ms. On each iteration, it fills one byte
245 * to 1, 2 or 4 files and sends poll notification if
246 * requested.
247 */
248 for (i = 0, t = idx; i < nr;
249 i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
250 if (fsel_cnt[t] == FSEL_CNT_MAX)
251 continue;
252
253 fsel_cnt[t]++;
254 if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
255 struct fuse_pollhandle *ph;
256
257 printf("NOTIFY %X\n", t);
258 ph = fsel_poll_handle[t];
259 fuse_notify_poll(ph);
261 fsel_poll_notify_mask &= ~(1 << t);
262 fsel_poll_handle[t] = NULL;
263 }
264 }
265
266 idx = (idx + 1) % FSEL_FILES;
267 if (idx == 0)
268 nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
269
270 pthread_mutex_unlock(&fsel_mutex);
271
272 nanosleep(&interval, NULL);
273 }
274
275 return NULL;
276}
277
278int main(int argc, char *argv[])
279{
280 pthread_attr_t attr;
281 int ret;
282
283 errno = pthread_mutex_init(&fsel_mutex, NULL);
284 if (errno) {
285 perror("pthread_mutex_init");
286 return 1;
287 }
288
289 errno = pthread_attr_init(&attr);
290 if (errno) {
291 perror("pthread_attr_init");
292 return 1;
293 }
294
295 errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL);
296 if (errno) {
297 perror("pthread_create");
298 return 1;
299 }
300
301 ret = fuse_main(argc, argv, &fsel_oper, NULL);
302
303 return ret;
304}
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
struct fuse * fuse
Definition fuse.h:862
uint32_t nonseekable
Definition fuse_common.h:78
uint32_t direct_io
Definition fuse_common.h:63
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/example_2poll__client_8c_source.html0000644000175000017500000003063615156613442022345 0ustar berndbernd libfuse: example/poll_client.c Source File
libfuse
poll_client.c
Go to the documentation of this file.
1/*
2 FUSE fselclient: FUSE select example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
23#include <sys/select.h>
24#include <sys/time.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include <ctype.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33
34#define FSEL_FILES 16
35
36int main(void)
37{
38 static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
39 int fds[FSEL_FILES];
40 int i, nfds, tries;
41
42 for (i = 0; i < FSEL_FILES; i++) {
43 char name[] = { hex_map[i], '\0' };
44 fds[i] = open(name, O_RDONLY);
45 if (fds[i] < 0) {
46 perror("open");
47 return 1;
48 }
49 }
50 nfds = fds[FSEL_FILES - 1] + 1;
51
52 for(tries=0; tries < 16; tries++) {
53 static char buf[4096];
54 fd_set rfds;
55 int rc;
56
57 FD_ZERO(&rfds);
58 for (i = 0; i < FSEL_FILES; i++)
59 FD_SET(fds[i], &rfds);
60
61 rc = select(nfds, &rfds, NULL, NULL, NULL);
62
63 if (rc < 0) {
64 perror("select");
65 return 1;
66 }
67
68 for (i = 0; i < FSEL_FILES; i++) {
69 if (!FD_ISSET(fds[i], &rfds)) {
70 printf("_: ");
71 continue;
72 }
73 printf("%X:", i);
74 rc = read(fds[i], buf, sizeof(buf));
75 if (rc < 0) {
76 perror("read");
77 return 1;
78 }
79 printf("%02d ", rc);
80 }
81 printf("\n");
82 }
83 return 0;
84}
fuse-3.18.2/doc/html/fuse-3_817_84_2example_2poll__client_8c_source.html0000644000175000017500000003103715156613442024517 0ustar berndbernd libfuse: fuse-3.17.4/example/poll_client.c Source File
libfuse
poll_client.c
Go to the documentation of this file.
1/*
2 FUSE fselclient: FUSE select example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file COPYING.
8*/
9
23#include <sys/select.h>
24#include <sys/time.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include <ctype.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33
34#define FSEL_FILES 16
35
36int main(void)
37{
38 static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
39 int fds[FSEL_FILES];
40 int i, nfds, tries;
41
42 for (i = 0; i < FSEL_FILES; i++) {
43 char name[] = { hex_map[i], '\0' };
44 fds[i] = open(name, O_RDONLY);
45 if (fds[i] < 0) {
46 perror("open");
47 return 1;
48 }
49 }
50 nfds = fds[FSEL_FILES - 1] + 1;
51
52 for(tries=0; tries < 16; tries++) {
53 static char buf[4096];
54 fd_set rfds;
55 int rc;
56
57 FD_ZERO(&rfds);
58 for (i = 0; i < FSEL_FILES; i++)
59 FD_SET(fds[i], &rfds);
60
61 rc = select(nfds, &rfds, NULL, NULL, NULL);
62
63 if (rc < 0) {
64 perror("select");
65 return 1;
66 }
67
68 for (i = 0; i < FSEL_FILES; i++) {
69 if (!FD_ISSET(fds[i], &rfds)) {
70 printf("_: ");
71 continue;
72 }
73 printf("%X:", i);
74 rc = read(fds[i], buf, sizeof(buf));
75 if (rc < 0) {
76 perror("read");
77 return 1;
78 }
79 printf("%02d ", rc);
80 }
81 printf("\n");
82 }
83 return 0;
84}
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2poll__client_8c_source.html0000644000175000017500000003104015156613442024507 0ustar berndbernd libfuse: fuse-3.18.1/example/poll_client.c Source File
libfuse
poll_client.c
Go to the documentation of this file.
1/*
2 FUSE fselclient: FUSE select example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
23#include <sys/select.h>
24#include <sys/time.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include <ctype.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33
34#define FSEL_FILES 16
35
36int main(void)
37{
38 static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
39 int fds[FSEL_FILES];
40 int i, nfds, tries;
41
42 for (i = 0; i < FSEL_FILES; i++) {
43 char name[] = { hex_map[i], '\0' };
44 fds[i] = open(name, O_RDONLY);
45 if (fds[i] < 0) {
46 perror("open");
47 return 1;
48 }
49 }
50 nfds = fds[FSEL_FILES - 1] + 1;
51
52 for(tries=0; tries < 16; tries++) {
53 static char buf[4096];
54 fd_set rfds;
55 int rc;
56
57 FD_ZERO(&rfds);
58 for (i = 0; i < FSEL_FILES; i++)
59 FD_SET(fds[i], &rfds);
60
61 rc = select(nfds, &rfds, NULL, NULL, NULL);
62
63 if (rc < 0) {
64 perror("select");
65 return 1;
66 }
67
68 for (i = 0; i < FSEL_FILES; i++) {
69 if (!FD_ISSET(fds[i], &rfds)) {
70 printf("_: ");
71 continue;
72 }
73 printf("%X:", i);
74 rc = read(fds[i], buf, sizeof(buf));
75 if (rc < 0) {
76 perror("read");
77 return 1;
78 }
79 printf("%02d ", rc);
80 }
81 printf("\n");
82 }
83 return 0;
84}
fuse-3.18.2/doc/html/example_2printcap_8c_source.html0000644000175000017500000013325515156613442021523 0ustar berndbernd libfuse: example/printcap.c Source File
libfuse
printcap.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2017 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
22#define FUSE_USE_VERSION 31
23
24#include <fuse_lowlevel.h>
25#include <stdio.h>
26#include <unistd.h>
27#include <string.h>
28#include <stdlib.h>
29
30struct fuse_session *se;
31
32// Define a structure to hold capability information
33struct cap_info {
34 uint64_t flag;
35 const char *name;
36};
37
38// Define an array of all capabilities
39static const struct cap_info capabilities[] = {
40 {FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"},
41 {FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"},
42 {FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"},
43 {FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"},
44 {FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"},
45 {FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"},
46 {FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"},
47 {FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"},
48 {FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"},
49 {FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"},
50 {FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"},
51 {FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"},
52 {FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"},
53 {FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"},
54 {FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"},
55 {FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"},
56 {FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"},
57 {FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"},
58 {FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"},
59 {FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"},
60 {FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"},
61 {FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"},
62 {FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"},
63 {FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"},
64 {FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"},
65 {FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"},
66 {FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"},
67 {FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"},
68 // Add any new capabilities here
69 {0, NULL} // Sentinel to mark the end of the array
70};
71
72static void print_capabilities(struct fuse_conn_info *conn)
73{
74 printf("Capabilities:\n");
75 for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) {
76 if (fuse_get_feature_flag(conn, cap->flag)) {
77 printf("\t%s\n", cap->name);
78 }
79 }
80}
81
82static void pc_init(void *userdata, struct fuse_conn_info *conn)
83{
84 (void) userdata;
85
86 printf("Protocol version: %d.%d\n", conn->proto_major,
87 conn->proto_minor);
88 print_capabilities(conn);
90}
91
92
93static const struct fuse_lowlevel_ops pc_oper = {
94 .init = pc_init,
95};
96
97int main(int argc, char **argv)
98{
99 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
100 char *mountpoint;
101 int ret = -1;
102
103 mountpoint = strdup("/tmp/fuse_printcap_XXXXXX");
104 if(mkdtemp(mountpoint) == NULL) {
105 perror("mkdtemp");
106 return 1;
107 }
108
109 printf("FUSE library version %s\n", fuse_pkgversion());
111
112 se = fuse_session_new(&args, &pc_oper,
113 sizeof(pc_oper), NULL);
114 if (se == NULL)
115 goto err_out1;
116
117 if (fuse_set_signal_handlers(se) != 0)
118 goto err_out2;
119
120 if (fuse_session_mount(se, mountpoint) != 0)
121 goto err_out3;
122
123 ret = fuse_session_loop(se);
124
126err_out3:
128err_out2:
130err_out1:
131 rmdir(mountpoint);
132 free(mountpoint);
133 fuse_opt_free_args(&args);
134
135 return ret ? 1 : 0;
136}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t proto_major
uint32_t proto_minor
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_817_84_2example_2printcap_8c_source.html0000644000175000017500000013345615156613442023704 0ustar berndbernd libfuse: fuse-3.17.4/example/printcap.c Source File
libfuse
printcap.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2017 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file COPYING.
7*/
8
22#define FUSE_USE_VERSION 31
23
24#include <fuse_lowlevel.h>
25#include <stdio.h>
26#include <unistd.h>
27#include <string.h>
28#include <stdlib.h>
29
30struct fuse_session *se;
31
32// Define a structure to hold capability information
33struct cap_info {
34 uint64_t flag;
35 const char *name;
36};
37
38// Define an array of all capabilities
39static const struct cap_info capabilities[] = {
40 {FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"},
41 {FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"},
42 {FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"},
43 {FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"},
44 {FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"},
45 {FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"},
46 {FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"},
47 {FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"},
48 {FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"},
49 {FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"},
50 {FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"},
51 {FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"},
52 {FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"},
53 {FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"},
54 {FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"},
55 {FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"},
56 {FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"},
57 {FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"},
58 {FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"},
59 {FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"},
60 {FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"},
61 {FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"},
62 {FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"},
63 {FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"},
64 {FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"},
65 {FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"},
66 {FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"},
67 {FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"},
68 // Add any new capabilities here
69 {0, NULL} // Sentinel to mark the end of the array
70};
71
72static void print_capabilities(struct fuse_conn_info *conn)
73{
74 printf("Capabilities:\n");
75 for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) {
76 if (fuse_get_feature_flag(conn, cap->flag)) {
77 printf("\t%s\n", cap->name);
78 }
79 }
80}
81
82static void pc_init(void *userdata, struct fuse_conn_info *conn)
83{
84 (void) userdata;
85
86 printf("Protocol version: %d.%d\n", conn->proto_major,
87 conn->proto_minor);
88 print_capabilities(conn);
90}
91
92
93static const struct fuse_lowlevel_ops pc_oper = {
94 .init = pc_init,
95};
96
97int main(int argc, char **argv)
98{
99 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
100 char *mountpoint;
101 int ret = -1;
102
103 mountpoint = strdup("/tmp/fuse_printcap_XXXXXX");
104 if(mkdtemp(mountpoint) == NULL) {
105 perror("mkdtemp");
106 return 1;
107 }
108
109 printf("FUSE library version %s\n", fuse_pkgversion());
111
112 se = fuse_session_new(&args, &pc_oper,
113 sizeof(pc_oper), NULL);
114 if (se == NULL)
115 goto err_out1;
116
117 if (fuse_set_signal_handlers(se) != 0)
118 goto err_out2;
119
120 if (fuse_session_mount(se, mountpoint) != 0)
121 goto err_out3;
122
123 ret = fuse_session_loop(se);
124
126err_out3:
128err_out2:
130err_out1:
131 rmdir(mountpoint);
132 free(mountpoint);
133 fuse_opt_free_args(&args);
134
135 return ret ? 1 : 0;
136}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t proto_major
uint32_t proto_minor
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_81_2example_2printcap_8c_source.html0000644000175000017500000013345715156613442023703 0ustar berndbernd libfuse: fuse-3.18.1/example/printcap.c Source File
libfuse
printcap.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2017 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
22#define FUSE_USE_VERSION 31
23
24#include <fuse_lowlevel.h>
25#include <stdio.h>
26#include <unistd.h>
27#include <string.h>
28#include <stdlib.h>
29
30struct fuse_session *se;
31
32// Define a structure to hold capability information
33struct cap_info {
34 uint64_t flag;
35 const char *name;
36};
37
38// Define an array of all capabilities
39static const struct cap_info capabilities[] = {
40 {FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"},
41 {FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"},
42 {FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"},
43 {FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"},
44 {FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"},
45 {FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"},
46 {FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"},
47 {FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"},
48 {FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"},
49 {FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"},
50 {FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"},
51 {FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"},
52 {FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"},
53 {FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"},
54 {FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"},
55 {FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"},
56 {FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"},
57 {FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"},
58 {FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"},
59 {FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"},
60 {FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"},
61 {FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"},
62 {FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"},
63 {FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"},
64 {FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"},
65 {FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"},
66 {FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"},
67 {FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"},
68 // Add any new capabilities here
69 {0, NULL} // Sentinel to mark the end of the array
70};
71
72static void print_capabilities(struct fuse_conn_info *conn)
73{
74 printf("Capabilities:\n");
75 for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) {
76 if (fuse_get_feature_flag(conn, cap->flag)) {
77 printf("\t%s\n", cap->name);
78 }
79 }
80}
81
82static void pc_init(void *userdata, struct fuse_conn_info *conn)
83{
84 (void) userdata;
85
86 printf("Protocol version: %d.%d\n", conn->proto_major,
87 conn->proto_minor);
88 print_capabilities(conn);
90}
91
92
93static const struct fuse_lowlevel_ops pc_oper = {
94 .init = pc_init,
95};
96
97int main(int argc, char **argv)
98{
99 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
100 char *mountpoint;
101 int ret = -1;
102
103 mountpoint = strdup("/tmp/fuse_printcap_XXXXXX");
104 if(mkdtemp(mountpoint) == NULL) {
105 perror("mkdtemp");
106 return 1;
107 }
108
109 printf("FUSE library version %s\n", fuse_pkgversion());
111
112 se = fuse_session_new(&args, &pc_oper,
113 sizeof(pc_oper), NULL);
114 if (se == NULL)
115 goto err_out1;
116
117 if (fuse_set_signal_handlers(se) != 0)
118 goto err_out2;
119
120 if (fuse_session_mount(se, mountpoint) != 0)
121 goto err_out3;
122
123 ret = fuse_session_loop(se);
124
126err_out3:
128err_out2:
130err_out1:
131 rmdir(mountpoint);
132 free(mountpoint);
133 fuse_opt_free_args(&args);
134
135 return ret ? 1 : 0;
136}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t proto_major
uint32_t proto_minor
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_817_84_2include_2cuse__lowlevel_8h_source.html0000644000175000017500000004601315156613442025060 0ustar berndbernd libfuse: fuse-3.17.4/include/cuse_lowlevel.h Source File
libfuse
cuse_lowlevel.h
1/*
2 CUSE: Character device in Userspace
3 Copyright (C) 2008-2009 SUSE Linux Products GmbH
4 Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
5
6 This program can be distributed under the terms of the GNU LGPLv2.
7 See the file COPYING.LIB.
8
9 Read example/cusexmp.c for usages.
10*/
11
12#ifndef CUSE_LOWLEVEL_H_
13#define CUSE_LOWLEVEL_H_
14
15#ifndef FUSE_USE_VERSION
16#define FUSE_USE_VERSION 29
17#endif
18
19#include "fuse_lowlevel.h"
20
21#include <fcntl.h>
22#include <sys/types.h>
23#include <sys/uio.h>
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */
30
31struct fuse_session;
32
33struct cuse_info {
34 unsigned dev_major;
35 unsigned dev_minor;
36 unsigned dev_info_argc;
37 const char **dev_info_argv;
38 unsigned flags;
39};
40
41/*
42 * Most ops behave almost identically to the matching fuse_lowlevel
43 * ops except that they don't take @ino.
44 *
45 * init_done : called after initialization is complete
46 * read/write : always direct IO, simultaneous operations allowed
47 * ioctl : might be in unrestricted mode depending on ci->flags
48 */
49struct cuse_lowlevel_ops {
50 void (*init) (void *userdata, struct fuse_conn_info *conn);
51 void (*init_done) (void *userdata);
52 void (*destroy) (void *userdata);
53 void (*open) (fuse_req_t req, struct fuse_file_info *fi);
54 void (*read) (fuse_req_t req, size_t size, off_t off,
55 struct fuse_file_info *fi);
56 void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off,
57 struct fuse_file_info *fi);
58 void (*flush) (fuse_req_t req, struct fuse_file_info *fi);
59 void (*release) (fuse_req_t req, struct fuse_file_info *fi);
60 void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi);
61 void (*ioctl) (fuse_req_t req, int cmd, void *arg,
62 struct fuse_file_info *fi, unsigned int flags,
63 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
64 void (*poll) (fuse_req_t req, struct fuse_file_info *fi,
65 struct fuse_pollhandle *ph);
66};
67
68struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
69 const struct cuse_info *ci,
70 const struct cuse_lowlevel_ops *clop,
71 void *userdata);
72
73struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
74 const struct cuse_info *ci,
75 const struct cuse_lowlevel_ops *clop,
76 int *multithreaded, void *userdata);
77
78void cuse_lowlevel_teardown(struct fuse_session *se);
79
80int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
81 const struct cuse_lowlevel_ops *clop, void *userdata);
82
83#ifdef __cplusplus
84}
85#endif
86
87#endif /* CUSE_LOWLEVEL_H_ */
struct fuse_req * fuse_req_t
fuse-3.18.2/doc/html/fuse-3_818_81_2include_2cuse__lowlevel_8h_source.html0000644000175000017500000004601115156613442025054 0ustar berndbernd libfuse: fuse-3.18.1/include/cuse_lowlevel.h Source File
libfuse
cuse_lowlevel.h
1/*
2 CUSE: Character device in Userspace
3 Copyright (C) 2008-2009 SUSE Linux Products GmbH
4 Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
5
6 This program can be distributed under the terms of the GNU LGPLv2.
7 See the file LGPL2.txt.
8
9 Read example/cusexmp.c for usages.
10*/
11
12#ifndef CUSE_LOWLEVEL_H_
13#define CUSE_LOWLEVEL_H_
14
15#ifndef FUSE_USE_VERSION
16#define FUSE_USE_VERSION 29
17#endif
18
19#include "fuse_lowlevel.h"
20
21#include <fcntl.h>
22#include <sys/types.h>
23#include <sys/uio.h>
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */
30
31struct fuse_session;
32
33struct cuse_info {
34 unsigned dev_major;
35 unsigned dev_minor;
36 unsigned dev_info_argc;
37 const char **dev_info_argv;
38 unsigned flags;
39};
40
41/*
42 * Most ops behave almost identically to the matching fuse_lowlevel
43 * ops except that they don't take @ino.
44 *
45 * init_done : called after initialization is complete
46 * read/write : always direct IO, simultaneous operations allowed
47 * ioctl : might be in unrestricted mode depending on ci->flags
48 */
49struct cuse_lowlevel_ops {
50 void (*init) (void *userdata, struct fuse_conn_info *conn);
51 void (*init_done) (void *userdata);
52 void (*destroy) (void *userdata);
53 void (*open) (fuse_req_t req, struct fuse_file_info *fi);
54 void (*read) (fuse_req_t req, size_t size, off_t off,
55 struct fuse_file_info *fi);
56 void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off,
57 struct fuse_file_info *fi);
58 void (*flush) (fuse_req_t req, struct fuse_file_info *fi);
59 void (*release) (fuse_req_t req, struct fuse_file_info *fi);
60 void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi);
61 void (*ioctl) (fuse_req_t req, int cmd, void *arg,
62 struct fuse_file_info *fi, unsigned int flags,
63 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
64 void (*poll) (fuse_req_t req, struct fuse_file_info *fi,
65 struct fuse_pollhandle *ph);
66};
67
68struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
69 const struct cuse_info *ci,
70 const struct cuse_lowlevel_ops *clop,
71 void *userdata);
72
73struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
74 const struct cuse_info *ci,
75 const struct cuse_lowlevel_ops *clop,
76 int *multithreaded, void *userdata);
77
78void cuse_lowlevel_teardown(struct fuse_session *se);
79
80int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
81 const struct cuse_lowlevel_ops *clop, void *userdata);
82
83#ifdef __cplusplus
84}
85#endif
86
87#endif /* CUSE_LOWLEVEL_H_ */
struct fuse_req * fuse_req_t
fuse-3.18.2/doc/html/include_2cuse__lowlevel_8h_source.html0000644000175000017500000004562615156613442022713 0ustar berndbernd libfuse: include/cuse_lowlevel.h Source File
libfuse
cuse_lowlevel.h
1/*
2 CUSE: Character device in Userspace
3 Copyright (C) 2008-2009 SUSE Linux Products GmbH
4 Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
5
6 This program can be distributed under the terms of the GNU LGPLv2.
7 See the file LGPL2.txt.
8
9 Read example/cusexmp.c for usages.
10*/
11
12#ifndef CUSE_LOWLEVEL_H_
13#define CUSE_LOWLEVEL_H_
14
15#ifndef FUSE_USE_VERSION
16#define FUSE_USE_VERSION 29
17#endif
18
19#include "fuse_lowlevel.h"
20
21#include <fcntl.h>
22#include <sys/types.h>
23#include <sys/uio.h>
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */
30
31struct fuse_session;
32
33struct cuse_info {
34 unsigned dev_major;
35 unsigned dev_minor;
36 unsigned dev_info_argc;
37 const char **dev_info_argv;
38 unsigned flags;
39};
40
41/*
42 * Most ops behave almost identically to the matching fuse_lowlevel
43 * ops except that they don't take @ino.
44 *
45 * init_done : called after initialization is complete
46 * read/write : always direct IO, simultaneous operations allowed
47 * ioctl : might be in unrestricted mode depending on ci->flags
48 */
49struct cuse_lowlevel_ops {
50 void (*init) (void *userdata, struct fuse_conn_info *conn);
51 void (*init_done) (void *userdata);
52 void (*destroy) (void *userdata);
53 void (*open) (fuse_req_t req, struct fuse_file_info *fi);
54 void (*read) (fuse_req_t req, size_t size, off_t off,
55 struct fuse_file_info *fi);
56 void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off,
57 struct fuse_file_info *fi);
58 void (*flush) (fuse_req_t req, struct fuse_file_info *fi);
59 void (*release) (fuse_req_t req, struct fuse_file_info *fi);
60 void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi);
61 void (*ioctl) (fuse_req_t req, int cmd, void *arg,
62 struct fuse_file_info *fi, unsigned int flags,
63 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
64 void (*poll) (fuse_req_t req, struct fuse_file_info *fi,
65 struct fuse_pollhandle *ph);
66};
67
68struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
69 const struct cuse_info *ci,
70 const struct cuse_lowlevel_ops *clop,
71 void *userdata);
72
73struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
74 const struct cuse_info *ci,
75 const struct cuse_lowlevel_ops *clop,
76 int *multithreaded, void *userdata);
77
78void cuse_lowlevel_teardown(struct fuse_session *se);
79
80int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
81 const struct cuse_lowlevel_ops *clop, void *userdata);
82
83#ifdef __cplusplus
84}
85#endif
86
87#endif /* CUSE_LOWLEVEL_H_ */
struct fuse_req * fuse_req_t
fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse_8h_source.html0000644000175000017500000043751315156613442023024 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse.h Source File
libfuse
fuse.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
7*/
8
9#ifndef FUSE_H_
10#define FUSE_H_
11
19#include "fuse_common.h"
20
21#include <fcntl.h>
22#include <time.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <sys/statvfs.h>
26#include <sys/uio.h>
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32/* ----------------------------------------------------------- *
33 * Basic FUSE API *
34 * ----------------------------------------------------------- */
35
37struct fuse;
38
52 FUSE_READDIR_PLUS = (1 << 0)
53};
54
69 FUSE_FILL_DIR_PLUS = (1 << 1)
70};
71
87typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
88 const struct stat *stbuf, off_t off,
89 enum fuse_fill_dir_flags flags);
106 int32_t set_gid;
107 uint32_t gid;
108
113 int32_t set_uid;
114 uint32_t uid;
115
120 int32_t set_mode;
121 uint32_t umask;
122
128
138
144
148 int32_t intr;
149
155 int32_t intr_signal;
156
167 int32_t remember;
168
185 int32_t hard_remove;
186
198 int32_t use_ino;
199
207 int32_t readdir_ino;
208
226 int32_t direct_io;
227
246
253 int32_t auto_cache;
254
255 /*
256 * The timeout in seconds for which file attributes are cached
257 * for the purpose of checking if auto_cache should flush the
258 * file data on open.
259 */
260 int32_t ac_attr_timeout_set;
261 double ac_attr_timeout;
262
273 int32_t nullpath_ok;
274
279 int32_t show_help;
280 char *modules;
281 int32_t debug;
282
288 uint32_t fmask;
289 uint32_t dmask;
290
298
313
314
318 uint32_t flags;
319
323 uint64_t reserved[48];
324};
325
326
361 int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi);
362
371 int (*readlink) (const char *, char *, size_t);
372
379 int (*mknod) (const char *, mode_t, dev_t);
380
387 int (*mkdir) (const char *, mode_t);
388
390 int (*unlink) (const char *);
391
393 int (*rmdir) (const char *);
394
396 int (*symlink) (const char *, const char *);
397
407 int (*rename) (const char *, const char *, unsigned int flags);
408
410 int (*link) (const char *, const char *);
411
417 int (*chmod) (const char *, mode_t, struct fuse_file_info *fi);
418
427 int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi);
428
437 int (*truncate) (const char *, off_t, struct fuse_file_info *fi);
438
486 int (*open) (const char *, struct fuse_file_info *);
487
497 int (*read) (const char *, char *, size_t, off_t,
498 struct fuse_file_info *);
499
509 int (*write) (const char *, const char *, size_t, off_t,
510 struct fuse_file_info *);
511
516 int (*statfs) (const char *, struct statvfs *);
517
546 int (*flush) (const char *, struct fuse_file_info *);
547
560 int (*release) (const char *, struct fuse_file_info *);
561
567 int (*fsync) (const char *, int, struct fuse_file_info *);
568
570 int (*setxattr) (const char *, const char *, const char *, size_t, int);
571
573 int (*getxattr) (const char *, const char *, char *, size_t);
574
576 int (*listxattr) (const char *, char *, size_t);
577
579 int (*removexattr) (const char *, const char *);
580
589 int (*opendir) (const char *, struct fuse_file_info *);
590
613 int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
614 struct fuse_file_info *, enum fuse_readdir_flags);
615
621 int (*releasedir) (const char *, struct fuse_file_info *);
622
631 int (*fsyncdir) (const char *, int, struct fuse_file_info *);
632
641 void *(*init) (struct fuse_conn_info *conn,
642 struct fuse_config *cfg);
643
649 void (*destroy) (void *private_data);
650
660 int (*access) (const char *, int);
661
672 int (*create) (const char *, mode_t, struct fuse_file_info *);
673
704 int (*lock) (const char *, struct fuse_file_info *, int cmd,
705 struct flock *);
706
719 int (*utimens) (const char *, const struct timespec tv[2],
720 struct fuse_file_info *fi);
721
728 int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
729
730#if FUSE_USE_VERSION < 35
731 int (*ioctl) (const char *, int cmd, void *arg,
732 struct fuse_file_info *, unsigned int flags, void *data);
733#else
750 int (*ioctl) (const char *, unsigned int cmd, void *arg,
751 struct fuse_file_info *, unsigned int flags, void *data);
752#endif
753
769 int (*poll) (const char *, struct fuse_file_info *,
770 struct fuse_pollhandle *ph, unsigned *reventsp);
771
781 int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off,
782 struct fuse_file_info *);
783
798 int (*read_buf) (const char *, struct fuse_bufvec **bufp,
799 size_t size, off_t off, struct fuse_file_info *);
818 int (*flock) (const char *, struct fuse_file_info *, int op);
819
828 int (*fallocate) (const char *, int, off_t, off_t,
829 struct fuse_file_info *);
830
843 ssize_t (*copy_file_range) (const char *path_in,
844 struct fuse_file_info *fi_in,
845 off_t offset_in, const char *path_out,
846 struct fuse_file_info *fi_out,
847 off_t offset_out, size_t size, int flags);
848
852 off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
853};
854
862 struct fuse *fuse;
863
865 uid_t uid;
866
868 gid_t gid;
869
871 pid_t pid;
872
875
877 mode_t umask;
878};
879
885int fuse_main_real_versioned(int argc, char *argv[],
886 const struct fuse_operations *op, size_t op_size,
887 struct libfuse_version *version, void *user_data);
888static inline int fuse_main_real(int argc, char *argv[],
889 const struct fuse_operations *op,
890 size_t op_size, void *user_data)
891{
892 struct libfuse_version version = { .major = FUSE_MAJOR_VERSION,
893 .minor = FUSE_MINOR_VERSION,
894 .hotfix = FUSE_HOTFIX_VERSION,
895 .padding = 0 };
896
897 fuse_log(FUSE_LOG_ERR,
898 "%s is a libfuse internal function, please use fuse_main()\n",
899 __func__);
900
901 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
902 user_data);
903}
904
959static inline int fuse_main_fn(int argc, char *argv[],
960 const struct fuse_operations *op,
961 void *user_data)
962{
963 struct libfuse_version version = {
964 .major = FUSE_MAJOR_VERSION,
965 .minor = FUSE_MINOR_VERSION,
966 .hotfix = FUSE_HOTFIX_VERSION,
967 .padding = 0
968 };
969
970 return fuse_main_real_versioned(argc, argv, op, sizeof(*(op)), &version,
971 user_data);
972}
973#define fuse_main(argc, argv, op, user_data) \
974 fuse_main_fn(argc, argv, op, user_data)
975
976/* ----------------------------------------------------------- *
977 * More detailed API *
978 * ----------------------------------------------------------- */
979
991void fuse_lib_help(struct fuse_args *args);
992
993/* Do not call this directly, use fuse_new() instead */
994struct fuse *_fuse_new_30(struct fuse_args *args,
995 const struct fuse_operations *op, size_t op_size,
996 struct libfuse_version *version, void *user_data);
997struct fuse *_fuse_new_31(struct fuse_args *args,
998 const struct fuse_operations *op, size_t op_size,
999 struct libfuse_version *version, void *user_data);
1000
1028#if FUSE_USE_VERSION == 30
1029static inline struct fuse *fuse_new_fn(struct fuse_args *args,
1030 const struct fuse_operations *op,
1031 size_t op_size, void *user_data)
1032{
1033 struct libfuse_version version = {
1034 .major = FUSE_MAJOR_VERSION,
1035 .minor = FUSE_MINOR_VERSION,
1036 .hotfix = FUSE_HOTFIX_VERSION,
1037 .padding = 0
1038 };
1039
1040 return _fuse_new_30(args, op, op_size, &version, user_data);
1041}
1042#else /* FUSE_USE_VERSION */
1043static inline struct fuse *fuse_new_fn(struct fuse_args *args,
1044 const struct fuse_operations *op,
1045 size_t op_size, void *user_data)
1046{
1047 struct libfuse_version version = {
1048 .major = FUSE_MAJOR_VERSION,
1049 .minor = FUSE_MINOR_VERSION,
1050 .hotfix = FUSE_HOTFIX_VERSION,
1051 .padding = 0
1052 };
1053
1054 return _fuse_new_31(args, op, op_size, &version, user_data);
1055}
1056#endif
1057#define fuse_new(args, op, size, data) fuse_new_fn(args, op, size, data)
1058
1067int fuse_mount(struct fuse *f, const char *mountpoint);
1068
1076void fuse_unmount(struct fuse *f);
1077
1086void fuse_destroy(struct fuse *f);
1087
1103int fuse_loop(struct fuse *f);
1104
1113void fuse_exit(struct fuse *f);
1114
1115#if FUSE_USE_VERSION < 32
1116int fuse_loop_mt_31(struct fuse *f, int clone_fd);
1117#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd)
1118#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
1119int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
1120#define fuse_loop_mt(f, config) fuse_loop_mt_32(f, config)
1121#else
1153#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
1154int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config);
1155#else
1156#define fuse_loop_mt(f, config) fuse_loop_mt_312(f, config)
1157#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
1158#endif
1159
1160
1169struct fuse_context *fuse_get_context(void);
1170
1189int fuse_getgroups(int size, gid_t list[]);
1190
1196int fuse_interrupted(void);
1197
1209int fuse_invalidate_path(struct fuse *f, const char *path);
1210
1219
1226void fuse_stop_cleanup_thread(struct fuse *fuse);
1227
1237int fuse_clean_cache(struct fuse *fuse);
1238
1239/*
1240 * Stacking API
1241 */
1242
1248struct fuse_fs;
1249
1250/*
1251 * These functions call the relevant filesystem operation, and return
1252 * the result.
1253 *
1254 * If the operation is not defined, they return -ENOSYS, with the
1255 * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
1256 * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
1257 */
1258
1259int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
1260 struct fuse_file_info *fi);
1261int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
1262 const char *newpath, unsigned int flags);
1263int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
1264int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
1265int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
1266 const char *path);
1267int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
1268int fuse_fs_release(struct fuse_fs *fs, const char *path,
1269 struct fuse_file_info *fi);
1270int fuse_fs_open(struct fuse_fs *fs, const char *path,
1271 struct fuse_file_info *fi);
1272int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
1273 off_t off, struct fuse_file_info *fi);
1274int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
1275 struct fuse_bufvec **bufp, size_t size, off_t off,
1276 struct fuse_file_info *fi);
1277int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
1278 size_t size, off_t off, struct fuse_file_info *fi);
1279int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
1280 struct fuse_bufvec *buf, off_t off,
1281 struct fuse_file_info *fi);
1282int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
1283 struct fuse_file_info *fi);
1284int fuse_fs_flush(struct fuse_fs *fs, const char *path,
1285 struct fuse_file_info *fi);
1286int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
1287int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
1288 struct fuse_file_info *fi);
1289int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
1290 fuse_fill_dir_t filler, off_t off,
1291 struct fuse_file_info *fi, enum fuse_readdir_flags flags);
1292int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
1293 struct fuse_file_info *fi);
1294int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
1295 struct fuse_file_info *fi);
1296int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
1297 struct fuse_file_info *fi);
1298int fuse_fs_lock(struct fuse_fs *fs, const char *path,
1299 struct fuse_file_info *fi, int cmd, struct flock *lock);
1300int fuse_fs_flock(struct fuse_fs *fs, const char *path,
1301 struct fuse_file_info *fi, int op);
1302int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
1303 struct fuse_file_info *fi);
1304int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid,
1305 struct fuse_file_info *fi);
1306int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
1307 struct fuse_file_info *fi);
1308int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
1309 const struct timespec tv[2], struct fuse_file_info *fi);
1310int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
1311int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
1312 size_t len);
1313int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
1314 dev_t rdev);
1315int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
1316int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
1317 const char *value, size_t size, int flags);
1318int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
1319 char *value, size_t size);
1320int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
1321 size_t size);
1322int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
1323 const char *name);
1324int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
1325 uint64_t *idx);
1326#if FUSE_USE_VERSION < 35
1327int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd,
1328 void *arg, struct fuse_file_info *fi, unsigned int flags,
1329 void *data);
1330#else
1331int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
1332 void *arg, struct fuse_file_info *fi, unsigned int flags,
1333 void *data);
1334#endif
1335int fuse_fs_poll(struct fuse_fs *fs, const char *path,
1336 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
1337 unsigned *reventsp);
1338int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
1339 off_t offset, off_t length, struct fuse_file_info *fi);
1340ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
1341 struct fuse_file_info *fi_in, off_t off_in,
1342 const char *path_out,
1343 struct fuse_file_info *fi_out, off_t off_out,
1344 size_t len, int flags);
1345off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
1346 struct fuse_file_info *fi);
1347void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
1348 struct fuse_config *cfg);
1349void fuse_fs_destroy(struct fuse_fs *fs);
1350
1351int fuse_notify_poll(struct fuse_pollhandle *ph);
1352
1366struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
1367 void *private_data);
1368
1383typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
1384 struct fuse_fs *fs[]);
1395#define FUSE_REGISTER_MODULE(name_, factory_) \
1396 fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
1397
1399struct fuse_session *fuse_get_session(struct fuse *f);
1400
1409int fuse_open_channel(const char *mountpoint, const char *options);
1410
1411#ifdef __cplusplus
1412}
1413#endif
1414
1415#endif /* FUSE_H_ */
int fuse_getgroups(int size, gid_t list[])
Definition fuse.c:4654
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
int fuse_interrupted(void)
Definition fuse.c:4663
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
Definition helper.c:307
int fuse_start_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4906
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_exit(struct fuse *f)
Definition fuse.c:4639
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4433
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4914
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
@ FUSE_READDIR_DEFAULTS
Definition fuse.h:51
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:93
fuse_readdir_flags
Definition fuse.h:45
uint32_t fmask
Definition fuse.h:288
int32_t show_help
Definition fuse.h:279
uint32_t flags
Definition fuse.h:318
int32_t direct_io
Definition fuse.h:226
int32_t no_rofd_flush
Definition fuse.h:297
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t intr_signal
Definition fuse.h:155
int32_t remember
Definition fuse.h:167
int32_t kernel_cache
Definition fuse.h:245
int32_t readdir_ino
Definition fuse.h:207
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t set_mode
Definition fuse.h:120
int32_t auto_cache
Definition fuse.h:253
uint64_t reserved[48]
Definition fuse.h:323
int32_t intr
Definition fuse.h:148
double negative_timeout
Definition fuse.h:137
int32_t set_gid
Definition fuse.h:106
int32_t set_uid
Definition fuse.h:113
int32_t hard_remove
Definition fuse.h:185
double attr_timeout
Definition fuse.h:143
void * private_data
Definition fuse.h:874
uid_t uid
Definition fuse.h:865
pid_t pid
Definition fuse.h:871
struct fuse * fuse
Definition fuse.h:862
gid_t gid
Definition fuse.h:868
mode_t umask
Definition fuse.h:877
int(* mkdir)(const char *, mode_t)
Definition fuse.h:387
int(* truncate)(const char *, off_t, struct fuse_file_info *fi)
Definition fuse.h:437
int(* mknod)(const char *, mode_t, dev_t)
Definition fuse.h:379
int(* utimens)(const char *, const struct timespec tv[2], struct fuse_file_info *fi)
Definition fuse.h:719
int(* open)(const char *, struct fuse_file_info *)
Definition fuse.h:486
int(* opendir)(const char *, struct fuse_file_info *)
Definition fuse.h:589
int(* link)(const char *, const char *)
Definition fuse.h:410
int(* lock)(const char *, struct fuse_file_info *, int cmd, struct flock *)
Definition fuse.h:704
int(* read_buf)(const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *)
Definition fuse.h:798
int(* access)(const char *, int)
Definition fuse.h:660
int(* read)(const char *, char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:497
int(* poll)(const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp)
Definition fuse.h:769
void(* destroy)(void *private_data)
Definition fuse.h:649
int(* statfs)(const char *, struct statvfs *)
Definition fuse.h:516
int(* fallocate)(const char *, int, off_t, off_t, struct fuse_file_info *)
Definition fuse.h:828
int(* removexattr)(const char *, const char *)
Definition fuse.h:579
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
int(* releasedir)(const char *, struct fuse_file_info *)
Definition fuse.h:621
int(* rename)(const char *, const char *, unsigned int flags)
Definition fuse.h:407
off_t(* lseek)(const char *, off_t off, int whence, struct fuse_file_info *)
Definition fuse.h:852
int(* write)(const char *, const char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:509
int(* write_buf)(const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *)
Definition fuse.h:781
int(* unlink)(const char *)
Definition fuse.h:390
ssize_t(* copy_file_range)(const char *path_in, struct fuse_file_info *fi_in, off_t offset_in, const char *path_out, struct fuse_file_info *fi_out, off_t offset_out, size_t size, int flags)
Definition fuse.h:843
int(* fsync)(const char *, int, struct fuse_file_info *)
Definition fuse.h:567
int(* create)(const char *, mode_t, struct fuse_file_info *)
Definition fuse.h:672
int(* setxattr)(const char *, const char *, const char *, size_t, int)
Definition fuse.h:570
int(* listxattr)(const char *, char *, size_t)
Definition fuse.h:576
int(* readlink)(const char *, char *, size_t)
Definition fuse.h:371
int(* symlink)(const char *, const char *)
Definition fuse.h:396
int(* fsyncdir)(const char *, int, struct fuse_file_info *)
Definition fuse.h:631
int(* release)(const char *, struct fuse_file_info *)
Definition fuse.h:560
int(* chown)(const char *, uid_t, gid_t, struct fuse_file_info *fi)
Definition fuse.h:427
int(* chmod)(const char *, mode_t, struct fuse_file_info *fi)
Definition fuse.h:417
int(* rmdir)(const char *)
Definition fuse.h:393
int(* flush)(const char *, struct fuse_file_info *)
Definition fuse.h:546
int(* flock)(const char *, struct fuse_file_info *, int op)
Definition fuse.h:818
int(* ioctl)(const char *, unsigned int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data)
Definition fuse.h:750
int(* readdir)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags)
Definition fuse.h:613
int(* getxattr)(const char *, const char *, char *, size_t)
Definition fuse.h:573
int(* bmap)(const char *, size_t blocksize, uint64_t *idx)
Definition fuse.h:728
fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse_8h_source.html0000644000175000017500000042750215156613442023017 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse.h Source File
libfuse
fuse.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_H_
10#define FUSE_H_
11
19#include "fuse_common.h"
20
21#include <fcntl.h>
22#include <time.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <sys/statvfs.h>
26#include <sys/uio.h>
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32/* ----------------------------------------------------------- *
33 * Basic FUSE API *
34 * ----------------------------------------------------------- */
35
36/* Forward declaration */
37struct statx;
38
40struct fuse;
41
55 FUSE_READDIR_PLUS = (1 << 0)
56};
57
75 FUSE_FILL_DIR_PLUS = (1 << 1)
76};
77
93typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
94 const struct stat *stbuf, off_t off,
95 enum fuse_fill_dir_flags flags);
107struct fuse_config {
112 int32_t set_gid;
113 uint32_t gid;
114
119 int32_t set_uid;
120 uint32_t uid;
121
126 int32_t set_mode;
127 uint32_t umask;
128
133 double entry_timeout;
134
143 double negative_timeout;
144
149 double attr_timeout;
150
154 int32_t intr;
155
161 int32_t intr_signal;
162
173 int32_t remember;
174
191 int32_t hard_remove;
192
204 int32_t use_ino;
205
213 int32_t readdir_ino;
214
232 int32_t direct_io;
233
251 int32_t kernel_cache;
252
259 int32_t auto_cache;
260
261 /*
262 * The timeout in seconds for which file attributes are cached
263 * for the purpose of checking if auto_cache should flush the
264 * file data on open.
265 */
266 int32_t ac_attr_timeout_set;
267 double ac_attr_timeout;
268
279 int32_t nullpath_ok;
280
285 int32_t show_help;
286 char *modules;
287 int32_t debug;
288
294 uint32_t fmask;
295 uint32_t dmask;
296
303 int32_t no_rofd_flush;
304
319
320
324 uint32_t flags;
325
329 uint64_t reserved[48];
330};
331
332
355struct fuse_operations {
367 int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi);
368
377 int (*readlink) (const char *, char *, size_t);
378
385 int (*mknod) (const char *, mode_t, dev_t);
386
393 int (*mkdir) (const char *, mode_t);
394
396 int (*unlink) (const char *);
397
399 int (*rmdir) (const char *);
400
402 int (*symlink) (const char *, const char *);
403
413 int (*rename) (const char *, const char *, unsigned int flags);
414
416 int (*link) (const char *, const char *);
417
423 int (*chmod) (const char *, mode_t, struct fuse_file_info *fi);
424
433 int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi);
434
443 int (*truncate) (const char *, off_t, struct fuse_file_info *fi);
444
492 int (*open) (const char *, struct fuse_file_info *);
493
503 int (*read) (const char *, char *, size_t, off_t,
504 struct fuse_file_info *);
505
515 int (*write) (const char *, const char *, size_t, off_t,
516 struct fuse_file_info *);
517
522 int (*statfs) (const char *, struct statvfs *);
523
552 int (*flush) (const char *, struct fuse_file_info *);
553
566 int (*release) (const char *, struct fuse_file_info *);
567
573 int (*fsync) (const char *, int, struct fuse_file_info *);
574
576 int (*setxattr) (const char *, const char *, const char *, size_t, int);
577
579 int (*getxattr) (const char *, const char *, char *, size_t);
580
582 int (*listxattr) (const char *, char *, size_t);
583
585 int (*removexattr) (const char *, const char *);
586
595 int (*opendir) (const char *, struct fuse_file_info *);
596
619 int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
620 struct fuse_file_info *, enum fuse_readdir_flags);
621
627 int (*releasedir) (const char *, struct fuse_file_info *);
628
637 int (*fsyncdir) (const char *, int, struct fuse_file_info *);
638
647 void *(*init) (struct fuse_conn_info *conn,
648 struct fuse_config *cfg);
649
655 void (*destroy) (void *private_data);
656
666 int (*access) (const char *, int);
667
678 int (*create) (const char *, mode_t, struct fuse_file_info *);
679
710 int (*lock) (const char *, struct fuse_file_info *, int cmd,
711 struct flock *);
712
725 int (*utimens) (const char *, const struct timespec tv[2],
726 struct fuse_file_info *fi);
727
734 int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
735
736#if FUSE_USE_VERSION < 35
737 int (*ioctl) (const char *, int cmd, void *arg,
738 struct fuse_file_info *, unsigned int flags, void *data);
739#else
756 int (*ioctl) (const char *, unsigned int cmd, void *arg,
757 struct fuse_file_info *, unsigned int flags, void *data);
758#endif
759
775 int (*poll) (const char *, struct fuse_file_info *,
776 struct fuse_pollhandle *ph, unsigned *reventsp);
777
787 int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off,
788 struct fuse_file_info *);
789
804 int (*read_buf) (const char *, struct fuse_bufvec **bufp,
805 size_t size, off_t off, struct fuse_file_info *);
824 int (*flock) (const char *, struct fuse_file_info *, int op);
825
834 int (*fallocate) (const char *, int, off_t, off_t,
835 struct fuse_file_info *);
836
849 ssize_t (*copy_file_range) (const char *path_in,
850 struct fuse_file_info *fi_in,
851 off_t offset_in, const char *path_out,
852 struct fuse_file_info *fi_out,
853 off_t offset_out, size_t size, int flags);
854
858 off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
859
868 int (*statx)(const char *path, int flags, int mask, struct statx *stxbuf,
869 struct fuse_file_info *fi);
870};
871
877struct fuse_context {
879 struct fuse *fuse;
880
882 uid_t uid;
883
885 gid_t gid;
886
888 pid_t pid;
889
891 void *private_data;
892
894 mode_t umask;
895};
896
902int fuse_main_real_versioned(int argc, char *argv[],
903 const struct fuse_operations *op, size_t op_size,
904 struct libfuse_version *version, void *user_data);
905static inline int fuse_main_real(int argc, char *argv[],
906 const struct fuse_operations *op,
907 size_t op_size, void *user_data)
908{
909 struct libfuse_version version = { .major = FUSE_MAJOR_VERSION,
910 .minor = FUSE_MINOR_VERSION,
911 .hotfix = FUSE_HOTFIX_VERSION,
912 .padding = 0 };
913
914 fuse_log(FUSE_LOG_ERR,
915 "%s is a libfuse internal function, please use fuse_main()\n",
916 __func__);
917
918 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
919 user_data);
920}
921
976static inline int fuse_main_fn(int argc, char *argv[],
977 const struct fuse_operations *op,
978 void *user_data)
979{
980 struct libfuse_version version = {
981 .major = FUSE_MAJOR_VERSION,
982 .minor = FUSE_MINOR_VERSION,
983 .hotfix = FUSE_HOTFIX_VERSION,
984 .padding = 0
985 };
986
987 return fuse_main_real_versioned(argc, argv, op, sizeof(*(op)), &version,
988 user_data);
989}
990#define fuse_main(argc, argv, op, user_data) \
991 fuse_main_fn(argc, argv, op, user_data)
992
993/* ----------------------------------------------------------- *
994 * More detailed API *
995 * ----------------------------------------------------------- */
996
1008void fuse_lib_help(struct fuse_args *args);
1009
1010/* Do not call this directly, use fuse_new() instead */
1011struct fuse *_fuse_new_30(struct fuse_args *args,
1012 const struct fuse_operations *op, size_t op_size,
1013 struct libfuse_version *version, void *user_data);
1014struct fuse *_fuse_new_31(struct fuse_args *args,
1015 const struct fuse_operations *op, size_t op_size,
1016 struct libfuse_version *version, void *user_data);
1017
1045#if FUSE_USE_VERSION == 30
1046static inline struct fuse *fuse_new_fn(struct fuse_args *args,
1047 const struct fuse_operations *op,
1048 size_t op_size, void *user_data)
1049{
1050 struct libfuse_version version = {
1051 .major = FUSE_MAJOR_VERSION,
1052 .minor = FUSE_MINOR_VERSION,
1053 .hotfix = FUSE_HOTFIX_VERSION,
1054 .padding = 0
1055 };
1056
1057 return _fuse_new_30(args, op, op_size, &version, user_data);
1058}
1059#else /* FUSE_USE_VERSION */
1060static inline struct fuse *fuse_new_fn(struct fuse_args *args,
1061 const struct fuse_operations *op,
1062 size_t op_size, void *user_data)
1063{
1064 struct libfuse_version version = {
1065 .major = FUSE_MAJOR_VERSION,
1066 .minor = FUSE_MINOR_VERSION,
1067 .hotfix = FUSE_HOTFIX_VERSION,
1068 .padding = 0
1069 };
1070
1071 return _fuse_new_31(args, op, op_size, &version, user_data);
1072}
1073#endif
1074#define fuse_new(args, op, size, data) fuse_new_fn(args, op, size, data)
1075
1084int fuse_mount(struct fuse *f, const char *mountpoint);
1085
1093void fuse_unmount(struct fuse *f);
1094
1103void fuse_destroy(struct fuse *f);
1104
1120int fuse_loop(struct fuse *f);
1121
1130void fuse_exit(struct fuse *f);
1131
1132#if FUSE_USE_VERSION < 32
1133int fuse_loop_mt_31(struct fuse *f, int clone_fd);
1134#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd)
1135#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
1136int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
1137#define fuse_loop_mt(f, config) fuse_loop_mt_32(f, config)
1138#else
1170#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
1171int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config);
1172#else
1173#define fuse_loop_mt(f, config) fuse_loop_mt_312(f, config)
1174#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
1175#endif
1176
1177
1186struct fuse_context *fuse_get_context(void);
1187
1206int fuse_getgroups(int size, gid_t list[]);
1207
1213int fuse_interrupted(void);
1214
1226int fuse_invalidate_path(struct fuse *f, const char *path);
1227
1236
1243void fuse_stop_cleanup_thread(struct fuse *fuse);
1244
1254int fuse_clean_cache(struct fuse *fuse);
1255
1256/*
1257 * Stacking API
1258 */
1259
1265struct fuse_fs;
1266
1267/*
1268 * These functions call the relevant filesystem operation, and return
1269 * the result.
1270 *
1271 * If the operation is not defined, they return -ENOSYS, with the
1272 * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
1273 * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
1274 */
1275
1276int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
1277 struct fuse_file_info *fi);
1278int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
1279 const char *newpath, unsigned int flags);
1280int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
1281int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
1282int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
1283 const char *path);
1284int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
1285int fuse_fs_release(struct fuse_fs *fs, const char *path,
1286 struct fuse_file_info *fi);
1287int fuse_fs_open(struct fuse_fs *fs, const char *path,
1288 struct fuse_file_info *fi);
1289int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
1290 off_t off, struct fuse_file_info *fi);
1291int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
1292 struct fuse_bufvec **bufp, size_t size, off_t off,
1293 struct fuse_file_info *fi);
1294int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
1295 size_t size, off_t off, struct fuse_file_info *fi);
1296int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
1297 struct fuse_bufvec *buf, off_t off,
1298 struct fuse_file_info *fi);
1299int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
1300 struct fuse_file_info *fi);
1301int fuse_fs_flush(struct fuse_fs *fs, const char *path,
1302 struct fuse_file_info *fi);
1303int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
1304int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
1305 struct fuse_file_info *fi);
1306int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
1307 fuse_fill_dir_t filler, off_t off,
1308 struct fuse_file_info *fi, enum fuse_readdir_flags flags);
1309int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
1310 struct fuse_file_info *fi);
1311int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
1312 struct fuse_file_info *fi);
1313int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
1314 struct fuse_file_info *fi);
1315int fuse_fs_lock(struct fuse_fs *fs, const char *path,
1316 struct fuse_file_info *fi, int cmd, struct flock *lock);
1317int fuse_fs_flock(struct fuse_fs *fs, const char *path,
1318 struct fuse_file_info *fi, int op);
1319int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
1320 struct fuse_file_info *fi);
1321int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid,
1322 struct fuse_file_info *fi);
1323int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
1324 struct fuse_file_info *fi);
1325int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
1326 const struct timespec tv[2], struct fuse_file_info *fi);
1327int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
1328int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
1329 size_t len);
1330int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
1331 dev_t rdev);
1332int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
1333int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
1334 const char *value, size_t size, int flags);
1335int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
1336 char *value, size_t size);
1337int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
1338 size_t size);
1339int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
1340 const char *name);
1341int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
1342 uint64_t *idx);
1343#if FUSE_USE_VERSION < 35
1344int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd,
1345 void *arg, struct fuse_file_info *fi, unsigned int flags,
1346 void *data);
1347#else
1348int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
1349 void *arg, struct fuse_file_info *fi, unsigned int flags,
1350 void *data);
1351#endif
1352int fuse_fs_poll(struct fuse_fs *fs, const char *path,
1353 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
1354 unsigned *reventsp);
1355int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
1356 off_t offset, off_t length, struct fuse_file_info *fi);
1357ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
1358 struct fuse_file_info *fi_in, off_t off_in,
1359 const char *path_out,
1360 struct fuse_file_info *fi_out, off_t off_out,
1361 size_t len, int flags);
1362off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
1363 struct fuse_file_info *fi);
1364int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
1365 struct statx *stxbuf, struct fuse_file_info *fi);
1366void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
1367 struct fuse_config *cfg);
1368void fuse_fs_destroy(struct fuse_fs *fs);
1369
1370int fuse_notify_poll(struct fuse_pollhandle *ph);
1371
1385struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
1386 void *private_data);
1387
1402typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
1403 struct fuse_fs *fs[]);
1414#define FUSE_REGISTER_MODULE(name_, factory_) \
1415 fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
1416
1418struct fuse_session *fuse_get_session(struct fuse *f);
1419
1428int fuse_open_channel(const char *mountpoint, const char *options);
1429
1430#ifdef __cplusplus
1431}
1432#endif
1433
1434#endif /* FUSE_H_ */
int fuse_getgroups(int size, gid_t list[])
Definition fuse.c:4654
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
int fuse_interrupted(void)
Definition fuse.c:4663
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
Definition helper.c:307
int fuse_start_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4906
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_exit(struct fuse *f)
Definition fuse.c:4639
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4433
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4914
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
@ FUSE_READDIR_DEFAULTS
Definition fuse.h:51
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:93
fuse_readdir_flags
Definition fuse.h:45
uint32_t fmask
Definition fuse.h:288
int32_t show_help
Definition fuse.h:279
uint32_t flags
Definition fuse.h:318
int32_t direct_io
Definition fuse.h:226
int32_t no_rofd_flush
Definition fuse.h:297
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t intr_signal
Definition fuse.h:155
int32_t remember
Definition fuse.h:167
int32_t kernel_cache
Definition fuse.h:245
int32_t readdir_ino
Definition fuse.h:207
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t set_mode
Definition fuse.h:120
int32_t auto_cache
Definition fuse.h:253
uint64_t reserved[48]
Definition fuse.h:323
int32_t intr
Definition fuse.h:148
double negative_timeout
Definition fuse.h:137
int32_t set_gid
Definition fuse.h:106
int32_t set_uid
Definition fuse.h:113
int32_t hard_remove
Definition fuse.h:185
double attr_timeout
Definition fuse.h:143
void * private_data
Definition fuse.h:874
uid_t uid
Definition fuse.h:865
pid_t pid
Definition fuse.h:871
struct fuse * fuse
Definition fuse.h:862
gid_t gid
Definition fuse.h:868
mode_t umask
Definition fuse.h:877
int(* mkdir)(const char *, mode_t)
Definition fuse.h:387
int(* truncate)(const char *, off_t, struct fuse_file_info *fi)
Definition fuse.h:437
int(* mknod)(const char *, mode_t, dev_t)
Definition fuse.h:379
int(* utimens)(const char *, const struct timespec tv[2], struct fuse_file_info *fi)
Definition fuse.h:719
int(* open)(const char *, struct fuse_file_info *)
Definition fuse.h:486
int(* opendir)(const char *, struct fuse_file_info *)
Definition fuse.h:589
int(* link)(const char *, const char *)
Definition fuse.h:410
int(* lock)(const char *, struct fuse_file_info *, int cmd, struct flock *)
Definition fuse.h:704
int(* read_buf)(const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *)
Definition fuse.h:798
int(* access)(const char *, int)
Definition fuse.h:660
int(* read)(const char *, char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:497
int(* poll)(const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp)
Definition fuse.h:769
void(* destroy)(void *private_data)
Definition fuse.h:649
int(* statfs)(const char *, struct statvfs *)
Definition fuse.h:516
int(* fallocate)(const char *, int, off_t, off_t, struct fuse_file_info *)
Definition fuse.h:828
int(* removexattr)(const char *, const char *)
Definition fuse.h:579
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
int(* releasedir)(const char *, struct fuse_file_info *)
Definition fuse.h:621
int(* rename)(const char *, const char *, unsigned int flags)
Definition fuse.h:407
off_t(* lseek)(const char *, off_t off, int whence, struct fuse_file_info *)
Definition fuse.h:852
int(* write)(const char *, const char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:509
int(* write_buf)(const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *)
Definition fuse.h:781
int(* unlink)(const char *)
Definition fuse.h:390
ssize_t(* copy_file_range)(const char *path_in, struct fuse_file_info *fi_in, off_t offset_in, const char *path_out, struct fuse_file_info *fi_out, off_t offset_out, size_t size, int flags)
Definition fuse.h:843
int(* fsync)(const char *, int, struct fuse_file_info *)
Definition fuse.h:567
int(* create)(const char *, mode_t, struct fuse_file_info *)
Definition fuse.h:672
int(* setxattr)(const char *, const char *, const char *, size_t, int)
Definition fuse.h:570
int(* statx)(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi)
Definition fuse.h:868
int(* listxattr)(const char *, char *, size_t)
Definition fuse.h:576
int(* readlink)(const char *, char *, size_t)
Definition fuse.h:371
int(* symlink)(const char *, const char *)
Definition fuse.h:396
int(* fsyncdir)(const char *, int, struct fuse_file_info *)
Definition fuse.h:631
int(* release)(const char *, struct fuse_file_info *)
Definition fuse.h:560
int(* chown)(const char *, uid_t, gid_t, struct fuse_file_info *fi)
Definition fuse.h:427
int(* chmod)(const char *, mode_t, struct fuse_file_info *fi)
Definition fuse.h:417
int(* rmdir)(const char *)
Definition fuse.h:393
int(* flush)(const char *, struct fuse_file_info *)
Definition fuse.h:546
int(* flock)(const char *, struct fuse_file_info *, int op)
Definition fuse.h:818
int(* ioctl)(const char *, unsigned int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data)
Definition fuse.h:750
int(* readdir)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags)
Definition fuse.h:613
int(* getxattr)(const char *, const char *, char *, size_t)
Definition fuse.h:573
int(* bmap)(const char *, size_t blocksize, uint64_t *idx)
Definition fuse.h:728
fuse-3.18.2/doc/html/include_2fuse_8h_source.html0000644000175000017500000042677615156613442020657 0ustar berndbernd libfuse: include/fuse.h Source File
libfuse
fuse.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_H_
10#define FUSE_H_
11
19#include "fuse_common.h"
20
21#include <fcntl.h>
22#include <time.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <sys/statvfs.h>
26#include <sys/uio.h>
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32/* ----------------------------------------------------------- *
33 * Basic FUSE API *
34 * ----------------------------------------------------------- */
35
36/* Forward declaration */
37struct statx;
38
40struct fuse;
41
55 FUSE_READDIR_PLUS = (1 << 0)
56};
57
75 FUSE_FILL_DIR_PLUS = (1 << 1)
76};
77
93typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
94 const struct stat *stbuf, off_t off,
95 enum fuse_fill_dir_flags flags);
107struct fuse_config {
112 int32_t set_gid;
113 uint32_t gid;
114
119 int32_t set_uid;
120 uint32_t uid;
121
126 int32_t set_mode;
127 uint32_t umask;
128
133 double entry_timeout;
134
143 double negative_timeout;
144
149 double attr_timeout;
150
154 int32_t intr;
155
161 int32_t intr_signal;
162
173 int32_t remember;
174
191 int32_t hard_remove;
192
204 int32_t use_ino;
205
213 int32_t readdir_ino;
214
232 int32_t direct_io;
233
251 int32_t kernel_cache;
252
259 int32_t auto_cache;
260
261 /*
262 * The timeout in seconds for which file attributes are cached
263 * for the purpose of checking if auto_cache should flush the
264 * file data on open.
265 */
266 int32_t ac_attr_timeout_set;
267 double ac_attr_timeout;
268
279 int32_t nullpath_ok;
280
285 int32_t show_help;
286 char *modules;
287 int32_t debug;
288
294 uint32_t fmask;
295 uint32_t dmask;
296
303 int32_t no_rofd_flush;
304
319
320
324 uint32_t flags;
325
329 uint64_t reserved[48];
330};
331
332
355struct fuse_operations {
367 int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi);
368
377 int (*readlink) (const char *, char *, size_t);
378
385 int (*mknod) (const char *, mode_t, dev_t);
386
393 int (*mkdir) (const char *, mode_t);
394
396 int (*unlink) (const char *);
397
399 int (*rmdir) (const char *);
400
402 int (*symlink) (const char *, const char *);
403
413 int (*rename) (const char *, const char *, unsigned int flags);
414
416 int (*link) (const char *, const char *);
417
423 int (*chmod) (const char *, mode_t, struct fuse_file_info *fi);
424
433 int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi);
434
443 int (*truncate) (const char *, off_t, struct fuse_file_info *fi);
444
492 int (*open) (const char *, struct fuse_file_info *);
493
503 int (*read) (const char *, char *, size_t, off_t,
504 struct fuse_file_info *);
505
515 int (*write) (const char *, const char *, size_t, off_t,
516 struct fuse_file_info *);
517
522 int (*statfs) (const char *, struct statvfs *);
523
552 int (*flush) (const char *, struct fuse_file_info *);
553
566 int (*release) (const char *, struct fuse_file_info *);
567
573 int (*fsync) (const char *, int, struct fuse_file_info *);
574
576 int (*setxattr) (const char *, const char *, const char *, size_t, int);
577
579 int (*getxattr) (const char *, const char *, char *, size_t);
580
582 int (*listxattr) (const char *, char *, size_t);
583
585 int (*removexattr) (const char *, const char *);
586
595 int (*opendir) (const char *, struct fuse_file_info *);
596
619 int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
620 struct fuse_file_info *, enum fuse_readdir_flags);
621
627 int (*releasedir) (const char *, struct fuse_file_info *);
628
637 int (*fsyncdir) (const char *, int, struct fuse_file_info *);
638
647 void *(*init) (struct fuse_conn_info *conn,
648 struct fuse_config *cfg);
649
655 void (*destroy) (void *private_data);
656
666 int (*access) (const char *, int);
667
678 int (*create) (const char *, mode_t, struct fuse_file_info *);
679
710 int (*lock) (const char *, struct fuse_file_info *, int cmd,
711 struct flock *);
712
725 int (*utimens) (const char *, const struct timespec tv[2],
726 struct fuse_file_info *fi);
727
734 int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
735
736#if FUSE_USE_VERSION < 35
737 int (*ioctl) (const char *, int cmd, void *arg,
738 struct fuse_file_info *, unsigned int flags, void *data);
739#else
756 int (*ioctl) (const char *, unsigned int cmd, void *arg,
757 struct fuse_file_info *, unsigned int flags, void *data);
758#endif
759
775 int (*poll) (const char *, struct fuse_file_info *,
776 struct fuse_pollhandle *ph, unsigned *reventsp);
777
787 int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off,
788 struct fuse_file_info *);
789
804 int (*read_buf) (const char *, struct fuse_bufvec **bufp,
805 size_t size, off_t off, struct fuse_file_info *);
824 int (*flock) (const char *, struct fuse_file_info *, int op);
825
834 int (*fallocate) (const char *, int, off_t, off_t,
835 struct fuse_file_info *);
836
849 ssize_t (*copy_file_range) (const char *path_in,
850 struct fuse_file_info *fi_in,
851 off_t offset_in, const char *path_out,
852 struct fuse_file_info *fi_out,
853 off_t offset_out, size_t size, int flags);
854
858 off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
859
868 int (*statx)(const char *path, int flags, int mask, struct statx *stxbuf,
869 struct fuse_file_info *fi);
870};
871
877struct fuse_context {
879 struct fuse *fuse;
880
882 uid_t uid;
883
885 gid_t gid;
886
888 pid_t pid;
889
891 void *private_data;
892
894 mode_t umask;
895};
896
902int fuse_main_real_versioned(int argc, char *argv[],
903 const struct fuse_operations *op, size_t op_size,
904 struct libfuse_version *version, void *user_data);
905static inline int fuse_main_real(int argc, char *argv[],
906 const struct fuse_operations *op,
907 size_t op_size, void *user_data)
908{
909 struct libfuse_version version = { .major = FUSE_MAJOR_VERSION,
910 .minor = FUSE_MINOR_VERSION,
911 .hotfix = FUSE_HOTFIX_VERSION,
912 .padding = 0 };
913
914 fuse_log(FUSE_LOG_ERR,
915 "%s is a libfuse internal function, please use fuse_main()\n",
916 __func__);
917
918 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
919 user_data);
920}
921
976static inline int fuse_main_fn(int argc, char *argv[],
977 const struct fuse_operations *op,
978 void *user_data)
979{
980 struct libfuse_version version = {
981 .major = FUSE_MAJOR_VERSION,
982 .minor = FUSE_MINOR_VERSION,
983 .hotfix = FUSE_HOTFIX_VERSION,
984 .padding = 0
985 };
986
987 return fuse_main_real_versioned(argc, argv, op, sizeof(*(op)), &version,
988 user_data);
989}
990#define fuse_main(argc, argv, op, user_data) \
991 fuse_main_fn(argc, argv, op, user_data)
992
993/* ----------------------------------------------------------- *
994 * More detailed API *
995 * ----------------------------------------------------------- */
996
1008void fuse_lib_help(struct fuse_args *args);
1009
1010/* Do not call this directly, use fuse_new() instead */
1011struct fuse *_fuse_new_30(struct fuse_args *args,
1012 const struct fuse_operations *op, size_t op_size,
1013 struct libfuse_version *version, void *user_data);
1014struct fuse *_fuse_new_31(struct fuse_args *args,
1015 const struct fuse_operations *op, size_t op_size,
1016 struct libfuse_version *version, void *user_data);
1017
1045#if FUSE_USE_VERSION == 30
1046static inline struct fuse *fuse_new_fn(struct fuse_args *args,
1047 const struct fuse_operations *op,
1048 size_t op_size, void *user_data)
1049{
1050 struct libfuse_version version = {
1051 .major = FUSE_MAJOR_VERSION,
1052 .minor = FUSE_MINOR_VERSION,
1053 .hotfix = FUSE_HOTFIX_VERSION,
1054 .padding = 0
1055 };
1056
1057 return _fuse_new_30(args, op, op_size, &version, user_data);
1058}
1059#else /* FUSE_USE_VERSION */
1060static inline struct fuse *fuse_new_fn(struct fuse_args *args,
1061 const struct fuse_operations *op,
1062 size_t op_size, void *user_data)
1063{
1064 struct libfuse_version version = {
1065 .major = FUSE_MAJOR_VERSION,
1066 .minor = FUSE_MINOR_VERSION,
1067 .hotfix = FUSE_HOTFIX_VERSION,
1068 .padding = 0
1069 };
1070
1071 return _fuse_new_31(args, op, op_size, &version, user_data);
1072}
1073#endif
1074#define fuse_new(args, op, size, data) fuse_new_fn(args, op, size, data)
1075
1084int fuse_mount(struct fuse *f, const char *mountpoint);
1085
1093void fuse_unmount(struct fuse *f);
1094
1103void fuse_destroy(struct fuse *f);
1104
1120int fuse_loop(struct fuse *f);
1121
1130void fuse_exit(struct fuse *f);
1131
1132#if FUSE_USE_VERSION < 32
1133int fuse_loop_mt_31(struct fuse *f, int clone_fd);
1134#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd)
1135#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
1136int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
1137#define fuse_loop_mt(f, config) fuse_loop_mt_32(f, config)
1138#else
1170#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
1171int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config);
1172#else
1173#define fuse_loop_mt(f, config) fuse_loop_mt_312(f, config)
1174#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
1175#endif
1176
1177
1186struct fuse_context *fuse_get_context(void);
1187
1206int fuse_getgroups(int size, gid_t list[]);
1207
1213int fuse_interrupted(void);
1214
1226int fuse_invalidate_path(struct fuse *f, const char *path);
1227
1236
1243void fuse_stop_cleanup_thread(struct fuse *fuse);
1244
1254int fuse_clean_cache(struct fuse *fuse);
1255
1256/*
1257 * Stacking API
1258 */
1259
1265struct fuse_fs;
1266
1267/*
1268 * These functions call the relevant filesystem operation, and return
1269 * the result.
1270 *
1271 * If the operation is not defined, they return -ENOSYS, with the
1272 * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
1273 * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
1274 */
1275
1276int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
1277 struct fuse_file_info *fi);
1278int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
1279 const char *newpath, unsigned int flags);
1280int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
1281int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
1282int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
1283 const char *path);
1284int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
1285int fuse_fs_release(struct fuse_fs *fs, const char *path,
1286 struct fuse_file_info *fi);
1287int fuse_fs_open(struct fuse_fs *fs, const char *path,
1288 struct fuse_file_info *fi);
1289int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
1290 off_t off, struct fuse_file_info *fi);
1291int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
1292 struct fuse_bufvec **bufp, size_t size, off_t off,
1293 struct fuse_file_info *fi);
1294int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
1295 size_t size, off_t off, struct fuse_file_info *fi);
1296int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
1297 struct fuse_bufvec *buf, off_t off,
1298 struct fuse_file_info *fi);
1299int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
1300 struct fuse_file_info *fi);
1301int fuse_fs_flush(struct fuse_fs *fs, const char *path,
1302 struct fuse_file_info *fi);
1303int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
1304int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
1305 struct fuse_file_info *fi);
1306int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
1307 fuse_fill_dir_t filler, off_t off,
1308 struct fuse_file_info *fi, enum fuse_readdir_flags flags);
1309int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
1310 struct fuse_file_info *fi);
1311int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
1312 struct fuse_file_info *fi);
1313int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
1314 struct fuse_file_info *fi);
1315int fuse_fs_lock(struct fuse_fs *fs, const char *path,
1316 struct fuse_file_info *fi, int cmd, struct flock *lock);
1317int fuse_fs_flock(struct fuse_fs *fs, const char *path,
1318 struct fuse_file_info *fi, int op);
1319int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
1320 struct fuse_file_info *fi);
1321int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid,
1322 struct fuse_file_info *fi);
1323int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
1324 struct fuse_file_info *fi);
1325int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
1326 const struct timespec tv[2], struct fuse_file_info *fi);
1327int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
1328int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
1329 size_t len);
1330int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
1331 dev_t rdev);
1332int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
1333int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
1334 const char *value, size_t size, int flags);
1335int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
1336 char *value, size_t size);
1337int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
1338 size_t size);
1339int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
1340 const char *name);
1341int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
1342 uint64_t *idx);
1343#if FUSE_USE_VERSION < 35
1344int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd,
1345 void *arg, struct fuse_file_info *fi, unsigned int flags,
1346 void *data);
1347#else
1348int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
1349 void *arg, struct fuse_file_info *fi, unsigned int flags,
1350 void *data);
1351#endif
1352int fuse_fs_poll(struct fuse_fs *fs, const char *path,
1353 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
1354 unsigned *reventsp);
1355int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
1356 off_t offset, off_t length, struct fuse_file_info *fi);
1357ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
1358 struct fuse_file_info *fi_in, off_t off_in,
1359 const char *path_out,
1360 struct fuse_file_info *fi_out, off_t off_out,
1361 size_t len, int flags);
1362off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
1363 struct fuse_file_info *fi);
1364int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
1365 struct statx *stxbuf, struct fuse_file_info *fi);
1366void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
1367 struct fuse_config *cfg);
1368void fuse_fs_destroy(struct fuse_fs *fs);
1369
1370int fuse_notify_poll(struct fuse_pollhandle *ph);
1371
1385struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
1386 void *private_data);
1387
1402typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
1403 struct fuse_fs *fs[]);
1414#define FUSE_REGISTER_MODULE(name_, factory_) \
1415 fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
1416
1418struct fuse_session *fuse_get_session(struct fuse *f);
1419
1428int fuse_open_channel(const char *mountpoint, const char *options);
1429
1430#ifdef __cplusplus
1431}
1432#endif
1433
1434#endif /* FUSE_H_ */
int fuse_getgroups(int size, gid_t list[])
Definition fuse.c:4654
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
int fuse_interrupted(void)
Definition fuse.c:4663
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
Definition helper.c:307
int fuse_start_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4906
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_exit(struct fuse *f)
Definition fuse.c:4639
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4433
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4914
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
@ FUSE_READDIR_DEFAULTS
Definition fuse.h:51
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:93
fuse_readdir_flags
Definition fuse.h:45
uint32_t fmask
Definition fuse.h:288
int32_t show_help
Definition fuse.h:279
uint32_t flags
Definition fuse.h:318
int32_t direct_io
Definition fuse.h:226
int32_t no_rofd_flush
Definition fuse.h:297
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t intr_signal
Definition fuse.h:155
int32_t remember
Definition fuse.h:167
int32_t kernel_cache
Definition fuse.h:245
int32_t readdir_ino
Definition fuse.h:207
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t set_mode
Definition fuse.h:120
int32_t auto_cache
Definition fuse.h:253
uint64_t reserved[48]
Definition fuse.h:323
int32_t intr
Definition fuse.h:148
double negative_timeout
Definition fuse.h:137
int32_t set_gid
Definition fuse.h:106
int32_t set_uid
Definition fuse.h:113
int32_t hard_remove
Definition fuse.h:185
double attr_timeout
Definition fuse.h:143
void * private_data
Definition fuse.h:874
uid_t uid
Definition fuse.h:865
pid_t pid
Definition fuse.h:871
struct fuse * fuse
Definition fuse.h:862
gid_t gid
Definition fuse.h:868
mode_t umask
Definition fuse.h:877
int(* mkdir)(const char *, mode_t)
Definition fuse.h:387
int(* truncate)(const char *, off_t, struct fuse_file_info *fi)
Definition fuse.h:437
int(* mknod)(const char *, mode_t, dev_t)
Definition fuse.h:379
int(* utimens)(const char *, const struct timespec tv[2], struct fuse_file_info *fi)
Definition fuse.h:719
int(* open)(const char *, struct fuse_file_info *)
Definition fuse.h:486
int(* opendir)(const char *, struct fuse_file_info *)
Definition fuse.h:589
int(* link)(const char *, const char *)
Definition fuse.h:410
int(* lock)(const char *, struct fuse_file_info *, int cmd, struct flock *)
Definition fuse.h:704
int(* read_buf)(const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *)
Definition fuse.h:798
int(* access)(const char *, int)
Definition fuse.h:660
int(* read)(const char *, char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:497
int(* poll)(const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp)
Definition fuse.h:769
void(* destroy)(void *private_data)
Definition fuse.h:649
int(* statfs)(const char *, struct statvfs *)
Definition fuse.h:516
int(* fallocate)(const char *, int, off_t, off_t, struct fuse_file_info *)
Definition fuse.h:828
int(* removexattr)(const char *, const char *)
Definition fuse.h:579
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
int(* releasedir)(const char *, struct fuse_file_info *)
Definition fuse.h:621
int(* rename)(const char *, const char *, unsigned int flags)
Definition fuse.h:407
off_t(* lseek)(const char *, off_t off, int whence, struct fuse_file_info *)
Definition fuse.h:852
int(* write)(const char *, const char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:509
int(* write_buf)(const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *)
Definition fuse.h:781
int(* unlink)(const char *)
Definition fuse.h:390
ssize_t(* copy_file_range)(const char *path_in, struct fuse_file_info *fi_in, off_t offset_in, const char *path_out, struct fuse_file_info *fi_out, off_t offset_out, size_t size, int flags)
Definition fuse.h:843
int(* fsync)(const char *, int, struct fuse_file_info *)
Definition fuse.h:567
int(* create)(const char *, mode_t, struct fuse_file_info *)
Definition fuse.h:672
int(* setxattr)(const char *, const char *, const char *, size_t, int)
Definition fuse.h:570
int(* statx)(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi)
Definition fuse.h:868
int(* listxattr)(const char *, char *, size_t)
Definition fuse.h:576
int(* readlink)(const char *, char *, size_t)
Definition fuse.h:371
int(* symlink)(const char *, const char *)
Definition fuse.h:396
int(* fsyncdir)(const char *, int, struct fuse_file_info *)
Definition fuse.h:631
int(* release)(const char *, struct fuse_file_info *)
Definition fuse.h:560
int(* chown)(const char *, uid_t, gid_t, struct fuse_file_info *fi)
Definition fuse.h:427
int(* chmod)(const char *, mode_t, struct fuse_file_info *fi)
Definition fuse.h:417
int(* rmdir)(const char *)
Definition fuse.h:393
int(* flush)(const char *, struct fuse_file_info *)
Definition fuse.h:546
int(* flock)(const char *, struct fuse_file_info *, int op)
Definition fuse.h:818
int(* ioctl)(const char *, unsigned int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data)
Definition fuse.h:750
int(* readdir)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags)
Definition fuse.h:613
int(* getxattr)(const char *, const char *, char *, size_t)
Definition fuse.h:573
int(* bmap)(const char *, size_t blocksize, uint64_t *idx)
Definition fuse.h:728
fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__common_8h_source.html0000644000175000017500000027637715156613442024544 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_common.h Source File
libfuse
fuse_common.h
Go to the documentation of this file.
1/* FUSE: Filesystem in Userspace
2 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
3
4 This program can be distributed under the terms of the GNU LGPLv2.
5 See the file COPYING.LIB.
6*/
7
10#include <stdbool.h>
11#if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_)
12#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
13#endif
14
15#ifndef FUSE_COMMON_H_
16#define FUSE_COMMON_H_
17
18#ifdef HAVE_LIBFUSE_PRIVATE_CONFIG_H
19#include "fuse_config.h"
20#endif
21
22#include "libfuse_config.h"
23
24#include "fuse_opt.h"
25#include "fuse_log.h"
26#include <stdint.h>
27#include <sys/types.h>
28#include <assert.h>
29
30#define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min))
31#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
32
33#ifdef __cplusplus
34extern "C" {
35#endif
36
52 int32_t flags;
53
60 uint32_t writepage : 1;
61
63 uint32_t direct_io : 1;
64
69 uint32_t keep_cache : 1;
70
74 uint32_t flush : 1;
75
78 uint32_t nonseekable : 1;
79
80 /* Indicates that flock locks for this file should be
81 released. If set, lock_owner shall contain a valid value.
82 May only be set in ->release(). */
83 uint32_t flock_release : 1;
84
89 uint32_t cache_readdir : 1;
90
93 uint32_t noflush : 1;
94
98
100 uint32_t padding : 23;
101 uint32_t padding2 : 32;
102 uint32_t padding3 : 32;
103
107 uint64_t fh;
108
110 uint64_t lock_owner;
111
114 uint32_t poll_events;
115
119 int32_t backing_id;
120
122 uint64_t compat_flags;
123
124 uint64_t reserved[2];
125};
126
137#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
138struct fuse_loop_config_v1; /* forward declaration */
140#else
141struct fuse_loop_config_v1 {
142#endif
148
159 unsigned int max_idle_threads;
160};
161
162
163/**************************************************************************
164 * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
165 **************************************************************************/
166
177#define FUSE_CAP_ASYNC_READ (1 << 0)
178
185#define FUSE_CAP_POSIX_LOCKS (1 << 1)
186
194#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
195
206#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
207
214#define FUSE_CAP_DONT_MASK (1 << 6)
215
222#define FUSE_CAP_SPLICE_WRITE (1 << 7)
223
230#define FUSE_CAP_SPLICE_MOVE (1 << 8)
231
239#define FUSE_CAP_SPLICE_READ (1 << 9)
240
252#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
253
259#define FUSE_CAP_IOCTL_DIR (1 << 11)
260
281#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12)
282
289#define FUSE_CAP_READDIRPLUS (1 << 13)
290
317#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14)
318
328#define FUSE_CAP_ASYNC_DIO (1 << 15)
329
337#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
338
352#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17)
353
360#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
361
379#define FUSE_CAP_POSIX_ACL (1 << 19)
380
388#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20)
389
405#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1 << 21)
406
418#define FUSE_CAP_CACHE_SYMLINKS (1 << 23)
419
433#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
434
456#define FUSE_CAP_EXPLICIT_INVAL_DATA (1 << 25)
457
472#define FUSE_CAP_EXPIRE_ONLY (1 << 26)
473
479#define FUSE_CAP_SETXATTR_EXT (1 << 27)
480
488#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1 << 28)
489
500#define FUSE_CAP_PASSTHROUGH (1 << 29)
501
508#define FUSE_CAP_NO_EXPORT_SUPPORT (1 << 30)
509
520#define FUSE_IOCTL_COMPAT (1 << 0)
521#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
522#define FUSE_IOCTL_RETRY (1 << 2)
523#define FUSE_IOCTL_DIR (1 << 4)
524
525#define FUSE_IOCTL_MAX_IOV 256
526
542 uint32_t proto_major;
543
547 uint32_t proto_minor;
548
552 uint32_t max_write;
553
566 uint32_t max_read;
567
572
578 uint32_t capable;
579
590 uint32_t want;
591
621
631
647 uint32_t time_gran;
648
665#define FUSE_BACKING_STACKED_UNDER (0)
666#define FUSE_BACKING_STACKED_OVER (1)
667 uint32_t max_backing_stack_depth;
668
677 uint32_t no_interrupt : 1;
678
679 /* reserved bits for future use */
680 uint32_t padding : 31;
681
686 uint64_t capable_ext;
687
696 uint64_t want_ext;
697
701 uint32_t reserved[16];
702};
703
704struct fuse_session;
705struct fuse_pollhandle;
706struct fuse_conn_info_opts;
707
750struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
751
759void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
760 struct fuse_conn_info *conn);
761
768int fuse_daemonize(int foreground);
769
775int fuse_version(void);
776
782const char *fuse_pkgversion(void);
783
789void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
790
791/* ----------------------------------------------------------- *
792 * Data buffer *
793 * ----------------------------------------------------------- */
794
805 FUSE_BUF_IS_FD = (1 << 1),
806
815
823 FUSE_BUF_FD_RETRY = (1 << 3)
825
867
874struct fuse_buf {
878 size_t size;
879
884
890 void *mem;
891
897 int fd;
898
904 off_t pos;
905
912 size_t mem_size;
913};
914
927 size_t count;
928
932 size_t idx;
933
937 size_t off;
938
942 struct fuse_buf buf[1];
943};
944
950{
951 uint32_t major;
952 uint32_t minor;
953 uint32_t hotfix;
954 uint32_t padding;
955};
956
957/* Initialize bufvec with a single buffer of given size */
958#define FUSE_BUFVEC_INIT(size__) \
959 ((struct fuse_bufvec) { \
960 /* .count= */ 1, \
961 /* .idx = */ 0, \
962 /* .off = */ 0, \
963 /* .buf = */ { /* [0] = */ { \
964 /* .size = */ (size__), \
965 /* .flags = */ (enum fuse_buf_flags) 0, \
966 /* .mem = */ NULL, \
967 /* .fd = */ -1, \
968 /* .pos = */ 0, \
969 /* .mem_size = */ 0, \
970 } } \
971 } )
972
979size_t fuse_buf_size(const struct fuse_bufvec *bufv);
980
989ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
990 enum fuse_buf_copy_flags flags);
991
992/* ----------------------------------------------------------- *
993 * Signal handling *
994 * ----------------------------------------------------------- */
995
1011int fuse_set_signal_handlers(struct fuse_session *se);
1012
1028int fuse_set_fail_signal_handlers(struct fuse_session *se);
1029
1041void fuse_remove_signal_handlers(struct fuse_session *se);
1042
1048#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
1054struct fuse_loop_config *fuse_loop_cfg_create(void);
1055
1059void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
1060
1064void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
1065 unsigned int value);
1066
1070void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
1071 unsigned int value);
1072
1076void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
1077 unsigned int value);
1078
1085void fuse_loop_cfg_convert(struct fuse_loop_config *config,
1086 struct fuse_loop_config_v1 *v1_conf);
1087#endif
1088
1096bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1097
1104void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1105
1113bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1114
1115/*
1116 * DO NOT USE: Not part of public API, for internal test use only.
1117 * The function signature or any use of it is not guaranteeed to
1118 * remain stable. And neither are results of what this function does.
1119 */
1121
1122
1123
1124/* ----------------------------------------------------------- *
1125 * Compatibility stuff *
1126 * ----------------------------------------------------------- */
1127
1128#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30
1129# error only API version 30 or greater is supported
1130#endif
1131
1132#ifdef __cplusplus
1133}
1134#endif
1135
1136
1137/*
1138 * This interface uses 64 bit off_t.
1139 *
1140 * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags!
1141 */
1142
1143#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
1144_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit");
1145#else
1146struct _fuse_off_t_must_be_64bit_dummy_struct \
1147 { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); };
1148#endif
1149
1150#endif /* FUSE_COMMON_H_ */
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
int fuse_set_fail_signal_handlers(struct fuse_session *se)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
Definition helper.c:412
fuse_buf_flags
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_FD_RETRY
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
Definition helper.c:466
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
int fuse_version(void)
Definition fuse.c:5206
void fuse_remove_signal_handlers(struct fuse_session *se)
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
fuse_buf_flags
enum fuse_buf_flags flags
size_t mem_size
off_t pos
void * mem
size_t size
uint32_t time_gran
uint32_t proto_major
uint32_t congestion_threshold
uint32_t proto_minor
uint32_t max_write
uint64_t capable_ext
uint32_t max_readahead
uint32_t no_interrupt
uint32_t max_read
uint32_t max_background
uint32_t capable
uint64_t want_ext
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t nonseekable
Definition fuse_common.h:78
int32_t backing_id
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t padding
uint32_t noflush
Definition fuse_common.h:93
uint64_t compat_flags
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
unsigned int max_idle_threads
fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__common_8h_source.html0000644000175000017500000024100315156613442024514 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_common.h Source File
libfuse
fuse_common.h
Go to the documentation of this file.
1/* FUSE: Filesystem in Userspace
2 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
3
4 This program can be distributed under the terms of the GNU LGPLv2.
5 See the file LGPL2.txt.
6*/
7
10#if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_)
11#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
12#endif
13
14#ifndef FUSE_COMMON_H_
15#define FUSE_COMMON_H_
16
17#ifdef HAVE_LIBFUSE_PRIVATE_CONFIG_H
18#include "fuse_config.h"
19#endif
20
21#include "libfuse_config.h"
22
23#include "fuse_opt.h"
24#include "fuse_log.h"
25#include <stdint.h>
26#include <stdbool.h>
27#include <sys/types.h>
28#include <assert.h>
29
30#define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min))
31#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
32
33#ifdef __cplusplus
34extern "C" {
35#endif
36
50struct fuse_file_info {
52 int32_t flags;
53
60 uint32_t writepage : 1;
61
63 uint32_t direct_io : 1;
64
69 uint32_t keep_cache : 1;
70
74 uint32_t flush : 1;
75
78 uint32_t nonseekable : 1;
79
80 /* Indicates that flock locks for this file should be
81 released. If set, lock_owner shall contain a valid value.
82 May only be set in ->release(). */
83 uint32_t flock_release : 1;
84
89 uint32_t cache_readdir : 1;
90
93 uint32_t noflush : 1;
94
97 uint32_t parallel_direct_writes : 1;
98
100 uint32_t padding : 23;
101 uint32_t padding2 : 32;
102 uint32_t padding3 : 32;
103
107 uint64_t fh;
108
110 uint64_t lock_owner;
111
114 uint32_t poll_events;
115
119 int32_t backing_id;
120
122 uint64_t compat_flags;
123
124 uint64_t reserved[2];
125};
126
137#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
138struct fuse_loop_config_v1; /* forward declaration */
139struct fuse_loop_config {
140#else
141struct fuse_loop_config_v1 {
142#endif
147 int clone_fd;
148
159 unsigned int max_idle_threads;
160};
161
162
163/**************************************************************************
164 * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
165 **************************************************************************/
166
177#define FUSE_CAP_ASYNC_READ (1UL << 0)
178
185#define FUSE_CAP_POSIX_LOCKS (1UL << 1)
186
194#define FUSE_CAP_ATOMIC_O_TRUNC (1UL << 3)
195
206#define FUSE_CAP_EXPORT_SUPPORT (1UL << 4)
207
214#define FUSE_CAP_DONT_MASK (1UL << 6)
215
222#define FUSE_CAP_SPLICE_WRITE (1UL << 7)
223
230#define FUSE_CAP_SPLICE_MOVE (1UL << 8)
231
239#define FUSE_CAP_SPLICE_READ (1UL << 9)
240
252#define FUSE_CAP_FLOCK_LOCKS (1UL << 10)
253
259#define FUSE_CAP_IOCTL_DIR (1UL << 11)
260
281#define FUSE_CAP_AUTO_INVAL_DATA (1UL << 12)
282
289#define FUSE_CAP_READDIRPLUS (1UL << 13)
290
317#define FUSE_CAP_READDIRPLUS_AUTO (1UL << 14)
318
328#define FUSE_CAP_ASYNC_DIO (1UL << 15)
329
337#define FUSE_CAP_WRITEBACK_CACHE (1UL << 16)
338
352#define FUSE_CAP_NO_OPEN_SUPPORT (1UL << 17)
353
360#define FUSE_CAP_PARALLEL_DIROPS (1UL << 18)
361
379#define FUSE_CAP_POSIX_ACL (1UL << 19)
380
388#define FUSE_CAP_HANDLE_KILLPRIV (1UL << 20)
389
405#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1UL << 21)
406
418#define FUSE_CAP_CACHE_SYMLINKS (1UL << 23)
419
433#define FUSE_CAP_NO_OPENDIR_SUPPORT (1UL << 24)
434
456#define FUSE_CAP_EXPLICIT_INVAL_DATA (1UL << 25)
457
472#define FUSE_CAP_EXPIRE_ONLY (1UL << 26)
473
479#define FUSE_CAP_SETXATTR_EXT (1UL << 27)
480
488#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1UL << 28)
489
500#define FUSE_CAP_PASSTHROUGH (1UL << 29)
501
508#define FUSE_CAP_NO_EXPORT_SUPPORT (1UL << 30)
509
513#define FUSE_CAP_OVER_IO_URING (1UL << 31)
514
525#define FUSE_IOCTL_COMPAT (1 << 0)
526#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
527#define FUSE_IOCTL_RETRY (1 << 2)
528#define FUSE_IOCTL_DIR (1 << 4)
529
530#define FUSE_IOCTL_MAX_IOV 256
531
543struct fuse_conn_info {
547 uint32_t proto_major;
548
552 uint32_t proto_minor;
553
557 uint32_t max_write;
558
571 uint32_t max_read;
572
576 uint32_t max_readahead;
577
583 uint32_t capable;
584
595 uint32_t want;
596
625 uint32_t max_background;
626
635 uint32_t congestion_threshold;
636
652 uint32_t time_gran;
653
670#define FUSE_BACKING_STACKED_UNDER (0)
671#define FUSE_BACKING_STACKED_OVER (1)
672 uint32_t max_backing_stack_depth;
673
682 uint32_t no_interrupt : 1;
683
684 /* reserved bits for future use */
685 uint32_t padding : 31;
686
691 uint64_t capable_ext;
692
701 uint64_t want_ext;
702
708
712 uint16_t reserved[31];
713};
714
715struct fuse_session;
716struct fuse_pollhandle;
717struct fuse_conn_info_opts;
718
761struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
762
770void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
771 struct fuse_conn_info *conn);
772
779int fuse_daemonize(int foreground);
780
786int fuse_version(void);
787
793const char *fuse_pkgversion(void);
794
800void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
801
802/* ----------------------------------------------------------- *
803 * Data buffer *
804 * ----------------------------------------------------------- */
805
816 FUSE_BUF_IS_FD = (1 << 1),
817
826
834 FUSE_BUF_FD_RETRY = (1 << 3)
836
878
885struct fuse_buf {
889 size_t size;
890
894 enum fuse_buf_flags flags;
895
901 void *mem;
902
908 int fd;
909
915 off_t pos;
916
923 size_t mem_size;
924};
925
934struct fuse_bufvec {
938 size_t count;
939
943 size_t idx;
944
948 size_t off;
949
953 struct fuse_buf buf[1];
954};
955
960struct libfuse_version
961{
962 uint32_t major;
963 uint32_t minor;
964 uint32_t hotfix;
965 uint32_t padding;
966};
967
968/* Initialize bufvec with a single buffer of given size */
969#define FUSE_BUFVEC_INIT(size__) \
970 ((struct fuse_bufvec) { \
971 /* .count= */ 1, \
972 /* .idx = */ 0, \
973 /* .off = */ 0, \
974 /* .buf = */ { /* [0] = */ { \
975 /* .size = */ (size__), \
976 /* .flags = */ (enum fuse_buf_flags) 0, \
977 /* .mem = */ NULL, \
978 /* .fd = */ -1, \
979 /* .pos = */ 0, \
980 /* .mem_size = */ 0, \
981 } } \
982 } )
983
990size_t fuse_buf_size(const struct fuse_bufvec *bufv);
991
1000ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
1001 enum fuse_buf_copy_flags flags);
1002
1003/* ----------------------------------------------------------- *
1004 * Signal handling *
1005 * ----------------------------------------------------------- */
1006
1022int fuse_set_signal_handlers(struct fuse_session *se);
1023
1039int fuse_set_fail_signal_handlers(struct fuse_session *se);
1040
1052void fuse_remove_signal_handlers(struct fuse_session *se);
1053
1059#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
1065struct fuse_loop_config *fuse_loop_cfg_create(void);
1066
1070void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
1071
1075void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
1076 unsigned int value);
1077
1081void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
1082 unsigned int value);
1083
1087void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
1088 unsigned int value);
1089
1096void fuse_loop_cfg_convert(struct fuse_loop_config *config,
1097 struct fuse_loop_config_v1 *v1_conf);
1098#endif
1099
1107bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1108
1115void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1116
1124bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1125
1126/*
1127 * DO NOT USE: Not part of public API, for internal test use only.
1128 * The function signature or any use of it is not guaranteeed to
1129 * remain stable. And neither are results of what this function does.
1130 */
1132
1133
1134
1135/* ----------------------------------------------------------- *
1136 * Compatibility stuff *
1137 * ----------------------------------------------------------- */
1138
1139#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30
1140# error only API version 30 or greater is supported
1141#endif
1142
1143#ifdef __cplusplus
1144}
1145#endif
1146
1147
1148/*
1149 * This interface uses 64 bit off_t.
1150 *
1151 * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags!
1152 */
1153
1154#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
1155_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit");
1156#else
1157struct _fuse_off_t_must_be_64bit_dummy_struct \
1158 { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); };
1159#endif
1160
1161#endif /* FUSE_COMMON_H_ */
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
int fuse_set_fail_signal_handlers(struct fuse_session *se)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
Definition helper.c:412
fuse_buf_flags
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_FD_RETRY
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
Definition helper.c:466
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
int fuse_version(void)
Definition fuse.c:5206
void fuse_remove_signal_handlers(struct fuse_session *se)
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
uint16_t request_timeout
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t nonseekable
Definition fuse_common.h:78
int32_t backing_id
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t padding
uint32_t noflush
Definition fuse_common.h:93
uint64_t compat_flags
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
fuse-3.18.2/doc/html/include_2fuse__common_8h_source.html0000644000175000017500000023617215156613442022353 0ustar berndbernd libfuse: include/fuse_common.h Source File
libfuse
fuse_common.h
Go to the documentation of this file.
1/* FUSE: Filesystem in Userspace
2 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
3
4 This program can be distributed under the terms of the GNU LGPLv2.
5 See the file LGPL2.txt.
6*/
7
10#if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_)
11#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
12#endif
13
14#ifndef FUSE_COMMON_H_
15#define FUSE_COMMON_H_
16
17#ifdef HAVE_LIBFUSE_PRIVATE_CONFIG_H
18#include "fuse_config.h"
19#endif
20
21#include "libfuse_config.h"
22
23#include "fuse_opt.h"
24#include "fuse_log.h"
25#include <stdint.h>
26#include <stdbool.h>
27#include <sys/types.h>
28#include <assert.h>
29
30#define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min))
31#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
32
33#ifdef __cplusplus
34extern "C" {
35#endif
36
50struct fuse_file_info {
52 int32_t flags;
53
60 uint32_t writepage : 1;
61
63 uint32_t direct_io : 1;
64
69 uint32_t keep_cache : 1;
70
74 uint32_t flush : 1;
75
78 uint32_t nonseekable : 1;
79
80 /* Indicates that flock locks for this file should be
81 released. If set, lock_owner shall contain a valid value.
82 May only be set in ->release(). */
83 uint32_t flock_release : 1;
84
89 uint32_t cache_readdir : 1;
90
93 uint32_t noflush : 1;
94
97 uint32_t parallel_direct_writes : 1;
98
100 uint32_t padding : 23;
101 uint32_t padding2 : 32;
102 uint32_t padding3 : 32;
103
107 uint64_t fh;
108
110 uint64_t lock_owner;
111
114 uint32_t poll_events;
115
119 int32_t backing_id;
120
122 uint64_t compat_flags;
123
124 uint64_t reserved[2];
125};
126
137#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
138struct fuse_loop_config_v1; /* forward declaration */
139struct fuse_loop_config {
140#else
141struct fuse_loop_config_v1 {
142#endif
147 int clone_fd;
148
159 unsigned int max_idle_threads;
160};
161
162
163/**************************************************************************
164 * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
165 **************************************************************************/
166
177#define FUSE_CAP_ASYNC_READ (1UL << 0)
178
185#define FUSE_CAP_POSIX_LOCKS (1UL << 1)
186
194#define FUSE_CAP_ATOMIC_O_TRUNC (1UL << 3)
195
206#define FUSE_CAP_EXPORT_SUPPORT (1UL << 4)
207
214#define FUSE_CAP_DONT_MASK (1UL << 6)
215
222#define FUSE_CAP_SPLICE_WRITE (1UL << 7)
223
230#define FUSE_CAP_SPLICE_MOVE (1UL << 8)
231
239#define FUSE_CAP_SPLICE_READ (1UL << 9)
240
252#define FUSE_CAP_FLOCK_LOCKS (1UL << 10)
253
259#define FUSE_CAP_IOCTL_DIR (1UL << 11)
260
281#define FUSE_CAP_AUTO_INVAL_DATA (1UL << 12)
282
289#define FUSE_CAP_READDIRPLUS (1UL << 13)
290
317#define FUSE_CAP_READDIRPLUS_AUTO (1UL << 14)
318
328#define FUSE_CAP_ASYNC_DIO (1UL << 15)
329
337#define FUSE_CAP_WRITEBACK_CACHE (1UL << 16)
338
352#define FUSE_CAP_NO_OPEN_SUPPORT (1UL << 17)
353
360#define FUSE_CAP_PARALLEL_DIROPS (1UL << 18)
361
379#define FUSE_CAP_POSIX_ACL (1UL << 19)
380
388#define FUSE_CAP_HANDLE_KILLPRIV (1UL << 20)
389
405#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1UL << 21)
406
418#define FUSE_CAP_CACHE_SYMLINKS (1UL << 23)
419
433#define FUSE_CAP_NO_OPENDIR_SUPPORT (1UL << 24)
434
456#define FUSE_CAP_EXPLICIT_INVAL_DATA (1UL << 25)
457
472#define FUSE_CAP_EXPIRE_ONLY (1UL << 26)
473
479#define FUSE_CAP_SETXATTR_EXT (1UL << 27)
480
488#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1UL << 28)
489
500#define FUSE_CAP_PASSTHROUGH (1UL << 29)
501
508#define FUSE_CAP_NO_EXPORT_SUPPORT (1UL << 30)
509
513#define FUSE_CAP_OVER_IO_URING (1UL << 31)
514
525#define FUSE_IOCTL_COMPAT (1 << 0)
526#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
527#define FUSE_IOCTL_RETRY (1 << 2)
528#define FUSE_IOCTL_DIR (1 << 4)
529
530#define FUSE_IOCTL_MAX_IOV 256
531
543struct fuse_conn_info {
547 uint32_t proto_major;
548
552 uint32_t proto_minor;
553
557 uint32_t max_write;
558
571 uint32_t max_read;
572
576 uint32_t max_readahead;
577
583 uint32_t capable;
584
595 uint32_t want;
596
625 uint32_t max_background;
626
635 uint32_t congestion_threshold;
636
652 uint32_t time_gran;
653
670#define FUSE_BACKING_STACKED_UNDER (0)
671#define FUSE_BACKING_STACKED_OVER (1)
672 uint32_t max_backing_stack_depth;
673
682 uint32_t no_interrupt : 1;
683
684 /* reserved bits for future use */
685 uint32_t padding : 31;
686
691 uint64_t capable_ext;
692
701 uint64_t want_ext;
702
707 uint16_t request_timeout;
708
712 uint16_t reserved[31];
713};
714
715struct fuse_session;
716struct fuse_pollhandle;
717struct fuse_conn_info_opts;
718
761struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
762
770void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
771 struct fuse_conn_info *conn);
772
779int fuse_daemonize(int foreground);
780
786int fuse_version(void);
787
793const char *fuse_pkgversion(void);
794
800void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
801
802/* ----------------------------------------------------------- *
803 * Data buffer *
804 * ----------------------------------------------------------- */
805
816 FUSE_BUF_IS_FD = (1 << 1),
817
826
834 FUSE_BUF_FD_RETRY = (1 << 3)
836
878
885struct fuse_buf {
889 size_t size;
890
894 enum fuse_buf_flags flags;
895
901 void *mem;
902
908 int fd;
909
915 off_t pos;
916
923 size_t mem_size;
924};
925
934struct fuse_bufvec {
938 size_t count;
939
943 size_t idx;
944
948 size_t off;
949
953 struct fuse_buf buf[1];
954};
955
960struct libfuse_version
961{
962 uint32_t major;
963 uint32_t minor;
964 uint32_t hotfix;
965 uint32_t padding;
966};
967
968/* Initialize bufvec with a single buffer of given size */
969#define FUSE_BUFVEC_INIT(size__) \
970 ((struct fuse_bufvec) { \
971 /* .count= */ 1, \
972 /* .idx = */ 0, \
973 /* .off = */ 0, \
974 /* .buf = */ { /* [0] = */ { \
975 /* .size = */ (size__), \
976 /* .flags = */ (enum fuse_buf_flags) 0, \
977 /* .mem = */ NULL, \
978 /* .fd = */ -1, \
979 /* .pos = */ 0, \
980 /* .mem_size = */ 0, \
981 } } \
982 } )
983
990size_t fuse_buf_size(const struct fuse_bufvec *bufv);
991
1000ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
1001 enum fuse_buf_copy_flags flags);
1002
1003/* ----------------------------------------------------------- *
1004 * Signal handling *
1005 * ----------------------------------------------------------- */
1006
1022int fuse_set_signal_handlers(struct fuse_session *se);
1023
1039int fuse_set_fail_signal_handlers(struct fuse_session *se);
1040
1052void fuse_remove_signal_handlers(struct fuse_session *se);
1053
1059#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
1065struct fuse_loop_config *fuse_loop_cfg_create(void);
1066
1070void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
1071
1075void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
1076 unsigned int value);
1077
1081void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
1082 unsigned int value);
1083
1087void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
1088 unsigned int value);
1089
1096void fuse_loop_cfg_convert(struct fuse_loop_config *config,
1097 struct fuse_loop_config_v1 *v1_conf);
1098#endif
1099
1107bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1108
1115void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1116
1124bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1125
1126/*
1127 * DO NOT USE: Not part of public API, for internal test use only.
1128 * The function signature or any use of it is not guaranteeed to
1129 * remain stable. And neither are results of what this function does.
1130 */
1132
1133
1134
1135/* ----------------------------------------------------------- *
1136 * Compatibility stuff *
1137 * ----------------------------------------------------------- */
1138
1139#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30
1140# error only API version 30 or greater is supported
1141#endif
1142
1143#ifdef __cplusplus
1144}
1145#endif
1146
1147
1148/*
1149 * This interface uses 64 bit off_t.
1150 *
1151 * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags!
1152 */
1153
1154#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
1155_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit");
1156#else
1157struct _fuse_off_t_must_be_64bit_dummy_struct \
1158 { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); };
1159#endif
1160
1161#endif /* FUSE_COMMON_H_ */
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
int fuse_set_fail_signal_handlers(struct fuse_session *se)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
Definition helper.c:412
fuse_buf_flags
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_FD_RETRY
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
Definition helper.c:466
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
int fuse_version(void)
Definition fuse.c:5206
void fuse_remove_signal_handlers(struct fuse_session *se)
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t nonseekable
Definition fuse_common.h:78
int32_t backing_id
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t padding
uint32_t noflush
Definition fuse_common.h:93
uint64_t compat_flags
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__kernel_8h_source.html0000644000175000017500000042344315156613442024520 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_kernel.h Source File
libfuse
fuse_kernel.h
1/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */
2/*
3 This file defines the kernel interface of FUSE
4 Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
5
6 This program can be distributed under the terms of the GNU GPL.
7 See the file COPYING.
8
9 This -- and only this -- header file may also be distributed under
10 the terms of the BSD Licence as follows:
11
12 Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
13
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions
16 are met:
17 1. Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19 2. Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in the
21 documentation and/or other materials provided with the distribution.
22
23 THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
27 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 SUCH DAMAGE.
34*/
35
36/*
37 * This file defines the kernel interface of FUSE
38 *
39 * Protocol changelog:
40 *
41 * 7.1:
42 * - add the following messages:
43 * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK,
44 * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE,
45 * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR,
46 * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR,
47 * FUSE_RELEASEDIR
48 * - add padding to messages to accommodate 32-bit servers on 64-bit kernels
49 *
50 * 7.2:
51 * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags
52 * - add FUSE_FSYNCDIR message
53 *
54 * 7.3:
55 * - add FUSE_ACCESS message
56 * - add FUSE_CREATE message
57 * - add filehandle to fuse_setattr_in
58 *
59 * 7.4:
60 * - add frsize to fuse_kstatfs
61 * - clean up request size limit checking
62 *
63 * 7.5:
64 * - add flags and max_write to fuse_init_out
65 *
66 * 7.6:
67 * - add max_readahead to fuse_init_in and fuse_init_out
68 *
69 * 7.7:
70 * - add FUSE_INTERRUPT message
71 * - add POSIX file lock support
72 *
73 * 7.8:
74 * - add lock_owner and flags fields to fuse_release_in
75 * - add FUSE_BMAP message
76 * - add FUSE_DESTROY message
77 *
78 * 7.9:
79 * - new fuse_getattr_in input argument of GETATTR
80 * - add lk_flags in fuse_lk_in
81 * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
82 * - add blksize field to fuse_attr
83 * - add file flags field to fuse_read_in and fuse_write_in
84 * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in
85 *
86 * 7.10
87 * - add nonseekable open flag
88 *
89 * 7.11
90 * - add IOCTL message
91 * - add unsolicited notification support
92 * - add POLL message and NOTIFY_POLL notification
93 *
94 * 7.12
95 * - add umask flag to input argument of create, mknod and mkdir
96 * - add notification messages for invalidation of inodes and
97 * directory entries
98 *
99 * 7.13
100 * - make max number of background requests and congestion threshold
101 * tunables
102 *
103 * 7.14
104 * - add splice support to fuse device
105 *
106 * 7.15
107 * - add store notify
108 * - add retrieve notify
109 *
110 * 7.16
111 * - add BATCH_FORGET request
112 * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct
113 * fuse_ioctl_iovec' instead of ambiguous 'struct iovec'
114 * - add FUSE_IOCTL_32BIT flag
115 *
116 * 7.17
117 * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK
118 *
119 * 7.18
120 * - add FUSE_IOCTL_DIR flag
121 * - add FUSE_NOTIFY_DELETE
122 *
123 * 7.19
124 * - add FUSE_FALLOCATE
125 *
126 * 7.20
127 * - add FUSE_AUTO_INVAL_DATA
128 *
129 * 7.21
130 * - add FUSE_READDIRPLUS
131 * - send the requested events in POLL request
132 *
133 * 7.22
134 * - add FUSE_ASYNC_DIO
135 *
136 * 7.23
137 * - add FUSE_WRITEBACK_CACHE
138 * - add time_gran to fuse_init_out
139 * - add reserved space to fuse_init_out
140 * - add FATTR_CTIME
141 * - add ctime and ctimensec to fuse_setattr_in
142 * - add FUSE_RENAME2 request
143 * - add FUSE_NO_OPEN_SUPPORT flag
144 *
145 * 7.24
146 * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support
147 *
148 * 7.25
149 * - add FUSE_PARALLEL_DIROPS
150 *
151 * 7.26
152 * - add FUSE_HANDLE_KILLPRIV
153 * - add FUSE_POSIX_ACL
154 *
155 * 7.27
156 * - add FUSE_ABORT_ERROR
157 *
158 * 7.28
159 * - add FUSE_COPY_FILE_RANGE
160 * - add FOPEN_CACHE_DIR
161 * - add FUSE_MAX_PAGES, add max_pages to init_out
162 * - add FUSE_CACHE_SYMLINKS
163 *
164 * 7.29
165 * - add FUSE_NO_OPENDIR_SUPPORT flag
166 *
167 * 7.30
168 * - add FUSE_EXPLICIT_INVAL_DATA
169 * - add FUSE_IOCTL_COMPAT_X32
170 *
171 * 7.31
172 * - add FUSE_WRITE_KILL_PRIV flag
173 * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
174 * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
175 *
176 * 7.32
177 * - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
178 *
179 * 7.33
180 * - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
181 * - add FUSE_OPEN_KILL_SUIDGID
182 * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
183 * - add FUSE_SETXATTR_ACL_KILL_SGID
184 *
185 * 7.34
186 * - add FUSE_SYNCFS
187 *
188 * 7.35
189 * - add FOPEN_NOFLUSH
190 *
191 * 7.36
192 * - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
193 * - add flags2 to fuse_init_in and fuse_init_out
194 * - add FUSE_SECURITY_CTX init flag
195 * - add security context to create, mkdir, symlink, and mknod requests
196 * - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
197 *
198 * 7.37
199 * - add FUSE_TMPFILE
200 *
201 * 7.38
202 * - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
203 * - add FOPEN_PARALLEL_DIRECT_WRITES
204 * - add total_extlen to fuse_in_header
205 * - add FUSE_MAX_NR_SECCTX
206 * - add extension header
207 * - add FUSE_EXT_GROUPS
208 * - add FUSE_CREATE_SUPP_GROUP
209 * - add FUSE_HAS_EXPIRE_ONLY
210 *
211 * 7.39
212 * - add FUSE_DIRECT_IO_ALLOW_MMAP
213 * - add FUSE_STATX and related structures
214 *
215 * 7.40
216 * - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag
217 * - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag
218 * - add FUSE_NO_EXPORT_SUPPORT init flag
219 * - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag
220 */
221
222#ifndef _LINUX_FUSE_H
223#define _LINUX_FUSE_H
224
225#ifdef __KERNEL__
226#include <linux/types.h>
227#else
228#include <stdint.h>
229#endif
230
231/*
232 * Version negotiation:
233 *
234 * Both the kernel and userspace send the version they support in the
235 * INIT request and reply respectively.
236 *
237 * If the major versions match then both shall use the smallest
238 * of the two minor versions for communication.
239 *
240 * If the kernel supports a larger major version, then userspace shall
241 * reply with the major version it supports, ignore the rest of the
242 * INIT message and expect a new INIT message from the kernel with a
243 * matching major version.
244 *
245 * If the library supports a larger major version, then it shall fall
246 * back to the major protocol version sent by the kernel for
247 * communication and reply with that major version (and an arbitrary
248 * supported minor version).
249 */
250
252#define FUSE_KERNEL_VERSION 7
253
255#define FUSE_KERNEL_MINOR_VERSION 40
256
258#define FUSE_ROOT_ID 1
259
260/* Make sure all structures are padded to 64bit boundary, so 32bit
261 userspace works under 64bit kernels */
262
263struct fuse_attr {
264 uint64_t ino;
265 uint64_t size;
266 uint64_t blocks;
267 uint64_t atime;
268 uint64_t mtime;
269 uint64_t ctime;
270 uint32_t atimensec;
271 uint32_t mtimensec;
272 uint32_t ctimensec;
273 uint32_t mode;
274 uint32_t nlink;
275 uint32_t uid;
276 uint32_t gid;
277 uint32_t rdev;
278 uint32_t blksize;
279 uint32_t flags;
280};
281
282/*
283 * The following structures are bit-for-bit compatible with the statx(2) ABI in
284 * Linux.
285 */
286struct fuse_sx_time {
287 int64_t tv_sec;
288 uint32_t tv_nsec;
289 int32_t __reserved;
290};
291
292struct fuse_statx {
293 uint32_t mask;
294 uint32_t blksize;
295 uint64_t attributes;
296 uint32_t nlink;
297 uint32_t uid;
298 uint32_t gid;
299 uint16_t mode;
300 uint16_t __spare0[1];
301 uint64_t ino;
302 uint64_t size;
303 uint64_t blocks;
304 uint64_t attributes_mask;
305 struct fuse_sx_time atime;
306 struct fuse_sx_time btime;
307 struct fuse_sx_time ctime;
308 struct fuse_sx_time mtime;
309 uint32_t rdev_major;
310 uint32_t rdev_minor;
311 uint32_t dev_major;
312 uint32_t dev_minor;
313 uint64_t __spare2[14];
314};
315
316struct fuse_kstatfs {
317 uint64_t blocks;
318 uint64_t bfree;
319 uint64_t bavail;
320 uint64_t files;
321 uint64_t ffree;
322 uint32_t bsize;
323 uint32_t namelen;
324 uint32_t frsize;
325 uint32_t padding;
326 uint32_t spare[6];
327};
328
329struct fuse_file_lock {
330 uint64_t start;
331 uint64_t end;
332 uint32_t type;
333 uint32_t pid; /* tgid */
334};
335
339#define FATTR_MODE (1 << 0)
340#define FATTR_UID (1 << 1)
341#define FATTR_GID (1 << 2)
342#define FATTR_SIZE (1 << 3)
343#define FATTR_ATIME (1 << 4)
344#define FATTR_MTIME (1 << 5)
345#define FATTR_FH (1 << 6)
346#define FATTR_ATIME_NOW (1 << 7)
347#define FATTR_MTIME_NOW (1 << 8)
348#define FATTR_LOCKOWNER (1 << 9)
349#define FATTR_CTIME (1 << 10)
350#define FATTR_KILL_SUIDGID (1 << 11)
351
364#define FOPEN_DIRECT_IO (1 << 0)
365#define FOPEN_KEEP_CACHE (1 << 1)
366#define FOPEN_NONSEEKABLE (1 << 2)
367#define FOPEN_CACHE_DIR (1 << 3)
368#define FOPEN_STREAM (1 << 4)
369#define FOPEN_NOFLUSH (1 << 5)
370#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
371#define FOPEN_PASSTHROUGH (1 << 7)
372
425#define FUSE_ASYNC_READ (1 << 0)
426#define FUSE_POSIX_LOCKS (1 << 1)
427#define FUSE_FILE_OPS (1 << 2)
428#define FUSE_ATOMIC_O_TRUNC (1 << 3)
429#define FUSE_EXPORT_SUPPORT (1 << 4)
430#define FUSE_BIG_WRITES (1 << 5)
431#define FUSE_DONT_MASK (1 << 6)
432#define FUSE_SPLICE_WRITE (1 << 7)
433#define FUSE_SPLICE_MOVE (1 << 8)
434#define FUSE_SPLICE_READ (1 << 9)
435#define FUSE_FLOCK_LOCKS (1 << 10)
436#define FUSE_HAS_IOCTL_DIR (1 << 11)
437#define FUSE_AUTO_INVAL_DATA (1 << 12)
438#define FUSE_DO_READDIRPLUS (1 << 13)
439#define FUSE_READDIRPLUS_AUTO (1 << 14)
440#define FUSE_ASYNC_DIO (1 << 15)
441#define FUSE_WRITEBACK_CACHE (1 << 16)
442#define FUSE_NO_OPEN_SUPPORT (1 << 17)
443#define FUSE_PARALLEL_DIROPS (1 << 18)
444#define FUSE_HANDLE_KILLPRIV (1 << 19)
445#define FUSE_POSIX_ACL (1 << 20)
446#define FUSE_ABORT_ERROR (1 << 21)
447#define FUSE_MAX_PAGES (1 << 22)
448#define FUSE_CACHE_SYMLINKS (1 << 23)
449#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
450#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
451#define FUSE_MAP_ALIGNMENT (1 << 26)
452#define FUSE_SUBMOUNTS (1 << 27)
453#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
454#define FUSE_SETXATTR_EXT (1 << 29)
455#define FUSE_INIT_EXT (1 << 30)
456#define FUSE_INIT_RESERVED (1 << 31)
457/* bits 32..63 get shifted down 32 bits into the flags2 field */
458#define FUSE_SECURITY_CTX (1ULL << 32)
459#define FUSE_HAS_INODE_DAX (1ULL << 33)
460#define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
461#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35)
462#define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36)
463#define FUSE_PASSTHROUGH (1ULL << 37)
464#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38)
465#define FUSE_HAS_RESEND (1ULL << 39)
466
467/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
468#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
469
475#define CUSE_UNRESTRICTED_IOCTL (1 << 0)
476
480#define FUSE_RELEASE_FLUSH (1 << 0)
481#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1)
482
486#define FUSE_GETATTR_FH (1 << 0)
487
491#define FUSE_LK_FLOCK (1 << 0)
492
500#define FUSE_WRITE_CACHE (1 << 0)
501#define FUSE_WRITE_LOCKOWNER (1 << 1)
502#define FUSE_WRITE_KILL_SUIDGID (1 << 2)
503
504/* Obsolete alias; this flag implies killing suid/sgid only. */
505#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID
506
510#define FUSE_READ_LOCKOWNER (1 << 1)
511
524#define FUSE_IOCTL_COMPAT (1 << 0)
525#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
526#define FUSE_IOCTL_RETRY (1 << 2)
527#define FUSE_IOCTL_32BIT (1 << 3)
528#define FUSE_IOCTL_DIR (1 << 4)
529#define FUSE_IOCTL_COMPAT_X32 (1 << 5)
530
531#define FUSE_IOCTL_MAX_IOV 256
532
538#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
539
545#define FUSE_FSYNC_FDATASYNC (1 << 0)
546
553#define FUSE_ATTR_SUBMOUNT (1 << 0)
554#define FUSE_ATTR_DAX (1 << 1)
555
560#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
561
566#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
567
572#define FUSE_EXPIRE_ONLY (1 << 0)
573
579enum fuse_ext_type {
580 /* Types 0..31 are reserved for fuse_secctx_header */
581 FUSE_MAX_NR_SECCTX = 31,
582 FUSE_EXT_GROUPS = 32,
583};
584
585enum fuse_opcode {
586 FUSE_LOOKUP = 1,
587 FUSE_FORGET = 2, /* no reply */
588 FUSE_GETATTR = 3,
589 FUSE_SETATTR = 4,
590 FUSE_READLINK = 5,
591 FUSE_SYMLINK = 6,
592 FUSE_MKNOD = 8,
593 FUSE_MKDIR = 9,
594 FUSE_UNLINK = 10,
595 FUSE_RMDIR = 11,
596 FUSE_RENAME = 12,
597 FUSE_LINK = 13,
598 FUSE_OPEN = 14,
599 FUSE_READ = 15,
600 FUSE_WRITE = 16,
601 FUSE_STATFS = 17,
602 FUSE_RELEASE = 18,
603 FUSE_FSYNC = 20,
604 FUSE_SETXATTR = 21,
605 FUSE_GETXATTR = 22,
606 FUSE_LISTXATTR = 23,
607 FUSE_REMOVEXATTR = 24,
608 FUSE_FLUSH = 25,
609 FUSE_INIT = 26,
610 FUSE_OPENDIR = 27,
611 FUSE_READDIR = 28,
612 FUSE_RELEASEDIR = 29,
613 FUSE_FSYNCDIR = 30,
614 FUSE_GETLK = 31,
615 FUSE_SETLK = 32,
616 FUSE_SETLKW = 33,
617 FUSE_ACCESS = 34,
618 FUSE_CREATE = 35,
619 FUSE_INTERRUPT = 36,
620 FUSE_BMAP = 37,
621 FUSE_DESTROY = 38,
622 FUSE_IOCTL = 39,
623 FUSE_POLL = 40,
624 FUSE_NOTIFY_REPLY = 41,
625 FUSE_BATCH_FORGET = 42,
626 FUSE_FALLOCATE = 43,
627 FUSE_READDIRPLUS = 44,
628 FUSE_RENAME2 = 45,
629 FUSE_LSEEK = 46,
630 FUSE_COPY_FILE_RANGE = 47,
631 FUSE_SETUPMAPPING = 48,
632 FUSE_REMOVEMAPPING = 49,
633 FUSE_SYNCFS = 50,
634 FUSE_TMPFILE = 51,
635 FUSE_STATX = 52,
636
637 /* CUSE specific operations */
638 CUSE_INIT = 4096,
639
640 /* Reserved opcodes: helpful to detect structure endian-ness */
641 CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */
642 FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */
643};
644
645enum fuse_notify_code {
646 FUSE_NOTIFY_POLL = 1,
647 FUSE_NOTIFY_INVAL_INODE = 2,
648 FUSE_NOTIFY_INVAL_ENTRY = 3,
649 FUSE_NOTIFY_STORE = 4,
650 FUSE_NOTIFY_RETRIEVE = 5,
651 FUSE_NOTIFY_DELETE = 6,
652 FUSE_NOTIFY_RESEND = 7,
653 FUSE_NOTIFY_CODE_MAX,
654};
655
656/* The read buffer is required to be at least 8k, but may be much larger */
657#define FUSE_MIN_READ_BUFFER 8192
658
659#define FUSE_COMPAT_ENTRY_OUT_SIZE 120
660
661struct fuse_entry_out {
662 uint64_t nodeid; /* Inode ID */
663 uint64_t generation; /* Inode generation: nodeid:gen must
664 be unique for the fs's lifetime */
665 uint64_t entry_valid; /* Cache timeout for the name */
666 uint64_t attr_valid; /* Cache timeout for the attributes */
667 uint32_t entry_valid_nsec;
668 uint32_t attr_valid_nsec;
669 struct fuse_attr attr;
670};
671
672struct fuse_forget_in {
673 uint64_t nlookup;
674};
675
676struct fuse_forget_one {
677 uint64_t nodeid;
678 uint64_t nlookup;
679};
680
681struct fuse_batch_forget_in {
682 uint32_t count;
683 uint32_t dummy;
684};
685
686struct fuse_getattr_in {
687 uint32_t getattr_flags;
688 uint32_t dummy;
689 uint64_t fh;
690};
691
692#define FUSE_COMPAT_ATTR_OUT_SIZE 96
693
694struct fuse_attr_out {
695 uint64_t attr_valid; /* Cache timeout for the attributes */
696 uint32_t attr_valid_nsec;
697 uint32_t dummy;
698 struct fuse_attr attr;
699};
700
701struct fuse_statx_in {
702 uint32_t getattr_flags;
703 uint32_t reserved;
704 uint64_t fh;
705 uint32_t sx_flags;
706 uint32_t sx_mask;
707};
708
709struct fuse_statx_out {
710 uint64_t attr_valid; /* Cache timeout for the attributes */
711 uint32_t attr_valid_nsec;
712 uint32_t flags;
713 uint64_t spare[2];
714 struct fuse_statx stat;
715};
716
717#define FUSE_COMPAT_MKNOD_IN_SIZE 8
718
719struct fuse_mknod_in {
720 uint32_t mode;
721 uint32_t rdev;
722 uint32_t umask;
723 uint32_t padding;
724};
725
726struct fuse_mkdir_in {
727 uint32_t mode;
728 uint32_t umask;
729};
730
731struct fuse_rename_in {
732 uint64_t newdir;
733};
734
735struct fuse_rename2_in {
736 uint64_t newdir;
737 uint32_t flags;
738 uint32_t padding;
739};
740
741struct fuse_link_in {
742 uint64_t oldnodeid;
743};
744
745struct fuse_setattr_in {
746 uint32_t valid;
747 uint32_t padding;
748 uint64_t fh;
749 uint64_t size;
750 uint64_t lock_owner;
751 uint64_t atime;
752 uint64_t mtime;
753 uint64_t ctime;
754 uint32_t atimensec;
755 uint32_t mtimensec;
756 uint32_t ctimensec;
757 uint32_t mode;
758 uint32_t unused4;
759 uint32_t uid;
760 uint32_t gid;
761 uint32_t unused5;
762};
763
764struct fuse_open_in {
765 uint32_t flags;
766 uint32_t open_flags; /* FUSE_OPEN_... */
767};
768
769struct fuse_create_in {
770 uint32_t flags;
771 uint32_t mode;
772 uint32_t umask;
773 uint32_t open_flags; /* FUSE_OPEN_... */
774};
775
776struct fuse_open_out {
777 uint64_t fh;
778 uint32_t open_flags;
779 int32_t backing_id;
780};
781
782struct fuse_release_in {
783 uint64_t fh;
784 uint32_t flags;
785 uint32_t release_flags;
786 uint64_t lock_owner;
787};
788
789struct fuse_flush_in {
790 uint64_t fh;
791 uint32_t unused;
792 uint32_t padding;
793 uint64_t lock_owner;
794};
795
796struct fuse_read_in {
797 uint64_t fh;
798 uint64_t offset;
799 uint32_t size;
800 uint32_t read_flags;
801 uint64_t lock_owner;
802 uint32_t flags;
803 uint32_t padding;
804};
805
806#define FUSE_COMPAT_WRITE_IN_SIZE 24
807
808struct fuse_write_in {
809 uint64_t fh;
810 uint64_t offset;
811 uint32_t size;
812 uint32_t write_flags;
813 uint64_t lock_owner;
814 uint32_t flags;
815 uint32_t padding;
816};
817
818struct fuse_write_out {
819 uint32_t size;
820 uint32_t padding;
821};
822
823#define FUSE_COMPAT_STATFS_SIZE 48
824
825struct fuse_statfs_out {
826 struct fuse_kstatfs st;
827};
828
829struct fuse_fsync_in {
830 uint64_t fh;
831 uint32_t fsync_flags;
832 uint32_t padding;
833};
834
835#define FUSE_COMPAT_SETXATTR_IN_SIZE 8
836
837struct fuse_setxattr_in {
838 uint32_t size;
839 uint32_t flags;
840 uint32_t setxattr_flags;
841 uint32_t padding;
842};
843
844struct fuse_getxattr_in {
845 uint32_t size;
846 uint32_t padding;
847};
848
849struct fuse_getxattr_out {
850 uint32_t size;
851 uint32_t padding;
852};
853
854struct fuse_lk_in {
855 uint64_t fh;
856 uint64_t owner;
857 struct fuse_file_lock lk;
858 uint32_t lk_flags;
859 uint32_t padding;
860};
861
862struct fuse_lk_out {
863 struct fuse_file_lock lk;
864};
865
866struct fuse_access_in {
867 uint32_t mask;
868 uint32_t padding;
869};
870
871struct fuse_init_in {
872 uint32_t major;
873 uint32_t minor;
874 uint32_t max_readahead;
875 uint32_t flags;
876 uint32_t flags2;
877 uint32_t unused[11];
878};
879
880#define FUSE_COMPAT_INIT_OUT_SIZE 8
881#define FUSE_COMPAT_22_INIT_OUT_SIZE 24
882
883struct fuse_init_out {
884 uint32_t major;
885 uint32_t minor;
886 uint32_t max_readahead;
887 uint32_t flags;
888 uint16_t max_background;
889 uint16_t congestion_threshold;
890 uint32_t max_write;
891 uint32_t time_gran;
892 uint16_t max_pages;
893 uint16_t map_alignment;
894 uint32_t flags2;
895 uint32_t max_stack_depth;
896 uint32_t unused[6];
897};
898
899#define CUSE_INIT_INFO_MAX 4096
900
901struct cuse_init_in {
902 uint32_t major;
903 uint32_t minor;
904 uint32_t unused;
905 uint32_t flags;
906};
907
908struct cuse_init_out {
909 uint32_t major;
910 uint32_t minor;
911 uint32_t unused;
912 uint32_t flags;
913 uint32_t max_read;
914 uint32_t max_write;
915 uint32_t dev_major; /* chardev major */
916 uint32_t dev_minor; /* chardev minor */
917 uint32_t spare[10];
918};
919
920struct fuse_interrupt_in {
921 uint64_t unique;
922};
923
924struct fuse_bmap_in {
925 uint64_t block;
926 uint32_t blocksize;
927 uint32_t padding;
928};
929
930struct fuse_bmap_out {
931 uint64_t block;
932};
933
934struct fuse_ioctl_in {
935 uint64_t fh;
936 uint32_t flags;
937 uint32_t cmd;
938 uint64_t arg;
939 uint32_t in_size;
940 uint32_t out_size;
941};
942
943struct fuse_ioctl_iovec {
944 uint64_t base;
945 uint64_t len;
946};
947
948struct fuse_ioctl_out {
949 int32_t result;
950 uint32_t flags;
951 uint32_t in_iovs;
952 uint32_t out_iovs;
953};
954
955struct fuse_poll_in {
956 uint64_t fh;
957 uint64_t kh;
958 uint32_t flags;
959 uint32_t events;
960};
961
962struct fuse_poll_out {
963 uint32_t revents;
964 uint32_t padding;
965};
966
967struct fuse_notify_poll_wakeup_out {
968 uint64_t kh;
969};
970
971struct fuse_fallocate_in {
972 uint64_t fh;
973 uint64_t offset;
974 uint64_t length;
975 uint32_t mode;
976 uint32_t padding;
977};
978
985#define FUSE_UNIQUE_RESEND (1ULL << 63)
986
987struct fuse_in_header {
988 uint32_t len;
989 uint32_t opcode;
990 uint64_t unique;
991 uint64_t nodeid;
992 uint32_t uid;
993 uint32_t gid;
994 uint32_t pid;
995 uint16_t total_extlen; /* length of extensions in 8byte units */
996 uint16_t padding;
997};
998
999struct fuse_out_header {
1000 uint32_t len;
1001 int32_t error;
1002 uint64_t unique;
1003};
1004
1005struct fuse_dirent {
1006 uint64_t ino;
1007 uint64_t off;
1008 uint32_t namelen;
1009 uint32_t type;
1010 char name[];
1011};
1012
1013/* Align variable length records to 64bit boundary */
1014#define FUSE_REC_ALIGN(x) \
1015 (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
1016
1017#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
1018#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
1019#define FUSE_DIRENT_SIZE(d) \
1020 FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
1021
1022struct fuse_direntplus {
1023 struct fuse_entry_out entry_out;
1024 struct fuse_dirent dirent;
1025};
1026
1027#define FUSE_NAME_OFFSET_DIRENTPLUS \
1028 offsetof(struct fuse_direntplus, dirent.name)
1029#define FUSE_DIRENTPLUS_SIZE(d) \
1030 FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen)
1031
1032struct fuse_notify_inval_inode_out {
1033 uint64_t ino;
1034 int64_t off;
1035 int64_t len;
1036};
1037
1038struct fuse_notify_inval_entry_out {
1039 uint64_t parent;
1040 uint32_t namelen;
1041 uint32_t flags;
1042};
1043
1044struct fuse_notify_delete_out {
1045 uint64_t parent;
1046 uint64_t child;
1047 uint32_t namelen;
1048 uint32_t padding;
1049};
1050
1051struct fuse_notify_store_out {
1052 uint64_t nodeid;
1053 uint64_t offset;
1054 uint32_t size;
1055 uint32_t padding;
1056};
1057
1058struct fuse_notify_retrieve_out {
1059 uint64_t notify_unique;
1060 uint64_t nodeid;
1061 uint64_t offset;
1062 uint32_t size;
1063 uint32_t padding;
1064};
1065
1066/* Matches the size of fuse_write_in */
1067struct fuse_notify_retrieve_in {
1068 uint64_t dummy1;
1069 uint64_t offset;
1070 uint32_t size;
1071 uint32_t dummy2;
1072 uint64_t dummy3;
1073 uint64_t dummy4;
1074};
1075
1076struct fuse_backing_map {
1077 int32_t fd;
1078 uint32_t flags;
1079 uint64_t padding;
1080};
1081
1082/* Device ioctls: */
1083#define FUSE_DEV_IOC_MAGIC 229
1084#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
1085#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \
1086 struct fuse_backing_map)
1087#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t)
1088
1089struct fuse_lseek_in {
1090 uint64_t fh;
1091 uint64_t offset;
1092 uint32_t whence;
1093 uint32_t padding;
1094};
1095
1096struct fuse_lseek_out {
1097 uint64_t offset;
1098};
1099
1100struct fuse_copy_file_range_in {
1101 uint64_t fh_in;
1102 uint64_t off_in;
1103 uint64_t nodeid_out;
1104 uint64_t fh_out;
1105 uint64_t off_out;
1106 uint64_t len;
1107 uint64_t flags;
1108};
1109
1110#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
1111#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
1112struct fuse_setupmapping_in {
1113 /* An already open handle */
1114 uint64_t fh;
1115 /* Offset into the file to start the mapping */
1116 uint64_t foffset;
1117 /* Length of mapping required */
1118 uint64_t len;
1119 /* Flags, FUSE_SETUPMAPPING_FLAG_* */
1120 uint64_t flags;
1121 /* Offset in Memory Window */
1122 uint64_t moffset;
1123};
1124
1125struct fuse_removemapping_in {
1126 /* number of fuse_removemapping_one follows */
1127 uint32_t count;
1128};
1129
1130struct fuse_removemapping_one {
1131 /* Offset into the dax window start the unmapping */
1132 uint64_t moffset;
1133 /* Length of mapping required */
1134 uint64_t len;
1135};
1136
1137#define FUSE_REMOVEMAPPING_MAX_ENTRY \
1138 (PAGE_SIZE / sizeof(struct fuse_removemapping_one))
1139
1140struct fuse_syncfs_in {
1141 uint64_t padding;
1142};
1143
1144/*
1145 * For each security context, send fuse_secctx with size of security context
1146 * fuse_secctx will be followed by security context name and this in turn
1147 * will be followed by actual context label.
1148 * fuse_secctx, name, context
1149 */
1150struct fuse_secctx {
1151 uint32_t size;
1152 uint32_t padding;
1153};
1154
1155/*
1156 * Contains the information about how many fuse_secctx structures are being
1157 * sent and what's the total size of all security contexts (including
1158 * size of fuse_secctx_header).
1159 *
1160 */
1161struct fuse_secctx_header {
1162 uint32_t size;
1163 uint32_t nr_secctx;
1164};
1165
1175 uint32_t size;
1176 uint32_t type;
1177};
1178
1185 uint32_t nr_groups;
1186 uint32_t groups[];
1187};
1188
1189#endif /* _LINUX_FUSE_H */
fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__kernel_8h_source.html0000644000175000017500000045311515156613442024515 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_kernel.h Source File
libfuse
fuse_kernel.h
1/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */
2/*
3 This file defines the kernel interface of FUSE
4 Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
5
6 This program can be distributed under the terms of the GNU GPL.
7 See the file COPYING.
8
9 This -- and only this -- header file may also be distributed under
10 the terms of the BSD Licence as follows:
11
12 Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
13
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions
16 are met:
17 1. Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19 2. Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in the
21 documentation and/or other materials provided with the distribution.
22
23 THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
27 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 SUCH DAMAGE.
34*/
35
36/*
37 * This file defines the kernel interface of FUSE
38 *
39 * Protocol changelog:
40 *
41 * 7.1:
42 * - add the following messages:
43 * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK,
44 * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE,
45 * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR,
46 * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR,
47 * FUSE_RELEASEDIR
48 * - add padding to messages to accommodate 32-bit servers on 64-bit kernels
49 *
50 * 7.2:
51 * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags
52 * - add FUSE_FSYNCDIR message
53 *
54 * 7.3:
55 * - add FUSE_ACCESS message
56 * - add FUSE_CREATE message
57 * - add filehandle to fuse_setattr_in
58 *
59 * 7.4:
60 * - add frsize to fuse_kstatfs
61 * - clean up request size limit checking
62 *
63 * 7.5:
64 * - add flags and max_write to fuse_init_out
65 *
66 * 7.6:
67 * - add max_readahead to fuse_init_in and fuse_init_out
68 *
69 * 7.7:
70 * - add FUSE_INTERRUPT message
71 * - add POSIX file lock support
72 *
73 * 7.8:
74 * - add lock_owner and flags fields to fuse_release_in
75 * - add FUSE_BMAP message
76 * - add FUSE_DESTROY message
77 *
78 * 7.9:
79 * - new fuse_getattr_in input argument of GETATTR
80 * - add lk_flags in fuse_lk_in
81 * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
82 * - add blksize field to fuse_attr
83 * - add file flags field to fuse_read_in and fuse_write_in
84 * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in
85 *
86 * 7.10
87 * - add nonseekable open flag
88 *
89 * 7.11
90 * - add IOCTL message
91 * - add unsolicited notification support
92 * - add POLL message and NOTIFY_POLL notification
93 *
94 * 7.12
95 * - add umask flag to input argument of create, mknod and mkdir
96 * - add notification messages for invalidation of inodes and
97 * directory entries
98 *
99 * 7.13
100 * - make max number of background requests and congestion threshold
101 * tunables
102 *
103 * 7.14
104 * - add splice support to fuse device
105 *
106 * 7.15
107 * - add store notify
108 * - add retrieve notify
109 *
110 * 7.16
111 * - add BATCH_FORGET request
112 * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct
113 * fuse_ioctl_iovec' instead of ambiguous 'struct iovec'
114 * - add FUSE_IOCTL_32BIT flag
115 *
116 * 7.17
117 * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK
118 *
119 * 7.18
120 * - add FUSE_IOCTL_DIR flag
121 * - add FUSE_NOTIFY_DELETE
122 *
123 * 7.19
124 * - add FUSE_FALLOCATE
125 *
126 * 7.20
127 * - add FUSE_AUTO_INVAL_DATA
128 *
129 * 7.21
130 * - add FUSE_READDIRPLUS
131 * - send the requested events in POLL request
132 *
133 * 7.22
134 * - add FUSE_ASYNC_DIO
135 *
136 * 7.23
137 * - add FUSE_WRITEBACK_CACHE
138 * - add time_gran to fuse_init_out
139 * - add reserved space to fuse_init_out
140 * - add FATTR_CTIME
141 * - add ctime and ctimensec to fuse_setattr_in
142 * - add FUSE_RENAME2 request
143 * - add FUSE_NO_OPEN_SUPPORT flag
144 *
145 * 7.24
146 * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support
147 *
148 * 7.25
149 * - add FUSE_PARALLEL_DIROPS
150 *
151 * 7.26
152 * - add FUSE_HANDLE_KILLPRIV
153 * - add FUSE_POSIX_ACL
154 *
155 * 7.27
156 * - add FUSE_ABORT_ERROR
157 *
158 * 7.28
159 * - add FUSE_COPY_FILE_RANGE
160 * - add FOPEN_CACHE_DIR
161 * - add FUSE_MAX_PAGES, add max_pages to init_out
162 * - add FUSE_CACHE_SYMLINKS
163 *
164 * 7.29
165 * - add FUSE_NO_OPENDIR_SUPPORT flag
166 *
167 * 7.30
168 * - add FUSE_EXPLICIT_INVAL_DATA
169 * - add FUSE_IOCTL_COMPAT_X32
170 *
171 * 7.31
172 * - add FUSE_WRITE_KILL_PRIV flag
173 * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
174 * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
175 *
176 * 7.32
177 * - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
178 *
179 * 7.33
180 * - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
181 * - add FUSE_OPEN_KILL_SUIDGID
182 * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
183 * - add FUSE_SETXATTR_ACL_KILL_SGID
184 *
185 * 7.34
186 * - add FUSE_SYNCFS
187 *
188 * 7.35
189 * - add FOPEN_NOFLUSH
190 *
191 * 7.36
192 * - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
193 * - add flags2 to fuse_init_in and fuse_init_out
194 * - add FUSE_SECURITY_CTX init flag
195 * - add security context to create, mkdir, symlink, and mknod requests
196 * - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
197 *
198 * 7.37
199 * - add FUSE_TMPFILE
200 *
201 * 7.38
202 * - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
203 * - add FOPEN_PARALLEL_DIRECT_WRITES
204 * - add total_extlen to fuse_in_header
205 * - add FUSE_MAX_NR_SECCTX
206 * - add extension header
207 * - add FUSE_EXT_GROUPS
208 * - add FUSE_CREATE_SUPP_GROUP
209 * - add FUSE_HAS_EXPIRE_ONLY
210 *
211 * 7.39
212 * - add FUSE_DIRECT_IO_ALLOW_MMAP
213 * - add FUSE_STATX and related structures
214 *
215 * 7.40
216 * - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag
217 * - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag
218 * - add FUSE_NO_EXPORT_SUPPORT init flag
219 * - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag
220 *
221 * 7.41
222 * - add FUSE_ALLOW_IDMAP
223 * 7.42
224 * - Add FUSE_OVER_IO_URING and all other io-uring related flags and data
225 * structures:
226 * - struct fuse_uring_ent_in_out
227 * - struct fuse_uring_req_header
228 * - struct fuse_uring_cmd_req
229 * - FUSE_URING_IN_OUT_HEADER_SZ
230 * - FUSE_URING_OP_IN_OUT_SZ
231 * - enum fuse_uring_cmd
232 *
233 * 7.43
234 * - add FUSE_REQUEST_TIMEOUT
235 *
236 * 7.44
237 * - add FUSE_NOTIFY_INC_EPOCH
238 *
239 * 7.45
240 * - add FUSE_COPY_FILE_RANGE_64
241 * - add struct fuse_copy_file_range_out
242 */
243
244#ifndef _LINUX_FUSE_H
245#define _LINUX_FUSE_H
246
247#ifdef __KERNEL__
248#include <linux/types.h>
249#else
250#include <stdint.h>
251#endif
252
253/*
254 * Version negotiation:
255 *
256 * Both the kernel and userspace send the version they support in the
257 * INIT request and reply respectively.
258 *
259 * If the major versions match then both shall use the smallest
260 * of the two minor versions for communication.
261 *
262 * If the kernel supports a larger major version, then userspace shall
263 * reply with the major version it supports, ignore the rest of the
264 * INIT message and expect a new INIT message from the kernel with a
265 * matching major version.
266 *
267 * If the library supports a larger major version, then it shall fall
268 * back to the major protocol version sent by the kernel for
269 * communication and reply with that major version (and an arbitrary
270 * supported minor version).
271 */
272
274#define FUSE_KERNEL_VERSION 7
275
277#define FUSE_KERNEL_MINOR_VERSION 45
278
280#define FUSE_ROOT_ID 1
281
282/* Make sure all structures are padded to 64bit boundary, so 32bit
283 userspace works under 64bit kernels */
284
285struct fuse_attr {
286 uint64_t ino;
287 uint64_t size;
288 uint64_t blocks;
289 uint64_t atime;
290 uint64_t mtime;
291 uint64_t ctime;
292 uint32_t atimensec;
293 uint32_t mtimensec;
294 uint32_t ctimensec;
295 uint32_t mode;
296 uint32_t nlink;
297 uint32_t uid;
298 uint32_t gid;
299 uint32_t rdev;
300 uint32_t blksize;
301 uint32_t flags;
302};
303
304/*
305 * The following structures are bit-for-bit compatible with the statx(2) ABI in
306 * Linux.
307 */
308struct fuse_sx_time {
309 int64_t tv_sec;
310 uint32_t tv_nsec;
311 int32_t __reserved;
312};
313
314struct fuse_statx {
315 uint32_t mask;
316 uint32_t blksize;
317 uint64_t attributes;
318 uint32_t nlink;
319 uint32_t uid;
320 uint32_t gid;
321 uint16_t mode;
322 uint16_t __spare0[1];
323 uint64_t ino;
324 uint64_t size;
325 uint64_t blocks;
326 uint64_t attributes_mask;
327 struct fuse_sx_time atime;
328 struct fuse_sx_time btime;
329 struct fuse_sx_time ctime;
330 struct fuse_sx_time mtime;
331 uint32_t rdev_major;
332 uint32_t rdev_minor;
333 uint32_t dev_major;
334 uint32_t dev_minor;
335 uint64_t __spare2[14];
336};
337
338struct fuse_kstatfs {
339 uint64_t blocks;
340 uint64_t bfree;
341 uint64_t bavail;
342 uint64_t files;
343 uint64_t ffree;
344 uint32_t bsize;
345 uint32_t namelen;
346 uint32_t frsize;
347 uint32_t padding;
348 uint32_t spare[6];
349};
350
351struct fuse_file_lock {
352 uint64_t start;
353 uint64_t end;
354 uint32_t type;
355 uint32_t pid; /* tgid */
356};
357
361#define FATTR_MODE (1 << 0)
362#define FATTR_UID (1 << 1)
363#define FATTR_GID (1 << 2)
364#define FATTR_SIZE (1 << 3)
365#define FATTR_ATIME (1 << 4)
366#define FATTR_MTIME (1 << 5)
367#define FATTR_FH (1 << 6)
368#define FATTR_ATIME_NOW (1 << 7)
369#define FATTR_MTIME_NOW (1 << 8)
370#define FATTR_LOCKOWNER (1 << 9)
371#define FATTR_CTIME (1 << 10)
372#define FATTR_KILL_SUIDGID (1 << 11)
373
386#define FOPEN_DIRECT_IO (1 << 0)
387#define FOPEN_KEEP_CACHE (1 << 1)
388#define FOPEN_NONSEEKABLE (1 << 2)
389#define FOPEN_CACHE_DIR (1 << 3)
390#define FOPEN_STREAM (1 << 4)
391#define FOPEN_NOFLUSH (1 << 5)
392#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
393#define FOPEN_PASSTHROUGH (1 << 7)
394
451#define FUSE_ASYNC_READ (1 << 0)
452#define FUSE_POSIX_LOCKS (1 << 1)
453#define FUSE_FILE_OPS (1 << 2)
454#define FUSE_ATOMIC_O_TRUNC (1 << 3)
455#define FUSE_EXPORT_SUPPORT (1 << 4)
456#define FUSE_BIG_WRITES (1 << 5)
457#define FUSE_DONT_MASK (1 << 6)
458#define FUSE_SPLICE_WRITE (1 << 7)
459#define FUSE_SPLICE_MOVE (1 << 8)
460#define FUSE_SPLICE_READ (1 << 9)
461#define FUSE_FLOCK_LOCKS (1 << 10)
462#define FUSE_HAS_IOCTL_DIR (1 << 11)
463#define FUSE_AUTO_INVAL_DATA (1 << 12)
464#define FUSE_DO_READDIRPLUS (1 << 13)
465#define FUSE_READDIRPLUS_AUTO (1 << 14)
466#define FUSE_ASYNC_DIO (1 << 15)
467#define FUSE_WRITEBACK_CACHE (1 << 16)
468#define FUSE_NO_OPEN_SUPPORT (1 << 17)
469#define FUSE_PARALLEL_DIROPS (1 << 18)
470#define FUSE_HANDLE_KILLPRIV (1 << 19)
471#define FUSE_POSIX_ACL (1 << 20)
472#define FUSE_ABORT_ERROR (1 << 21)
473#define FUSE_MAX_PAGES (1 << 22)
474#define FUSE_CACHE_SYMLINKS (1 << 23)
475#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
476#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
477#define FUSE_MAP_ALIGNMENT (1 << 26)
478#define FUSE_SUBMOUNTS (1 << 27)
479#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
480#define FUSE_SETXATTR_EXT (1 << 29)
481#define FUSE_INIT_EXT (1 << 30)
482#define FUSE_INIT_RESERVED (1 << 31)
483/* bits 32..63 get shifted down 32 bits into the flags2 field */
484#define FUSE_SECURITY_CTX (1ULL << 32)
485#define FUSE_HAS_INODE_DAX (1ULL << 33)
486#define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
487#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35)
488#define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36)
489#define FUSE_PASSTHROUGH (1ULL << 37)
490#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38)
491#define FUSE_HAS_RESEND (1ULL << 39)
492/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
493#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
494#define FUSE_ALLOW_IDMAP (1ULL << 40)
495#define FUSE_OVER_IO_URING (1ULL << 41)
496#define FUSE_REQUEST_TIMEOUT (1ULL << 42)
497
503#define CUSE_UNRESTRICTED_IOCTL (1 << 0)
504
508#define FUSE_RELEASE_FLUSH (1 << 0)
509#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1)
510
514#define FUSE_GETATTR_FH (1 << 0)
515
519#define FUSE_LK_FLOCK (1 << 0)
520
528#define FUSE_WRITE_CACHE (1 << 0)
529#define FUSE_WRITE_LOCKOWNER (1 << 1)
530#define FUSE_WRITE_KILL_SUIDGID (1 << 2)
531
532/* Obsolete alias; this flag implies killing suid/sgid only. */
533#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID
534
538#define FUSE_READ_LOCKOWNER (1 << 1)
539
552#define FUSE_IOCTL_COMPAT (1 << 0)
553#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
554#define FUSE_IOCTL_RETRY (1 << 2)
555#define FUSE_IOCTL_32BIT (1 << 3)
556#define FUSE_IOCTL_DIR (1 << 4)
557#define FUSE_IOCTL_COMPAT_X32 (1 << 5)
558
559#define FUSE_IOCTL_MAX_IOV 256
560
566#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
567
573#define FUSE_FSYNC_FDATASYNC (1 << 0)
574
581#define FUSE_ATTR_SUBMOUNT (1 << 0)
582#define FUSE_ATTR_DAX (1 << 1)
583
588#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
589
594#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
595
600#define FUSE_EXPIRE_ONLY (1 << 0)
601
607enum fuse_ext_type {
608 /* Types 0..31 are reserved for fuse_secctx_header */
609 FUSE_MAX_NR_SECCTX = 31,
610 FUSE_EXT_GROUPS = 32,
611};
612
613enum fuse_opcode {
614 FUSE_LOOKUP = 1,
615 FUSE_FORGET = 2, /* no reply */
616 FUSE_GETATTR = 3,
617 FUSE_SETATTR = 4,
618 FUSE_READLINK = 5,
619 FUSE_SYMLINK = 6,
620 FUSE_MKNOD = 8,
621 FUSE_MKDIR = 9,
622 FUSE_UNLINK = 10,
623 FUSE_RMDIR = 11,
624 FUSE_RENAME = 12,
625 FUSE_LINK = 13,
626 FUSE_OPEN = 14,
627 FUSE_READ = 15,
628 FUSE_WRITE = 16,
629 FUSE_STATFS = 17,
630 FUSE_RELEASE = 18,
631 FUSE_FSYNC = 20,
632 FUSE_SETXATTR = 21,
633 FUSE_GETXATTR = 22,
634 FUSE_LISTXATTR = 23,
635 FUSE_REMOVEXATTR = 24,
636 FUSE_FLUSH = 25,
637 FUSE_INIT = 26,
638 FUSE_OPENDIR = 27,
639 FUSE_READDIR = 28,
640 FUSE_RELEASEDIR = 29,
641 FUSE_FSYNCDIR = 30,
642 FUSE_GETLK = 31,
643 FUSE_SETLK = 32,
644 FUSE_SETLKW = 33,
645 FUSE_ACCESS = 34,
646 FUSE_CREATE = 35,
647 FUSE_INTERRUPT = 36,
648 FUSE_BMAP = 37,
649 FUSE_DESTROY = 38,
650 FUSE_IOCTL = 39,
651 FUSE_POLL = 40,
652 FUSE_NOTIFY_REPLY = 41,
653 FUSE_BATCH_FORGET = 42,
654 FUSE_FALLOCATE = 43,
655 FUSE_READDIRPLUS = 44,
656 FUSE_RENAME2 = 45,
657 FUSE_LSEEK = 46,
658 FUSE_COPY_FILE_RANGE = 47,
659 FUSE_SETUPMAPPING = 48,
660 FUSE_REMOVEMAPPING = 49,
661 FUSE_SYNCFS = 50,
662 FUSE_TMPFILE = 51,
663 FUSE_STATX = 52,
664 FUSE_COPY_FILE_RANGE_64 = 53,
665
666 /* CUSE specific operations */
667 CUSE_INIT = 4096,
668
669 /* Reserved opcodes: helpful to detect structure endian-ness */
670 CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */
671 FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */
672};
673
674enum fuse_notify_code {
675 FUSE_NOTIFY_POLL = 1,
676 FUSE_NOTIFY_INVAL_INODE = 2,
677 FUSE_NOTIFY_INVAL_ENTRY = 3,
678 FUSE_NOTIFY_STORE = 4,
679 FUSE_NOTIFY_RETRIEVE = 5,
680 FUSE_NOTIFY_DELETE = 6,
681 FUSE_NOTIFY_RESEND = 7,
682 FUSE_NOTIFY_INC_EPOCH = 8,
683 FUSE_NOTIFY_CODE_MAX,
684};
685
686/* The read buffer is required to be at least 8k, but may be much larger */
687#define FUSE_MIN_READ_BUFFER 8192
688
689#define FUSE_COMPAT_ENTRY_OUT_SIZE 120
690
691struct fuse_entry_out {
692 uint64_t nodeid; /* Inode ID */
693 uint64_t generation; /* Inode generation: nodeid:gen must
694 be unique for the fs's lifetime */
695 uint64_t entry_valid; /* Cache timeout for the name */
696 uint64_t attr_valid; /* Cache timeout for the attributes */
697 uint32_t entry_valid_nsec;
698 uint32_t attr_valid_nsec;
699 struct fuse_attr attr;
700};
701
702struct fuse_forget_in {
703 uint64_t nlookup;
704};
705
706struct fuse_forget_one {
707 uint64_t nodeid;
708 uint64_t nlookup;
709};
710
711struct fuse_batch_forget_in {
712 uint32_t count;
713 uint32_t dummy;
714};
715
716struct fuse_getattr_in {
717 uint32_t getattr_flags;
718 uint32_t dummy;
719 uint64_t fh;
720};
721
722#define FUSE_COMPAT_ATTR_OUT_SIZE 96
723
724struct fuse_attr_out {
725 uint64_t attr_valid; /* Cache timeout for the attributes */
726 uint32_t attr_valid_nsec;
727 uint32_t dummy;
728 struct fuse_attr attr;
729};
730
731struct fuse_statx_in {
732 uint32_t getattr_flags;
733 uint32_t reserved;
734 uint64_t fh;
735 uint32_t sx_flags;
736 uint32_t sx_mask;
737};
738
739struct fuse_statx_out {
740 uint64_t attr_valid; /* Cache timeout for the attributes */
741 uint32_t attr_valid_nsec;
742 uint32_t flags;
743 uint64_t spare[2];
744 struct fuse_statx stat;
745};
746
747#define FUSE_COMPAT_MKNOD_IN_SIZE 8
748
749struct fuse_mknod_in {
750 uint32_t mode;
751 uint32_t rdev;
752 uint32_t umask;
753 uint32_t padding;
754};
755
756struct fuse_mkdir_in {
757 uint32_t mode;
758 uint32_t umask;
759};
760
761struct fuse_rename_in {
762 uint64_t newdir;
763};
764
765struct fuse_rename2_in {
766 uint64_t newdir;
767 uint32_t flags;
768 uint32_t padding;
769};
770
771struct fuse_link_in {
772 uint64_t oldnodeid;
773};
774
775struct fuse_setattr_in {
776 uint32_t valid;
777 uint32_t padding;
778 uint64_t fh;
779 uint64_t size;
780 uint64_t lock_owner;
781 uint64_t atime;
782 uint64_t mtime;
783 uint64_t ctime;
784 uint32_t atimensec;
785 uint32_t mtimensec;
786 uint32_t ctimensec;
787 uint32_t mode;
788 uint32_t unused4;
789 uint32_t uid;
790 uint32_t gid;
791 uint32_t unused5;
792};
793
794struct fuse_open_in {
795 uint32_t flags;
796 uint32_t open_flags; /* FUSE_OPEN_... */
797};
798
799struct fuse_create_in {
800 uint32_t flags;
801 uint32_t mode;
802 uint32_t umask;
803 uint32_t open_flags; /* FUSE_OPEN_... */
804};
805
806struct fuse_open_out {
807 uint64_t fh;
808 uint32_t open_flags;
809 int32_t backing_id;
810};
811
812struct fuse_release_in {
813 uint64_t fh;
814 uint32_t flags;
815 uint32_t release_flags;
816 uint64_t lock_owner;
817};
818
819struct fuse_flush_in {
820 uint64_t fh;
821 uint32_t unused;
822 uint32_t padding;
823 uint64_t lock_owner;
824};
825
826struct fuse_read_in {
827 uint64_t fh;
828 uint64_t offset;
829 uint32_t size;
830 uint32_t read_flags;
831 uint64_t lock_owner;
832 uint32_t flags;
833 uint32_t padding;
834};
835
836#define FUSE_COMPAT_WRITE_IN_SIZE 24
837
838struct fuse_write_in {
839 uint64_t fh;
840 uint64_t offset;
841 uint32_t size;
842 uint32_t write_flags;
843 uint64_t lock_owner;
844 uint32_t flags;
845 uint32_t padding;
846};
847
848struct fuse_write_out {
849 uint32_t size;
850 uint32_t padding;
851};
852
853#define FUSE_COMPAT_STATFS_SIZE 48
854
855struct fuse_statfs_out {
856 struct fuse_kstatfs st;
857};
858
859struct fuse_fsync_in {
860 uint64_t fh;
861 uint32_t fsync_flags;
862 uint32_t padding;
863};
864
865#define FUSE_COMPAT_SETXATTR_IN_SIZE 8
866
867struct fuse_setxattr_in {
868 uint32_t size;
869 uint32_t flags;
870 uint32_t setxattr_flags;
871 uint32_t padding;
872};
873
874struct fuse_getxattr_in {
875 uint32_t size;
876 uint32_t padding;
877};
878
879struct fuse_getxattr_out {
880 uint32_t size;
881 uint32_t padding;
882};
883
884struct fuse_lk_in {
885 uint64_t fh;
886 uint64_t owner;
887 struct fuse_file_lock lk;
888 uint32_t lk_flags;
889 uint32_t padding;
890};
891
892struct fuse_lk_out {
893 struct fuse_file_lock lk;
894};
895
896struct fuse_access_in {
897 uint32_t mask;
898 uint32_t padding;
899};
900
901struct fuse_init_in {
902 uint32_t major;
903 uint32_t minor;
904 uint32_t max_readahead;
905 uint32_t flags;
906 uint32_t flags2;
907 uint32_t unused[11];
908};
909
910#define FUSE_COMPAT_INIT_OUT_SIZE 8
911#define FUSE_COMPAT_22_INIT_OUT_SIZE 24
912
913struct fuse_init_out {
914 uint32_t major;
915 uint32_t minor;
916 uint32_t max_readahead;
917 uint32_t flags;
918 uint16_t max_background;
919 uint16_t congestion_threshold;
920 uint32_t max_write;
921 uint32_t time_gran;
922 uint16_t max_pages;
923 uint16_t map_alignment;
924 uint32_t flags2;
925 uint32_t max_stack_depth;
926 uint16_t request_timeout;
927 uint16_t unused[11];
928};
929
930#define CUSE_INIT_INFO_MAX 4096
931
932struct cuse_init_in {
933 uint32_t major;
934 uint32_t minor;
935 uint32_t unused;
936 uint32_t flags;
937};
938
939struct cuse_init_out {
940 uint32_t major;
941 uint32_t minor;
942 uint32_t unused;
943 uint32_t flags;
944 uint32_t max_read;
945 uint32_t max_write;
946 uint32_t dev_major; /* chardev major */
947 uint32_t dev_minor; /* chardev minor */
948 uint32_t spare[10];
949};
950
951struct fuse_interrupt_in {
952 uint64_t unique;
953};
954
955struct fuse_bmap_in {
956 uint64_t block;
957 uint32_t blocksize;
958 uint32_t padding;
959};
960
961struct fuse_bmap_out {
962 uint64_t block;
963};
964
965struct fuse_ioctl_in {
966 uint64_t fh;
967 uint32_t flags;
968 uint32_t cmd;
969 uint64_t arg;
970 uint32_t in_size;
971 uint32_t out_size;
972};
973
974struct fuse_ioctl_iovec {
975 uint64_t base;
976 uint64_t len;
977};
978
979struct fuse_ioctl_out {
980 int32_t result;
981 uint32_t flags;
982 uint32_t in_iovs;
983 uint32_t out_iovs;
984};
985
986struct fuse_poll_in {
987 uint64_t fh;
988 uint64_t kh;
989 uint32_t flags;
990 uint32_t events;
991};
992
993struct fuse_poll_out {
994 uint32_t revents;
995 uint32_t padding;
996};
997
998struct fuse_notify_poll_wakeup_out {
999 uint64_t kh;
1000};
1001
1002struct fuse_fallocate_in {
1003 uint64_t fh;
1004 uint64_t offset;
1005 uint64_t length;
1006 uint32_t mode;
1007 uint32_t padding;
1008};
1009
1016#define FUSE_UNIQUE_RESEND (1ULL << 63)
1017
1031#define FUSE_INVALID_UIDGID ((uint32_t)(-1))
1032
1033struct fuse_in_header {
1034 uint32_t len;
1035 uint32_t opcode;
1036 uint64_t unique;
1037 uint64_t nodeid;
1038 uint32_t uid;
1039 uint32_t gid;
1040 uint32_t pid;
1041 uint16_t total_extlen; /* length of extensions in 8byte units */
1042 uint16_t padding;
1043};
1044
1045struct fuse_out_header {
1046 uint32_t len;
1047 int32_t error;
1048 uint64_t unique;
1049};
1050
1051struct fuse_dirent {
1052 uint64_t ino;
1053 uint64_t off;
1054 uint32_t namelen;
1055 uint32_t type;
1056 char name[];
1057};
1058
1059/* Align variable length records to 64bit boundary */
1060#define FUSE_REC_ALIGN(x) \
1061 (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
1062
1063#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
1064#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
1065#define FUSE_DIRENT_SIZE(d) \
1066 FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
1067
1068struct fuse_direntplus {
1069 struct fuse_entry_out entry_out;
1070 struct fuse_dirent dirent;
1071};
1072
1073#define FUSE_NAME_OFFSET_DIRENTPLUS \
1074 offsetof(struct fuse_direntplus, dirent.name)
1075#define FUSE_DIRENTPLUS_SIZE(d) \
1076 FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen)
1077
1078struct fuse_notify_inval_inode_out {
1079 uint64_t ino;
1080 int64_t off;
1081 int64_t len;
1082};
1083
1084struct fuse_notify_inval_entry_out {
1085 uint64_t parent;
1086 uint32_t namelen;
1087 uint32_t flags;
1088};
1089
1090struct fuse_notify_delete_out {
1091 uint64_t parent;
1092 uint64_t child;
1093 uint32_t namelen;
1094 uint32_t padding;
1095};
1096
1097struct fuse_notify_store_out {
1098 uint64_t nodeid;
1099 uint64_t offset;
1100 uint32_t size;
1101 uint32_t padding;
1102};
1103
1104struct fuse_notify_retrieve_out {
1105 uint64_t notify_unique;
1106 uint64_t nodeid;
1107 uint64_t offset;
1108 uint32_t size;
1109 uint32_t padding;
1110};
1111
1112/* Matches the size of fuse_write_in */
1113struct fuse_notify_retrieve_in {
1114 uint64_t dummy1;
1115 uint64_t offset;
1116 uint32_t size;
1117 uint32_t dummy2;
1118 uint64_t dummy3;
1119 uint64_t dummy4;
1120};
1121
1122struct fuse_backing_map {
1123 int32_t fd;
1124 uint32_t flags;
1125 uint64_t padding;
1126};
1127
1128/* Device ioctls: */
1129#define FUSE_DEV_IOC_MAGIC 229
1130#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
1131#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \
1132 struct fuse_backing_map)
1133#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t)
1134
1135struct fuse_lseek_in {
1136 uint64_t fh;
1137 uint64_t offset;
1138 uint32_t whence;
1139 uint32_t padding;
1140};
1141
1142struct fuse_lseek_out {
1143 uint64_t offset;
1144};
1145
1146struct fuse_copy_file_range_in {
1147 uint64_t fh_in;
1148 uint64_t off_in;
1149 uint64_t nodeid_out;
1150 uint64_t fh_out;
1151 uint64_t off_out;
1152 uint64_t len;
1153 uint64_t flags;
1154};
1155
1156/* For FUSE_COPY_FILE_RANGE_64 */
1157struct fuse_copy_file_range_out {
1158 uint64_t bytes_copied;
1159};
1160
1161#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
1162#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
1163struct fuse_setupmapping_in {
1164 /* An already open handle */
1165 uint64_t fh;
1166 /* Offset into the file to start the mapping */
1167 uint64_t foffset;
1168 /* Length of mapping required */
1169 uint64_t len;
1170 /* Flags, FUSE_SETUPMAPPING_FLAG_* */
1171 uint64_t flags;
1172 /* Offset in Memory Window */
1173 uint64_t moffset;
1174};
1175
1176struct fuse_removemapping_in {
1177 /* number of fuse_removemapping_one follows */
1178 uint32_t count;
1179};
1180
1181struct fuse_removemapping_one {
1182 /* Offset into the dax window start the unmapping */
1183 uint64_t moffset;
1184 /* Length of mapping required */
1185 uint64_t len;
1186};
1187
1188#define FUSE_REMOVEMAPPING_MAX_ENTRY \
1189 (PAGE_SIZE / sizeof(struct fuse_removemapping_one))
1190
1191struct fuse_syncfs_in {
1192 uint64_t padding;
1193};
1194
1195/*
1196 * For each security context, send fuse_secctx with size of security context
1197 * fuse_secctx will be followed by security context name and this in turn
1198 * will be followed by actual context label.
1199 * fuse_secctx, name, context
1200 */
1201struct fuse_secctx {
1202 uint32_t size;
1203 uint32_t padding;
1204};
1205
1206/*
1207 * Contains the information about how many fuse_secctx structures are being
1208 * sent and what's the total size of all security contexts (including
1209 * size of fuse_secctx_header).
1210 *
1211 */
1212struct fuse_secctx_header {
1213 uint32_t size;
1214 uint32_t nr_secctx;
1215};
1216
1225struct fuse_ext_header {
1226 uint32_t size;
1227 uint32_t type;
1228};
1229
1235struct fuse_supp_groups {
1236 uint32_t nr_groups;
1237 uint32_t groups[];
1238};
1239
1243#define FUSE_URING_IN_OUT_HEADER_SZ 128
1244#define FUSE_URING_OP_IN_OUT_SZ 128
1245
1246/* Used as part of the fuse_uring_req_header */
1247struct fuse_uring_ent_in_out {
1248 uint64_t flags;
1249
1250 /*
1251 * commit ID to be used in a reply to a ring request (see also
1252 * struct fuse_uring_cmd_req)
1253 */
1254 uint64_t commit_id;
1255
1256 /* size of user payload buffer */
1257 uint32_t payload_sz;
1258 uint32_t padding;
1259
1260 uint64_t reserved;
1261};
1262
1267 /* struct fuse_in_header / struct fuse_out_header */
1268 char in_out[FUSE_URING_IN_OUT_HEADER_SZ];
1269
1270 /* per op code header */
1271 char op_in[FUSE_URING_OP_IN_OUT_SZ];
1272
1273 struct fuse_uring_ent_in_out ring_ent_in_out;
1274};
1275
1279enum fuse_uring_cmd {
1280 FUSE_IO_URING_CMD_INVALID = 0,
1281
1282 /* register the request buffer and fetch a fuse request */
1283 FUSE_IO_URING_CMD_REGISTER = 1,
1284
1285 /* commit fuse request result and fetch next request */
1286 FUSE_IO_URING_CMD_COMMIT_AND_FETCH = 2,
1287};
1288
1293 uint64_t flags;
1294
1295 /* entry identifier for commits */
1296 uint64_t commit_id;
1297
1298 /* queue the command is for (queue index) */
1299 uint16_t qid;
1300 uint8_t padding[6];
1301};
1302
1303#endif /* _LINUX_FUSE_H */
fuse-3.18.2/doc/html/include_2fuse__kernel_8h_source.html0000644000175000017500000045230315156613442022337 0ustar berndbernd libfuse: include/fuse_kernel.h Source File
libfuse
fuse_kernel.h
1/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */
2/*
3 This file defines the kernel interface of FUSE
4 Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
5
6 This program can be distributed under the terms of the GNU GPL.
7 See the file COPYING.
8
9 This -- and only this -- header file may also be distributed under
10 the terms of the BSD Licence as follows:
11
12 Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
13
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions
16 are met:
17 1. Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19 2. Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in the
21 documentation and/or other materials provided with the distribution.
22
23 THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
27 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 SUCH DAMAGE.
34*/
35
36/*
37 * This file defines the kernel interface of FUSE
38 *
39 * Protocol changelog:
40 *
41 * 7.1:
42 * - add the following messages:
43 * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK,
44 * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE,
45 * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR,
46 * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR,
47 * FUSE_RELEASEDIR
48 * - add padding to messages to accommodate 32-bit servers on 64-bit kernels
49 *
50 * 7.2:
51 * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags
52 * - add FUSE_FSYNCDIR message
53 *
54 * 7.3:
55 * - add FUSE_ACCESS message
56 * - add FUSE_CREATE message
57 * - add filehandle to fuse_setattr_in
58 *
59 * 7.4:
60 * - add frsize to fuse_kstatfs
61 * - clean up request size limit checking
62 *
63 * 7.5:
64 * - add flags and max_write to fuse_init_out
65 *
66 * 7.6:
67 * - add max_readahead to fuse_init_in and fuse_init_out
68 *
69 * 7.7:
70 * - add FUSE_INTERRUPT message
71 * - add POSIX file lock support
72 *
73 * 7.8:
74 * - add lock_owner and flags fields to fuse_release_in
75 * - add FUSE_BMAP message
76 * - add FUSE_DESTROY message
77 *
78 * 7.9:
79 * - new fuse_getattr_in input argument of GETATTR
80 * - add lk_flags in fuse_lk_in
81 * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
82 * - add blksize field to fuse_attr
83 * - add file flags field to fuse_read_in and fuse_write_in
84 * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in
85 *
86 * 7.10
87 * - add nonseekable open flag
88 *
89 * 7.11
90 * - add IOCTL message
91 * - add unsolicited notification support
92 * - add POLL message and NOTIFY_POLL notification
93 *
94 * 7.12
95 * - add umask flag to input argument of create, mknod and mkdir
96 * - add notification messages for invalidation of inodes and
97 * directory entries
98 *
99 * 7.13
100 * - make max number of background requests and congestion threshold
101 * tunables
102 *
103 * 7.14
104 * - add splice support to fuse device
105 *
106 * 7.15
107 * - add store notify
108 * - add retrieve notify
109 *
110 * 7.16
111 * - add BATCH_FORGET request
112 * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct
113 * fuse_ioctl_iovec' instead of ambiguous 'struct iovec'
114 * - add FUSE_IOCTL_32BIT flag
115 *
116 * 7.17
117 * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK
118 *
119 * 7.18
120 * - add FUSE_IOCTL_DIR flag
121 * - add FUSE_NOTIFY_DELETE
122 *
123 * 7.19
124 * - add FUSE_FALLOCATE
125 *
126 * 7.20
127 * - add FUSE_AUTO_INVAL_DATA
128 *
129 * 7.21
130 * - add FUSE_READDIRPLUS
131 * - send the requested events in POLL request
132 *
133 * 7.22
134 * - add FUSE_ASYNC_DIO
135 *
136 * 7.23
137 * - add FUSE_WRITEBACK_CACHE
138 * - add time_gran to fuse_init_out
139 * - add reserved space to fuse_init_out
140 * - add FATTR_CTIME
141 * - add ctime and ctimensec to fuse_setattr_in
142 * - add FUSE_RENAME2 request
143 * - add FUSE_NO_OPEN_SUPPORT flag
144 *
145 * 7.24
146 * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support
147 *
148 * 7.25
149 * - add FUSE_PARALLEL_DIROPS
150 *
151 * 7.26
152 * - add FUSE_HANDLE_KILLPRIV
153 * - add FUSE_POSIX_ACL
154 *
155 * 7.27
156 * - add FUSE_ABORT_ERROR
157 *
158 * 7.28
159 * - add FUSE_COPY_FILE_RANGE
160 * - add FOPEN_CACHE_DIR
161 * - add FUSE_MAX_PAGES, add max_pages to init_out
162 * - add FUSE_CACHE_SYMLINKS
163 *
164 * 7.29
165 * - add FUSE_NO_OPENDIR_SUPPORT flag
166 *
167 * 7.30
168 * - add FUSE_EXPLICIT_INVAL_DATA
169 * - add FUSE_IOCTL_COMPAT_X32
170 *
171 * 7.31
172 * - add FUSE_WRITE_KILL_PRIV flag
173 * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
174 * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
175 *
176 * 7.32
177 * - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
178 *
179 * 7.33
180 * - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
181 * - add FUSE_OPEN_KILL_SUIDGID
182 * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
183 * - add FUSE_SETXATTR_ACL_KILL_SGID
184 *
185 * 7.34
186 * - add FUSE_SYNCFS
187 *
188 * 7.35
189 * - add FOPEN_NOFLUSH
190 *
191 * 7.36
192 * - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
193 * - add flags2 to fuse_init_in and fuse_init_out
194 * - add FUSE_SECURITY_CTX init flag
195 * - add security context to create, mkdir, symlink, and mknod requests
196 * - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
197 *
198 * 7.37
199 * - add FUSE_TMPFILE
200 *
201 * 7.38
202 * - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
203 * - add FOPEN_PARALLEL_DIRECT_WRITES
204 * - add total_extlen to fuse_in_header
205 * - add FUSE_MAX_NR_SECCTX
206 * - add extension header
207 * - add FUSE_EXT_GROUPS
208 * - add FUSE_CREATE_SUPP_GROUP
209 * - add FUSE_HAS_EXPIRE_ONLY
210 *
211 * 7.39
212 * - add FUSE_DIRECT_IO_ALLOW_MMAP
213 * - add FUSE_STATX and related structures
214 *
215 * 7.40
216 * - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag
217 * - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag
218 * - add FUSE_NO_EXPORT_SUPPORT init flag
219 * - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag
220 *
221 * 7.41
222 * - add FUSE_ALLOW_IDMAP
223 * 7.42
224 * - Add FUSE_OVER_IO_URING and all other io-uring related flags and data
225 * structures:
226 * - struct fuse_uring_ent_in_out
227 * - struct fuse_uring_req_header
228 * - struct fuse_uring_cmd_req
229 * - FUSE_URING_IN_OUT_HEADER_SZ
230 * - FUSE_URING_OP_IN_OUT_SZ
231 * - enum fuse_uring_cmd
232 *
233 * 7.43
234 * - add FUSE_REQUEST_TIMEOUT
235 *
236 * 7.44
237 * - add FUSE_NOTIFY_INC_EPOCH
238 *
239 * 7.45
240 * - add FUSE_COPY_FILE_RANGE_64
241 * - add struct fuse_copy_file_range_out
242 */
243
244#ifndef _LINUX_FUSE_H
245#define _LINUX_FUSE_H
246
247#ifdef __KERNEL__
248#include <linux/types.h>
249#else
250#include <stdint.h>
251#endif
252
253/*
254 * Version negotiation:
255 *
256 * Both the kernel and userspace send the version they support in the
257 * INIT request and reply respectively.
258 *
259 * If the major versions match then both shall use the smallest
260 * of the two minor versions for communication.
261 *
262 * If the kernel supports a larger major version, then userspace shall
263 * reply with the major version it supports, ignore the rest of the
264 * INIT message and expect a new INIT message from the kernel with a
265 * matching major version.
266 *
267 * If the library supports a larger major version, then it shall fall
268 * back to the major protocol version sent by the kernel for
269 * communication and reply with that major version (and an arbitrary
270 * supported minor version).
271 */
272
274#define FUSE_KERNEL_VERSION 7
275
277#define FUSE_KERNEL_MINOR_VERSION 45
278
280#define FUSE_ROOT_ID 1
281
282/* Make sure all structures are padded to 64bit boundary, so 32bit
283 userspace works under 64bit kernels */
284
285struct fuse_attr {
286 uint64_t ino;
287 uint64_t size;
288 uint64_t blocks;
289 uint64_t atime;
290 uint64_t mtime;
291 uint64_t ctime;
292 uint32_t atimensec;
293 uint32_t mtimensec;
294 uint32_t ctimensec;
295 uint32_t mode;
296 uint32_t nlink;
297 uint32_t uid;
298 uint32_t gid;
299 uint32_t rdev;
300 uint32_t blksize;
301 uint32_t flags;
302};
303
304/*
305 * The following structures are bit-for-bit compatible with the statx(2) ABI in
306 * Linux.
307 */
308struct fuse_sx_time {
309 int64_t tv_sec;
310 uint32_t tv_nsec;
311 int32_t __reserved;
312};
313
314struct fuse_statx {
315 uint32_t mask;
316 uint32_t blksize;
317 uint64_t attributes;
318 uint32_t nlink;
319 uint32_t uid;
320 uint32_t gid;
321 uint16_t mode;
322 uint16_t __spare0[1];
323 uint64_t ino;
324 uint64_t size;
325 uint64_t blocks;
326 uint64_t attributes_mask;
327 struct fuse_sx_time atime;
328 struct fuse_sx_time btime;
329 struct fuse_sx_time ctime;
330 struct fuse_sx_time mtime;
331 uint32_t rdev_major;
332 uint32_t rdev_minor;
333 uint32_t dev_major;
334 uint32_t dev_minor;
335 uint64_t __spare2[14];
336};
337
338struct fuse_kstatfs {
339 uint64_t blocks;
340 uint64_t bfree;
341 uint64_t bavail;
342 uint64_t files;
343 uint64_t ffree;
344 uint32_t bsize;
345 uint32_t namelen;
346 uint32_t frsize;
347 uint32_t padding;
348 uint32_t spare[6];
349};
350
351struct fuse_file_lock {
352 uint64_t start;
353 uint64_t end;
354 uint32_t type;
355 uint32_t pid; /* tgid */
356};
357
361#define FATTR_MODE (1 << 0)
362#define FATTR_UID (1 << 1)
363#define FATTR_GID (1 << 2)
364#define FATTR_SIZE (1 << 3)
365#define FATTR_ATIME (1 << 4)
366#define FATTR_MTIME (1 << 5)
367#define FATTR_FH (1 << 6)
368#define FATTR_ATIME_NOW (1 << 7)
369#define FATTR_MTIME_NOW (1 << 8)
370#define FATTR_LOCKOWNER (1 << 9)
371#define FATTR_CTIME (1 << 10)
372#define FATTR_KILL_SUIDGID (1 << 11)
373
386#define FOPEN_DIRECT_IO (1 << 0)
387#define FOPEN_KEEP_CACHE (1 << 1)
388#define FOPEN_NONSEEKABLE (1 << 2)
389#define FOPEN_CACHE_DIR (1 << 3)
390#define FOPEN_STREAM (1 << 4)
391#define FOPEN_NOFLUSH (1 << 5)
392#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
393#define FOPEN_PASSTHROUGH (1 << 7)
394
451#define FUSE_ASYNC_READ (1 << 0)
452#define FUSE_POSIX_LOCKS (1 << 1)
453#define FUSE_FILE_OPS (1 << 2)
454#define FUSE_ATOMIC_O_TRUNC (1 << 3)
455#define FUSE_EXPORT_SUPPORT (1 << 4)
456#define FUSE_BIG_WRITES (1 << 5)
457#define FUSE_DONT_MASK (1 << 6)
458#define FUSE_SPLICE_WRITE (1 << 7)
459#define FUSE_SPLICE_MOVE (1 << 8)
460#define FUSE_SPLICE_READ (1 << 9)
461#define FUSE_FLOCK_LOCKS (1 << 10)
462#define FUSE_HAS_IOCTL_DIR (1 << 11)
463#define FUSE_AUTO_INVAL_DATA (1 << 12)
464#define FUSE_DO_READDIRPLUS (1 << 13)
465#define FUSE_READDIRPLUS_AUTO (1 << 14)
466#define FUSE_ASYNC_DIO (1 << 15)
467#define FUSE_WRITEBACK_CACHE (1 << 16)
468#define FUSE_NO_OPEN_SUPPORT (1 << 17)
469#define FUSE_PARALLEL_DIROPS (1 << 18)
470#define FUSE_HANDLE_KILLPRIV (1 << 19)
471#define FUSE_POSIX_ACL (1 << 20)
472#define FUSE_ABORT_ERROR (1 << 21)
473#define FUSE_MAX_PAGES (1 << 22)
474#define FUSE_CACHE_SYMLINKS (1 << 23)
475#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
476#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
477#define FUSE_MAP_ALIGNMENT (1 << 26)
478#define FUSE_SUBMOUNTS (1 << 27)
479#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
480#define FUSE_SETXATTR_EXT (1 << 29)
481#define FUSE_INIT_EXT (1 << 30)
482#define FUSE_INIT_RESERVED (1 << 31)
483/* bits 32..63 get shifted down 32 bits into the flags2 field */
484#define FUSE_SECURITY_CTX (1ULL << 32)
485#define FUSE_HAS_INODE_DAX (1ULL << 33)
486#define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
487#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35)
488#define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36)
489#define FUSE_PASSTHROUGH (1ULL << 37)
490#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38)
491#define FUSE_HAS_RESEND (1ULL << 39)
492/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
493#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
494#define FUSE_ALLOW_IDMAP (1ULL << 40)
495#define FUSE_OVER_IO_URING (1ULL << 41)
496#define FUSE_REQUEST_TIMEOUT (1ULL << 42)
497
503#define CUSE_UNRESTRICTED_IOCTL (1 << 0)
504
508#define FUSE_RELEASE_FLUSH (1 << 0)
509#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1)
510
514#define FUSE_GETATTR_FH (1 << 0)
515
519#define FUSE_LK_FLOCK (1 << 0)
520
528#define FUSE_WRITE_CACHE (1 << 0)
529#define FUSE_WRITE_LOCKOWNER (1 << 1)
530#define FUSE_WRITE_KILL_SUIDGID (1 << 2)
531
532/* Obsolete alias; this flag implies killing suid/sgid only. */
533#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID
534
538#define FUSE_READ_LOCKOWNER (1 << 1)
539
552#define FUSE_IOCTL_COMPAT (1 << 0)
553#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
554#define FUSE_IOCTL_RETRY (1 << 2)
555#define FUSE_IOCTL_32BIT (1 << 3)
556#define FUSE_IOCTL_DIR (1 << 4)
557#define FUSE_IOCTL_COMPAT_X32 (1 << 5)
558
559#define FUSE_IOCTL_MAX_IOV 256
560
566#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
567
573#define FUSE_FSYNC_FDATASYNC (1 << 0)
574
581#define FUSE_ATTR_SUBMOUNT (1 << 0)
582#define FUSE_ATTR_DAX (1 << 1)
583
588#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
589
594#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
595
600#define FUSE_EXPIRE_ONLY (1 << 0)
601
607enum fuse_ext_type {
608 /* Types 0..31 are reserved for fuse_secctx_header */
609 FUSE_MAX_NR_SECCTX = 31,
610 FUSE_EXT_GROUPS = 32,
611};
612
613enum fuse_opcode {
614 FUSE_LOOKUP = 1,
615 FUSE_FORGET = 2, /* no reply */
616 FUSE_GETATTR = 3,
617 FUSE_SETATTR = 4,
618 FUSE_READLINK = 5,
619 FUSE_SYMLINK = 6,
620 FUSE_MKNOD = 8,
621 FUSE_MKDIR = 9,
622 FUSE_UNLINK = 10,
623 FUSE_RMDIR = 11,
624 FUSE_RENAME = 12,
625 FUSE_LINK = 13,
626 FUSE_OPEN = 14,
627 FUSE_READ = 15,
628 FUSE_WRITE = 16,
629 FUSE_STATFS = 17,
630 FUSE_RELEASE = 18,
631 FUSE_FSYNC = 20,
632 FUSE_SETXATTR = 21,
633 FUSE_GETXATTR = 22,
634 FUSE_LISTXATTR = 23,
635 FUSE_REMOVEXATTR = 24,
636 FUSE_FLUSH = 25,
637 FUSE_INIT = 26,
638 FUSE_OPENDIR = 27,
639 FUSE_READDIR = 28,
640 FUSE_RELEASEDIR = 29,
641 FUSE_FSYNCDIR = 30,
642 FUSE_GETLK = 31,
643 FUSE_SETLK = 32,
644 FUSE_SETLKW = 33,
645 FUSE_ACCESS = 34,
646 FUSE_CREATE = 35,
647 FUSE_INTERRUPT = 36,
648 FUSE_BMAP = 37,
649 FUSE_DESTROY = 38,
650 FUSE_IOCTL = 39,
651 FUSE_POLL = 40,
652 FUSE_NOTIFY_REPLY = 41,
653 FUSE_BATCH_FORGET = 42,
654 FUSE_FALLOCATE = 43,
655 FUSE_READDIRPLUS = 44,
656 FUSE_RENAME2 = 45,
657 FUSE_LSEEK = 46,
658 FUSE_COPY_FILE_RANGE = 47,
659 FUSE_SETUPMAPPING = 48,
660 FUSE_REMOVEMAPPING = 49,
661 FUSE_SYNCFS = 50,
662 FUSE_TMPFILE = 51,
663 FUSE_STATX = 52,
664 FUSE_COPY_FILE_RANGE_64 = 53,
665
666 /* CUSE specific operations */
667 CUSE_INIT = 4096,
668
669 /* Reserved opcodes: helpful to detect structure endian-ness */
670 CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */
671 FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */
672};
673
674enum fuse_notify_code {
675 FUSE_NOTIFY_POLL = 1,
676 FUSE_NOTIFY_INVAL_INODE = 2,
677 FUSE_NOTIFY_INVAL_ENTRY = 3,
678 FUSE_NOTIFY_STORE = 4,
679 FUSE_NOTIFY_RETRIEVE = 5,
680 FUSE_NOTIFY_DELETE = 6,
681 FUSE_NOTIFY_RESEND = 7,
682 FUSE_NOTIFY_INC_EPOCH = 8,
683 FUSE_NOTIFY_CODE_MAX,
684};
685
686/* The read buffer is required to be at least 8k, but may be much larger */
687#define FUSE_MIN_READ_BUFFER 8192
688
689#define FUSE_COMPAT_ENTRY_OUT_SIZE 120
690
691struct fuse_entry_out {
692 uint64_t nodeid; /* Inode ID */
693 uint64_t generation; /* Inode generation: nodeid:gen must
694 be unique for the fs's lifetime */
695 uint64_t entry_valid; /* Cache timeout for the name */
696 uint64_t attr_valid; /* Cache timeout for the attributes */
697 uint32_t entry_valid_nsec;
698 uint32_t attr_valid_nsec;
699 struct fuse_attr attr;
700};
701
702struct fuse_forget_in {
703 uint64_t nlookup;
704};
705
706struct fuse_forget_one {
707 uint64_t nodeid;
708 uint64_t nlookup;
709};
710
711struct fuse_batch_forget_in {
712 uint32_t count;
713 uint32_t dummy;
714};
715
716struct fuse_getattr_in {
717 uint32_t getattr_flags;
718 uint32_t dummy;
719 uint64_t fh;
720};
721
722#define FUSE_COMPAT_ATTR_OUT_SIZE 96
723
724struct fuse_attr_out {
725 uint64_t attr_valid; /* Cache timeout for the attributes */
726 uint32_t attr_valid_nsec;
727 uint32_t dummy;
728 struct fuse_attr attr;
729};
730
731struct fuse_statx_in {
732 uint32_t getattr_flags;
733 uint32_t reserved;
734 uint64_t fh;
735 uint32_t sx_flags;
736 uint32_t sx_mask;
737};
738
739struct fuse_statx_out {
740 uint64_t attr_valid; /* Cache timeout for the attributes */
741 uint32_t attr_valid_nsec;
742 uint32_t flags;
743 uint64_t spare[2];
744 struct fuse_statx stat;
745};
746
747#define FUSE_COMPAT_MKNOD_IN_SIZE 8
748
749struct fuse_mknod_in {
750 uint32_t mode;
751 uint32_t rdev;
752 uint32_t umask;
753 uint32_t padding;
754};
755
756struct fuse_mkdir_in {
757 uint32_t mode;
758 uint32_t umask;
759};
760
761struct fuse_rename_in {
762 uint64_t newdir;
763};
764
765struct fuse_rename2_in {
766 uint64_t newdir;
767 uint32_t flags;
768 uint32_t padding;
769};
770
771struct fuse_link_in {
772 uint64_t oldnodeid;
773};
774
775struct fuse_setattr_in {
776 uint32_t valid;
777 uint32_t padding;
778 uint64_t fh;
779 uint64_t size;
780 uint64_t lock_owner;
781 uint64_t atime;
782 uint64_t mtime;
783 uint64_t ctime;
784 uint32_t atimensec;
785 uint32_t mtimensec;
786 uint32_t ctimensec;
787 uint32_t mode;
788 uint32_t unused4;
789 uint32_t uid;
790 uint32_t gid;
791 uint32_t unused5;
792};
793
794struct fuse_open_in {
795 uint32_t flags;
796 uint32_t open_flags; /* FUSE_OPEN_... */
797};
798
799struct fuse_create_in {
800 uint32_t flags;
801 uint32_t mode;
802 uint32_t umask;
803 uint32_t open_flags; /* FUSE_OPEN_... */
804};
805
806struct fuse_open_out {
807 uint64_t fh;
808 uint32_t open_flags;
809 int32_t backing_id;
810};
811
812struct fuse_release_in {
813 uint64_t fh;
814 uint32_t flags;
815 uint32_t release_flags;
816 uint64_t lock_owner;
817};
818
819struct fuse_flush_in {
820 uint64_t fh;
821 uint32_t unused;
822 uint32_t padding;
823 uint64_t lock_owner;
824};
825
826struct fuse_read_in {
827 uint64_t fh;
828 uint64_t offset;
829 uint32_t size;
830 uint32_t read_flags;
831 uint64_t lock_owner;
832 uint32_t flags;
833 uint32_t padding;
834};
835
836#define FUSE_COMPAT_WRITE_IN_SIZE 24
837
838struct fuse_write_in {
839 uint64_t fh;
840 uint64_t offset;
841 uint32_t size;
842 uint32_t write_flags;
843 uint64_t lock_owner;
844 uint32_t flags;
845 uint32_t padding;
846};
847
848struct fuse_write_out {
849 uint32_t size;
850 uint32_t padding;
851};
852
853#define FUSE_COMPAT_STATFS_SIZE 48
854
855struct fuse_statfs_out {
856 struct fuse_kstatfs st;
857};
858
859struct fuse_fsync_in {
860 uint64_t fh;
861 uint32_t fsync_flags;
862 uint32_t padding;
863};
864
865#define FUSE_COMPAT_SETXATTR_IN_SIZE 8
866
867struct fuse_setxattr_in {
868 uint32_t size;
869 uint32_t flags;
870 uint32_t setxattr_flags;
871 uint32_t padding;
872};
873
874struct fuse_getxattr_in {
875 uint32_t size;
876 uint32_t padding;
877};
878
879struct fuse_getxattr_out {
880 uint32_t size;
881 uint32_t padding;
882};
883
884struct fuse_lk_in {
885 uint64_t fh;
886 uint64_t owner;
887 struct fuse_file_lock lk;
888 uint32_t lk_flags;
889 uint32_t padding;
890};
891
892struct fuse_lk_out {
893 struct fuse_file_lock lk;
894};
895
896struct fuse_access_in {
897 uint32_t mask;
898 uint32_t padding;
899};
900
901struct fuse_init_in {
902 uint32_t major;
903 uint32_t minor;
904 uint32_t max_readahead;
905 uint32_t flags;
906 uint32_t flags2;
907 uint32_t unused[11];
908};
909
910#define FUSE_COMPAT_INIT_OUT_SIZE 8
911#define FUSE_COMPAT_22_INIT_OUT_SIZE 24
912
913struct fuse_init_out {
914 uint32_t major;
915 uint32_t minor;
916 uint32_t max_readahead;
917 uint32_t flags;
918 uint16_t max_background;
919 uint16_t congestion_threshold;
920 uint32_t max_write;
921 uint32_t time_gran;
922 uint16_t max_pages;
923 uint16_t map_alignment;
924 uint32_t flags2;
925 uint32_t max_stack_depth;
926 uint16_t request_timeout;
927 uint16_t unused[11];
928};
929
930#define CUSE_INIT_INFO_MAX 4096
931
932struct cuse_init_in {
933 uint32_t major;
934 uint32_t minor;
935 uint32_t unused;
936 uint32_t flags;
937};
938
939struct cuse_init_out {
940 uint32_t major;
941 uint32_t minor;
942 uint32_t unused;
943 uint32_t flags;
944 uint32_t max_read;
945 uint32_t max_write;
946 uint32_t dev_major; /* chardev major */
947 uint32_t dev_minor; /* chardev minor */
948 uint32_t spare[10];
949};
950
951struct fuse_interrupt_in {
952 uint64_t unique;
953};
954
955struct fuse_bmap_in {
956 uint64_t block;
957 uint32_t blocksize;
958 uint32_t padding;
959};
960
961struct fuse_bmap_out {
962 uint64_t block;
963};
964
965struct fuse_ioctl_in {
966 uint64_t fh;
967 uint32_t flags;
968 uint32_t cmd;
969 uint64_t arg;
970 uint32_t in_size;
971 uint32_t out_size;
972};
973
974struct fuse_ioctl_iovec {
975 uint64_t base;
976 uint64_t len;
977};
978
979struct fuse_ioctl_out {
980 int32_t result;
981 uint32_t flags;
982 uint32_t in_iovs;
983 uint32_t out_iovs;
984};
985
986struct fuse_poll_in {
987 uint64_t fh;
988 uint64_t kh;
989 uint32_t flags;
990 uint32_t events;
991};
992
993struct fuse_poll_out {
994 uint32_t revents;
995 uint32_t padding;
996};
997
998struct fuse_notify_poll_wakeup_out {
999 uint64_t kh;
1000};
1001
1002struct fuse_fallocate_in {
1003 uint64_t fh;
1004 uint64_t offset;
1005 uint64_t length;
1006 uint32_t mode;
1007 uint32_t padding;
1008};
1009
1016#define FUSE_UNIQUE_RESEND (1ULL << 63)
1017
1031#define FUSE_INVALID_UIDGID ((uint32_t)(-1))
1032
1033struct fuse_in_header {
1034 uint32_t len;
1035 uint32_t opcode;
1036 uint64_t unique;
1037 uint64_t nodeid;
1038 uint32_t uid;
1039 uint32_t gid;
1040 uint32_t pid;
1041 uint16_t total_extlen; /* length of extensions in 8byte units */
1042 uint16_t padding;
1043};
1044
1045struct fuse_out_header {
1046 uint32_t len;
1047 int32_t error;
1048 uint64_t unique;
1049};
1050
1051struct fuse_dirent {
1052 uint64_t ino;
1053 uint64_t off;
1054 uint32_t namelen;
1055 uint32_t type;
1056 char name[];
1057};
1058
1059/* Align variable length records to 64bit boundary */
1060#define FUSE_REC_ALIGN(x) \
1061 (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
1062
1063#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
1064#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
1065#define FUSE_DIRENT_SIZE(d) \
1066 FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
1067
1068struct fuse_direntplus {
1069 struct fuse_entry_out entry_out;
1070 struct fuse_dirent dirent;
1071};
1072
1073#define FUSE_NAME_OFFSET_DIRENTPLUS \
1074 offsetof(struct fuse_direntplus, dirent.name)
1075#define FUSE_DIRENTPLUS_SIZE(d) \
1076 FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen)
1077
1078struct fuse_notify_inval_inode_out {
1079 uint64_t ino;
1080 int64_t off;
1081 int64_t len;
1082};
1083
1084struct fuse_notify_inval_entry_out {
1085 uint64_t parent;
1086 uint32_t namelen;
1087 uint32_t flags;
1088};
1089
1090struct fuse_notify_delete_out {
1091 uint64_t parent;
1092 uint64_t child;
1093 uint32_t namelen;
1094 uint32_t padding;
1095};
1096
1097struct fuse_notify_store_out {
1098 uint64_t nodeid;
1099 uint64_t offset;
1100 uint32_t size;
1101 uint32_t padding;
1102};
1103
1104struct fuse_notify_retrieve_out {
1105 uint64_t notify_unique;
1106 uint64_t nodeid;
1107 uint64_t offset;
1108 uint32_t size;
1109 uint32_t padding;
1110};
1111
1112/* Matches the size of fuse_write_in */
1113struct fuse_notify_retrieve_in {
1114 uint64_t dummy1;
1115 uint64_t offset;
1116 uint32_t size;
1117 uint32_t dummy2;
1118 uint64_t dummy3;
1119 uint64_t dummy4;
1120};
1121
1122struct fuse_backing_map {
1123 int32_t fd;
1124 uint32_t flags;
1125 uint64_t padding;
1126};
1127
1128/* Device ioctls: */
1129#define FUSE_DEV_IOC_MAGIC 229
1130#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
1131#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \
1132 struct fuse_backing_map)
1133#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t)
1134
1135struct fuse_lseek_in {
1136 uint64_t fh;
1137 uint64_t offset;
1138 uint32_t whence;
1139 uint32_t padding;
1140};
1141
1142struct fuse_lseek_out {
1143 uint64_t offset;
1144};
1145
1146struct fuse_copy_file_range_in {
1147 uint64_t fh_in;
1148 uint64_t off_in;
1149 uint64_t nodeid_out;
1150 uint64_t fh_out;
1151 uint64_t off_out;
1152 uint64_t len;
1153 uint64_t flags;
1154};
1155
1156/* For FUSE_COPY_FILE_RANGE_64 */
1157struct fuse_copy_file_range_out {
1158 uint64_t bytes_copied;
1159};
1160
1161#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
1162#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
1163struct fuse_setupmapping_in {
1164 /* An already open handle */
1165 uint64_t fh;
1166 /* Offset into the file to start the mapping */
1167 uint64_t foffset;
1168 /* Length of mapping required */
1169 uint64_t len;
1170 /* Flags, FUSE_SETUPMAPPING_FLAG_* */
1171 uint64_t flags;
1172 /* Offset in Memory Window */
1173 uint64_t moffset;
1174};
1175
1176struct fuse_removemapping_in {
1177 /* number of fuse_removemapping_one follows */
1178 uint32_t count;
1179};
1180
1181struct fuse_removemapping_one {
1182 /* Offset into the dax window start the unmapping */
1183 uint64_t moffset;
1184 /* Length of mapping required */
1185 uint64_t len;
1186};
1187
1188#define FUSE_REMOVEMAPPING_MAX_ENTRY \
1189 (PAGE_SIZE / sizeof(struct fuse_removemapping_one))
1190
1191struct fuse_syncfs_in {
1192 uint64_t padding;
1193};
1194
1195/*
1196 * For each security context, send fuse_secctx with size of security context
1197 * fuse_secctx will be followed by security context name and this in turn
1198 * will be followed by actual context label.
1199 * fuse_secctx, name, context
1200 */
1201struct fuse_secctx {
1202 uint32_t size;
1203 uint32_t padding;
1204};
1205
1206/*
1207 * Contains the information about how many fuse_secctx structures are being
1208 * sent and what's the total size of all security contexts (including
1209 * size of fuse_secctx_header).
1210 *
1211 */
1212struct fuse_secctx_header {
1213 uint32_t size;
1214 uint32_t nr_secctx;
1215};
1216
1225struct fuse_ext_header {
1226 uint32_t size;
1227 uint32_t type;
1228};
1229
1235struct fuse_supp_groups {
1236 uint32_t nr_groups;
1237 uint32_t groups[];
1238};
1239
1243#define FUSE_URING_IN_OUT_HEADER_SZ 128
1244#define FUSE_URING_OP_IN_OUT_SZ 128
1245
1246/* Used as part of the fuse_uring_req_header */
1247struct fuse_uring_ent_in_out {
1248 uint64_t flags;
1249
1250 /*
1251 * commit ID to be used in a reply to a ring request (see also
1252 * struct fuse_uring_cmd_req)
1253 */
1254 uint64_t commit_id;
1255
1256 /* size of user payload buffer */
1257 uint32_t payload_sz;
1258 uint32_t padding;
1259
1260 uint64_t reserved;
1261};
1262
1266struct fuse_uring_req_header {
1267 /* struct fuse_in_header / struct fuse_out_header */
1268 char in_out[FUSE_URING_IN_OUT_HEADER_SZ];
1269
1270 /* per op code header */
1271 char op_in[FUSE_URING_OP_IN_OUT_SZ];
1272
1273 struct fuse_uring_ent_in_out ring_ent_in_out;
1274};
1275
1279enum fuse_uring_cmd {
1280 FUSE_IO_URING_CMD_INVALID = 0,
1281
1282 /* register the request buffer and fetch a fuse request */
1283 FUSE_IO_URING_CMD_REGISTER = 1,
1284
1285 /* commit fuse request result and fetch next request */
1286 FUSE_IO_URING_CMD_COMMIT_AND_FETCH = 2,
1287};
1288
1292struct fuse_uring_cmd_req {
1293 uint64_t flags;
1294
1295 /* entry identifier for commits */
1296 uint64_t commit_id;
1297
1298 /* queue the command is for (queue index) */
1299 uint16_t qid;
1300 uint8_t padding[6];
1301};
1302
1303#endif /* _LINUX_FUSE_H */
fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__log_8h_source.html0000644000175000017500000003025715156613442024016 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_log.h Source File
libfuse
fuse_log.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2019 Red Hat, Inc.
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
7*/
8
9#ifndef FUSE_LOG_H_
10#define FUSE_LOG_H_
11
17#include <stdarg.h>
18
19#ifdef __cplusplus
20extern "C" {
21#endif
22
29 FUSE_LOG_EMERG,
30 FUSE_LOG_ALERT,
31 FUSE_LOG_CRIT,
32 FUSE_LOG_ERR,
33 FUSE_LOG_WARNING,
34 FUSE_LOG_NOTICE,
35 FUSE_LOG_INFO,
36 FUSE_LOG_DEBUG
37};
38
52typedef void (*fuse_log_func_t)(enum fuse_log_level level,
53 const char *fmt, va_list ap);
54
69
76void fuse_log(enum fuse_log_level level, const char *fmt, ...);
77
83void fuse_log_enable_syslog(const char *ident, int option, int facility);
84
88void fuse_log_close_syslog(void);
89
90#ifdef __cplusplus
91}
92#endif
93
94#endif /* FUSE_LOG_H_ */
void fuse_log_close_syslog(void)
Definition fuse_log.c:93
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void(* fuse_log_func_t)(enum fuse_log_level level, const char *fmt, va_list ap)
Definition fuse_log.h:52
void fuse_log_enable_syslog(const char *ident, int option, int facility)
Definition fuse_log.c:86
fuse_log_level
Definition fuse_log.h:28
void fuse_set_log_func(fuse_log_func_t func)
Definition fuse_log.c:69
fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__log_8h_source.html0000644000175000017500000003043015156613442024005 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_log.h Source File
libfuse
fuse_log.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2019 Red Hat, Inc.
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_LOG_H_
10#define FUSE_LOG_H_
11
17#include <stdarg.h>
18
19#ifdef __cplusplus
20extern "C" {
21#endif
22
29 FUSE_LOG_EMERG,
30 FUSE_LOG_ALERT,
31 FUSE_LOG_CRIT,
32 FUSE_LOG_ERR,
33 FUSE_LOG_WARNING,
34 FUSE_LOG_NOTICE,
35 FUSE_LOG_INFO,
36 FUSE_LOG_DEBUG
37};
38
52typedef void (*fuse_log_func_t)(enum fuse_log_level level,
53 const char *fmt, va_list ap);
54
69
76void fuse_log(enum fuse_log_level level, const char *fmt, ...)
77 __attribute__((format(printf, 2, 3)));
78
84void fuse_log_enable_syslog(const char *ident, int option, int facility);
85
89void fuse_log_close_syslog(void);
90
91#ifdef __cplusplus
92}
93#endif
94
95#endif /* FUSE_LOG_H_ */
void fuse_log_close_syslog(void)
Definition fuse_log.c:93
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void(* fuse_log_func_t)(enum fuse_log_level level, const char *fmt, va_list ap)
Definition fuse_log.h:52
void fuse_log_enable_syslog(const char *ident, int option, int facility)
Definition fuse_log.c:86
fuse_log_level
Definition fuse_log.h:28
void fuse_set_log_func(fuse_log_func_t func)
Definition fuse_log.c:69
fuse-3.18.2/doc/html/include_2fuse__log_8h_source.html0000644000175000017500000003017015156613442021632 0ustar berndbernd libfuse: include/fuse_log.h Source File
libfuse
fuse_log.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2019 Red Hat, Inc.
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_LOG_H_
10#define FUSE_LOG_H_
11
17#include <stdarg.h>
18
19#ifdef __cplusplus
20extern "C" {
21#endif
22
29 FUSE_LOG_EMERG,
30 FUSE_LOG_ALERT,
31 FUSE_LOG_CRIT,
32 FUSE_LOG_ERR,
33 FUSE_LOG_WARNING,
34 FUSE_LOG_NOTICE,
35 FUSE_LOG_INFO,
36 FUSE_LOG_DEBUG
37};
38
52typedef void (*fuse_log_func_t)(enum fuse_log_level level,
53 const char *fmt, va_list ap);
54
69
76void fuse_log(enum fuse_log_level level, const char *fmt, ...)
77 __attribute__((format(printf, 2, 3)));
78
84void fuse_log_enable_syslog(const char *ident, int option, int facility);
85
89void fuse_log_close_syslog(void);
90
91#ifdef __cplusplus
92}
93#endif
94
95#endif /* FUSE_LOG_H_ */
void fuse_log_close_syslog(void)
Definition fuse_log.c:93
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void(* fuse_log_func_t)(enum fuse_log_level level, const char *fmt, va_list ap)
Definition fuse_log.h:52
void fuse_log_enable_syslog(const char *ident, int option, int facility)
Definition fuse_log.c:86
fuse_log_level
Definition fuse_log.h:28
void fuse_set_log_func(fuse_log_func_t func)
Definition fuse_log.c:69
fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__lowlevel_8h_source.html0000644000175000017500000051676415156613442025102 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_lowlevel.h Source File
libfuse
fuse_lowlevel.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
7*/
8
9#ifndef FUSE_LOWLEVEL_H_
10#define FUSE_LOWLEVEL_H_
11
21#ifndef FUSE_USE_VERSION
22#error FUSE_USE_VERSION not defined
23#endif
24
25#include "fuse_common.h"
26
27#include <stddef.h>
28#include <utime.h>
29#include <fcntl.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/statvfs.h>
33#include <sys/uio.h>
34
35#ifdef __cplusplus
36extern "C" {
37#endif
38
39/* ----------------------------------------------------------- *
40 * Miscellaneous definitions *
41 * ----------------------------------------------------------- */
42
44#define FUSE_ROOT_ID 1
45
47typedef uint64_t fuse_ino_t;
48
50typedef struct fuse_req *fuse_req_t;
51
57struct fuse_session;
58
69
80 uint64_t generation;
81
89 struct stat attr;
90
96
102};
103
112struct fuse_ctx {
114 uid_t uid;
115
117 gid_t gid;
118
120 pid_t pid;
121
123 mode_t umask;
124};
125
126struct fuse_forget_data {
127 fuse_ino_t ino;
128 uint64_t nlookup;
129};
130
131struct fuse_custom_io {
132 ssize_t (*writev)(int fd, struct iovec *iov, int count, void *userdata);
133 ssize_t (*read)(int fd, void *buf, size_t buf_len, void *userdata);
134 ssize_t (*splice_receive)(int fdin, off_t *offin, int fdout,
135 off_t *offout, size_t len,
136 unsigned int flags, void *userdata);
137 ssize_t (*splice_send)(int fdin, off_t *offin, int fdout,
138 off_t *offout, size_t len,
139 unsigned int flags, void *userdata);
140 int (*clone_fd)(int master_fd);
141};
142
149 FUSE_LL_INVALIDATE = 0,
150 FUSE_LL_EXPIRE_ONLY = (1 << 0),
151};
152
153/* 'to_set' flags in setattr */
154#define FUSE_SET_ATTR_MODE (1 << 0)
155#define FUSE_SET_ATTR_UID (1 << 1)
156#define FUSE_SET_ATTR_GID (1 << 2)
157#define FUSE_SET_ATTR_SIZE (1 << 3)
158#define FUSE_SET_ATTR_ATIME (1 << 4)
159#define FUSE_SET_ATTR_MTIME (1 << 5)
160#define FUSE_SET_ATTR_ATIME_NOW (1 << 7)
161#define FUSE_SET_ATTR_MTIME_NOW (1 << 8)
162#define FUSE_SET_ATTR_FORCE (1 << 9)
163#define FUSE_SET_ATTR_CTIME (1 << 10)
164#define FUSE_SET_ATTR_KILL_SUID (1 << 11)
165#define FUSE_SET_ATTR_KILL_SGID (1 << 12)
166#define FUSE_SET_ATTR_FILE (1 << 13)
167#define FUSE_SET_ATTR_KILL_PRIV (1 << 14)
168#define FUSE_SET_ATTR_OPEN (1 << 15)
169#define FUSE_SET_ATTR_TIMES_SET (1 << 16)
170#define FUSE_SET_ATTR_TOUCH (1 << 17)
171
172/* ----------------------------------------------------------- *
173 * Request methods and replies *
174 * ----------------------------------------------------------- */
175
223 void (*init) (void *userdata, struct fuse_conn_info *conn);
224
236 void (*destroy) (void *userdata);
237
249 void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
250
287 void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup);
288
308 void (*getattr) (fuse_req_t req, fuse_ino_t ino,
309 struct fuse_file_info *fi);
310
345 void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
346 int to_set, struct fuse_file_info *fi);
347
358 void (*readlink) (fuse_req_t req, fuse_ino_t ino);
359
376 void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
377 mode_t mode, dev_t rdev);
378
391 void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
392 mode_t mode);
393
409 void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
410
426 void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
427
440 void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
441 const char *name);
442
472 void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
473 fuse_ino_t newparent, const char *newname,
474 unsigned int flags);
475
488 void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
489 const char *newname);
490
554 void (*open) (fuse_req_t req, fuse_ino_t ino,
555 struct fuse_file_info *fi);
556
582 void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
583 struct fuse_file_info *fi);
584
611 void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
612 size_t size, off_t off, struct fuse_file_info *fi);
613
652 void (*flush) (fuse_req_t req, fuse_ino_t ino,
653 struct fuse_file_info *fi);
654
680 void (*release) (fuse_req_t req, fuse_ino_t ino,
681 struct fuse_file_info *fi);
682
702 void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
703 struct fuse_file_info *fi);
704
734 void (*opendir) (fuse_req_t req, fuse_ino_t ino,
735 struct fuse_file_info *fi);
736
780 void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
781 struct fuse_file_info *fi);
782
800 struct fuse_file_info *fi);
801
824 void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
825 struct fuse_file_info *fi);
826
837 void (*statfs) (fuse_req_t req, fuse_ino_t ino);
838
850 void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
851 const char *value, size_t size, int flags);
852
881 void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
882 size_t size);
883
912 void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
913
929 void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
930
951 void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
952
980 void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
981 mode_t mode, struct fuse_file_info *fi);
982
995 void (*getlk) (fuse_req_t req, fuse_ino_t ino,
996 struct fuse_file_info *fi, struct flock *lock);
997
1020 void (*setlk) (fuse_req_t req, fuse_ino_t ino,
1021 struct fuse_file_info *fi,
1022 struct flock *lock, int sleep);
1023
1044 void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
1045 uint64_t idx);
1046
1047#if FUSE_USE_VERSION < 35
1048 void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd,
1049 void *arg, struct fuse_file_info *fi, unsigned flags,
1050 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
1051#else
1080 void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
1081 void *arg, struct fuse_file_info *fi, unsigned flags,
1082 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
1083#endif
1084
1117 void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
1118 struct fuse_pollhandle *ph);
1119
1148 struct fuse_bufvec *bufv, off_t off,
1149 struct fuse_file_info *fi);
1150
1163 void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino,
1164 off_t offset, struct fuse_bufvec *bufv);
1165
1177 void (*forget_multi) (fuse_req_t req, size_t count,
1178 struct fuse_forget_data *forgets);
1179
1195 void (*flock) (fuse_req_t req, fuse_ino_t ino,
1196 struct fuse_file_info *fi, int op);
1197
1218 void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
1219 off_t offset, off_t length, struct fuse_file_info *fi);
1220
1246 void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1247 struct fuse_file_info *fi);
1248
1280 off_t off_in, struct fuse_file_info *fi_in,
1281 fuse_ino_t ino_out, off_t off_out,
1282 struct fuse_file_info *fi_out, size_t len,
1283 int flags);
1284
1303 void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1304 struct fuse_file_info *fi);
1305
1306
1325 void (*tmpfile) (fuse_req_t req, fuse_ino_t parent,
1326 mode_t mode, struct fuse_file_info *fi);
1327
1328};
1329
1351int fuse_reply_err(fuse_req_t req, int err);
1352
1363void fuse_reply_none(fuse_req_t req);
1364
1378int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
1379
1398int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
1399 const struct fuse_file_info *fi);
1400
1412int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
1413 double attr_timeout);
1414
1425int fuse_reply_readlink(fuse_req_t req, const char *link);
1426
1439int fuse_passthrough_open(fuse_req_t req, int fd);
1440int fuse_passthrough_close(fuse_req_t req, int backing_id);
1441
1456int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi);
1457
1468int fuse_reply_write(fuse_req_t req, size_t count);
1469
1481int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size);
1482
1526int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
1528
1540int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count);
1541
1552int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf);
1553
1564int fuse_reply_xattr(fuse_req_t req, size_t count);
1565
1576int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
1577
1588int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
1589
1590/* ----------------------------------------------------------- *
1591 * Filling a buffer in readdir *
1592 * ----------------------------------------------------------- */
1593
1621size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
1622 const char *name, const struct stat *stbuf,
1623 off_t off);
1624
1638size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
1639 const char *name,
1640 const struct fuse_entry_param *e, off_t off);
1641
1658 const struct iovec *in_iov, size_t in_count,
1659 const struct iovec *out_iov, size_t out_count);
1660
1672int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
1673
1685int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
1686 int count);
1687
1694int fuse_reply_poll(fuse_req_t req, unsigned revents);
1695
1706int fuse_reply_lseek(fuse_req_t req, off_t off);
1707
1708/* ----------------------------------------------------------- *
1709 * Notification *
1710 * ----------------------------------------------------------- */
1711
1719int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
1720
1744int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
1745 off_t off, off_t len);
1746
1771int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
1772 const char *name, size_t namelen);
1773
1802int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
1803 const char *name, size_t namelen);
1804
1833int fuse_lowlevel_notify_delete(struct fuse_session *se,
1834 fuse_ino_t parent, fuse_ino_t child,
1835 const char *name, size_t namelen);
1836
1862int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
1863 off_t offset, struct fuse_bufvec *bufv,
1894int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
1895 size_t size, off_t offset, void *cookie);
1896
1897
1898/* ----------------------------------------------------------- *
1899 * Utility functions *
1900 * ----------------------------------------------------------- */
1901
1908void *fuse_req_userdata(fuse_req_t req);
1909
1919const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
1920
1940int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
1941
1948typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data);
1949
1962 void *data);
1963
1971
1972
1973/* ----------------------------------------------------------- *
1974 * Inquiry functions *
1975 * ----------------------------------------------------------- */
1976
1980void fuse_lowlevel_version(void);
1981
1987void fuse_lowlevel_help(void);
1988
1992void fuse_cmdline_help(void);
1993
1994/* ----------------------------------------------------------- *
1995 * Filesystem setup & teardown *
1996 * ----------------------------------------------------------- */
1997
2004 int singlethread;
2005 int foreground;
2006 int debug;
2007 int nodefault_subtype;
2008 char *mountpoint;
2009 int show_version;
2010 int show_help;
2011 int clone_fd;
2012 unsigned int max_idle_threads; /* discouraged, due to thread
2013 * destruct overhead */
2014
2015 /* Added in libfuse-3.12 */
2016 unsigned int max_threads;
2017};
2018
2037#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
2038int fuse_parse_cmdline(struct fuse_args *args,
2039 struct fuse_cmdline_opts *opts);
2040#else
2041#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
2042int fuse_parse_cmdline_30(struct fuse_args *args,
2043 struct fuse_cmdline_opts *opts);
2044#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_30(args, opts)
2045#else
2046int fuse_parse_cmdline_312(struct fuse_args *args,
2047 struct fuse_cmdline_opts *opts);
2048#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_312(args, opts)
2049#endif
2050#endif
2051
2052/* Do not call this directly, use fuse_session_new() instead */
2053struct fuse_session *
2054fuse_session_new_versioned(struct fuse_args *args,
2055 const struct fuse_lowlevel_ops *op, size_t op_size,
2056 struct libfuse_version *version, void *userdata);
2057
2086static inline struct fuse_session *
2087fuse_session_new_fn(struct fuse_args *args, const struct fuse_lowlevel_ops *op,
2088 size_t op_size, void *userdata)
2089{
2090 struct libfuse_version version = {
2091 .major = FUSE_MAJOR_VERSION,
2092 .minor = FUSE_MINOR_VERSION,
2093 .hotfix = FUSE_HOTFIX_VERSION,
2094 .padding = 0
2095 };
2096
2097 return fuse_session_new_versioned(args, op, op_size, &version,
2098 userdata);
2099}
2100#define fuse_session_new(args, op, op_size, userdata) \
2101 fuse_session_new_fn(args, op, op_size, userdata)
2102
2103/*
2104 * This should mostly not be called directly, but instead the
2105 * fuse_session_custom_io() should be used.
2106 */
2107int fuse_session_custom_io_317(struct fuse_session *se,
2108 const struct fuse_custom_io *io, size_t op_size, int fd);
2109
2137#if FUSE_MAKE_VERSION(3, 17) <= FUSE_USE_VERSION
2138static inline int fuse_session_custom_io(struct fuse_session *se,
2139 const struct fuse_custom_io *io, size_t op_size, int fd)
2140{
2141 return fuse_session_custom_io_317(se, io, op_size, fd);
2142}
2143#else
2144static inline int fuse_session_custom_io(struct fuse_session *se,
2145 const struct fuse_custom_io *io, int fd)
2146{
2147 return fuse_session_custom_io_317(se, io,
2148 offsetof(struct fuse_custom_io, clone_fd), fd);
2149}
2150#endif
2151
2160int fuse_session_mount(struct fuse_session *se, const char *mountpoint);
2161
2184int fuse_session_loop(struct fuse_session *se);
2185
2186#if FUSE_USE_VERSION < 32
2187 int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
2188 #define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd)
2189#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
2190 int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
2191 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_32(se, config)
2192#else
2193 #if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
2205 int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config);
2206 #else
2207 int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
2208 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_312(se, config)
2209 #endif
2210#endif
2211
2224void fuse_session_exit(struct fuse_session *se);
2225
2231void fuse_session_reset(struct fuse_session *se);
2232
2239int fuse_session_exited(struct fuse_session *se);
2240
2265void fuse_session_unmount(struct fuse_session *se);
2266
2272void fuse_session_destroy(struct fuse_session *se);
2273
2274/* ----------------------------------------------------------- *
2275 * Custom event loop support *
2276 * ----------------------------------------------------------- */
2277
2292int fuse_session_fd(struct fuse_session *se);
2293
2302void fuse_session_process_buf(struct fuse_session *se,
2303 const struct fuse_buf *buf);
2304
2316int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf);
2317
2318#ifdef __cplusplus
2319}
2320#endif
2321
2322#endif /* FUSE_LOWLEVEL_H_ */
fuse_buf_copy_flags
void fuse_session_destroy(struct fuse_session *se)
fuse_notify_entry_flags
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_session_fd(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
void fuse_session_reset(struct fuse_session *se)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_passthrough_open(fuse_req_t req, int fd)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_xattr(fuse_req_t req, size_t count)
struct fuse_req * fuse_req_t
uint64_t fuse_ino_t
mode_t umask
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
int32_t backing_id
void(* flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op)
void(* write)(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi)
void(* lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi)
void(* listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size)
void(* removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name)
void(* forget_multi)(fuse_req_t req, size_t count, struct fuse_forget_data *forgets)
void(* retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv)
void(* create)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi)
void(* mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
void(* symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)
void(* write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, off_t off, struct fuse_file_info *fi)
void(* rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name)
void(* link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)
void(* rename)(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags)
void(* copy_file_range)(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fuse_file_info *fi_in, fuse_ino_t ino_out, off_t off_out, struct fuse_file_info *fi_out, size_t len, int flags)
void(* poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph)
void(* opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* mknod)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
void(* fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi)
void(* setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)
void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
void(* fsync)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
void(* destroy)(void *userdata)
void(* forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
void(* getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* ioctl)(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz)
void(* open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
void(* fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
void(* init)(void *userdata, struct fuse_conn_info *conn)
void(* read)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags)
void(* release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* access)(fuse_req_t req, fuse_ino_t ino, int mask)
void(* releasedir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* tmpfile)(fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info *fi)
void(* bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)
void(* readlink)(fuse_req_t req, fuse_ino_t ino)
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
void(* statfs)(fuse_req_t req, fuse_ino_t ino)
void(* readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)
void(* readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* flush)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* unlink)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__lowlevel_8h_source.html0000644000175000017500000052253215156613442025066 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_lowlevel.h Source File
libfuse
fuse_lowlevel.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_LOWLEVEL_H_
10#define FUSE_LOWLEVEL_H_
11
21#ifndef FUSE_USE_VERSION
22#error FUSE_USE_VERSION not defined
23#endif
24
25#include "fuse_common.h"
26
27#include <stddef.h>
28#include <utime.h>
29#include <fcntl.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/statvfs.h>
33#include <sys/uio.h>
34
35#ifdef __cplusplus
36extern "C" {
37#endif
38
39/* ----------------------------------------------------------- *
40 * Miscellaneous definitions *
41 * ----------------------------------------------------------- */
42
44#define FUSE_ROOT_ID 1
45
47typedef uint64_t fuse_ino_t;
48
50typedef struct fuse_req *fuse_req_t;
51
52/* Forward declaration */
53struct statx;
54
60struct fuse_session;
61
63struct fuse_entry_param {
72
83 uint64_t generation;
84
92 struct stat attr;
93
98 double attr_timeout;
99
104 double entry_timeout;
105};
106
115struct fuse_ctx {
117 uid_t uid;
118
120 gid_t gid;
121
123 pid_t pid;
124
126 mode_t umask;
127};
128
129struct fuse_forget_data {
130 fuse_ino_t ino;
131 uint64_t nlookup;
132};
133
134struct fuse_custom_io {
135 ssize_t (*writev)(int fd, struct iovec *iov, int count, void *userdata);
136 ssize_t (*read)(int fd, void *buf, size_t buf_len, void *userdata);
137 ssize_t (*splice_receive)(int fdin, off_t *offin, int fdout,
138 off_t *offout, size_t len,
139 unsigned int flags, void *userdata);
140 ssize_t (*splice_send)(int fdin, off_t *offin, int fdout,
141 off_t *offout, size_t len,
142 unsigned int flags, void *userdata);
143 int (*clone_fd)(int master_fd);
144};
145
152 FUSE_LL_INVALIDATE = 0,
153 FUSE_LL_EXPIRE_ONLY = (1 << 0),
154};
155
156/* 'to_set' flags in setattr */
157#define FUSE_SET_ATTR_MODE (1 << 0)
158#define FUSE_SET_ATTR_UID (1 << 1)
159#define FUSE_SET_ATTR_GID (1 << 2)
160#define FUSE_SET_ATTR_SIZE (1 << 3)
161#define FUSE_SET_ATTR_ATIME (1 << 4)
162#define FUSE_SET_ATTR_MTIME (1 << 5)
163#define FUSE_SET_ATTR_ATIME_NOW (1 << 7)
164#define FUSE_SET_ATTR_MTIME_NOW (1 << 8)
165#define FUSE_SET_ATTR_FORCE (1 << 9)
166#define FUSE_SET_ATTR_CTIME (1 << 10)
167#define FUSE_SET_ATTR_KILL_SUID (1 << 11)
168#define FUSE_SET_ATTR_KILL_SGID (1 << 12)
169#define FUSE_SET_ATTR_FILE (1 << 13)
170#define FUSE_SET_ATTR_KILL_PRIV (1 << 14)
171#define FUSE_SET_ATTR_OPEN (1 << 15)
172#define FUSE_SET_ATTR_TIMES_SET (1 << 16)
173#define FUSE_SET_ATTR_TOUCH (1 << 17)
174
175/* ----------------------------------------------------------- *
176 * Request methods and replies *
177 * ----------------------------------------------------------- */
178
208struct fuse_lowlevel_ops {
225 void (*init) (void *userdata, struct fuse_conn_info *conn);
226
238 void (*destroy) (void *userdata);
239
251 void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
252
289 void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup);
290
310 void (*getattr) (fuse_req_t req, fuse_ino_t ino,
311 struct fuse_file_info *fi);
312
347 void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
348 int to_set, struct fuse_file_info *fi);
349
360 void (*readlink) (fuse_req_t req, fuse_ino_t ino);
361
378 void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
379 mode_t mode, dev_t rdev);
380
393 void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
394 mode_t mode);
395
411 void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
412
428 void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
429
442 void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
443 const char *name);
444
474 void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
475 fuse_ino_t newparent, const char *newname,
476 unsigned int flags);
477
490 void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
491 const char *newname);
492
556 void (*open) (fuse_req_t req, fuse_ino_t ino,
557 struct fuse_file_info *fi);
558
584 void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
585 struct fuse_file_info *fi);
586
613 void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
614 size_t size, off_t off, struct fuse_file_info *fi);
615
654 void (*flush) (fuse_req_t req, fuse_ino_t ino,
655 struct fuse_file_info *fi);
656
682 void (*release) (fuse_req_t req, fuse_ino_t ino,
683 struct fuse_file_info *fi);
684
704 void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
705 struct fuse_file_info *fi);
706
736 void (*opendir) (fuse_req_t req, fuse_ino_t ino,
737 struct fuse_file_info *fi);
738
782 void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
783 struct fuse_file_info *fi);
784
801 void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
802 struct fuse_file_info *fi);
803
826 void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
827 struct fuse_file_info *fi);
828
839 void (*statfs) (fuse_req_t req, fuse_ino_t ino);
840
852 void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
853 const char *value, size_t size, int flags);
854
883 void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
884 size_t size);
885
914 void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
915
931 void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
932
953 void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
954
982 void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
983 mode_t mode, struct fuse_file_info *fi);
984
997 void (*getlk) (fuse_req_t req, fuse_ino_t ino,
998 struct fuse_file_info *fi, struct flock *lock);
999
1022 void (*setlk) (fuse_req_t req, fuse_ino_t ino,
1023 struct fuse_file_info *fi,
1024 struct flock *lock, int sleep);
1025
1046 void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
1047 uint64_t idx);
1048
1049#if FUSE_USE_VERSION < 35
1050 void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd,
1051 void *arg, struct fuse_file_info *fi, unsigned flags,
1052 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
1053#else
1082 void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
1083 void *arg, struct fuse_file_info *fi, unsigned flags,
1084 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
1085#endif
1086
1119 void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
1120 struct fuse_pollhandle *ph);
1121
1149 void (*write_buf) (fuse_req_t req, fuse_ino_t ino,
1150 struct fuse_bufvec *bufv, off_t off,
1151 struct fuse_file_info *fi);
1152
1165 void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino,
1166 off_t offset, struct fuse_bufvec *bufv);
1167
1179 void (*forget_multi) (fuse_req_t req, size_t count,
1180 struct fuse_forget_data *forgets);
1181
1197 void (*flock) (fuse_req_t req, fuse_ino_t ino,
1198 struct fuse_file_info *fi, int op);
1199
1220 void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
1221 off_t offset, off_t length, struct fuse_file_info *fi);
1222
1248 void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1249 struct fuse_file_info *fi);
1250
1281 void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in,
1282 off_t off_in, struct fuse_file_info *fi_in,
1283 fuse_ino_t ino_out, off_t off_out,
1284 struct fuse_file_info *fi_out, size_t len,
1285 int flags);
1286
1305 void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1306 struct fuse_file_info *fi);
1307
1326 void (*tmpfile) (fuse_req_t req, fuse_ino_t parent,
1327 mode_t mode, struct fuse_file_info *fi);
1328
1342 void (*statx)(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
1343 struct fuse_file_info *fi);
1344};
1345
1367int fuse_reply_err(fuse_req_t req, int err);
1368
1379void fuse_reply_none(fuse_req_t req);
1380
1394int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
1395
1414int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
1415 const struct fuse_file_info *fi);
1416
1428int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
1429 double attr_timeout);
1430
1441int fuse_reply_readlink(fuse_req_t req, const char *link);
1442
1455int fuse_passthrough_open(fuse_req_t req, int fd);
1456int fuse_passthrough_close(fuse_req_t req, int backing_id);
1457
1472int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi);
1473
1484int fuse_reply_write(fuse_req_t req, size_t count);
1485
1497int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size);
1498
1542int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
1544
1556int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count);
1557
1568int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf);
1569
1580int fuse_reply_xattr(fuse_req_t req, size_t count);
1581
1592int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
1593
1604int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
1605
1606/* ----------------------------------------------------------- *
1607 * Filling a buffer in readdir *
1608 * ----------------------------------------------------------- */
1609
1637size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
1638 const char *name, const struct stat *stbuf,
1639 off_t off);
1640
1654size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
1655 const char *name,
1656 const struct fuse_entry_param *e, off_t off);
1657
1674 const struct iovec *in_iov, size_t in_count,
1675 const struct iovec *out_iov, size_t out_count);
1676
1688int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
1689
1701int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
1702 int count);
1703
1710int fuse_reply_poll(fuse_req_t req, unsigned revents);
1711
1722int fuse_reply_lseek(fuse_req_t req, off_t off);
1723
1736int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout);
1737
1738/* ----------------------------------------------------------- *
1739 * Notification *
1740 * ----------------------------------------------------------- */
1741
1749int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
1750
1774int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
1775 off_t off, off_t len);
1776
1789int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se);
1790
1815int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
1816 const char *name, size_t namelen);
1817
1846int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
1847 const char *name, size_t namelen);
1848
1877int fuse_lowlevel_notify_delete(struct fuse_session *se,
1878 fuse_ino_t parent, fuse_ino_t child,
1879 const char *name, size_t namelen);
1880
1906int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
1907 off_t offset, struct fuse_bufvec *bufv,
1938int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
1939 size_t size, off_t offset, void *cookie);
1940
1941
1942/* ----------------------------------------------------------- *
1943 * Utility functions *
1944 * ----------------------------------------------------------- */
1945
1952void *fuse_req_userdata(fuse_req_t req);
1953
1963const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
1964
1984int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
1985
1992typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data);
1993
2006 void *data);
2007
2015
2016
2017/* ----------------------------------------------------------- *
2018 * Inquiry functions *
2019 * ----------------------------------------------------------- */
2020
2024void fuse_lowlevel_version(void);
2025
2031void fuse_lowlevel_help(void);
2032
2036void fuse_cmdline_help(void);
2037
2038/* ----------------------------------------------------------- *
2039 * Filesystem setup & teardown *
2040 * ----------------------------------------------------------- */
2041
2047struct fuse_cmdline_opts {
2048 int singlethread;
2049 int foreground;
2050 int debug;
2051 int nodefault_subtype;
2052 char *mountpoint;
2053 int show_version;
2054 int show_help;
2055 int clone_fd;
2056 unsigned int max_idle_threads; /* discouraged, due to thread
2057 * destruct overhead */
2058
2059 /* Added in libfuse-3.12 */
2060 unsigned int max_threads;
2061};
2062
2081#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
2082int fuse_parse_cmdline(struct fuse_args *args,
2083 struct fuse_cmdline_opts *opts);
2084#else
2085#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
2086int fuse_parse_cmdline_30(struct fuse_args *args,
2087 struct fuse_cmdline_opts *opts);
2088#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_30(args, opts)
2089#else
2090int fuse_parse_cmdline_312(struct fuse_args *args,
2091 struct fuse_cmdline_opts *opts);
2092#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_312(args, opts)
2093#endif
2094#endif
2095
2096/* Do not call this directly, use fuse_session_new() instead */
2097struct fuse_session *
2098fuse_session_new_versioned(struct fuse_args *args,
2099 const struct fuse_lowlevel_ops *op, size_t op_size,
2100 struct libfuse_version *version, void *userdata);
2101
2132static inline struct fuse_session *
2133fuse_session_new_fn(struct fuse_args *args, const struct fuse_lowlevel_ops *op,
2134 size_t op_size, void *userdata)
2135{
2136 struct libfuse_version version = {
2137 .major = FUSE_MAJOR_VERSION,
2138 .minor = FUSE_MINOR_VERSION,
2139 .hotfix = FUSE_HOTFIX_VERSION,
2140 .padding = 0
2141 };
2142
2143 return fuse_session_new_versioned(args, op, op_size, &version,
2144 userdata);
2145}
2146#define fuse_session_new(args, op, op_size, userdata) \
2147 fuse_session_new_fn(args, op, op_size, userdata)
2148
2149/*
2150 * This should mostly not be called directly, but instead the
2151 * fuse_session_custom_io() should be used.
2152 */
2153int fuse_session_custom_io_317(struct fuse_session *se,
2154 const struct fuse_custom_io *io, size_t op_size, int fd);
2155
2183#if FUSE_MAKE_VERSION(3, 17) <= FUSE_USE_VERSION
2184static inline int fuse_session_custom_io(struct fuse_session *se,
2185 const struct fuse_custom_io *io, size_t op_size, int fd)
2186{
2187 return fuse_session_custom_io_317(se, io, op_size, fd);
2188}
2189#else
2190static inline int fuse_session_custom_io(struct fuse_session *se,
2191 const struct fuse_custom_io *io, int fd)
2192{
2193 return fuse_session_custom_io_317(se, io,
2194 offsetof(struct fuse_custom_io, clone_fd), fd);
2195}
2196#endif
2197
2206int fuse_session_mount(struct fuse_session *se, const char *mountpoint);
2207
2230int fuse_session_loop(struct fuse_session *se);
2231
2232#if FUSE_USE_VERSION < 32
2233 int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
2234 #define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd)
2235#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
2236 int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
2237 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_32(se, config)
2238#else
2239 #if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
2251 int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config);
2252 #else
2253 int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
2254 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_312(se, config)
2255 #endif
2256#endif
2257
2270void fuse_session_exit(struct fuse_session *se);
2271
2277void fuse_session_reset(struct fuse_session *se);
2278
2285int fuse_session_exited(struct fuse_session *se);
2286
2311void fuse_session_unmount(struct fuse_session *se);
2312
2318void fuse_session_destroy(struct fuse_session *se);
2319
2320/* ----------------------------------------------------------- *
2321 * Custom event loop support *
2322 * ----------------------------------------------------------- */
2323
2338int fuse_session_fd(struct fuse_session *se);
2339
2348void fuse_session_process_buf(struct fuse_session *se,
2349 const struct fuse_buf *buf);
2350
2362int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf);
2363
2368
2384int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
2385 void **mr);
2386
2387#ifdef __cplusplus
2388}
2389#endif
2390
2391#endif /* FUSE_LOWLEVEL_H_ */
fuse_buf_copy_flags
void fuse_session_destroy(struct fuse_session *se)
fuse_notify_entry_flags
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_session_fd(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
void fuse_session_reset(struct fuse_session *se)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_passthrough_open(fuse_req_t req, int fd)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_xattr(fuse_req_t req, size_t count)
bool fuse_req_is_uring(fuse_req_t req)
int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
struct fuse_req * fuse_req_t
uint64_t fuse_ino_t
mode_t umask
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
int32_t backing_id
void(* flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op)
void(* write)(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi)
void(* lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi)
void(* listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size)
void(* removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name)
void(* forget_multi)(fuse_req_t req, size_t count, struct fuse_forget_data *forgets)
void(* retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv)
void(* create)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi)
void(* mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
void(* symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)
void(* write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, off_t off, struct fuse_file_info *fi)
void(* rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name)
void(* link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)
void(* rename)(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags)
void(* copy_file_range)(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fuse_file_info *fi_in, fuse_ino_t ino_out, off_t off_out, struct fuse_file_info *fi_out, size_t len, int flags)
void(* poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph)
void(* opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* mknod)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
void(* fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi)
void(* setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)
void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
void(* fsync)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
void(* destroy)(void *userdata)
void(* forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
void(* getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* ioctl)(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz)
void(* open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
void(* fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
void(* init)(void *userdata, struct fuse_conn_info *conn)
void(* read)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags)
void(* release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* access)(fuse_req_t req, fuse_ino_t ino, int mask)
void(* releasedir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* statx)(fuse_req_t req, fuse_ino_t ino, int flags, int mask, struct fuse_file_info *fi)
void(* tmpfile)(fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info *fi)
void(* bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)
void(* readlink)(fuse_req_t req, fuse_ino_t ino)
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
void(* statfs)(fuse_req_t req, fuse_ino_t ino)
void(* readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)
void(* readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* flush)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* unlink)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/include_2fuse__lowlevel_8h_source.html0000644000175000017500000052044515156613442022713 0ustar berndbernd libfuse: include/fuse_lowlevel.h Source File
libfuse
fuse_lowlevel.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_LOWLEVEL_H_
10#define FUSE_LOWLEVEL_H_
11
21#ifndef FUSE_USE_VERSION
22#error FUSE_USE_VERSION not defined
23#endif
24
25#include "fuse_common.h"
26
27#include <stddef.h>
28#include <utime.h>
29#include <fcntl.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/statvfs.h>
33#include <sys/uio.h>
34
35#ifdef __cplusplus
36extern "C" {
37#endif
38
39/* ----------------------------------------------------------- *
40 * Miscellaneous definitions *
41 * ----------------------------------------------------------- */
42
44#define FUSE_ROOT_ID 1
45
47typedef uint64_t fuse_ino_t;
48
50typedef struct fuse_req *fuse_req_t;
51
52/* Forward declaration */
53struct statx;
54
60struct fuse_session;
61
63struct fuse_entry_param {
72
83 uint64_t generation;
84
92 struct stat attr;
93
98 double attr_timeout;
99
104 double entry_timeout;
105};
106
115struct fuse_ctx {
117 uid_t uid;
118
120 gid_t gid;
121
123 pid_t pid;
124
126 mode_t umask;
127};
128
129struct fuse_forget_data {
130 fuse_ino_t ino;
131 uint64_t nlookup;
132};
133
134struct fuse_custom_io {
135 ssize_t (*writev)(int fd, struct iovec *iov, int count, void *userdata);
136 ssize_t (*read)(int fd, void *buf, size_t buf_len, void *userdata);
137 ssize_t (*splice_receive)(int fdin, off_t *offin, int fdout,
138 off_t *offout, size_t len,
139 unsigned int flags, void *userdata);
140 ssize_t (*splice_send)(int fdin, off_t *offin, int fdout,
141 off_t *offout, size_t len,
142 unsigned int flags, void *userdata);
143 int (*clone_fd)(int master_fd);
144};
145
152 FUSE_LL_INVALIDATE = 0,
153 FUSE_LL_EXPIRE_ONLY = (1 << 0),
154};
155
156/* 'to_set' flags in setattr */
157#define FUSE_SET_ATTR_MODE (1 << 0)
158#define FUSE_SET_ATTR_UID (1 << 1)
159#define FUSE_SET_ATTR_GID (1 << 2)
160#define FUSE_SET_ATTR_SIZE (1 << 3)
161#define FUSE_SET_ATTR_ATIME (1 << 4)
162#define FUSE_SET_ATTR_MTIME (1 << 5)
163#define FUSE_SET_ATTR_ATIME_NOW (1 << 7)
164#define FUSE_SET_ATTR_MTIME_NOW (1 << 8)
165#define FUSE_SET_ATTR_FORCE (1 << 9)
166#define FUSE_SET_ATTR_CTIME (1 << 10)
167#define FUSE_SET_ATTR_KILL_SUID (1 << 11)
168#define FUSE_SET_ATTR_KILL_SGID (1 << 12)
169#define FUSE_SET_ATTR_FILE (1 << 13)
170#define FUSE_SET_ATTR_KILL_PRIV (1 << 14)
171#define FUSE_SET_ATTR_OPEN (1 << 15)
172#define FUSE_SET_ATTR_TIMES_SET (1 << 16)
173#define FUSE_SET_ATTR_TOUCH (1 << 17)
174
175/* ----------------------------------------------------------- *
176 * Request methods and replies *
177 * ----------------------------------------------------------- */
178
208struct fuse_lowlevel_ops {
225 void (*init) (void *userdata, struct fuse_conn_info *conn);
226
238 void (*destroy) (void *userdata);
239
251 void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
252
289 void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup);
290
310 void (*getattr) (fuse_req_t req, fuse_ino_t ino,
311 struct fuse_file_info *fi);
312
347 void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
348 int to_set, struct fuse_file_info *fi);
349
360 void (*readlink) (fuse_req_t req, fuse_ino_t ino);
361
378 void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
379 mode_t mode, dev_t rdev);
380
393 void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
394 mode_t mode);
395
411 void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
412
428 void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
429
442 void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
443 const char *name);
444
474 void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
475 fuse_ino_t newparent, const char *newname,
476 unsigned int flags);
477
490 void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
491 const char *newname);
492
556 void (*open) (fuse_req_t req, fuse_ino_t ino,
557 struct fuse_file_info *fi);
558
584 void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
585 struct fuse_file_info *fi);
586
613 void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
614 size_t size, off_t off, struct fuse_file_info *fi);
615
654 void (*flush) (fuse_req_t req, fuse_ino_t ino,
655 struct fuse_file_info *fi);
656
682 void (*release) (fuse_req_t req, fuse_ino_t ino,
683 struct fuse_file_info *fi);
684
704 void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
705 struct fuse_file_info *fi);
706
736 void (*opendir) (fuse_req_t req, fuse_ino_t ino,
737 struct fuse_file_info *fi);
738
782 void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
783 struct fuse_file_info *fi);
784
801 void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
802 struct fuse_file_info *fi);
803
826 void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
827 struct fuse_file_info *fi);
828
839 void (*statfs) (fuse_req_t req, fuse_ino_t ino);
840
852 void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
853 const char *value, size_t size, int flags);
854
883 void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
884 size_t size);
885
914 void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
915
931 void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
932
953 void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
954
982 void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
983 mode_t mode, struct fuse_file_info *fi);
984
997 void (*getlk) (fuse_req_t req, fuse_ino_t ino,
998 struct fuse_file_info *fi, struct flock *lock);
999
1022 void (*setlk) (fuse_req_t req, fuse_ino_t ino,
1023 struct fuse_file_info *fi,
1024 struct flock *lock, int sleep);
1025
1046 void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
1047 uint64_t idx);
1048
1049#if FUSE_USE_VERSION < 35
1050 void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd,
1051 void *arg, struct fuse_file_info *fi, unsigned flags,
1052 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
1053#else
1082 void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
1083 void *arg, struct fuse_file_info *fi, unsigned flags,
1084 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
1085#endif
1086
1119 void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
1120 struct fuse_pollhandle *ph);
1121
1149 void (*write_buf) (fuse_req_t req, fuse_ino_t ino,
1150 struct fuse_bufvec *bufv, off_t off,
1151 struct fuse_file_info *fi);
1152
1165 void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino,
1166 off_t offset, struct fuse_bufvec *bufv);
1167
1179 void (*forget_multi) (fuse_req_t req, size_t count,
1180 struct fuse_forget_data *forgets);
1181
1197 void (*flock) (fuse_req_t req, fuse_ino_t ino,
1198 struct fuse_file_info *fi, int op);
1199
1220 void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
1221 off_t offset, off_t length, struct fuse_file_info *fi);
1222
1248 void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1249 struct fuse_file_info *fi);
1250
1281 void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in,
1282 off_t off_in, struct fuse_file_info *fi_in,
1283 fuse_ino_t ino_out, off_t off_out,
1284 struct fuse_file_info *fi_out, size_t len,
1285 int flags);
1286
1305 void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1306 struct fuse_file_info *fi);
1307
1326 void (*tmpfile) (fuse_req_t req, fuse_ino_t parent,
1327 mode_t mode, struct fuse_file_info *fi);
1328
1342 void (*statx)(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
1343 struct fuse_file_info *fi);
1344};
1345
1367int fuse_reply_err(fuse_req_t req, int err);
1368
1379void fuse_reply_none(fuse_req_t req);
1380
1394int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
1395
1414int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
1415 const struct fuse_file_info *fi);
1416
1428int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
1429 double attr_timeout);
1430
1441int fuse_reply_readlink(fuse_req_t req, const char *link);
1442
1455int fuse_passthrough_open(fuse_req_t req, int fd);
1456int fuse_passthrough_close(fuse_req_t req, int backing_id);
1457
1472int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi);
1473
1484int fuse_reply_write(fuse_req_t req, size_t count);
1485
1497int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size);
1498
1542int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
1544
1556int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count);
1557
1568int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf);
1569
1580int fuse_reply_xattr(fuse_req_t req, size_t count);
1581
1592int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
1593
1604int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
1605
1606/* ----------------------------------------------------------- *
1607 * Filling a buffer in readdir *
1608 * ----------------------------------------------------------- */
1609
1637size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
1638 const char *name, const struct stat *stbuf,
1639 off_t off);
1640
1654size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
1655 const char *name,
1656 const struct fuse_entry_param *e, off_t off);
1657
1674 const struct iovec *in_iov, size_t in_count,
1675 const struct iovec *out_iov, size_t out_count);
1676
1688int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
1689
1701int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
1702 int count);
1703
1710int fuse_reply_poll(fuse_req_t req, unsigned revents);
1711
1722int fuse_reply_lseek(fuse_req_t req, off_t off);
1723
1736int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout);
1737
1738/* ----------------------------------------------------------- *
1739 * Notification *
1740 * ----------------------------------------------------------- */
1741
1749int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
1750
1774int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
1775 off_t off, off_t len);
1776
1789int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se);
1790
1815int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
1816 const char *name, size_t namelen);
1817
1846int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
1847 const char *name, size_t namelen);
1848
1877int fuse_lowlevel_notify_delete(struct fuse_session *se,
1878 fuse_ino_t parent, fuse_ino_t child,
1879 const char *name, size_t namelen);
1880
1906int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
1907 off_t offset, struct fuse_bufvec *bufv,
1938int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
1939 size_t size, off_t offset, void *cookie);
1940
1941
1942/* ----------------------------------------------------------- *
1943 * Utility functions *
1944 * ----------------------------------------------------------- */
1945
1952void *fuse_req_userdata(fuse_req_t req);
1953
1963const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
1964
1984int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
1985
1992typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data);
1993
2006 void *data);
2007
2015
2016
2017/* ----------------------------------------------------------- *
2018 * Inquiry functions *
2019 * ----------------------------------------------------------- */
2020
2024void fuse_lowlevel_version(void);
2025
2031void fuse_lowlevel_help(void);
2032
2036void fuse_cmdline_help(void);
2037
2038/* ----------------------------------------------------------- *
2039 * Filesystem setup & teardown *
2040 * ----------------------------------------------------------- */
2041
2047struct fuse_cmdline_opts {
2048 int singlethread;
2049 int foreground;
2050 int debug;
2051 int nodefault_subtype;
2052 char *mountpoint;
2053 int show_version;
2054 int show_help;
2055 int clone_fd;
2056 unsigned int max_idle_threads; /* discouraged, due to thread
2057 * destruct overhead */
2058
2059 /* Added in libfuse-3.12 */
2060 unsigned int max_threads;
2061};
2062
2081#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
2082int fuse_parse_cmdline(struct fuse_args *args,
2083 struct fuse_cmdline_opts *opts);
2084#else
2085#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
2086int fuse_parse_cmdline_30(struct fuse_args *args,
2087 struct fuse_cmdline_opts *opts);
2088#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_30(args, opts)
2089#else
2090int fuse_parse_cmdline_312(struct fuse_args *args,
2091 struct fuse_cmdline_opts *opts);
2092#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_312(args, opts)
2093#endif
2094#endif
2095
2096/* Do not call this directly, use fuse_session_new() instead */
2097struct fuse_session *
2098fuse_session_new_versioned(struct fuse_args *args,
2099 const struct fuse_lowlevel_ops *op, size_t op_size,
2100 struct libfuse_version *version, void *userdata);
2101
2132static inline struct fuse_session *
2133fuse_session_new_fn(struct fuse_args *args, const struct fuse_lowlevel_ops *op,
2134 size_t op_size, void *userdata)
2135{
2136 struct libfuse_version version = {
2137 .major = FUSE_MAJOR_VERSION,
2138 .minor = FUSE_MINOR_VERSION,
2139 .hotfix = FUSE_HOTFIX_VERSION,
2140 .padding = 0
2141 };
2142
2143 return fuse_session_new_versioned(args, op, op_size, &version,
2144 userdata);
2145}
2146#define fuse_session_new(args, op, op_size, userdata) \
2147 fuse_session_new_fn(args, op, op_size, userdata)
2148
2149/*
2150 * This should mostly not be called directly, but instead the
2151 * fuse_session_custom_io() should be used.
2152 */
2153int fuse_session_custom_io_317(struct fuse_session *se,
2154 const struct fuse_custom_io *io, size_t op_size, int fd);
2155
2183#if FUSE_MAKE_VERSION(3, 17) <= FUSE_USE_VERSION
2184static inline int fuse_session_custom_io(struct fuse_session *se,
2185 const struct fuse_custom_io *io, size_t op_size, int fd)
2186{
2187 return fuse_session_custom_io_317(se, io, op_size, fd);
2188}
2189#else
2190static inline int fuse_session_custom_io(struct fuse_session *se,
2191 const struct fuse_custom_io *io, int fd)
2192{
2193 return fuse_session_custom_io_317(se, io,
2194 offsetof(struct fuse_custom_io, clone_fd), fd);
2195}
2196#endif
2197
2206int fuse_session_mount(struct fuse_session *se, const char *mountpoint);
2207
2230int fuse_session_loop(struct fuse_session *se);
2231
2232#if FUSE_USE_VERSION < 32
2233 int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
2234 #define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd)
2235#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
2236 int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
2237 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_32(se, config)
2238#else
2239 #if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
2251 int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config);
2252 #else
2253 int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
2254 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_312(se, config)
2255 #endif
2256#endif
2257
2270void fuse_session_exit(struct fuse_session *se);
2271
2277void fuse_session_reset(struct fuse_session *se);
2278
2285int fuse_session_exited(struct fuse_session *se);
2286
2311void fuse_session_unmount(struct fuse_session *se);
2312
2318void fuse_session_destroy(struct fuse_session *se);
2319
2320/* ----------------------------------------------------------- *
2321 * Custom event loop support *
2322 * ----------------------------------------------------------- */
2323
2338int fuse_session_fd(struct fuse_session *se);
2339
2348void fuse_session_process_buf(struct fuse_session *se,
2349 const struct fuse_buf *buf);
2350
2362int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf);
2363
2368
2384int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
2385 void **mr);
2386
2387#ifdef __cplusplus
2388}
2389#endif
2390
2391#endif /* FUSE_LOWLEVEL_H_ */
fuse_buf_copy_flags
void fuse_session_destroy(struct fuse_session *se)
fuse_notify_entry_flags
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_session_fd(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
void fuse_session_reset(struct fuse_session *se)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_passthrough_open(fuse_req_t req, int fd)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_xattr(fuse_req_t req, size_t count)
bool fuse_req_is_uring(fuse_req_t req)
int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
mode_t umask
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
int32_t backing_id
void(* flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op)
void(* write)(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi)
void(* lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi)
void(* listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size)
void(* removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name)
void(* forget_multi)(fuse_req_t req, size_t count, struct fuse_forget_data *forgets)
void(* retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv)
void(* create)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi)
void(* mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
void(* symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)
void(* write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, off_t off, struct fuse_file_info *fi)
void(* rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name)
void(* link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)
void(* rename)(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags)
void(* copy_file_range)(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fuse_file_info *fi_in, fuse_ino_t ino_out, off_t off_out, struct fuse_file_info *fi_out, size_t len, int flags)
void(* poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph)
void(* opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* mknod)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
void(* fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi)
void(* setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)
void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
void(* fsync)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
void(* destroy)(void *userdata)
void(* forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
void(* getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* ioctl)(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz)
void(* open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
void(* fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
void(* init)(void *userdata, struct fuse_conn_info *conn)
void(* read)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags)
void(* release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* access)(fuse_req_t req, fuse_ino_t ino, int mask)
void(* releasedir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* statx)(fuse_req_t req, fuse_ino_t ino, int flags, int mask, struct fuse_file_info *fi)
void(* tmpfile)(fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info *fi)
void(* bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)
void(* readlink)(fuse_req_t req, fuse_ino_t ino)
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
void(* statfs)(fuse_req_t req, fuse_ino_t ino)
void(* readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)
void(* readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* flush)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* unlink)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__mount__compat_8h_source.html0000644000175000017500000002247315156613442026102 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_mount_compat.h Source File
libfuse
fuse_mount_compat.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2023 Giulio Benetti <giulio.benetti@benettiengineering.com>
4
5 Logging API.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LICENSE
9*/
10
11#ifndef FUSE_MOUNT_COMPAT_H_
12#define FUSE_MOUNT_COMPAT_H_
13
14#include <sys/mount.h>
15
16/* Some libc don't define MS_*, so define them manually
17 * (values taken from https://elixir.bootlin.com/linux/v6.10/source/include/uapi/linux/mount.h#L13 on)
18 */
19#ifndef MS_DIRSYNC
20#define MS_DIRSYNC 128
21#endif
22
23#ifndef MS_NOSYMFOLLOW
24#define MS_NOSYMFOLLOW 256
25#endif
26
27#ifndef MS_REC
28#define MS_REC 16384
29#endif
30
31#ifndef MS_PRIVATE
32#define MS_PRIVATE (1<<18)
33#endif
34
35#ifndef MS_LAZYTIME
36#define MS_LAZYTIME (1<<25)
37#endif
38
39#ifndef UMOUNT_DETACH
40#define UMOUNT_DETACH 0x00000002 /* Just detach from the tree */
41#endif
42#ifndef UMOUNT_NOFOLLOW
43#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
44#endif
45#ifndef UMOUNT_UNUSED
46#define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
47#endif
48
49#endif /* FUSE_MOUNT_COMPAT_H_ */
fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__mount__compat_8h_source.html0000644000175000017500000002247315156613442026100 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_mount_compat.h Source File
libfuse
fuse_mount_compat.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2023 Giulio Benetti <giulio.benetti@benettiengineering.com>
4
5 Logging API.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LICENSE
9*/
10
11#ifndef FUSE_MOUNT_COMPAT_H_
12#define FUSE_MOUNT_COMPAT_H_
13
14#include <sys/mount.h>
15
16/* Some libc don't define MS_*, so define them manually
17 * (values taken from https://elixir.bootlin.com/linux/v6.10/source/include/uapi/linux/mount.h#L13 on)
18 */
19#ifndef MS_DIRSYNC
20#define MS_DIRSYNC 128
21#endif
22
23#ifndef MS_NOSYMFOLLOW
24#define MS_NOSYMFOLLOW 256
25#endif
26
27#ifndef MS_REC
28#define MS_REC 16384
29#endif
30
31#ifndef MS_PRIVATE
32#define MS_PRIVATE (1<<18)
33#endif
34
35#ifndef MS_LAZYTIME
36#define MS_LAZYTIME (1<<25)
37#endif
38
39#ifndef UMOUNT_DETACH
40#define UMOUNT_DETACH 0x00000002 /* Just detach from the tree */
41#endif
42#ifndef UMOUNT_NOFOLLOW
43#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
44#endif
45#ifndef UMOUNT_UNUSED
46#define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
47#endif
48
49#endif /* FUSE_MOUNT_COMPAT_H_ */
fuse-3.18.2/doc/html/include_2fuse__mount__compat_8h_source.html0000644000175000017500000002231015156613442023712 0ustar berndbernd libfuse: include/fuse_mount_compat.h Source File
libfuse
fuse_mount_compat.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2023 Giulio Benetti <giulio.benetti@benettiengineering.com>
4
5 Logging API.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LICENSE
9*/
10
11#ifndef FUSE_MOUNT_COMPAT_H_
12#define FUSE_MOUNT_COMPAT_H_
13
14#include <sys/mount.h>
15
16/* Some libc don't define MS_*, so define them manually
17 * (values taken from https://elixir.bootlin.com/linux/v6.10/source/include/uapi/linux/mount.h#L13 on)
18 */
19#ifndef MS_DIRSYNC
20#define MS_DIRSYNC 128
21#endif
22
23#ifndef MS_NOSYMFOLLOW
24#define MS_NOSYMFOLLOW 256
25#endif
26
27#ifndef MS_REC
28#define MS_REC 16384
29#endif
30
31#ifndef MS_PRIVATE
32#define MS_PRIVATE (1<<18)
33#endif
34
35#ifndef MS_LAZYTIME
36#define MS_LAZYTIME (1<<25)
37#endif
38
39#ifndef UMOUNT_DETACH
40#define UMOUNT_DETACH 0x00000002 /* Just detach from the tree */
41#endif
42#ifndef UMOUNT_NOFOLLOW
43#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
44#endif
45#ifndef UMOUNT_UNUSED
46#define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
47#endif
48
49#endif /* FUSE_MOUNT_COMPAT_H_ */
fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__opt_8h_source.html0000644000175000017500000005357415156613442024046 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_opt.h Source File
libfuse
fuse_opt.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
7*/
8
9#ifndef FUSE_OPT_H_
10#define FUSE_OPT_H_
11
17#ifdef __cplusplus
18extern "C" {
19#endif
20
77struct fuse_opt {
79 const char *templ;
80
85 unsigned long offset;
86
91 int value;
92};
93
98#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
99
104#define FUSE_OPT_END { NULL, 0, 0 }
105
109struct fuse_args {
111 int argc;
112
114 char **argv;
115
118};
119
123#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
124
129#define FUSE_OPT_KEY_OPT -1
130
137#define FUSE_OPT_KEY_NONOPT -2
138
145#define FUSE_OPT_KEY_KEEP -3
146
153#define FUSE_OPT_KEY_DISCARD -4
154
180typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
181 struct fuse_args *outargs);
182
203int fuse_opt_parse(struct fuse_args *args, void *data,
204 const struct fuse_opt opts[], fuse_opt_proc_t proc);
205
213int fuse_opt_add_opt(char **opts, const char *opt);
214
222int fuse_opt_add_opt_escaped(char **opts, const char *opt);
223
231int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
232
246int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
247
255void fuse_opt_free_args(struct fuse_args *args);
256
257
265int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
266
267#ifdef __cplusplus
268}
269#endif
270
271#endif /* FUSE_OPT_H_ */
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition fuse_opt.h:180
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition fuse_opt.c:95
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
int allocated
Definition fuse_opt.h:117
char ** argv
Definition fuse_opt.h:114
unsigned long offset
Definition fuse_opt.h:85
const char * templ
Definition fuse_opt.h:79
int value
Definition fuse_opt.h:91
fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__opt_8h_source.html0000644000175000017500000005221615156613442024034 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_opt.h Source File
libfuse
fuse_opt.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_OPT_H_
10#define FUSE_OPT_H_
11
17#ifdef __cplusplus
18extern "C" {
19#endif
20
77struct fuse_opt {
79 const char *templ;
80
85 unsigned long offset;
86
91 int value;
92};
93
98#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
99
104#define FUSE_OPT_END { NULL, 0, 0 }
105
109struct fuse_args {
111 int argc;
112
114 char **argv;
115
117 int allocated;
118};
119
123#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
124
129#define FUSE_OPT_KEY_OPT -1
130
137#define FUSE_OPT_KEY_NONOPT -2
138
145#define FUSE_OPT_KEY_KEEP -3
146
153#define FUSE_OPT_KEY_DISCARD -4
154
180typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
181 struct fuse_args *outargs);
182
203int fuse_opt_parse(struct fuse_args *args, void *data,
204 const struct fuse_opt opts[], fuse_opt_proc_t proc);
205
213int fuse_opt_add_opt(char **opts, const char *opt);
214
222int fuse_opt_add_opt_escaped(char **opts, const char *opt);
223
231int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
232
246int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
247
255void fuse_opt_free_args(struct fuse_args *args);
256
257
265int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
266
267#ifdef __cplusplus
268}
269#endif
270
271#endif /* FUSE_OPT_H_ */
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition fuse_opt.h:180
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition fuse_opt.c:95
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
int allocated
Definition fuse_opt.h:117
char ** argv
Definition fuse_opt.h:114
unsigned long offset
Definition fuse_opt.h:85
const char * templ
Definition fuse_opt.h:79
int value
Definition fuse_opt.h:91
fuse-3.18.2/doc/html/include_2fuse__opt_8h_source.html0000644000175000017500000005160515156613442021661 0ustar berndbernd libfuse: include/fuse_opt.h Source File
libfuse
fuse_opt.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_OPT_H_
10#define FUSE_OPT_H_
11
17#ifdef __cplusplus
18extern "C" {
19#endif
20
77struct fuse_opt {
79 const char *templ;
80
85 unsigned long offset;
86
91 int value;
92};
93
98#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
99
104#define FUSE_OPT_END { NULL, 0, 0 }
105
109struct fuse_args {
111 int argc;
112
114 char **argv;
115
117 int allocated;
118};
119
123#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
124
129#define FUSE_OPT_KEY_OPT -1
130
137#define FUSE_OPT_KEY_NONOPT -2
138
145#define FUSE_OPT_KEY_KEEP -3
146
153#define FUSE_OPT_KEY_DISCARD -4
154
180typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
181 struct fuse_args *outargs);
182
203int fuse_opt_parse(struct fuse_args *args, void *data,
204 const struct fuse_opt opts[], fuse_opt_proc_t proc);
205
213int fuse_opt_add_opt(char **opts, const char *opt);
214
222int fuse_opt_add_opt_escaped(char **opts, const char *opt);
223
231int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
232
246int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
247
255void fuse_opt_free_args(struct fuse_args *args);
256
257
265int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
266
267#ifdef __cplusplus
268}
269#endif
270
271#endif /* FUSE_OPT_H_ */
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition fuse_opt.h:180
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition fuse_opt.c:95
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
int allocated
Definition fuse_opt.h:117
char ** argv
Definition fuse_opt.h:114
unsigned long offset
Definition fuse_opt.h:85
const char * templ
Definition fuse_opt.h:79
int value
Definition fuse_opt.h:91
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2buffer_8c_source.html0000644000175000017500000020630315156613442022440 0ustar berndbernd libfuse: fuse-3.17.4/lib/buffer.c Source File
libfuse
buffer.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
4
5 Functions for dealing with `struct fuse_buf` and `struct
6 fuse_bufvec`.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file COPYING.LIB
10*/
11
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_lowlevel.h"
17#include <string.h>
18#include <unistd.h>
19#include <errno.h>
20#include <assert.h>
21
22size_t fuse_buf_size(const struct fuse_bufvec *bufv)
23{
24 size_t i;
25 size_t size = 0;
26
27 for (i = 0; i < bufv->count; i++) {
28 if (bufv->buf[i].size >= SIZE_MAX - size)
29 return SIZE_MAX;
30
31 size += bufv->buf[i].size;
32 }
33
34 return size;
35}
36
37static size_t min_size(size_t s1, size_t s2)
38{
39 return s1 < s2 ? s1 : s2;
40}
41
42static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
43 const struct fuse_buf *src, size_t src_off,
44 size_t len)
45{
46 ssize_t res = 0;
47 size_t copied = 0;
48
49 while (len) {
50 if (dst->flags & FUSE_BUF_FD_SEEK) {
51 res = pwrite(dst->fd, (char *)src->mem + src_off, len,
52 dst->pos + dst_off);
53 } else {
54 res = write(dst->fd, (char *)src->mem + src_off, len);
55 }
56 if (res == -1) {
57 if (!copied)
58 return -errno;
59 break;
60 }
61 if (res == 0)
62 break;
63
64 copied += res;
65 if (!(dst->flags & FUSE_BUF_FD_RETRY))
66 break;
67
68 src_off += res;
69 dst_off += res;
70 len -= res;
71 }
72
73 return copied;
74}
75
76static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
77 const struct fuse_buf *src, size_t src_off,
78 size_t len)
79{
80 ssize_t res = 0;
81 size_t copied = 0;
82
83 while (len) {
84 if (src->flags & FUSE_BUF_FD_SEEK) {
85 res = pread(src->fd, (char *)dst->mem + dst_off, len,
86 src->pos + src_off);
87 } else {
88 res = read(src->fd, (char *)dst->mem + dst_off, len);
89 }
90 if (res == -1) {
91 if (!copied)
92 return -errno;
93 break;
94 }
95 if (res == 0)
96 break;
97
98 copied += res;
99 if (!(src->flags & FUSE_BUF_FD_RETRY))
100 break;
101
102 dst_off += res;
103 src_off += res;
104 len -= res;
105 }
106
107 return copied;
108}
109
110static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
111 const struct fuse_buf *src, size_t src_off,
112 size_t len)
113{
114 char buf[4096];
115 struct fuse_buf tmp = {
116 .size = sizeof(buf),
117 .flags = 0,
118 };
119 ssize_t res;
120 size_t copied = 0;
121
122 tmp.mem = buf;
123
124 while (len) {
125 size_t this_len = min_size(tmp.size, len);
126 size_t read_len;
127
128 res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
129 if (res < 0) {
130 if (!copied)
131 return res;
132 break;
133 }
134 if (res == 0)
135 break;
136
137 read_len = res;
138 res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
139 if (res < 0) {
140 if (!copied)
141 return res;
142 break;
143 }
144 if (res == 0)
145 break;
146
147 copied += res;
148
149 if (res < this_len)
150 break;
151
152 dst_off += res;
153 src_off += res;
154 len -= res;
155 }
156
157 return copied;
158}
159
160#ifdef HAVE_SPLICE
161static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
162 const struct fuse_buf *src, size_t src_off,
163 size_t len, enum fuse_buf_copy_flags flags)
164{
165 int splice_flags = 0;
166 off_t *srcpos = NULL;
167 off_t *dstpos = NULL;
168 off_t srcpos_val;
169 off_t dstpos_val;
170 ssize_t res;
171 size_t copied = 0;
172
174 splice_flags |= SPLICE_F_MOVE;
176 splice_flags |= SPLICE_F_NONBLOCK;
177
178 if (src->flags & FUSE_BUF_FD_SEEK) {
179 srcpos_val = src->pos + src_off;
180 srcpos = &srcpos_val;
181 }
182 if (dst->flags & FUSE_BUF_FD_SEEK) {
183 dstpos_val = dst->pos + dst_off;
184 dstpos = &dstpos_val;
185 }
186
187 while (len) {
188 res = splice(src->fd, srcpos, dst->fd, dstpos, len,
189 splice_flags);
190 if (res == -1) {
191 if (copied)
192 break;
193
194 if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
195 return -errno;
196
197 /* Maybe splice is not supported for this combination */
198 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
199 len);
200 }
201 if (res == 0)
202 break;
203
204 copied += res;
205 if (!(src->flags & FUSE_BUF_FD_RETRY) &&
206 !(dst->flags & FUSE_BUF_FD_RETRY)) {
207 break;
208 }
209
210 len -= res;
211 }
212
213 return copied;
214}
215#else
216static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
217 const struct fuse_buf *src, size_t src_off,
218 size_t len, enum fuse_buf_copy_flags flags)
219{
220 (void) flags;
221
222 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
223}
224#endif
225
226
227static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
228 const struct fuse_buf *src, size_t src_off,
229 size_t len, enum fuse_buf_copy_flags flags)
230{
231 int src_is_fd = src->flags & FUSE_BUF_IS_FD;
232 int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
233
234 if (!src_is_fd && !dst_is_fd) {
235 char *dstmem = (char *)dst->mem + dst_off;
236 char *srcmem = (char *)src->mem + src_off;
237
238 if (dstmem != srcmem) {
239 if (dstmem + len <= srcmem || srcmem + len <= dstmem)
240 memcpy(dstmem, srcmem, len);
241 else
242 memmove(dstmem, srcmem, len);
243 }
244
245 return len;
246 } else if (!src_is_fd) {
247 return fuse_buf_write(dst, dst_off, src, src_off, len);
248 } else if (!dst_is_fd) {
249 return fuse_buf_read(dst, dst_off, src, src_off, len);
250 } else if (flags & FUSE_BUF_NO_SPLICE) {
251 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
252 } else {
253 return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
254 }
255}
256
257static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
258{
259 if (bufv->idx < bufv->count)
260 return &bufv->buf[bufv->idx];
261 else
262 return NULL;
263}
264
265static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
266{
267 const struct fuse_buf *buf = fuse_bufvec_current(bufv);
268
269 if (!buf)
270 return 0;
271
272 bufv->off += len;
273 assert(bufv->off <= buf->size);
274 if (bufv->off == buf->size) {
275 assert(bufv->idx < bufv->count);
276 bufv->idx++;
277 if (bufv->idx == bufv->count)
278 return 0;
279 bufv->off = 0;
280 }
281 return 1;
282}
283
284ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
286{
287 size_t copied = 0;
288
289 if (dstv == srcv)
290 return fuse_buf_size(dstv);
291
292 for (;;) {
293 const struct fuse_buf *src = fuse_bufvec_current(srcv);
294 const struct fuse_buf *dst = fuse_bufvec_current(dstv);
295 size_t src_len;
296 size_t dst_len;
297 size_t len;
298 ssize_t res;
299
300 if (src == NULL || dst == NULL)
301 break;
302
303 src_len = src->size - srcv->off;
304 dst_len = dst->size - dstv->off;
305 len = min_size(src_len, dst_len);
306
307 res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
308 if (res < 0) {
309 if (!copied)
310 return res;
311 break;
312 }
313 copied += res;
314
315 if (!fuse_bufvec_advance(srcv, res) ||
316 !fuse_bufvec_advance(dstv, res))
317 break;
318
319 if (res < len)
320 break;
321 }
322
323 return copied;
324}
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_FD_RETRY
@ FUSE_BUF_IS_FD
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
enum fuse_buf_flags flags
off_t pos
void * mem
size_t size
struct fuse_buf buf[1]
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2buffer_8c_source.html0000644000175000017500000020552315156613442022441 0ustar berndbernd libfuse: fuse-3.18.1/lib/buffer.c Source File
libfuse
buffer.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
4
5 Functions for dealing with `struct fuse_buf` and `struct
6 fuse_bufvec`.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_lowlevel.h"
17#include <string.h>
18#include <unistd.h>
19#include <errno.h>
20#include <assert.h>
21
22size_t fuse_buf_size(const struct fuse_bufvec *bufv)
23{
24 size_t i;
25 size_t size = 0;
26
27 for (i = 0; i < bufv->count; i++) {
28 if (bufv->buf[i].size >= SIZE_MAX - size)
29 return SIZE_MAX;
30
31 size += bufv->buf[i].size;
32 }
33
34 return size;
35}
36
37static size_t min_size(size_t s1, size_t s2)
38{
39 return s1 < s2 ? s1 : s2;
40}
41
42static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
43 const struct fuse_buf *src, size_t src_off,
44 size_t len)
45{
46 ssize_t res = 0;
47 size_t copied = 0;
48
49 while (len) {
50 if (dst->flags & FUSE_BUF_FD_SEEK) {
51 res = pwrite(dst->fd, (char *)src->mem + src_off, len,
52 dst->pos + dst_off);
53 } else {
54 res = write(dst->fd, (char *)src->mem + src_off, len);
55 }
56 if (res == -1) {
57 if (!copied)
58 return -errno;
59 break;
60 }
61 if (res == 0)
62 break;
63
64 copied += res;
65 if (!(dst->flags & FUSE_BUF_FD_RETRY))
66 break;
67
68 src_off += res;
69 dst_off += res;
70 len -= res;
71 }
72
73 return copied;
74}
75
76static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
77 const struct fuse_buf *src, size_t src_off,
78 size_t len)
79{
80 ssize_t res = 0;
81 size_t copied = 0;
82
83 while (len) {
84 if (src->flags & FUSE_BUF_FD_SEEK) {
85 res = pread(src->fd, (char *)dst->mem + dst_off, len,
86 src->pos + src_off);
87 } else {
88 res = read(src->fd, (char *)dst->mem + dst_off, len);
89 }
90 if (res == -1) {
91 if (!copied)
92 return -errno;
93 break;
94 }
95 if (res == 0)
96 break;
97
98 copied += res;
99 if (!(src->flags & FUSE_BUF_FD_RETRY))
100 break;
101
102 dst_off += res;
103 src_off += res;
104 len -= res;
105 }
106
107 return copied;
108}
109
110static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
111 const struct fuse_buf *src, size_t src_off,
112 size_t len)
113{
114 char buf[4096];
115 struct fuse_buf tmp = {
116 .size = sizeof(buf),
117 .flags = 0,
118 };
119 ssize_t res;
120 size_t copied = 0;
121
122 tmp.mem = buf;
123
124 while (len) {
125 size_t this_len = min_size(tmp.size, len);
126 size_t read_len;
127
128 res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
129 if (res < 0) {
130 if (!copied)
131 return res;
132 break;
133 }
134 if (res == 0)
135 break;
136
137 read_len = res;
138 res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
139 if (res < 0) {
140 if (!copied)
141 return res;
142 break;
143 }
144 if (res == 0)
145 break;
146
147 copied += res;
148
149 if (res < this_len)
150 break;
151
152 dst_off += res;
153 src_off += res;
154 len -= res;
155 }
156
157 return copied;
158}
159
160#ifdef HAVE_SPLICE
161static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
162 const struct fuse_buf *src, size_t src_off,
163 size_t len, enum fuse_buf_copy_flags flags)
164{
165 int splice_flags = 0;
166 off_t *srcpos = NULL;
167 off_t *dstpos = NULL;
168 off_t srcpos_val;
169 off_t dstpos_val;
170 ssize_t res;
171 size_t copied = 0;
172
174 splice_flags |= SPLICE_F_MOVE;
176 splice_flags |= SPLICE_F_NONBLOCK;
177
178 if (src->flags & FUSE_BUF_FD_SEEK) {
179 srcpos_val = src->pos + src_off;
180 srcpos = &srcpos_val;
181 }
182 if (dst->flags & FUSE_BUF_FD_SEEK) {
183 dstpos_val = dst->pos + dst_off;
184 dstpos = &dstpos_val;
185 }
186
187 while (len) {
188 res = splice(src->fd, srcpos, dst->fd, dstpos, len,
189 splice_flags);
190 if (res == -1) {
191 if (copied)
192 break;
193
194 if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
195 return -errno;
196
197 /* Maybe splice is not supported for this combination */
198 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
199 len);
200 }
201 if (res == 0)
202 break;
203
204 copied += res;
205 if (!(src->flags & FUSE_BUF_FD_RETRY) &&
206 !(dst->flags & FUSE_BUF_FD_RETRY)) {
207 break;
208 }
209
210 len -= res;
211 }
212
213 return copied;
214}
215#else
216static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
217 const struct fuse_buf *src, size_t src_off,
218 size_t len, enum fuse_buf_copy_flags flags)
219{
220 (void) flags;
221
222 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
223}
224#endif
225
226
227static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
228 const struct fuse_buf *src, size_t src_off,
229 size_t len, enum fuse_buf_copy_flags flags)
230{
231 int src_is_fd = src->flags & FUSE_BUF_IS_FD;
232 int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
233
234 if (!src_is_fd && !dst_is_fd) {
235 char *dstmem = (char *)dst->mem + dst_off;
236 char *srcmem = (char *)src->mem + src_off;
237
238 if (dstmem != srcmem) {
239 if (dstmem + len <= srcmem || srcmem + len <= dstmem)
240 memcpy(dstmem, srcmem, len);
241 else
242 memmove(dstmem, srcmem, len);
243 }
244
245 return len;
246 } else if (!src_is_fd) {
247 return fuse_buf_write(dst, dst_off, src, src_off, len);
248 } else if (!dst_is_fd) {
249 return fuse_buf_read(dst, dst_off, src, src_off, len);
250 } else if (flags & FUSE_BUF_NO_SPLICE) {
251 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
252 } else {
253 return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
254 }
255}
256
257static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
258{
259 if (bufv->idx < bufv->count)
260 return &bufv->buf[bufv->idx];
261 else
262 return NULL;
263}
264
265static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
266{
267 const struct fuse_buf *buf = fuse_bufvec_current(bufv);
268
269 if (!buf)
270 return 0;
271
272 bufv->off += len;
273 assert(bufv->off <= buf->size);
274 if (bufv->off == buf->size) {
275 assert(bufv->idx < bufv->count);
276 bufv->idx++;
277 if (bufv->idx == bufv->count)
278 return 0;
279 bufv->off = 0;
280 }
281 return 1;
282}
283
284ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
286{
287 size_t copied = 0;
288
289 if (dstv == srcv)
290 return fuse_buf_size(dstv);
291
292 for (;;) {
293 const struct fuse_buf *src = fuse_bufvec_current(srcv);
294 const struct fuse_buf *dst = fuse_bufvec_current(dstv);
295 size_t src_len;
296 size_t dst_len;
297 size_t len;
298 ssize_t res;
299
300 if (src == NULL || dst == NULL)
301 break;
302
303 src_len = src->size - srcv->off;
304 dst_len = dst->size - dstv->off;
305 len = min_size(src_len, dst_len);
306
307 res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
308 if (res < 0) {
309 if (!copied)
310 return res;
311 break;
312 }
313 copied += res;
314
315 if (!fuse_bufvec_advance(srcv, res) ||
316 !fuse_bufvec_advance(dstv, res))
317 break;
318
319 if (res < len)
320 break;
321 }
322
323 return copied;
324}
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_FD_RETRY
@ FUSE_BUF_IS_FD
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
enum fuse_buf_flags flags
off_t pos
void * mem
size_t size
struct fuse_buf buf[1]
fuse-3.18.2/doc/html/lib_2buffer_8c_source.html0000644000175000017500000020534015156613442020262 0ustar berndbernd libfuse: lib/buffer.c Source File
libfuse
buffer.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
4
5 Functions for dealing with `struct fuse_buf` and `struct
6 fuse_bufvec`.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_lowlevel.h"
17#include <string.h>
18#include <unistd.h>
19#include <errno.h>
20#include <assert.h>
21
22size_t fuse_buf_size(const struct fuse_bufvec *bufv)
23{
24 size_t i;
25 size_t size = 0;
26
27 for (i = 0; i < bufv->count; i++) {
28 if (bufv->buf[i].size >= SIZE_MAX - size)
29 return SIZE_MAX;
30
31 size += bufv->buf[i].size;
32 }
33
34 return size;
35}
36
37static size_t min_size(size_t s1, size_t s2)
38{
39 return s1 < s2 ? s1 : s2;
40}
41
42static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
43 const struct fuse_buf *src, size_t src_off,
44 size_t len)
45{
46 ssize_t res = 0;
47 size_t copied = 0;
48
49 while (len) {
50 if (dst->flags & FUSE_BUF_FD_SEEK) {
51 res = pwrite(dst->fd, (char *)src->mem + src_off, len,
52 dst->pos + dst_off);
53 } else {
54 res = write(dst->fd, (char *)src->mem + src_off, len);
55 }
56 if (res == -1) {
57 if (!copied)
58 return -errno;
59 break;
60 }
61 if (res == 0)
62 break;
63
64 copied += res;
65 if (!(dst->flags & FUSE_BUF_FD_RETRY))
66 break;
67
68 src_off += res;
69 dst_off += res;
70 len -= res;
71 }
72
73 return copied;
74}
75
76static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
77 const struct fuse_buf *src, size_t src_off,
78 size_t len)
79{
80 ssize_t res = 0;
81 size_t copied = 0;
82
83 while (len) {
84 if (src->flags & FUSE_BUF_FD_SEEK) {
85 res = pread(src->fd, (char *)dst->mem + dst_off, len,
86 src->pos + src_off);
87 } else {
88 res = read(src->fd, (char *)dst->mem + dst_off, len);
89 }
90 if (res == -1) {
91 if (!copied)
92 return -errno;
93 break;
94 }
95 if (res == 0)
96 break;
97
98 copied += res;
99 if (!(src->flags & FUSE_BUF_FD_RETRY))
100 break;
101
102 dst_off += res;
103 src_off += res;
104 len -= res;
105 }
106
107 return copied;
108}
109
110static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
111 const struct fuse_buf *src, size_t src_off,
112 size_t len)
113{
114 char buf[4096];
115 struct fuse_buf tmp = {
116 .size = sizeof(buf),
117 .flags = 0,
118 };
119 ssize_t res;
120 size_t copied = 0;
121
122 tmp.mem = buf;
123
124 while (len) {
125 size_t this_len = min_size(tmp.size, len);
126 size_t read_len;
127
128 res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
129 if (res < 0) {
130 if (!copied)
131 return res;
132 break;
133 }
134 if (res == 0)
135 break;
136
137 read_len = res;
138 res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
139 if (res < 0) {
140 if (!copied)
141 return res;
142 break;
143 }
144 if (res == 0)
145 break;
146
147 copied += res;
148
149 if (res < this_len)
150 break;
151
152 dst_off += res;
153 src_off += res;
154 len -= res;
155 }
156
157 return copied;
158}
159
160#ifdef HAVE_SPLICE
161static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
162 const struct fuse_buf *src, size_t src_off,
163 size_t len, enum fuse_buf_copy_flags flags)
164{
165 int splice_flags = 0;
166 off_t *srcpos = NULL;
167 off_t *dstpos = NULL;
168 off_t srcpos_val;
169 off_t dstpos_val;
170 ssize_t res;
171 size_t copied = 0;
172
174 splice_flags |= SPLICE_F_MOVE;
176 splice_flags |= SPLICE_F_NONBLOCK;
177
178 if (src->flags & FUSE_BUF_FD_SEEK) {
179 srcpos_val = src->pos + src_off;
180 srcpos = &srcpos_val;
181 }
182 if (dst->flags & FUSE_BUF_FD_SEEK) {
183 dstpos_val = dst->pos + dst_off;
184 dstpos = &dstpos_val;
185 }
186
187 while (len) {
188 res = splice(src->fd, srcpos, dst->fd, dstpos, len,
189 splice_flags);
190 if (res == -1) {
191 if (copied)
192 break;
193
194 if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
195 return -errno;
196
197 /* Maybe splice is not supported for this combination */
198 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
199 len);
200 }
201 if (res == 0)
202 break;
203
204 copied += res;
205 if (!(src->flags & FUSE_BUF_FD_RETRY) &&
206 !(dst->flags & FUSE_BUF_FD_RETRY)) {
207 break;
208 }
209
210 len -= res;
211 }
212
213 return copied;
214}
215#else
216static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
217 const struct fuse_buf *src, size_t src_off,
218 size_t len, enum fuse_buf_copy_flags flags)
219{
220 (void) flags;
221
222 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
223}
224#endif
225
226
227static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
228 const struct fuse_buf *src, size_t src_off,
229 size_t len, enum fuse_buf_copy_flags flags)
230{
231 int src_is_fd = src->flags & FUSE_BUF_IS_FD;
232 int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
233
234 if (!src_is_fd && !dst_is_fd) {
235 char *dstmem = (char *)dst->mem + dst_off;
236 char *srcmem = (char *)src->mem + src_off;
237
238 if (dstmem != srcmem) {
239 if (dstmem + len <= srcmem || srcmem + len <= dstmem)
240 memcpy(dstmem, srcmem, len);
241 else
242 memmove(dstmem, srcmem, len);
243 }
244
245 return len;
246 } else if (!src_is_fd) {
247 return fuse_buf_write(dst, dst_off, src, src_off, len);
248 } else if (!dst_is_fd) {
249 return fuse_buf_read(dst, dst_off, src, src_off, len);
250 } else if (flags & FUSE_BUF_NO_SPLICE) {
251 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
252 } else {
253 return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
254 }
255}
256
257static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
258{
259 if (bufv->idx < bufv->count)
260 return &bufv->buf[bufv->idx];
261 else
262 return NULL;
263}
264
265static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
266{
267 const struct fuse_buf *buf = fuse_bufvec_current(bufv);
268
269 if (!buf)
270 return 0;
271
272 bufv->off += len;
273 assert(bufv->off <= buf->size);
274 if (bufv->off == buf->size) {
275 assert(bufv->idx < bufv->count);
276 bufv->idx++;
277 if (bufv->idx == bufv->count)
278 return 0;
279 bufv->off = 0;
280 }
281 return 1;
282}
283
284ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
286{
287 size_t copied = 0;
288
289 if (dstv == srcv)
290 return fuse_buf_size(dstv);
291
292 for (;;) {
293 const struct fuse_buf *src = fuse_bufvec_current(srcv);
294 const struct fuse_buf *dst = fuse_bufvec_current(dstv);
295 size_t src_len;
296 size_t dst_len;
297 size_t len;
298 ssize_t res;
299
300 if (src == NULL || dst == NULL)
301 break;
302
303 src_len = src->size - srcv->off;
304 dst_len = dst->size - dstv->off;
305 len = min_size(src_len, dst_len);
306
307 res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
308 if (res < 0) {
309 if (!copied)
310 return res;
311 break;
312 }
313 copied += res;
314
315 if (!fuse_bufvec_advance(srcv, res) ||
316 !fuse_bufvec_advance(dstv, res))
317 break;
318
319 if (res < len)
320 break;
321 }
322
323 return copied;
324}
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_FD_RETRY
@ FUSE_BUF_IS_FD
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
enum fuse_buf_flags flags
off_t pos
void * mem
size_t size
struct fuse_buf buf[1]
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2compat_8c_source.html0000644000175000017500000004534415156613442022460 0ustar berndbernd libfuse: fuse-3.17.4/lib/compat.c Source File
libfuse
compat.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Helper functions to create (simple) standalone programs. With the
6 aid of these functions it should be possible to create full FUSE
7 file system by implementing nothing but the request handlers.
8
9 This program can be distributed under the terms of the GNU LGPLv2.
10 See the file COPYING.LIB.
11*/
12
13/* Description:
14 This file has compatibility symbols for platforms that do not
15 support version symboling
16*/
17
18#include "libfuse_config.h"
19
20#include <stddef.h>
21#include <stdint.h>
22
23struct fuse_args;
26struct fuse_session;
27struct fuse_custom_io;
28struct fuse_operations;
30
34#if (!defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
35/* With current libfuse fuse_parse_cmdline is a macro pointing to the
36 * versioned function. Here in this file we need to provide the ABI symbol
37 * and the redirecting macro is conflicting.
38 */
39#ifdef fuse_parse_cmdline
40#undef fuse_parse_cmdline
41#endif
42int fuse_parse_cmdline_30(struct fuse_args *args,
43 struct fuse_cmdline_opts *opts);
44int fuse_parse_cmdline(struct fuse_args *args,
45 struct fuse_cmdline_opts *opts);
46int fuse_parse_cmdline(struct fuse_args *args,
47 struct fuse_cmdline_opts *opts)
48{
49 return fuse_parse_cmdline_30(args, opts);
50}
51
52int fuse_session_custom_io_30(struct fuse_session *se,
53 const struct fuse_custom_io *io, int fd);
54int fuse_session_custom_io(struct fuse_session *se,
55 const struct fuse_custom_io *io, int fd);
56int fuse_session_custom_io(struct fuse_session *se,
57 const struct fuse_custom_io *io, int fd)
58
59{
60 return fuse_session_custom_io_30(se, io, fd);
61}
62
63#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
64
65int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
66 size_t op_size, void *user_data);
67int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
68 size_t op_size, void *user_data);
69int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
70 size_t op_size, void *user_data)
71{
72 return fuse_main_real_30(argc, argv, op, op_size, user_data);
73}
74
75struct fuse_session *fuse_session_new_30(struct fuse_args *args,
76 const struct fuse_lowlevel_ops *op,
77 size_t op_size, void *userdata);
78struct fuse_session *fuse_session_new(struct fuse_args *args,
79 const struct fuse_lowlevel_ops *op,
80 size_t op_size, void *userdata);
81struct fuse_session *fuse_session_new(struct fuse_args *args,
82 const struct fuse_lowlevel_ops *op,
83 size_t op_size, void *userdata)
84{
85 return fuse_session_new_30(args, op, op_size, userdata);
86}
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2compat_8c_source.html0000644000175000017500000004534215156613442022454 0ustar berndbernd libfuse: fuse-3.18.1/lib/compat.c Source File
libfuse
compat.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Helper functions to create (simple) standalone programs. With the
6 aid of these functions it should be possible to create full FUSE
7 file system by implementing nothing but the request handlers.
8
9 This program can be distributed under the terms of the GNU LGPLv2.
10 See the file LGPL2.txt.
11*/
12
13/* Description:
14 This file has compatibility symbols for platforms that do not
15 support version symboling
16*/
17
18#include "libfuse_config.h"
19
20#include <stddef.h>
21#include <stdint.h>
22
23struct fuse_args;
26struct fuse_session;
27struct fuse_custom_io;
28struct fuse_operations;
30
34#if (!defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
35/* With current libfuse fuse_parse_cmdline is a macro pointing to the
36 * versioned function. Here in this file we need to provide the ABI symbol
37 * and the redirecting macro is conflicting.
38 */
39#ifdef fuse_parse_cmdline
40#undef fuse_parse_cmdline
41#endif
42int fuse_parse_cmdline_30(struct fuse_args *args,
43 struct fuse_cmdline_opts *opts);
44int fuse_parse_cmdline(struct fuse_args *args,
45 struct fuse_cmdline_opts *opts);
46int fuse_parse_cmdline(struct fuse_args *args,
47 struct fuse_cmdline_opts *opts)
48{
49 return fuse_parse_cmdline_30(args, opts);
50}
51
52int fuse_session_custom_io_30(struct fuse_session *se,
53 const struct fuse_custom_io *io, int fd);
54int fuse_session_custom_io(struct fuse_session *se,
55 const struct fuse_custom_io *io, int fd);
56int fuse_session_custom_io(struct fuse_session *se,
57 const struct fuse_custom_io *io, int fd)
58
59{
60 return fuse_session_custom_io_30(se, io, fd);
61}
62
63#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
64
65int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
66 size_t op_size, void *user_data);
67int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
68 size_t op_size, void *user_data);
69int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
70 size_t op_size, void *user_data)
71{
72 return fuse_main_real_30(argc, argv, op, op_size, user_data);
73}
74
75struct fuse_session *fuse_session_new_30(struct fuse_args *args,
76 const struct fuse_lowlevel_ops *op,
77 size_t op_size, void *userdata);
78struct fuse_session *fuse_session_new(struct fuse_args *args,
79 const struct fuse_lowlevel_ops *op,
80 size_t op_size, void *userdata);
81struct fuse_session *fuse_session_new(struct fuse_args *args,
82 const struct fuse_lowlevel_ops *op,
83 size_t op_size, void *userdata)
84{
85 return fuse_session_new_30(args, op, op_size, userdata);
86}
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
fuse-3.18.2/doc/html/lib_2compat_8c_source.html0000644000175000017500000004515715156613442020304 0ustar berndbernd libfuse: lib/compat.c Source File
libfuse
compat.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Helper functions to create (simple) standalone programs. With the
6 aid of these functions it should be possible to create full FUSE
7 file system by implementing nothing but the request handlers.
8
9 This program can be distributed under the terms of the GNU LGPLv2.
10 See the file LGPL2.txt.
11*/
12
13/* Description:
14 This file has compatibility symbols for platforms that do not
15 support version symboling
16*/
17
18#include "libfuse_config.h"
19
20#include <stddef.h>
21#include <stdint.h>
22
23struct fuse_args;
26struct fuse_session;
27struct fuse_custom_io;
28struct fuse_operations;
30
34#if (!defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
35/* With current libfuse fuse_parse_cmdline is a macro pointing to the
36 * versioned function. Here in this file we need to provide the ABI symbol
37 * and the redirecting macro is conflicting.
38 */
39#ifdef fuse_parse_cmdline
40#undef fuse_parse_cmdline
41#endif
42int fuse_parse_cmdline_30(struct fuse_args *args,
43 struct fuse_cmdline_opts *opts);
44int fuse_parse_cmdline(struct fuse_args *args,
45 struct fuse_cmdline_opts *opts);
46int fuse_parse_cmdline(struct fuse_args *args,
47 struct fuse_cmdline_opts *opts)
48{
49 return fuse_parse_cmdline_30(args, opts);
50}
51
52int fuse_session_custom_io_30(struct fuse_session *se,
53 const struct fuse_custom_io *io, int fd);
54int fuse_session_custom_io(struct fuse_session *se,
55 const struct fuse_custom_io *io, int fd);
56int fuse_session_custom_io(struct fuse_session *se,
57 const struct fuse_custom_io *io, int fd)
58
59{
60 return fuse_session_custom_io_30(se, io, fd);
61}
62
63#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
64
65int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
66 size_t op_size, void *user_data);
67int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
68 size_t op_size, void *user_data);
69int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
70 size_t op_size, void *user_data)
71{
72 return fuse_main_real_30(argc, argv, op, op_size, user_data);
73}
74
75struct fuse_session *fuse_session_new_30(struct fuse_args *args,
76 const struct fuse_lowlevel_ops *op,
77 size_t op_size, void *userdata);
78struct fuse_session *fuse_session_new(struct fuse_args *args,
79 const struct fuse_lowlevel_ops *op,
80 size_t op_size, void *userdata);
81struct fuse_session *fuse_session_new(struct fuse_args *args,
82 const struct fuse_lowlevel_ops *op,
83 size_t op_size, void *userdata)
84{
85 return fuse_session_new_30(args, op, op_size, userdata);
86}
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2cuse__lowlevel_8c_source.html0000644000175000017500000021247315156613442024203 0ustar berndbernd libfuse: fuse-3.17.4/lib/cuse_lowlevel.c Source File
libfuse
cuse_lowlevel.c
1/*
2 CUSE: Character device in Userspace
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU LGPLv2.
7 See the file COPYING.LIB.
8*/
9
10#include "fuse_config.h"
11#include "cuse_lowlevel.h"
12#include "fuse_kernel.h"
13#include "fuse_i.h"
14#include "fuse_opt.h"
15
16#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19#include <stddef.h>
20#include <errno.h>
21#include <unistd.h>
22
23struct cuse_data {
24 struct cuse_lowlevel_ops clop;
25 unsigned max_read;
26 unsigned dev_major;
27 unsigned dev_minor;
28 unsigned flags;
29 unsigned dev_info_len;
30 char dev_info[];
31};
32
33static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
34{
35 return &req->se->cuse_data->clop;
36}
37
38static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
39 struct fuse_file_info *fi)
40{
41 (void)ino;
42 req_clop(req)->open(req, fi);
43}
44
45static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
46 off_t off, struct fuse_file_info *fi)
47{
48 (void)ino;
49 req_clop(req)->read(req, size, off, fi);
50}
51
52static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
53 size_t size, off_t off, struct fuse_file_info *fi)
54{
55 (void)ino;
56 req_clop(req)->write(req, buf, size, off, fi);
57}
58
59static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
60 struct fuse_file_info *fi)
61{
62 (void)ino;
63 req_clop(req)->flush(req, fi);
64}
65
66static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
67 struct fuse_file_info *fi)
68{
69 (void)ino;
70 req_clop(req)->release(req, fi);
71}
72
73static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
74 struct fuse_file_info *fi)
75{
76 (void)ino;
77 req_clop(req)->fsync(req, datasync, fi);
78}
79
80static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg,
81 struct fuse_file_info *fi, unsigned int flags,
82 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
83{
84 (void)ino;
85 req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
86 out_bufsz);
87}
88
89static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
90 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
91{
92 (void)ino;
93 req_clop(req)->poll(req, fi, ph);
94}
95
96static size_t cuse_pack_info(int argc, const char **argv, char *buf)
97{
98 size_t size = 0;
99 int i;
100
101 for (i = 0; i < argc; i++) {
102 size_t len;
103
104 len = strlen(argv[i]) + 1;
105 size += len;
106 if (buf) {
107 memcpy(buf, argv[i], len);
108 buf += len;
109 }
110 }
111
112 return size;
113}
114
115static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
116 const struct cuse_lowlevel_ops *clop)
117{
118 struct cuse_data *cd;
119 size_t dev_info_len;
120
121 dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
122 NULL);
123
124 if (dev_info_len > CUSE_INIT_INFO_MAX) {
125 fuse_log(FUSE_LOG_ERR, "cuse: dev_info (%zu) too large, limit=%u\n",
126 dev_info_len, CUSE_INIT_INFO_MAX);
127 return NULL;
128 }
129
130 cd = calloc(1, sizeof(*cd) + dev_info_len);
131 if (!cd) {
132 fuse_log(FUSE_LOG_ERR, "cuse: failed to allocate cuse_data\n");
133 return NULL;
134 }
135
136 memcpy(&cd->clop, clop, sizeof(cd->clop));
137 cd->max_read = 131072;
138 cd->dev_major = ci->dev_major;
139 cd->dev_minor = ci->dev_minor;
140 cd->dev_info_len = dev_info_len;
141 cd->flags = ci->flags;
142 cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
143
144 return cd;
145}
146
147struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
148 const struct cuse_info *ci,
149 const struct cuse_lowlevel_ops *clop,
150 void *userdata)
151{
152 struct fuse_lowlevel_ops lop;
153 struct cuse_data *cd;
154 struct fuse_session *se;
155
156 cd = cuse_prep_data(ci, clop);
157 if (!cd)
158 return NULL;
159
160 memset(&lop, 0, sizeof(lop));
161 lop.init = clop->init;
162 lop.destroy = clop->destroy;
163 lop.open = clop->open ? cuse_fll_open : NULL;
164 lop.read = clop->read ? cuse_fll_read : NULL;
165 lop.write = clop->write ? cuse_fll_write : NULL;
166 lop.flush = clop->flush ? cuse_fll_flush : NULL;
167 lop.release = clop->release ? cuse_fll_release : NULL;
168 lop.fsync = clop->fsync ? cuse_fll_fsync : NULL;
169 lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL;
170 lop.poll = clop->poll ? cuse_fll_poll : NULL;
171
172 se = fuse_session_new(args, &lop, sizeof(lop), userdata);
173 if (!se) {
174 free(cd);
175 return NULL;
176 }
177 se->cuse_data = cd;
178
179 return se;
180}
181
182static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
183 char *dev_info, unsigned dev_info_len)
184{
185 struct iovec iov[3];
186
187 iov[1].iov_base = arg;
188 iov[1].iov_len = sizeof(struct cuse_init_out);
189 iov[2].iov_base = dev_info;
190 iov[2].iov_len = dev_info_len;
191
192 return fuse_send_reply_iov_nofree(req, 0, iov, 3);
193}
194
195void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
196{
197 struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
198 struct cuse_init_out outarg;
199 struct fuse_session *se = req->se;
200 struct cuse_data *cd = se->cuse_data;
201 size_t bufsize = se->bufsize;
202 struct cuse_lowlevel_ops *clop = req_clop(req);
203
204 (void) nodeid;
205 if (se->debug) {
206 fuse_log(FUSE_LOG_DEBUG, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
207 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
208 }
209 se->conn.proto_major = arg->major;
210 se->conn.proto_minor = arg->minor;
211
212 /* XXX This is not right.*/
213 se->conn.capable_ext = 0;
214 se->conn.want_ext = 0;
215
216 if (arg->major < 7) {
217 fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n",
218 arg->major, arg->minor);
219 fuse_reply_err(req, EPROTO);
220 return;
221 }
222
223 if (bufsize < FUSE_MIN_READ_BUFFER) {
224 fuse_log(FUSE_LOG_ERR, "cuse: warning: buffer size too small: %zu\n",
225 bufsize);
226 bufsize = FUSE_MIN_READ_BUFFER;
227 }
228
229 bufsize -= 4096;
230 if (bufsize < se->conn.max_write)
231 se->conn.max_write = bufsize;
232
233 se->got_init = 1;
234 if (se->op.init)
235 se->op.init(se->userdata, &se->conn);
236
237 memset(&outarg, 0, sizeof(outarg));
238 outarg.major = FUSE_KERNEL_VERSION;
239 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
240 outarg.flags = cd->flags;
241 outarg.max_read = cd->max_read;
242 outarg.max_write = se->conn.max_write;
243 outarg.dev_major = cd->dev_major;
244 outarg.dev_minor = cd->dev_minor;
245
246 if (se->debug) {
247 fuse_log(FUSE_LOG_DEBUG, " CUSE_INIT: %u.%u\n",
248 outarg.major, outarg.minor);
249 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
250 fuse_log(FUSE_LOG_DEBUG, " max_read=0x%08x\n", outarg.max_read);
251 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
252 fuse_log(FUSE_LOG_DEBUG, " dev_major=%u\n", outarg.dev_major);
253 fuse_log(FUSE_LOG_DEBUG, " dev_minor=%u\n", outarg.dev_minor);
254 fuse_log(FUSE_LOG_DEBUG, " dev_info: %.*s\n", cd->dev_info_len,
255 cd->dev_info);
256 }
257
258 cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
259
260 if (clop->init_done)
261 clop->init_done(se->userdata);
262
263 fuse_free_req(req);
264}
265
266struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
267 const struct cuse_info *ci,
268 const struct cuse_lowlevel_ops *clop,
269 int *multithreaded, void *userdata)
270{
271 const char *devname = "/dev/cuse";
272 static const struct fuse_opt kill_subtype_opts[] = {
275 };
276 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
277 struct fuse_session *se;
278 struct fuse_cmdline_opts opts;
279 int fd;
280 int res;
281
282 if (fuse_parse_cmdline(&args, &opts) == -1)
283 return NULL;
284 *multithreaded = !opts.singlethread;
285
286 /* Remove subtype= option */
287 res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
288 if (res == -1)
289 goto out1;
290
291 /*
292 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
293 * would ensue.
294 */
295 do {
296 fd = open("/dev/null", O_RDWR);
297 if (fd > 2)
298 close(fd);
299 } while (fd >= 0 && fd <= 2);
300
301 se = cuse_lowlevel_new(&args, ci, clop, userdata);
302 if (se == NULL)
303 goto out1;
304
305 fd = open(devname, O_RDWR);
306 if (fd == -1) {
307 if (errno == ENODEV || errno == ENOENT)
308 fuse_log(FUSE_LOG_ERR, "cuse: device not found, try 'modprobe cuse' first\n");
309 else
310 fuse_log(FUSE_LOG_ERR, "cuse: failed to open %s: %s\n",
311 devname, strerror(errno));
312 goto err_se;
313 }
314 se->fd = fd;
315
316 res = fuse_set_signal_handlers(se);
317 if (res == -1)
318 goto err_se;
319
320 res = fuse_daemonize(opts.foreground);
321 if (res == -1)
322 goto err_sig;
323
324 fuse_opt_free_args(&args);
325 return se;
326
327err_sig:
329err_se:
331out1:
332 free(opts.mountpoint);
333 fuse_opt_free_args(&args);
334 return NULL;
335}
336
337void cuse_lowlevel_teardown(struct fuse_session *se)
338{
341}
342
343int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
344 const struct cuse_lowlevel_ops *clop, void *userdata)
345{
346 struct fuse_session *se;
347 int multithreaded;
348 int res;
349
350 se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
351 userdata);
352 if (se == NULL)
353 return 1;
354
355 if (multithreaded) {
356 struct fuse_loop_config *config = fuse_loop_cfg_create();
357 res = fuse_session_loop_mt(se, config);
358 fuse_loop_cfg_destroy(config);
359 }
360 else
361 res = fuse_session_loop(se);
362
363 cuse_lowlevel_teardown(se);
364 if (res == -1)
365 return 1;
366
367 return 0;
368}
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
uint64_t fuse_ino_t
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_DISCARD
Definition fuse_opt.h:153
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2cuse__lowlevel_8c_source.html0000644000175000017500000021504415156613442024176 0ustar berndbernd libfuse: fuse-3.18.1/lib/cuse_lowlevel.c Source File
libfuse
cuse_lowlevel.c
1/*
2 CUSE: Character device in Userspace
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU LGPLv2.
7 See the file LGPL2.txt.
8*/
9
10#include "fuse_config.h"
11#include "cuse_lowlevel.h"
12#include "fuse_kernel.h"
13#include "fuse_i.h"
14#include "fuse_opt.h"
15
16#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19#include <stddef.h>
20#include <errno.h>
21#include <unistd.h>
22
23struct cuse_data {
24 struct cuse_lowlevel_ops clop;
25 unsigned max_read;
26 unsigned dev_major;
27 unsigned dev_minor;
28 unsigned flags;
29 unsigned dev_info_len;
30 char dev_info[];
31};
32
33static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
34{
35 return &req->se->cuse_data->clop;
36}
37
38static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
39 struct fuse_file_info *fi)
40{
41 (void)ino;
42 req_clop(req)->open(req, fi);
43}
44
45static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
46 off_t off, struct fuse_file_info *fi)
47{
48 (void)ino;
49 req_clop(req)->read(req, size, off, fi);
50}
51
52static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
53 size_t size, off_t off, struct fuse_file_info *fi)
54{
55 (void)ino;
56 req_clop(req)->write(req, buf, size, off, fi);
57}
58
59static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
60 struct fuse_file_info *fi)
61{
62 (void)ino;
63 req_clop(req)->flush(req, fi);
64}
65
66static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
67 struct fuse_file_info *fi)
68{
69 (void)ino;
70 req_clop(req)->release(req, fi);
71}
72
73static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
74 struct fuse_file_info *fi)
75{
76 (void)ino;
77 req_clop(req)->fsync(req, datasync, fi);
78}
79
80static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg,
81 struct fuse_file_info *fi, unsigned int flags,
82 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
83{
84 (void)ino;
85 req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
86 out_bufsz);
87}
88
89static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
90 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
91{
92 (void)ino;
93 req_clop(req)->poll(req, fi, ph);
94}
95
96static size_t cuse_pack_info(int argc, const char **argv, char *buf)
97{
98 size_t size = 0;
99 int i;
100
101 for (i = 0; i < argc; i++) {
102 size_t len;
103
104 len = strlen(argv[i]) + 1;
105 size += len;
106 if (buf) {
107 memcpy(buf, argv[i], len);
108 buf += len;
109 }
110 }
111
112 return size;
113}
114
115static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
116 const struct cuse_lowlevel_ops *clop)
117{
118 struct cuse_data *cd;
119 size_t dev_info_len;
120
121 dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
122 NULL);
123
124 if (dev_info_len > CUSE_INIT_INFO_MAX) {
125 fuse_log(FUSE_LOG_ERR, "cuse: dev_info (%zu) too large, limit=%u\n",
126 dev_info_len, CUSE_INIT_INFO_MAX);
127 return NULL;
128 }
129
130 cd = calloc(1, sizeof(*cd) + dev_info_len);
131 if (!cd) {
132 fuse_log(FUSE_LOG_ERR, "cuse: failed to allocate cuse_data\n");
133 return NULL;
134 }
135
136 memcpy(&cd->clop, clop, sizeof(cd->clop));
137 cd->max_read = 131072;
138 cd->dev_major = ci->dev_major;
139 cd->dev_minor = ci->dev_minor;
140 cd->dev_info_len = dev_info_len;
141 cd->flags = ci->flags;
142 cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
143
144 return cd;
145}
146
147struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
148 const struct cuse_info *ci,
149 const struct cuse_lowlevel_ops *clop,
150 void *userdata)
151{
152 struct fuse_lowlevel_ops lop;
153 struct cuse_data *cd;
154 struct fuse_session *se;
155
156 cd = cuse_prep_data(ci, clop);
157 if (!cd)
158 return NULL;
159
160 memset(&lop, 0, sizeof(lop));
161 lop.init = clop->init;
162 lop.destroy = clop->destroy;
163 lop.open = clop->open ? cuse_fll_open : NULL;
164 lop.read = clop->read ? cuse_fll_read : NULL;
165 lop.write = clop->write ? cuse_fll_write : NULL;
166 lop.flush = clop->flush ? cuse_fll_flush : NULL;
167 lop.release = clop->release ? cuse_fll_release : NULL;
168 lop.fsync = clop->fsync ? cuse_fll_fsync : NULL;
169 lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL;
170 lop.poll = clop->poll ? cuse_fll_poll : NULL;
171
172 se = fuse_session_new(args, &lop, sizeof(lop), userdata);
173 if (!se) {
174 free(cd);
175 return NULL;
176 }
177 se->cuse_data = cd;
178
179 return se;
180}
181
182static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
183 char *dev_info, unsigned dev_info_len)
184{
185 struct iovec iov[3];
186
187 iov[1].iov_base = arg;
188 iov[1].iov_len = sizeof(struct cuse_init_out);
189 iov[2].iov_base = dev_info;
190 iov[2].iov_len = dev_info_len;
191
192 return fuse_send_reply_iov_nofree(req, 0, iov, 3);
193}
194
195void _cuse_lowlevel_init(fuse_req_t req, const fuse_ino_t nodeid,
196 const void *op_in, const void *req_payload)
197{
198 const struct fuse_init_in *arg = op_in;
199 (void)req_payload;
200 struct cuse_init_out outarg;
201 struct fuse_session *se = req->se;
202 struct cuse_data *cd = se->cuse_data;
203 size_t bufsize = se->bufsize;
204 struct cuse_lowlevel_ops *clop = req_clop(req);
205
206 (void) nodeid;
207 if (se->debug) {
208 fuse_log(FUSE_LOG_DEBUG, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
209 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
210 }
211 se->conn.proto_major = arg->major;
212 se->conn.proto_minor = arg->minor;
213
214 /* XXX This is not right.*/
215 se->conn.capable_ext = 0;
216 se->conn.want_ext = 0;
217
218 if (arg->major < 7) {
219 fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n",
220 arg->major, arg->minor);
221 fuse_reply_err(req, EPROTO);
222 return;
223 }
224
225 if (bufsize < FUSE_MIN_READ_BUFFER) {
226 fuse_log(FUSE_LOG_ERR, "cuse: warning: buffer size too small: %zu\n",
227 bufsize);
228 bufsize = FUSE_MIN_READ_BUFFER;
229 }
230
231 bufsize -= 4096;
232 if (bufsize < se->conn.max_write)
233 se->conn.max_write = bufsize;
234
235 se->got_init = 1;
236 if (se->op.init)
237 se->op.init(se->userdata, &se->conn);
238
239 memset(&outarg, 0, sizeof(outarg));
240 outarg.major = FUSE_KERNEL_VERSION;
241 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
242 outarg.flags = cd->flags;
243 outarg.max_read = cd->max_read;
244 outarg.max_write = se->conn.max_write;
245 outarg.dev_major = cd->dev_major;
246 outarg.dev_minor = cd->dev_minor;
247
248 if (se->debug) {
249 fuse_log(FUSE_LOG_DEBUG, " CUSE_INIT: %u.%u\n",
250 outarg.major, outarg.minor);
251 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
252 fuse_log(FUSE_LOG_DEBUG, " max_read=0x%08x\n", outarg.max_read);
253 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
254 fuse_log(FUSE_LOG_DEBUG, " dev_major=%u\n", outarg.dev_major);
255 fuse_log(FUSE_LOG_DEBUG, " dev_minor=%u\n", outarg.dev_minor);
256 fuse_log(FUSE_LOG_DEBUG, " dev_info: %.*s\n", cd->dev_info_len,
257 cd->dev_info);
258 }
259
260 cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
261
262 if (clop->init_done)
263 clop->init_done(se->userdata);
264
265 fuse_free_req(req);
266}
267
268void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
269{
270 _cuse_lowlevel_init(req, nodeid, inarg, NULL);
271}
272
273struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
274 const struct cuse_info *ci,
275 const struct cuse_lowlevel_ops *clop,
276 int *multithreaded, void *userdata)
277{
278 const char *devname = "/dev/cuse";
279 static const struct fuse_opt kill_subtype_opts[] = {
282 };
283 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
284 struct fuse_session *se;
285 struct fuse_cmdline_opts opts;
286 int fd;
287 int res;
288
289 if (fuse_parse_cmdline(&args, &opts) == -1)
290 return NULL;
291 *multithreaded = !opts.singlethread;
292
293 /* Remove subtype= option */
294 res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
295 if (res == -1)
296 goto out1;
297
298 /*
299 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
300 * would ensue.
301 */
302 do {
303 fd = open("/dev/null", O_RDWR);
304 if (fd > 2)
305 close(fd);
306 } while (fd >= 0 && fd <= 2);
307
308 se = cuse_lowlevel_new(&args, ci, clop, userdata);
309 if (se == NULL)
310 goto out1;
311
312 fd = open(devname, O_RDWR);
313 if (fd == -1) {
314 if (errno == ENODEV || errno == ENOENT)
315 fuse_log(FUSE_LOG_ERR, "cuse: device not found, try 'modprobe cuse' first\n");
316 else
317 fuse_log(FUSE_LOG_ERR, "cuse: failed to open %s: %s\n",
318 devname, strerror(errno));
319 goto err_se;
320 }
321 se->fd = fd;
322
323 res = fuse_set_signal_handlers(se);
324 if (res == -1)
325 goto err_se;
326
327 res = fuse_daemonize(opts.foreground);
328 if (res == -1)
329 goto err_sig;
330
331 fuse_opt_free_args(&args);
332 return se;
333
334err_sig:
336err_se:
338out1:
339 free(opts.mountpoint);
340 fuse_opt_free_args(&args);
341 return NULL;
342}
343
344void cuse_lowlevel_teardown(struct fuse_session *se)
345{
348}
349
350int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
351 const struct cuse_lowlevel_ops *clop, void *userdata)
352{
353 struct fuse_session *se;
354 int multithreaded;
355 int res;
356
357 se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
358 userdata);
359 if (se == NULL)
360 return 1;
361
362 if (multithreaded) {
363 struct fuse_loop_config *config = fuse_loop_cfg_create();
364 res = fuse_session_loop_mt(se, config);
365 fuse_loop_cfg_destroy(config);
366 }
367 else
368 res = fuse_session_loop(se);
369
370 cuse_lowlevel_teardown(se);
371 if (res == -1)
372 return 1;
373
374 return 0;
375}
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
uint64_t fuse_ino_t
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_DISCARD
Definition fuse_opt.h:153
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
fuse-3.18.2/doc/html/lib_2cuse__lowlevel_8c_source.html0000644000175000017500000021466115156613442022026 0ustar berndbernd libfuse: lib/cuse_lowlevel.c Source File
libfuse
cuse_lowlevel.c
1/*
2 CUSE: Character device in Userspace
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU LGPLv2.
7 See the file LGPL2.txt.
8*/
9
10#include "fuse_config.h"
11#include "cuse_lowlevel.h"
12#include "fuse_kernel.h"
13#include "fuse_i.h"
14#include "fuse_opt.h"
15
16#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19#include <stddef.h>
20#include <errno.h>
21#include <unistd.h>
22
23struct cuse_data {
24 struct cuse_lowlevel_ops clop;
25 unsigned max_read;
26 unsigned dev_major;
27 unsigned dev_minor;
28 unsigned flags;
29 unsigned dev_info_len;
30 char dev_info[];
31};
32
33static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
34{
35 return &req->se->cuse_data->clop;
36}
37
38static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
39 struct fuse_file_info *fi)
40{
41 (void)ino;
42 req_clop(req)->open(req, fi);
43}
44
45static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
46 off_t off, struct fuse_file_info *fi)
47{
48 (void)ino;
49 req_clop(req)->read(req, size, off, fi);
50}
51
52static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
53 size_t size, off_t off, struct fuse_file_info *fi)
54{
55 (void)ino;
56 req_clop(req)->write(req, buf, size, off, fi);
57}
58
59static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
60 struct fuse_file_info *fi)
61{
62 (void)ino;
63 req_clop(req)->flush(req, fi);
64}
65
66static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
67 struct fuse_file_info *fi)
68{
69 (void)ino;
70 req_clop(req)->release(req, fi);
71}
72
73static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
74 struct fuse_file_info *fi)
75{
76 (void)ino;
77 req_clop(req)->fsync(req, datasync, fi);
78}
79
80static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg,
81 struct fuse_file_info *fi, unsigned int flags,
82 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
83{
84 (void)ino;
85 req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
86 out_bufsz);
87}
88
89static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
90 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
91{
92 (void)ino;
93 req_clop(req)->poll(req, fi, ph);
94}
95
96static size_t cuse_pack_info(int argc, const char **argv, char *buf)
97{
98 size_t size = 0;
99 int i;
100
101 for (i = 0; i < argc; i++) {
102 size_t len;
103
104 len = strlen(argv[i]) + 1;
105 size += len;
106 if (buf) {
107 memcpy(buf, argv[i], len);
108 buf += len;
109 }
110 }
111
112 return size;
113}
114
115static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
116 const struct cuse_lowlevel_ops *clop)
117{
118 struct cuse_data *cd;
119 size_t dev_info_len;
120
121 dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
122 NULL);
123
124 if (dev_info_len > CUSE_INIT_INFO_MAX) {
125 fuse_log(FUSE_LOG_ERR, "cuse: dev_info (%zu) too large, limit=%u\n",
126 dev_info_len, CUSE_INIT_INFO_MAX);
127 return NULL;
128 }
129
130 cd = calloc(1, sizeof(*cd) + dev_info_len);
131 if (!cd) {
132 fuse_log(FUSE_LOG_ERR, "cuse: failed to allocate cuse_data\n");
133 return NULL;
134 }
135
136 memcpy(&cd->clop, clop, sizeof(cd->clop));
137 cd->max_read = 131072;
138 cd->dev_major = ci->dev_major;
139 cd->dev_minor = ci->dev_minor;
140 cd->dev_info_len = dev_info_len;
141 cd->flags = ci->flags;
142 cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
143
144 return cd;
145}
146
147struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
148 const struct cuse_info *ci,
149 const struct cuse_lowlevel_ops *clop,
150 void *userdata)
151{
152 struct fuse_lowlevel_ops lop;
153 struct cuse_data *cd;
154 struct fuse_session *se;
155
156 cd = cuse_prep_data(ci, clop);
157 if (!cd)
158 return NULL;
159
160 memset(&lop, 0, sizeof(lop));
161 lop.init = clop->init;
162 lop.destroy = clop->destroy;
163 lop.open = clop->open ? cuse_fll_open : NULL;
164 lop.read = clop->read ? cuse_fll_read : NULL;
165 lop.write = clop->write ? cuse_fll_write : NULL;
166 lop.flush = clop->flush ? cuse_fll_flush : NULL;
167 lop.release = clop->release ? cuse_fll_release : NULL;
168 lop.fsync = clop->fsync ? cuse_fll_fsync : NULL;
169 lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL;
170 lop.poll = clop->poll ? cuse_fll_poll : NULL;
171
172 se = fuse_session_new(args, &lop, sizeof(lop), userdata);
173 if (!se) {
174 free(cd);
175 return NULL;
176 }
177 se->cuse_data = cd;
178
179 return se;
180}
181
182static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
183 char *dev_info, unsigned dev_info_len)
184{
185 struct iovec iov[3];
186
187 iov[1].iov_base = arg;
188 iov[1].iov_len = sizeof(struct cuse_init_out);
189 iov[2].iov_base = dev_info;
190 iov[2].iov_len = dev_info_len;
191
192 return fuse_send_reply_iov_nofree(req, 0, iov, 3);
193}
194
195void _cuse_lowlevel_init(fuse_req_t req, const fuse_ino_t nodeid,
196 const void *op_in, const void *req_payload)
197{
198 const struct fuse_init_in *arg = op_in;
199 (void)req_payload;
200 struct cuse_init_out outarg;
201 struct fuse_session *se = req->se;
202 struct cuse_data *cd = se->cuse_data;
203 size_t bufsize = se->bufsize;
204 struct cuse_lowlevel_ops *clop = req_clop(req);
205
206 (void) nodeid;
207 if (se->debug) {
208 fuse_log(FUSE_LOG_DEBUG, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
209 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
210 }
211 se->conn.proto_major = arg->major;
212 se->conn.proto_minor = arg->minor;
213
214 /* XXX This is not right.*/
215 se->conn.capable_ext = 0;
216 se->conn.want_ext = 0;
217
218 if (arg->major < 7) {
219 fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n",
220 arg->major, arg->minor);
221 fuse_reply_err(req, EPROTO);
222 return;
223 }
224
225 if (bufsize < FUSE_MIN_READ_BUFFER) {
226 fuse_log(FUSE_LOG_ERR, "cuse: warning: buffer size too small: %zu\n",
227 bufsize);
228 bufsize = FUSE_MIN_READ_BUFFER;
229 }
230
231 bufsize -= 4096;
232 if (bufsize < se->conn.max_write)
233 se->conn.max_write = bufsize;
234
235 se->got_init = 1;
236 if (se->op.init)
237 se->op.init(se->userdata, &se->conn);
238
239 memset(&outarg, 0, sizeof(outarg));
240 outarg.major = FUSE_KERNEL_VERSION;
241 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
242 outarg.flags = cd->flags;
243 outarg.max_read = cd->max_read;
244 outarg.max_write = se->conn.max_write;
245 outarg.dev_major = cd->dev_major;
246 outarg.dev_minor = cd->dev_minor;
247
248 if (se->debug) {
249 fuse_log(FUSE_LOG_DEBUG, " CUSE_INIT: %u.%u\n",
250 outarg.major, outarg.minor);
251 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
252 fuse_log(FUSE_LOG_DEBUG, " max_read=0x%08x\n", outarg.max_read);
253 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
254 fuse_log(FUSE_LOG_DEBUG, " dev_major=%u\n", outarg.dev_major);
255 fuse_log(FUSE_LOG_DEBUG, " dev_minor=%u\n", outarg.dev_minor);
256 fuse_log(FUSE_LOG_DEBUG, " dev_info: %.*s\n", cd->dev_info_len,
257 cd->dev_info);
258 }
259
260 cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
261
262 if (clop->init_done)
263 clop->init_done(se->userdata);
264
265 fuse_free_req(req);
266}
267
268void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
269{
270 _cuse_lowlevel_init(req, nodeid, inarg, NULL);
271}
272
273struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
274 const struct cuse_info *ci,
275 const struct cuse_lowlevel_ops *clop,
276 int *multithreaded, void *userdata)
277{
278 const char *devname = "/dev/cuse";
279 static const struct fuse_opt kill_subtype_opts[] = {
282 };
283 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
284 struct fuse_session *se;
285 struct fuse_cmdline_opts opts;
286 int fd;
287 int res;
288
289 if (fuse_parse_cmdline(&args, &opts) == -1)
290 return NULL;
291 *multithreaded = !opts.singlethread;
292
293 /* Remove subtype= option */
294 res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
295 if (res == -1)
296 goto out1;
297
298 /*
299 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
300 * would ensue.
301 */
302 do {
303 fd = open("/dev/null", O_RDWR);
304 if (fd > 2)
305 close(fd);
306 } while (fd >= 0 && fd <= 2);
307
308 se = cuse_lowlevel_new(&args, ci, clop, userdata);
309 if (se == NULL)
310 goto out1;
311
312 fd = open(devname, O_RDWR);
313 if (fd == -1) {
314 if (errno == ENODEV || errno == ENOENT)
315 fuse_log(FUSE_LOG_ERR, "cuse: device not found, try 'modprobe cuse' first\n");
316 else
317 fuse_log(FUSE_LOG_ERR, "cuse: failed to open %s: %s\n",
318 devname, strerror(errno));
319 goto err_se;
320 }
321 se->fd = fd;
322
323 res = fuse_set_signal_handlers(se);
324 if (res == -1)
325 goto err_se;
326
327 res = fuse_daemonize(opts.foreground);
328 if (res == -1)
329 goto err_sig;
330
331 fuse_opt_free_args(&args);
332 return se;
333
334err_sig:
336err_se:
338out1:
339 free(opts.mountpoint);
340 fuse_opt_free_args(&args);
341 return NULL;
342}
343
344void cuse_lowlevel_teardown(struct fuse_session *se)
345{
348}
349
350int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
351 const struct cuse_lowlevel_ops *clop, void *userdata)
352{
353 struct fuse_session *se;
354 int multithreaded;
355 int res;
356
357 se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
358 userdata);
359 if (se == NULL)
360 return 1;
361
362 if (multithreaded) {
363 struct fuse_loop_config *config = fuse_loop_cfg_create();
364 res = fuse_session_loop_mt(se, config);
365 fuse_loop_cfg_destroy(config);
366 }
367 else
368 res = fuse_session_loop(se);
369
370 cuse_lowlevel_teardown(se);
371 if (res == -1)
372 return 1;
373
374 return 0;
375}
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
uint64_t fuse_ino_t
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_DISCARD
Definition fuse_opt.h:153
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2fuse_8c_source.html0000644000175000017500000327634115156613443022146 0ustar berndbernd libfuse: fuse-3.17.4/lib/fuse.c Source File
libfuse
fuse.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the high-level FUSE API on top of the low-level
6 API.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file COPYING.LIB
10*/
11
12#define _GNU_SOURCE
13#include "fuse.h"
14#include <pthread.h>
15
16#include "fuse_config.h"
17#include "fuse_i.h"
18#include "fuse_lowlevel.h"
19#include "fuse_opt.h"
20#include "fuse_misc.h"
21#include "fuse_kernel.h"
22#include "util.h"
23
24#include <stdint.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <stddef.h>
29#include <stdbool.h>
30#include <unistd.h>
31#include <time.h>
32#include <fcntl.h>
33#include <limits.h>
34#include <errno.h>
35#include <signal.h>
36#include <dlfcn.h>
37#include <assert.h>
38#include <poll.h>
39#include <sys/param.h>
40#include <sys/uio.h>
41#include <sys/time.h>
42#include <sys/mman.h>
43#include <sys/file.h>
44
45#define FUSE_NODE_SLAB 1
46
47#ifndef MAP_ANONYMOUS
48#undef FUSE_NODE_SLAB
49#endif
50
51#ifndef RENAME_EXCHANGE
52#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
53#endif
54
55#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
56
57#define FUSE_UNKNOWN_INO 0xffffffff
58#define OFFSET_MAX 0x7fffffffffffffffLL
59
60#define NODE_TABLE_MIN_SIZE 8192
61
62struct fuse_fs {
63 struct fuse_operations op;
64 void *user_data;
65 int debug;
66};
67
68struct fusemod_so {
69 void *handle;
70 int ctr;
71};
72
73struct lock_queue_element {
74 struct lock_queue_element *next;
75 pthread_cond_t cond;
76 fuse_ino_t nodeid1;
77 const char *name1;
78 char **path1;
79 struct node **wnode1;
80 fuse_ino_t nodeid2;
81 const char *name2;
82 char **path2;
83 struct node **wnode2;
84 int err;
85 bool done : 1;
86};
87
88struct node_table {
89 struct node **array;
90 size_t use;
91 size_t size;
92 size_t split;
93};
94
95#define list_entry(ptr, type, member) \
96 container_of(ptr, type, member)
97
98struct list_head {
99 struct list_head *next;
100 struct list_head *prev;
101};
102
103struct node_slab {
104 struct list_head list; /* must be the first member */
105 struct list_head freelist;
106 int used;
107};
108
109struct fuse {
110 struct fuse_session *se;
111 struct node_table name_table;
112 struct node_table id_table;
113 struct list_head lru_table;
114 fuse_ino_t ctr;
115 unsigned int generation;
116 unsigned int hidectr;
117 pthread_mutex_t lock;
118 struct fuse_config conf;
119 int intr_installed;
120 struct fuse_fs *fs;
121 struct lock_queue_element *lockq;
122 int pagesize;
123 struct list_head partial_slabs;
124 struct list_head full_slabs;
125 pthread_t prune_thread;
126};
127
128struct lock {
129 int type;
130 off_t start;
131 off_t end;
132 pid_t pid;
133 uint64_t owner;
134 struct lock *next;
135};
136
137struct node {
138 struct node *name_next;
139 struct node *id_next;
140 fuse_ino_t nodeid;
141 unsigned int generation;
142 int refctr;
143 struct node *parent;
144 char *name;
145 uint64_t nlookup;
146 int open_count;
147 struct timespec stat_updated;
148 struct timespec mtime;
149 off_t size;
150 struct lock *locks;
151 unsigned int is_hidden : 1;
152 unsigned int cache_valid : 1;
153 int treelock;
154 char inline_name[32];
155};
156
157#define TREELOCK_WRITE -1
158#define TREELOCK_WAIT_OFFSET INT_MIN
159
160struct node_lru {
161 struct node node;
162 struct list_head lru;
163 struct timespec forget_time;
164};
165
166struct fuse_direntry {
167 struct stat stat;
168 enum fuse_fill_dir_flags flags;
169 char *name;
170 struct fuse_direntry *next;
171};
172
173struct fuse_dh {
174 pthread_mutex_t lock;
175 struct fuse *fuse;
176 fuse_req_t req;
177 char *contents;
178 struct fuse_direntry *first;
179 struct fuse_direntry **last;
180 unsigned len;
181 unsigned size;
182 unsigned needlen;
183 int filled;
184 uint64_t fh;
185 int error;
186 fuse_ino_t nodeid;
187};
188
189struct fuse_context_i {
190 struct fuse_context ctx;
191 fuse_req_t req;
192};
193
194/* Defined by FUSE_REGISTER_MODULE() in lib/modules/subdir.c and iconv.c. */
195extern fuse_module_factory_t fuse_module_subdir_factory;
196#ifdef HAVE_ICONV
197extern fuse_module_factory_t fuse_module_iconv_factory;
198#endif
199
200static pthread_key_t fuse_context_key;
201static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
202static int fuse_context_ref;
203static struct fuse_module *fuse_modules = NULL;
204
205static int fuse_register_module(const char *name,
206 fuse_module_factory_t factory,
207 struct fusemod_so *so)
208{
209 struct fuse_module *mod;
210
211 mod = calloc(1, sizeof(struct fuse_module));
212 if (!mod) {
213 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module\n");
214 return -1;
215 }
216 mod->name = strdup(name);
217 if (!mod->name) {
218 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module name\n");
219 free(mod);
220 return -1;
221 }
222 mod->factory = factory;
223 mod->ctr = 0;
224 mod->so = so;
225 if (mod->so)
226 mod->so->ctr++;
227 mod->next = fuse_modules;
228 fuse_modules = mod;
229
230 return 0;
231}
232
233static void fuse_unregister_module(struct fuse_module *m)
234{
235 struct fuse_module **mp;
236 for (mp = &fuse_modules; *mp; mp = &(*mp)->next) {
237 if (*mp == m) {
238 *mp = (*mp)->next;
239 break;
240 }
241 }
242 free(m->name);
243 free(m);
244}
245
246static int fuse_load_so_module(const char *module)
247{
248 int ret = -1;
249 char *tmp;
250 struct fusemod_so *so;
251 fuse_module_factory_t *factory;
252
253 tmp = malloc(strlen(module) + 64);
254 if (!tmp) {
255 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
256 return -1;
257 }
258 sprintf(tmp, "libfusemod_%s.so", module);
259 so = calloc(1, sizeof(struct fusemod_so));
260 if (!so) {
261 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module so\n");
262 goto out;
263 }
264
265 so->handle = dlopen(tmp, RTLD_NOW);
266 if (so->handle == NULL) {
267 fuse_log(FUSE_LOG_ERR, "fuse: dlopen(%s) failed: %s\n",
268 tmp, dlerror());
269 goto out_free_so;
270 }
271
272 sprintf(tmp, "fuse_module_%s_factory", module);
273 factory = (fuse_module_factory_t*)dlsym(so->handle, tmp);
274 if (factory == NULL) {
275 fuse_log(FUSE_LOG_ERR, "fuse: symbol <%s> not found in module: %s\n",
276 tmp, dlerror());
277 goto out_dlclose;
278 }
279 ret = fuse_register_module(module, *factory, so);
280 if (ret)
281 goto out_dlclose;
282
283out:
284 free(tmp);
285 return ret;
286
287out_dlclose:
288 dlclose(so->handle);
289out_free_so:
290 free(so);
291 goto out;
292}
293
294static struct fuse_module *fuse_find_module(const char *module)
295{
296 struct fuse_module *m;
297 for (m = fuse_modules; m; m = m->next) {
298 if (strcmp(module, m->name) == 0) {
299 m->ctr++;
300 break;
301 }
302 }
303 return m;
304}
305
306static struct fuse_module *fuse_get_module(const char *module)
307{
308 struct fuse_module *m;
309
310 pthread_mutex_lock(&fuse_context_lock);
311 m = fuse_find_module(module);
312 if (!m) {
313 int err = fuse_load_so_module(module);
314 if (!err)
315 m = fuse_find_module(module);
316 }
317 pthread_mutex_unlock(&fuse_context_lock);
318 return m;
319}
320
321static void fuse_put_module(struct fuse_module *m)
322{
323 pthread_mutex_lock(&fuse_context_lock);
324 if (m->so)
325 assert(m->ctr > 0);
326 /* Builtin modules may already have m->ctr == 0 */
327 if (m->ctr > 0)
328 m->ctr--;
329 if (!m->ctr && m->so) {
330 struct fusemod_so *so = m->so;
331 assert(so->ctr > 0);
332 so->ctr--;
333 if (!so->ctr) {
334 struct fuse_module **mp;
335 for (mp = &fuse_modules; *mp;) {
336 if ((*mp)->so == so)
337 fuse_unregister_module(*mp);
338 else
339 mp = &(*mp)->next;
340 }
341 dlclose(so->handle);
342 free(so);
343 }
344 } else if (!m->ctr) {
345 fuse_unregister_module(m);
346 }
347 pthread_mutex_unlock(&fuse_context_lock);
348}
349
350static void init_list_head(struct list_head *list)
351{
352 list->next = list;
353 list->prev = list;
354}
355
356static int list_empty(const struct list_head *head)
357{
358 return head->next == head;
359}
360
361static void list_add(struct list_head *new, struct list_head *prev,
362 struct list_head *next)
363{
364 next->prev = new;
365 new->next = next;
366 new->prev = prev;
367 prev->next = new;
368}
369
370static inline void list_add_head(struct list_head *new, struct list_head *head)
371{
372 list_add(new, head, head->next);
373}
374
375static inline void list_add_tail(struct list_head *new, struct list_head *head)
376{
377 list_add(new, head->prev, head);
378}
379
380static inline void list_del(struct list_head *entry)
381{
382 struct list_head *prev = entry->prev;
383 struct list_head *next = entry->next;
384
385 next->prev = prev;
386 prev->next = next;
387}
388
389static inline int lru_enabled(struct fuse *f)
390{
391 return f->conf.remember > 0;
392}
393
394static struct node_lru *node_lru(struct node *node)
395{
396 return (struct node_lru *) node;
397}
398
399static size_t get_node_size(struct fuse *f)
400{
401 if (lru_enabled(f))
402 return sizeof(struct node_lru);
403 else
404 return sizeof(struct node);
405}
406
407#ifdef FUSE_NODE_SLAB
408static struct node_slab *list_to_slab(struct list_head *head)
409{
410 return (struct node_slab *) head;
411}
412
413static struct node_slab *node_to_slab(struct fuse *f, struct node *node)
414{
415 return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1));
416}
417
418static int alloc_slab(struct fuse *f)
419{
420 void *mem;
421 struct node_slab *slab;
422 char *start;
423 size_t num;
424 size_t i;
425 size_t node_size = get_node_size(f);
426
427 mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE,
428 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
429
430 if (mem == MAP_FAILED)
431 return -1;
432
433 slab = mem;
434 init_list_head(&slab->freelist);
435 slab->used = 0;
436 num = (f->pagesize - sizeof(struct node_slab)) / node_size;
437
438 start = (char *) mem + f->pagesize - num * node_size;
439 for (i = 0; i < num; i++) {
440 struct list_head *n;
441
442 n = (struct list_head *) (start + i * node_size);
443 list_add_tail(n, &slab->freelist);
444 }
445 list_add_tail(&slab->list, &f->partial_slabs);
446
447 return 0;
448}
449
450static struct node *alloc_node(struct fuse *f)
451{
452 struct node_slab *slab;
453 struct list_head *node;
454
455 if (list_empty(&f->partial_slabs)) {
456 int res = alloc_slab(f);
457 if (res != 0)
458 return NULL;
459 }
460 slab = list_to_slab(f->partial_slabs.next);
461 slab->used++;
462 node = slab->freelist.next;
463 list_del(node);
464 if (list_empty(&slab->freelist)) {
465 list_del(&slab->list);
466 list_add_tail(&slab->list, &f->full_slabs);
467 }
468 memset(node, 0, sizeof(struct node));
469
470 return (struct node *) node;
471}
472
473static void free_slab(struct fuse *f, struct node_slab *slab)
474{
475 int res;
476
477 list_del(&slab->list);
478 res = munmap(slab, f->pagesize);
479 if (res == -1)
480 fuse_log(FUSE_LOG_WARNING, "fuse warning: munmap(%p) failed\n",
481 slab);
482}
483
484static void free_node_mem(struct fuse *f, struct node *node)
485{
486 struct node_slab *slab = node_to_slab(f, node);
487 struct list_head *n = (struct list_head *) node;
488
489 slab->used--;
490 if (slab->used) {
491 if (list_empty(&slab->freelist)) {
492 list_del(&slab->list);
493 list_add_tail(&slab->list, &f->partial_slabs);
494 }
495 list_add_head(n, &slab->freelist);
496 } else {
497 free_slab(f, slab);
498 }
499}
500#else
501static struct node *alloc_node(struct fuse *f)
502{
503 return (struct node *) calloc(1, get_node_size(f));
504}
505
506static void free_node_mem(struct fuse *f, struct node *node)
507{
508 (void) f;
509 free(node);
510}
511#endif
512
513static size_t id_hash(struct fuse *f, fuse_ino_t ino)
514{
515 uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size;
516 uint64_t oldhash = hash % (f->id_table.size / 2);
517
518 if (oldhash >= f->id_table.split)
519 return oldhash;
520 else
521 return hash;
522}
523
524static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
525{
526 size_t hash = id_hash(f, nodeid);
527 struct node *node;
528
529 for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
530 if (node->nodeid == nodeid)
531 return node;
532
533 return NULL;
534}
535
536static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
537{
538 struct node *node = get_node_nocheck(f, nodeid);
539 if (!node) {
540 fuse_log(FUSE_LOG_ERR, "fuse internal error: node %llu not found\n",
541 (unsigned long long) nodeid);
542 abort();
543 }
544 return node;
545}
546
547static void curr_time(struct timespec *now);
548static double diff_timespec(const struct timespec *t1,
549 const struct timespec *t2);
550
551static void remove_node_lru(struct node *node)
552{
553 struct node_lru *lnode = node_lru(node);
554 list_del(&lnode->lru);
555 init_list_head(&lnode->lru);
556}
557
558static void set_forget_time(struct fuse *f, struct node *node)
559{
560 struct node_lru *lnode = node_lru(node);
561
562 list_del(&lnode->lru);
563 list_add_tail(&lnode->lru, &f->lru_table);
564 curr_time(&lnode->forget_time);
565}
566
567static void free_node(struct fuse *f, struct node *node)
568{
569 if (node->name != node->inline_name)
570 free(node->name);
571 free_node_mem(f, node);
572}
573
574static void node_table_reduce(struct node_table *t)
575{
576 size_t newsize = t->size / 2;
577 void *newarray;
578
579 if (newsize < NODE_TABLE_MIN_SIZE)
580 return;
581
582 newarray = realloc(t->array, sizeof(struct node *) * newsize);
583 if (newarray != NULL)
584 t->array = newarray;
585
586 t->size = newsize;
587 t->split = t->size / 2;
588}
589
590static void remerge_id(struct fuse *f)
591{
592 struct node_table *t = &f->id_table;
593 int iter;
594
595 if (t->split == 0)
596 node_table_reduce(t);
597
598 for (iter = 8; t->split > 0 && iter; iter--) {
599 struct node **upper;
600
601 t->split--;
602 upper = &t->array[t->split + t->size / 2];
603 if (*upper) {
604 struct node **nodep;
605
606 for (nodep = &t->array[t->split]; *nodep;
607 nodep = &(*nodep)->id_next);
608
609 *nodep = *upper;
610 *upper = NULL;
611 break;
612 }
613 }
614}
615
616static void unhash_id(struct fuse *f, struct node *node)
617{
618 struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
619
620 for (; *nodep != NULL; nodep = &(*nodep)->id_next)
621 if (*nodep == node) {
622 *nodep = node->id_next;
623 f->id_table.use--;
624
625 if(f->id_table.use < f->id_table.size / 4)
626 remerge_id(f);
627 return;
628 }
629}
630
631static int node_table_resize(struct node_table *t)
632{
633 size_t newsize = t->size * 2;
634 void *newarray;
635
636 newarray = realloc(t->array, sizeof(struct node *) * newsize);
637 if (newarray == NULL)
638 return -1;
639
640 t->array = newarray;
641 memset(t->array + t->size, 0, t->size * sizeof(struct node *));
642 t->size = newsize;
643 t->split = 0;
644
645 return 0;
646}
647
648static void rehash_id(struct fuse *f)
649{
650 struct node_table *t = &f->id_table;
651 struct node **nodep;
652 struct node **next;
653 size_t hash;
654
655 if (t->split == t->size / 2)
656 return;
657
658 hash = t->split;
659 t->split++;
660 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
661 struct node *node = *nodep;
662 size_t newhash = id_hash(f, node->nodeid);
663
664 if (newhash != hash) {
665 next = nodep;
666 *nodep = node->id_next;
667 node->id_next = t->array[newhash];
668 t->array[newhash] = node;
669 } else {
670 next = &node->id_next;
671 }
672 }
673 if (t->split == t->size / 2)
674 node_table_resize(t);
675}
676
677static void hash_id(struct fuse *f, struct node *node)
678{
679 size_t hash = id_hash(f, node->nodeid);
680 node->id_next = f->id_table.array[hash];
681 f->id_table.array[hash] = node;
682 f->id_table.use++;
683
684 if (f->id_table.use >= f->id_table.size / 2)
685 rehash_id(f);
686}
687
688static size_t name_hash(struct fuse *f, fuse_ino_t parent,
689 const char *name)
690{
691 uint64_t hash = parent;
692 uint64_t oldhash;
693
694 for (; *name; name++)
695 hash = hash * 31 + (unsigned char) *name;
696
697 hash %= f->name_table.size;
698 oldhash = hash % (f->name_table.size / 2);
699 if (oldhash >= f->name_table.split)
700 return oldhash;
701 else
702 return hash;
703}
704
705static void unref_node(struct fuse *f, struct node *node);
706
707static void remerge_name(struct fuse *f)
708{
709 struct node_table *t = &f->name_table;
710 int iter;
711
712 if (t->split == 0)
713 node_table_reduce(t);
714
715 for (iter = 8; t->split > 0 && iter; iter--) {
716 struct node **upper;
717
718 t->split--;
719 upper = &t->array[t->split + t->size / 2];
720 if (*upper) {
721 struct node **nodep;
722
723 for (nodep = &t->array[t->split]; *nodep;
724 nodep = &(*nodep)->name_next);
725
726 *nodep = *upper;
727 *upper = NULL;
728 break;
729 }
730 }
731}
732
733static void unhash_name(struct fuse *f, struct node *node)
734{
735 if (node->name) {
736 size_t hash = name_hash(f, node->parent->nodeid, node->name);
737 struct node **nodep = &f->name_table.array[hash];
738
739 for (; *nodep != NULL; nodep = &(*nodep)->name_next)
740 if (*nodep == node) {
741 *nodep = node->name_next;
742 node->name_next = NULL;
743 unref_node(f, node->parent);
744 if (node->name != node->inline_name)
745 free(node->name);
746 node->name = NULL;
747 node->parent = NULL;
748 f->name_table.use--;
749
750 if (f->name_table.use < f->name_table.size / 4)
751 remerge_name(f);
752 return;
753 }
754 fuse_log(FUSE_LOG_ERR,
755 "fuse internal error: unable to unhash node: %llu\n",
756 (unsigned long long) node->nodeid);
757 abort();
758 }
759}
760
761static void rehash_name(struct fuse *f)
762{
763 struct node_table *t = &f->name_table;
764 struct node **nodep;
765 struct node **next;
766 size_t hash;
767
768 if (t->split == t->size / 2)
769 return;
770
771 hash = t->split;
772 t->split++;
773 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
774 struct node *node = *nodep;
775 size_t newhash = name_hash(f, node->parent->nodeid, node->name);
776
777 if (newhash != hash) {
778 next = nodep;
779 *nodep = node->name_next;
780 node->name_next = t->array[newhash];
781 t->array[newhash] = node;
782 } else {
783 next = &node->name_next;
784 }
785 }
786 if (t->split == t->size / 2)
787 node_table_resize(t);
788}
789
790static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
791 const char *name)
792{
793 size_t hash = name_hash(f, parentid, name);
794 struct node *parent = get_node(f, parentid);
795 if (strlen(name) < sizeof(node->inline_name)) {
796 strcpy(node->inline_name, name);
797 node->name = node->inline_name;
798 } else {
799 node->name = strdup(name);
800 if (node->name == NULL)
801 return -1;
802 }
803
804 parent->refctr ++;
805 node->parent = parent;
806 node->name_next = f->name_table.array[hash];
807 f->name_table.array[hash] = node;
808 f->name_table.use++;
809
810 if (f->name_table.use >= f->name_table.size / 2)
811 rehash_name(f);
812
813 return 0;
814}
815
816static void delete_node(struct fuse *f, struct node *node)
817{
818 if (f->conf.debug)
819 fuse_log(FUSE_LOG_DEBUG, "DELETE: %llu\n",
820 (unsigned long long) node->nodeid);
821
822 assert(node->treelock == 0);
823 unhash_name(f, node);
824 if (lru_enabled(f))
825 remove_node_lru(node);
826 unhash_id(f, node);
827 free_node(f, node);
828}
829
830static void unref_node(struct fuse *f, struct node *node)
831{
832 assert(node->refctr > 0);
833 node->refctr --;
834 if (!node->refctr)
835 delete_node(f, node);
836}
837
838static fuse_ino_t next_id(struct fuse *f)
839{
840 do {
841 f->ctr = (f->ctr + 1) & 0xffffffff;
842 if (!f->ctr)
843 f->generation ++;
844 } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
845 get_node_nocheck(f, f->ctr) != NULL);
846 return f->ctr;
847}
848
849static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
850 const char *name)
851{
852 size_t hash = name_hash(f, parent, name);
853 struct node *node;
854
855 for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
856 if (node->parent->nodeid == parent &&
857 strcmp(node->name, name) == 0)
858 return node;
859
860 return NULL;
861}
862
863static void inc_nlookup(struct node *node)
864{
865 if (!node->nlookup)
866 node->refctr++;
867 node->nlookup++;
868}
869
870static struct node *find_node(struct fuse *f, fuse_ino_t parent,
871 const char *name)
872{
873 struct node *node;
874
875 pthread_mutex_lock(&f->lock);
876 if (!name)
877 node = get_node(f, parent);
878 else
879 node = lookup_node(f, parent, name);
880 if (node == NULL) {
881 node = alloc_node(f);
882 if (node == NULL)
883 goto out_err;
884
885 node->nodeid = next_id(f);
886 node->generation = f->generation;
887 if (f->conf.remember)
888 inc_nlookup(node);
889
890 if (hash_name(f, node, parent, name) == -1) {
891 free_node(f, node);
892 node = NULL;
893 goto out_err;
894 }
895 hash_id(f, node);
896 if (lru_enabled(f)) {
897 struct node_lru *lnode = node_lru(node);
898 init_list_head(&lnode->lru);
899 }
900 } else if (lru_enabled(f) && node->nlookup == 1) {
901 remove_node_lru(node);
902 }
903 inc_nlookup(node);
904out_err:
905 pthread_mutex_unlock(&f->lock);
906 return node;
907}
908
909static int lookup_path_in_cache(struct fuse *f,
910 const char *path, fuse_ino_t *inop)
911{
912 char *tmp = strdup(path);
913 if (!tmp)
914 return -ENOMEM;
915
916 pthread_mutex_lock(&f->lock);
917 fuse_ino_t ino = FUSE_ROOT_ID;
918
919 int err = 0;
920 char *save_ptr;
921 char *path_element = strtok_r(tmp, "/", &save_ptr);
922 while (path_element != NULL) {
923 struct node *node = lookup_node(f, ino, path_element);
924 if (node == NULL) {
925 err = -ENOENT;
926 break;
927 }
928 ino = node->nodeid;
929 path_element = strtok_r(NULL, "/", &save_ptr);
930 }
931 pthread_mutex_unlock(&f->lock);
932 free(tmp);
933
934 if (!err)
935 *inop = ino;
936 return err;
937}
938
939static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name)
940{
941 size_t len = strlen(name);
942
943 if (s - len <= *buf) {
944 unsigned pathlen = *bufsize - (s - *buf);
945 unsigned newbufsize = *bufsize;
946 char *newbuf;
947
948 while (newbufsize < pathlen + len + 1) {
949 if (newbufsize >= 0x80000000)
950 newbufsize = 0xffffffff;
951 else
952 newbufsize *= 2;
953 }
954
955 newbuf = realloc(*buf, newbufsize);
956 if (newbuf == NULL)
957 return NULL;
958
959 *buf = newbuf;
960 s = newbuf + newbufsize - pathlen;
961 memmove(s, newbuf + *bufsize - pathlen, pathlen);
962 *bufsize = newbufsize;
963 }
964 s -= len;
965 memcpy(s, name, len);
966 s--;
967 *s = '/';
968
969 return s;
970}
971
972static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
973 struct node *end)
974{
975 struct node *node;
976
977 if (wnode) {
978 assert(wnode->treelock == TREELOCK_WRITE);
979 wnode->treelock = 0;
980 }
981
982 for (node = get_node(f, nodeid);
983 node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) {
984 assert(node->treelock != 0);
985 assert(node->treelock != TREELOCK_WAIT_OFFSET);
986 assert(node->treelock != TREELOCK_WRITE);
987 node->treelock--;
988 if (node->treelock == TREELOCK_WAIT_OFFSET)
989 node->treelock = 0;
990 }
991}
992
993static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
994 char **path, struct node **wnodep, bool need_lock)
995{
996 unsigned bufsize = 256;
997 char *buf;
998 char *s;
999 struct node *node;
1000 struct node *wnode = NULL;
1001 int err;
1002
1003 *path = NULL;
1004
1005 err = -ENOMEM;
1006 buf = malloc(bufsize);
1007 if (buf == NULL)
1008 goto out_err;
1009
1010 s = buf + bufsize - 1;
1011 *s = '\0';
1012
1013 if (name != NULL) {
1014 s = add_name(&buf, &bufsize, s, name);
1015 err = -ENOMEM;
1016 if (s == NULL)
1017 goto out_free;
1018 }
1019
1020 if (wnodep) {
1021 assert(need_lock);
1022 wnode = lookup_node(f, nodeid, name);
1023 if (wnode) {
1024 if (wnode->treelock != 0) {
1025 if (wnode->treelock > 0)
1026 wnode->treelock += TREELOCK_WAIT_OFFSET;
1027 err = -EAGAIN;
1028 goto out_free;
1029 }
1030 wnode->treelock = TREELOCK_WRITE;
1031 }
1032 }
1033
1034 for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
1035 node = node->parent) {
1036 err = -ESTALE;
1037 if (node->name == NULL || node->parent == NULL)
1038 goto out_unlock;
1039
1040 err = -ENOMEM;
1041 s = add_name(&buf, &bufsize, s, node->name);
1042 if (s == NULL)
1043 goto out_unlock;
1044
1045 if (need_lock) {
1046 err = -EAGAIN;
1047 if (node->treelock < 0)
1048 goto out_unlock;
1049
1050 node->treelock++;
1051 }
1052 }
1053
1054 if (s[0])
1055 memmove(buf, s, bufsize - (s - buf));
1056 else
1057 strcpy(buf, "/");
1058
1059 *path = buf;
1060 if (wnodep)
1061 *wnodep = wnode;
1062
1063 return 0;
1064
1065 out_unlock:
1066 if (need_lock)
1067 unlock_path(f, nodeid, wnode, node);
1068 out_free:
1069 free(buf);
1070
1071 out_err:
1072 return err;
1073}
1074
1075static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1076 fuse_ino_t nodeid2, const char *name2,
1077 char **path1, char **path2,
1078 struct node **wnode1, struct node **wnode2)
1079{
1080 int err;
1081
1082 /* FIXME: locking two paths needs deadlock checking */
1083 err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
1084 if (!err) {
1085 err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
1086 if (err) {
1087 struct node *wn1 = wnode1 ? *wnode1 : NULL;
1088
1089 unlock_path(f, nodeid1, wn1, NULL);
1090 free(*path1);
1091 }
1092 }
1093 return err;
1094}
1095
1096static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
1097{
1098 int err;
1099
1100 if (!qe->path1) {
1101 /* Just waiting for it to be unlocked */
1102 if (get_node(f, qe->nodeid1)->treelock == 0)
1103 pthread_cond_signal(&qe->cond);
1104
1105 return;
1106 }
1107
1108 if (qe->done)
1109 return; // Don't try to double-lock the element
1110
1111 if (!qe->path2) {
1112 err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
1113 qe->wnode1, true);
1114 } else {
1115 err = try_get_path2(f, qe->nodeid1, qe->name1, qe->nodeid2,
1116 qe->name2, qe->path1, qe->path2, qe->wnode1,
1117 qe->wnode2);
1118 }
1119
1120 if (err == -EAGAIN)
1121 return; /* keep trying */
1122
1123 qe->err = err;
1124 qe->done = true;
1125 pthread_cond_signal(&qe->cond);
1126}
1127
1128static void wake_up_queued(struct fuse *f)
1129{
1130 struct lock_queue_element *qe;
1131
1132 for (qe = f->lockq; qe != NULL; qe = qe->next)
1133 queue_element_wakeup(f, qe);
1134}
1135
1136static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid,
1137 const char *name, bool wr)
1138{
1139 if (f->conf.debug) {
1140 struct node *wnode = NULL;
1141
1142 if (wr)
1143 wnode = lookup_node(f, nodeid, name);
1144
1145 if (wnode) {
1146 fuse_log(FUSE_LOG_DEBUG, "%s %llu (w)\n",
1147 msg, (unsigned long long) wnode->nodeid);
1148 } else {
1149 fuse_log(FUSE_LOG_DEBUG, "%s %llu\n",
1150 msg, (unsigned long long) nodeid);
1151 }
1152 }
1153}
1154
1155static void queue_path(struct fuse *f, struct lock_queue_element *qe)
1156{
1157 struct lock_queue_element **qp;
1158
1159 qe->done = false;
1160 pthread_cond_init(&qe->cond, NULL);
1161 qe->next = NULL;
1162 for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
1163 *qp = qe;
1164}
1165
1166static void dequeue_path(struct fuse *f, struct lock_queue_element *qe)
1167{
1168 struct lock_queue_element **qp;
1169
1170 pthread_cond_destroy(&qe->cond);
1171 for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next);
1172 *qp = qe->next;
1173}
1174
1175static int wait_path(struct fuse *f, struct lock_queue_element *qe)
1176{
1177 queue_path(f, qe);
1178
1179 do {
1180 pthread_cond_wait(&qe->cond, &f->lock);
1181 } while (!qe->done);
1182
1183 dequeue_path(f, qe);
1184
1185 return qe->err;
1186}
1187
1188static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name,
1189 char **path, struct node **wnode)
1190{
1191 int err;
1192
1193 pthread_mutex_lock(&f->lock);
1194 err = try_get_path(f, nodeid, name, path, wnode, true);
1195 if (err == -EAGAIN) {
1196 struct lock_queue_element qe = {
1197 .nodeid1 = nodeid,
1198 .name1 = name,
1199 .path1 = path,
1200 .wnode1 = wnode,
1201 };
1202 debug_path(f, "QUEUE PATH", nodeid, name, !!wnode);
1203 err = wait_path(f, &qe);
1204 debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode);
1205 }
1206 pthread_mutex_unlock(&f->lock);
1207
1208 return err;
1209}
1210
1211static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
1212{
1213 return get_path_common(f, nodeid, NULL, path, NULL);
1214}
1215
1216static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
1217{
1218 int err = 0;
1219
1220 if (f->conf.nullpath_ok) {
1221 *path = NULL;
1222 } else {
1223 err = get_path_common(f, nodeid, NULL, path, NULL);
1224 if (err == -ESTALE)
1225 err = 0;
1226 }
1227
1228 return err;
1229}
1230
1231static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
1232 char **path)
1233{
1234 return get_path_common(f, nodeid, name, path, NULL);
1235}
1236
1237static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
1238 char **path, struct node **wnode)
1239{
1240 return get_path_common(f, nodeid, name, path, wnode);
1241}
1242
1243#if defined(__FreeBSD__)
1244#define CHECK_DIR_LOOP
1245#endif
1246
1247#if defined(CHECK_DIR_LOOP)
1248static int check_dir_loop(struct fuse *f,
1249 fuse_ino_t nodeid1, const char *name1,
1250 fuse_ino_t nodeid2, const char *name2)
1251{
1252 struct node *node, *node1, *node2;
1253 fuse_ino_t id1, id2;
1254
1255 node1 = lookup_node(f, nodeid1, name1);
1256 id1 = node1 ? node1->nodeid : nodeid1;
1257
1258 node2 = lookup_node(f, nodeid2, name2);
1259 id2 = node2 ? node2->nodeid : nodeid2;
1260
1261 for (node = get_node(f, id2); node->nodeid != FUSE_ROOT_ID;
1262 node = node->parent) {
1263 if (node->name == NULL || node->parent == NULL)
1264 break;
1265
1266 if (node->nodeid != id2 && node->nodeid == id1)
1267 return -EINVAL;
1268 }
1269
1270 if (node2)
1271 {
1272 for (node = get_node(f, id1); node->nodeid != FUSE_ROOT_ID;
1273 node = node->parent) {
1274 if (node->name == NULL || node->parent == NULL)
1275 break;
1276
1277 if (node->nodeid != id1 && node->nodeid == id2)
1278 return -ENOTEMPTY;
1279 }
1280 }
1281
1282 return 0;
1283}
1284#endif
1285
1286static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1287 fuse_ino_t nodeid2, const char *name2,
1288 char **path1, char **path2,
1289 struct node **wnode1, struct node **wnode2)
1290{
1291 int err;
1292
1293 pthread_mutex_lock(&f->lock);
1294
1295#if defined(CHECK_DIR_LOOP)
1296 if (name1)
1297 {
1298 // called during rename; perform dir loop check
1299 err = check_dir_loop(f, nodeid1, name1, nodeid2, name2);
1300 if (err)
1301 goto out_unlock;
1302 }
1303#endif
1304
1305 err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
1306 path1, path2, wnode1, wnode2);
1307 if (err == -EAGAIN) {
1308 struct lock_queue_element qe = {
1309 .nodeid1 = nodeid1,
1310 .name1 = name1,
1311 .path1 = path1,
1312 .wnode1 = wnode1,
1313 .nodeid2 = nodeid2,
1314 .name2 = name2,
1315 .path2 = path2,
1316 .wnode2 = wnode2,
1317 };
1318
1319 debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1);
1320 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1321 err = wait_path(f, &qe);
1322 debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
1323 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1324 }
1325
1326#if defined(CHECK_DIR_LOOP)
1327out_unlock:
1328#endif
1329 pthread_mutex_unlock(&f->lock);
1330
1331 return err;
1332}
1333
1334static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid,
1335 struct node *wnode, char *path)
1336{
1337 pthread_mutex_lock(&f->lock);
1338 unlock_path(f, nodeid, wnode, NULL);
1339 if (f->lockq)
1340 wake_up_queued(f);
1341 pthread_mutex_unlock(&f->lock);
1342 free(path);
1343}
1344
1345static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path)
1346{
1347 if (path)
1348 free_path_wrlock(f, nodeid, NULL, path);
1349}
1350
1351static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2,
1352 struct node *wnode1, struct node *wnode2,
1353 char *path1, char *path2)
1354{
1355 pthread_mutex_lock(&f->lock);
1356 unlock_path(f, nodeid1, wnode1, NULL);
1357 unlock_path(f, nodeid2, wnode2, NULL);
1358 wake_up_queued(f);
1359 pthread_mutex_unlock(&f->lock);
1360 free(path1);
1361 free(path2);
1362}
1363
1364static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
1365{
1366 struct node *node;
1367 if (nodeid == FUSE_ROOT_ID)
1368 return;
1369 pthread_mutex_lock(&f->lock);
1370 node = get_node(f, nodeid);
1371
1372 /*
1373 * Node may still be locked due to interrupt idiocy in open,
1374 * create and opendir
1375 */
1376 while (node->nlookup == nlookup && node->treelock) {
1377 struct lock_queue_element qe = {
1378 .nodeid1 = nodeid,
1379 };
1380
1381 debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false);
1382 queue_path(f, &qe);
1383
1384 do {
1385 pthread_cond_wait(&qe.cond, &f->lock);
1386 } while (node->nlookup == nlookup && node->treelock);
1387
1388 dequeue_path(f, &qe);
1389 debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false);
1390 }
1391
1392 assert(node->nlookup >= nlookup);
1393 node->nlookup -= nlookup;
1394 if (!node->nlookup) {
1395 unref_node(f, node);
1396 } else if (lru_enabled(f) && node->nlookup == 1) {
1397 set_forget_time(f, node);
1398 }
1399 pthread_mutex_unlock(&f->lock);
1400}
1401
1402static void unlink_node(struct fuse *f, struct node *node)
1403{
1404 if (f->conf.remember) {
1405 assert(node->nlookup > 1);
1406 node->nlookup--;
1407 }
1408 unhash_name(f, node);
1409}
1410
1411static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
1412{
1413 struct node *node;
1414
1415 pthread_mutex_lock(&f->lock);
1416 node = lookup_node(f, dir, name);
1417 if (node != NULL)
1418 unlink_node(f, node);
1419 pthread_mutex_unlock(&f->lock);
1420}
1421
1422static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1423 fuse_ino_t newdir, const char *newname, int hide)
1424{
1425 struct node *node;
1426 struct node *newnode;
1427 int err = 0;
1428
1429 pthread_mutex_lock(&f->lock);
1430 node = lookup_node(f, olddir, oldname);
1431 newnode = lookup_node(f, newdir, newname);
1432 if (node == NULL)
1433 goto out;
1434
1435 if (newnode != NULL) {
1436 if (hide) {
1437 fuse_log(FUSE_LOG_ERR, "fuse: hidden file got created during hiding\n");
1438 err = -EBUSY;
1439 goto out;
1440 }
1441 unlink_node(f, newnode);
1442 }
1443
1444 unhash_name(f, node);
1445 if (hash_name(f, node, newdir, newname) == -1) {
1446 err = -ENOMEM;
1447 goto out;
1448 }
1449
1450 if (hide)
1451 node->is_hidden = 1;
1452
1453out:
1454 pthread_mutex_unlock(&f->lock);
1455 return err;
1456}
1457
1458static int exchange_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1459 fuse_ino_t newdir, const char *newname)
1460{
1461 struct node *oldnode;
1462 struct node *newnode;
1463 int err;
1464
1465 pthread_mutex_lock(&f->lock);
1466 oldnode = lookup_node(f, olddir, oldname);
1467 newnode = lookup_node(f, newdir, newname);
1468
1469 if (oldnode)
1470 unhash_name(f, oldnode);
1471 if (newnode)
1472 unhash_name(f, newnode);
1473
1474 err = -ENOMEM;
1475 if (oldnode) {
1476 if (hash_name(f, oldnode, newdir, newname) == -1)
1477 goto out;
1478 }
1479 if (newnode) {
1480 if (hash_name(f, newnode, olddir, oldname) == -1)
1481 goto out;
1482 }
1483 err = 0;
1484out:
1485 pthread_mutex_unlock(&f->lock);
1486 return err;
1487}
1488
1489static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
1490{
1491 if (!f->conf.use_ino)
1492 stbuf->st_ino = nodeid;
1493 if (f->conf.set_mode) {
1494 if (f->conf.dmask && S_ISDIR(stbuf->st_mode))
1495 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1496 (0777 & ~f->conf.dmask);
1497 else if (f->conf.fmask)
1498 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1499 (0777 & ~f->conf.fmask);
1500 else
1501 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1502 (0777 & ~f->conf.umask);
1503 }
1504 if (f->conf.set_uid)
1505 stbuf->st_uid = f->conf.uid;
1506 if (f->conf.set_gid)
1507 stbuf->st_gid = f->conf.gid;
1508}
1509
1510static struct fuse *req_fuse(fuse_req_t req)
1511{
1512 return (struct fuse *) fuse_req_userdata(req);
1513}
1514
1515static void fuse_intr_sighandler(int sig)
1516{
1517 (void) sig;
1518 /* Nothing to do */
1519}
1520
1521struct fuse_intr_data {
1522 pthread_t id;
1523 pthread_cond_t cond;
1524 int finished;
1525};
1526
1527static void fuse_interrupt(fuse_req_t req, void *d_)
1528{
1529 struct fuse_intr_data *d = d_;
1530 struct fuse *f = req_fuse(req);
1531
1532 if (d->id == pthread_self())
1533 return;
1534
1535 pthread_mutex_lock(&f->lock);
1536 while (!d->finished) {
1537 struct timeval now;
1538 struct timespec timeout;
1539
1540 pthread_kill(d->id, f->conf.intr_signal);
1541 gettimeofday(&now, NULL);
1542 timeout.tv_sec = now.tv_sec + 1;
1543 timeout.tv_nsec = now.tv_usec * 1000;
1544 pthread_cond_timedwait(&d->cond, &f->lock, &timeout);
1545 }
1546 pthread_mutex_unlock(&f->lock);
1547}
1548
1549static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req,
1550 struct fuse_intr_data *d)
1551{
1552 pthread_mutex_lock(&f->lock);
1553 d->finished = 1;
1554 pthread_cond_broadcast(&d->cond);
1555 pthread_mutex_unlock(&f->lock);
1556 fuse_req_interrupt_func(req, NULL, NULL);
1557 pthread_cond_destroy(&d->cond);
1558}
1559
1560static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d)
1561{
1562 d->id = pthread_self();
1563 pthread_cond_init(&d->cond, NULL);
1564 d->finished = 0;
1565 fuse_req_interrupt_func(req, fuse_interrupt, d);
1566}
1567
1568static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req,
1569 struct fuse_intr_data *d)
1570{
1571 if (f->conf.intr)
1572 fuse_do_finish_interrupt(f, req, d);
1573}
1574
1575static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
1576 struct fuse_intr_data *d)
1577{
1578 if (f->conf.intr)
1579 fuse_do_prepare_interrupt(req, d);
1580}
1581
1582static const char* file_info_string(struct fuse_file_info *fi,
1583 char* buf, size_t len)
1584{
1585 if(fi == NULL)
1586 return "NULL";
1587 snprintf(buf, len, "%llu", (unsigned long long) fi->fh);
1588 return buf;
1589}
1590
1591int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
1592 struct fuse_file_info *fi)
1593{
1594 fuse_get_context()->private_data = fs->user_data;
1595 if (fs->op.getattr) {
1596 if (fs->debug) {
1597 char buf[10];
1598 fuse_log(FUSE_LOG_DEBUG, "getattr[%s] %s\n",
1599 file_info_string(fi, buf, sizeof(buf)),
1600 path);
1601 }
1602 return fs->op.getattr(path, buf, fi);
1603 } else {
1604 return -ENOSYS;
1605 }
1606}
1607
1608int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
1609 const char *newpath, unsigned int flags)
1610{
1611 fuse_get_context()->private_data = fs->user_data;
1612 if (fs->op.rename) {
1613 if (fs->debug)
1614 fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath,
1615 flags);
1616
1617 return fs->op.rename(oldpath, newpath, flags);
1618 } else {
1619 return -ENOSYS;
1620 }
1621}
1622
1623int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
1624{
1625 fuse_get_context()->private_data = fs->user_data;
1626 if (fs->op.unlink) {
1627 if (fs->debug)
1628 fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path);
1629
1630 return fs->op.unlink(path);
1631 } else {
1632 return -ENOSYS;
1633 }
1634}
1635
1636int fuse_fs_rmdir(struct fuse_fs *fs, const char *path)
1637{
1638 fuse_get_context()->private_data = fs->user_data;
1639 if (fs->op.rmdir) {
1640 if (fs->debug)
1641 fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path);
1642
1643 return fs->op.rmdir(path);
1644 } else {
1645 return -ENOSYS;
1646 }
1647}
1648
1649int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path)
1650{
1651 fuse_get_context()->private_data = fs->user_data;
1652 if (fs->op.symlink) {
1653 if (fs->debug)
1654 fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path);
1655
1656 return fs->op.symlink(linkname, path);
1657 } else {
1658 return -ENOSYS;
1659 }
1660}
1661
1662int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath)
1663{
1664 fuse_get_context()->private_data = fs->user_data;
1665 if (fs->op.link) {
1666 if (fs->debug)
1667 fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath);
1668
1669 return fs->op.link(oldpath, newpath);
1670 } else {
1671 return -ENOSYS;
1672 }
1673}
1674
1675int fuse_fs_release(struct fuse_fs *fs, const char *path,
1676 struct fuse_file_info *fi)
1677{
1678 fuse_get_context()->private_data = fs->user_data;
1679 if (fs->op.release) {
1680 if (fs->debug)
1681 fuse_log(FUSE_LOG_DEBUG, "release%s[%llu] flags: 0x%x\n",
1682 fi->flush ? "+flush" : "",
1683 (unsigned long long) fi->fh, fi->flags);
1684
1685 return fs->op.release(path, fi);
1686 } else {
1687 return 0;
1688 }
1689}
1690
1691int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
1692 struct fuse_file_info *fi)
1693{
1694 fuse_get_context()->private_data = fs->user_data;
1695 if (fs->op.opendir) {
1696 int err;
1697
1698 if (fs->debug)
1699 fuse_log(FUSE_LOG_DEBUG, "opendir flags: 0x%x %s\n", fi->flags,
1700 path);
1701
1702 err = fs->op.opendir(path, fi);
1703
1704 if (fs->debug && !err)
1705 fuse_log(FUSE_LOG_DEBUG, " opendir[%llu] flags: 0x%x %s\n",
1706 (unsigned long long) fi->fh, fi->flags, path);
1707
1708 return err;
1709 } else {
1710 return 0;
1711 }
1712}
1713
1714int fuse_fs_open(struct fuse_fs *fs, const char *path,
1715 struct fuse_file_info *fi)
1716{
1717 fuse_get_context()->private_data = fs->user_data;
1718 if (fs->op.open) {
1719 int err;
1720
1721 if (fs->debug)
1722 fuse_log(FUSE_LOG_DEBUG, "open flags: 0x%x %s\n", fi->flags,
1723 path);
1724
1725 err = fs->op.open(path, fi);
1726
1727 if (fs->debug && !err)
1728 fuse_log(FUSE_LOG_DEBUG, " open[%llu] flags: 0x%x %s\n",
1729 (unsigned long long) fi->fh, fi->flags, path);
1730
1731 return err;
1732 } else {
1733 return 0;
1734 }
1735}
1736
1737static void fuse_free_buf(struct fuse_bufvec *buf)
1738{
1739 if (buf != NULL) {
1740 size_t i;
1741
1742 for (i = 0; i < buf->count; i++)
1743 if (!(buf->buf[i].flags & FUSE_BUF_IS_FD))
1744 free(buf->buf[i].mem);
1745 free(buf);
1746 }
1747}
1748
1749int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
1750 struct fuse_bufvec **bufp, size_t size, off_t off,
1751 struct fuse_file_info *fi)
1752{
1753 fuse_get_context()->private_data = fs->user_data;
1754 if (fs->op.read || fs->op.read_buf) {
1755 int res;
1756
1757 if (fs->debug)
1758 fuse_log(FUSE_LOG_DEBUG,
1759 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1760 (unsigned long long) fi->fh,
1761 size, (unsigned long long) off, fi->flags);
1762
1763 if (fs->op.read_buf) {
1764 res = fs->op.read_buf(path, bufp, size, off, fi);
1765 } else {
1766 struct fuse_bufvec *buf;
1767 void *mem;
1768
1769 buf = malloc(sizeof(struct fuse_bufvec));
1770 if (buf == NULL)
1771 return -ENOMEM;
1772
1773 mem = malloc(size);
1774 if (mem == NULL) {
1775 free(buf);
1776 return -ENOMEM;
1777 }
1778 *buf = FUSE_BUFVEC_INIT(size);
1779 buf->buf[0].mem = mem;
1780 *bufp = buf;
1781
1782 res = fs->op.read(path, mem, size, off, fi);
1783 if (res >= 0)
1784 buf->buf[0].size = res;
1785 }
1786
1787 if (fs->debug && res >= 0)
1788 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %zu bytes from %llu\n",
1789 (unsigned long long) fi->fh,
1790 fuse_buf_size(*bufp),
1791 (unsigned long long) off);
1792 if (res >= 0 && fuse_buf_size(*bufp) > size)
1793 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1794
1795 if (res < 0)
1796 return res;
1797
1798 return 0;
1799 } else {
1800 return -ENOSYS;
1801 }
1802}
1803
1804int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
1805 off_t off, struct fuse_file_info *fi)
1806{
1807 fuse_get_context()->private_data = fs->user_data;
1808 if (fs->op.read || fs->op.read_buf) {
1809 int res;
1810
1811 if (fs->debug)
1812 fuse_log(FUSE_LOG_DEBUG,
1813 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1814 (unsigned long long) fi->fh,
1815 size, (unsigned long long) off, fi->flags);
1816
1817 if (fs->op.read_buf) {
1818 struct fuse_bufvec *buf = NULL;
1819
1820 res = fs->op.read_buf(path, &buf, size, off, fi);
1821 if (res == 0) {
1822 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
1823
1824 dst.buf[0].mem = mem;
1825 res = fuse_buf_copy(&dst, buf, 0);
1826 }
1827 fuse_free_buf(buf);
1828 } else {
1829 res = fs->op.read(path, mem, size, off, fi);
1830 }
1831
1832 if (fs->debug && res >= 0)
1833 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %u bytes from %llu\n",
1834 (unsigned long long) fi->fh,
1835 res,
1836 (unsigned long long) off);
1837 if (res >= 0 && res > (int) size)
1838 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1839
1840 return res;
1841 } else {
1842 return -ENOSYS;
1843 }
1844}
1845
1846int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
1847 struct fuse_bufvec *buf, off_t off,
1848 struct fuse_file_info *fi)
1849{
1850 fuse_get_context()->private_data = fs->user_data;
1851 if (fs->op.write_buf || fs->op.write) {
1852 int res;
1853 size_t size = fuse_buf_size(buf);
1854
1855 assert(buf->idx == 0 && buf->off == 0);
1856 if (fs->debug)
1857 fuse_log(FUSE_LOG_DEBUG,
1858 "write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
1859 fi->writepage ? "page" : "",
1860 (unsigned long long) fi->fh,
1861 size,
1862 (unsigned long long) off,
1863 fi->flags);
1864
1865 if (fs->op.write_buf) {
1866 res = fs->op.write_buf(path, buf, off, fi);
1867 } else {
1868 void *mem = NULL;
1869 struct fuse_buf *flatbuf;
1870 struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size);
1871
1872 if (buf->count == 1 &&
1873 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
1874 flatbuf = &buf->buf[0];
1875 } else {
1876 res = -ENOMEM;
1877 mem = malloc(size);
1878 if (mem == NULL)
1879 goto out;
1880
1881 tmp.buf[0].mem = mem;
1882 res = fuse_buf_copy(&tmp, buf, 0);
1883 if (res <= 0)
1884 goto out_free;
1885
1886 tmp.buf[0].size = res;
1887 flatbuf = &tmp.buf[0];
1888 }
1889
1890 res = fs->op.write(path, flatbuf->mem, flatbuf->size,
1891 off, fi);
1892out_free:
1893 free(mem);
1894 }
1895out:
1896 if (fs->debug && res >= 0)
1897 fuse_log(FUSE_LOG_DEBUG, " write%s[%llu] %u bytes to %llu\n",
1898 fi->writepage ? "page" : "",
1899 (unsigned long long) fi->fh, res,
1900 (unsigned long long) off);
1901 if (res > (int) size)
1902 fuse_log(FUSE_LOG_ERR, "fuse: wrote too many bytes\n");
1903
1904 return res;
1905 } else {
1906 return -ENOSYS;
1907 }
1908}
1909
1910int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem,
1911 size_t size, off_t off, struct fuse_file_info *fi)
1912{
1913 struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size);
1914
1915 bufv.buf[0].mem = (void *) mem;
1916
1917 return fuse_fs_write_buf(fs, path, &bufv, off, fi);
1918}
1919
1920int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
1921 struct fuse_file_info *fi)
1922{
1923 fuse_get_context()->private_data = fs->user_data;
1924 if (fs->op.fsync) {
1925 if (fs->debug)
1926 fuse_log(FUSE_LOG_DEBUG, "fsync[%llu] datasync: %i\n",
1927 (unsigned long long) fi->fh, datasync);
1928
1929 return fs->op.fsync(path, datasync, fi);
1930 } else {
1931 return -ENOSYS;
1932 }
1933}
1934
1935int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
1936 struct fuse_file_info *fi)
1937{
1938 fuse_get_context()->private_data = fs->user_data;
1939 if (fs->op.fsyncdir) {
1940 if (fs->debug)
1941 fuse_log(FUSE_LOG_DEBUG, "fsyncdir[%llu] datasync: %i\n",
1942 (unsigned long long) fi->fh, datasync);
1943
1944 return fs->op.fsyncdir(path, datasync, fi);
1945 } else {
1946 return -ENOSYS;
1947 }
1948}
1949
1950int fuse_fs_flush(struct fuse_fs *fs, const char *path,
1951 struct fuse_file_info *fi)
1952{
1953 fuse_get_context()->private_data = fs->user_data;
1954 if (fs->op.flush) {
1955 if (fs->debug)
1956 fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n",
1957 (unsigned long long) fi->fh);
1958
1959 return fs->op.flush(path, fi);
1960 } else {
1961 return -ENOSYS;
1962 }
1963}
1964
1965int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf)
1966{
1967 fuse_get_context()->private_data = fs->user_data;
1968 if (fs->op.statfs) {
1969 if (fs->debug)
1970 fuse_log(FUSE_LOG_DEBUG, "statfs %s\n", path);
1971
1972 return fs->op.statfs(path, buf);
1973 } else {
1974 buf->f_namemax = 255;
1975 buf->f_bsize = 512;
1976 return 0;
1977 }
1978}
1979
1980int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
1981 struct fuse_file_info *fi)
1982{
1983 fuse_get_context()->private_data = fs->user_data;
1984 if (fs->op.releasedir) {
1985 if (fs->debug)
1986 fuse_log(FUSE_LOG_DEBUG, "releasedir[%llu] flags: 0x%x\n",
1987 (unsigned long long) fi->fh, fi->flags);
1988
1989 return fs->op.releasedir(path, fi);
1990 } else {
1991 return 0;
1992 }
1993}
1994
1995int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
1996 fuse_fill_dir_t filler, off_t off,
1997 struct fuse_file_info *fi,
1998 enum fuse_readdir_flags flags)
1999{
2000 fuse_get_context()->private_data = fs->user_data;
2001 if (fs->op.readdir) {
2002 if (fs->debug) {
2003 fuse_log(FUSE_LOG_DEBUG, "readdir%s[%llu] from %llu\n",
2004 (flags & FUSE_READDIR_PLUS) ? "plus" : "",
2005 (unsigned long long) fi->fh,
2006 (unsigned long long) off);
2007 }
2008
2009 return fs->op.readdir(path, buf, filler, off, fi, flags);
2010 } else {
2011 return -ENOSYS;
2012 }
2013}
2014
2015int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
2016 struct fuse_file_info *fi)
2017{
2018 fuse_get_context()->private_data = fs->user_data;
2019 if (fs->op.create) {
2020 int err;
2021
2022 if (fs->debug)
2023 fuse_log(FUSE_LOG_DEBUG,
2024 "create flags: 0x%x %s 0%o umask=0%03o\n",
2025 fi->flags, path, mode,
2026 fuse_get_context()->umask);
2027
2028 err = fs->op.create(path, mode, fi);
2029
2030 if (fs->debug && !err)
2031 fuse_log(FUSE_LOG_DEBUG, " create[%llu] flags: 0x%x %s\n",
2032 (unsigned long long) fi->fh, fi->flags, path);
2033
2034 return err;
2035 } else {
2036 return -ENOSYS;
2037 }
2038}
2039
2040int fuse_fs_lock(struct fuse_fs *fs, const char *path,
2041 struct fuse_file_info *fi, int cmd, struct flock *lock)
2042{
2043 fuse_get_context()->private_data = fs->user_data;
2044 if (fs->op.lock) {
2045 if (fs->debug)
2046 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
2047 (unsigned long long) fi->fh,
2048 (cmd == F_GETLK ? "F_GETLK" :
2049 (cmd == F_SETLK ? "F_SETLK" :
2050 (cmd == F_SETLKW ? "F_SETLKW" : "???"))),
2051 (lock->l_type == F_RDLCK ? "F_RDLCK" :
2052 (lock->l_type == F_WRLCK ? "F_WRLCK" :
2053 (lock->l_type == F_UNLCK ? "F_UNLCK" :
2054 "???"))),
2055 (unsigned long long) lock->l_start,
2056 (unsigned long long) lock->l_len,
2057 (unsigned long long) lock->l_pid);
2058
2059 return fs->op.lock(path, fi, cmd, lock);
2060 } else {
2061 return -ENOSYS;
2062 }
2063}
2064
2065int fuse_fs_flock(struct fuse_fs *fs, const char *path,
2066 struct fuse_file_info *fi, int op)
2067{
2068 fuse_get_context()->private_data = fs->user_data;
2069 if (fs->op.flock) {
2070 if (fs->debug) {
2071 int xop = op & ~LOCK_NB;
2072
2073 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s%s\n",
2074 (unsigned long long) fi->fh,
2075 xop == LOCK_SH ? "LOCK_SH" :
2076 (xop == LOCK_EX ? "LOCK_EX" :
2077 (xop == LOCK_UN ? "LOCK_UN" : "???")),
2078 (op & LOCK_NB) ? "|LOCK_NB" : "");
2079 }
2080 return fs->op.flock(path, fi, op);
2081 } else {
2082 return -ENOSYS;
2083 }
2084}
2085
2086int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid,
2087 gid_t gid, struct fuse_file_info *fi)
2088{
2089 fuse_get_context()->private_data = fs->user_data;
2090 if (fs->op.chown) {
2091 if (fs->debug) {
2092 char buf[10];
2093 fuse_log(FUSE_LOG_DEBUG, "chown[%s] %s %lu %lu\n",
2094 file_info_string(fi, buf, sizeof(buf)),
2095 path, (unsigned long) uid, (unsigned long) gid);
2096 }
2097 return fs->op.chown(path, uid, gid, fi);
2098 } else {
2099 return -ENOSYS;
2100 }
2101}
2102
2103int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
2104 struct fuse_file_info *fi)
2105{
2106 fuse_get_context()->private_data = fs->user_data;
2107 if (fs->op.truncate) {
2108 if (fs->debug) {
2109 char buf[10];
2110 fuse_log(FUSE_LOG_DEBUG, "truncate[%s] %llu\n",
2111 file_info_string(fi, buf, sizeof(buf)),
2112 (unsigned long long) size);
2113 }
2114 return fs->op.truncate(path, size, fi);
2115 } else {
2116 return -ENOSYS;
2117 }
2118}
2119
2120int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
2121 const struct timespec tv[2], struct fuse_file_info *fi)
2122{
2123 fuse_get_context()->private_data = fs->user_data;
2124 if (fs->op.utimens) {
2125 if (fs->debug) {
2126 char buf[10];
2127 fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %li.%09lu %li.%09lu\n",
2128 file_info_string(fi, buf, sizeof(buf)),
2129 path, tv[0].tv_sec, tv[0].tv_nsec,
2130 tv[1].tv_sec, tv[1].tv_nsec);
2131 }
2132 return fs->op.utimens(path, tv, fi);
2133 } else {
2134 return -ENOSYS;
2135 }
2136}
2137
2138int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask)
2139{
2140 fuse_get_context()->private_data = fs->user_data;
2141 if (fs->op.access) {
2142 if (fs->debug)
2143 fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask);
2144
2145 return fs->op.access(path, mask);
2146 } else {
2147 return -ENOSYS;
2148 }
2149}
2150
2151int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
2152 size_t len)
2153{
2154 fuse_get_context()->private_data = fs->user_data;
2155 if (fs->op.readlink) {
2156 if (fs->debug)
2157 fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path,
2158 (unsigned long) len);
2159
2160 return fs->op.readlink(path, buf, len);
2161 } else {
2162 return -ENOSYS;
2163 }
2164}
2165
2166int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
2167 dev_t rdev)
2168{
2169 fuse_get_context()->private_data = fs->user_data;
2170 if (fs->op.mknod) {
2171 if (fs->debug)
2172 fuse_log(FUSE_LOG_DEBUG, "mknod %s 0%o 0x%llx umask=0%03o\n",
2173 path, mode, (unsigned long long) rdev,
2174 fuse_get_context()->umask);
2175
2176 return fs->op.mknod(path, mode, rdev);
2177 } else {
2178 return -ENOSYS;
2179 }
2180}
2181
2182int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode)
2183{
2184 fuse_get_context()->private_data = fs->user_data;
2185 if (fs->op.mkdir) {
2186 if (fs->debug)
2187 fuse_log(FUSE_LOG_DEBUG, "mkdir %s 0%o umask=0%03o\n",
2188 path, mode, fuse_get_context()->umask);
2189
2190 return fs->op.mkdir(path, mode);
2191 } else {
2192 return -ENOSYS;
2193 }
2194}
2195
2196int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
2197 const char *value, size_t size, int flags)
2198{
2199 fuse_get_context()->private_data = fs->user_data;
2200 if (fs->op.setxattr) {
2201 if (fs->debug)
2202 fuse_log(FUSE_LOG_DEBUG, "setxattr %s %s %lu 0x%x\n",
2203 path, name, (unsigned long) size, flags);
2204
2205 return fs->op.setxattr(path, name, value, size, flags);
2206 } else {
2207 return -ENOSYS;
2208 }
2209}
2210
2211int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
2212 char *value, size_t size)
2213{
2214 fuse_get_context()->private_data = fs->user_data;
2215 if (fs->op.getxattr) {
2216 if (fs->debug)
2217 fuse_log(FUSE_LOG_DEBUG, "getxattr %s %s %lu\n",
2218 path, name, (unsigned long) size);
2219
2220 return fs->op.getxattr(path, name, value, size);
2221 } else {
2222 return -ENOSYS;
2223 }
2224}
2225
2226int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
2227 size_t size)
2228{
2229 fuse_get_context()->private_data = fs->user_data;
2230 if (fs->op.listxattr) {
2231 if (fs->debug)
2232 fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n",
2233 path, (unsigned long) size);
2234
2235 return fs->op.listxattr(path, list, size);
2236 } else {
2237 return -ENOSYS;
2238 }
2239}
2240
2241int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
2242 uint64_t *idx)
2243{
2244 fuse_get_context()->private_data = fs->user_data;
2245 if (fs->op.bmap) {
2246 if (fs->debug)
2247 fuse_log(FUSE_LOG_DEBUG, "bmap %s blocksize: %lu index: %llu\n",
2248 path, (unsigned long) blocksize,
2249 (unsigned long long) *idx);
2250
2251 return fs->op.bmap(path, blocksize, idx);
2252 } else {
2253 return -ENOSYS;
2254 }
2255}
2256
2257int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
2258{
2259 fuse_get_context()->private_data = fs->user_data;
2260 if (fs->op.removexattr) {
2261 if (fs->debug)
2262 fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name);
2263
2264 return fs->op.removexattr(path, name);
2265 } else {
2266 return -ENOSYS;
2267 }
2268}
2269
2270int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
2271 void *arg, struct fuse_file_info *fi, unsigned int flags,
2272 void *data)
2273{
2274 fuse_get_context()->private_data = fs->user_data;
2275 if (fs->op.ioctl) {
2276 if (fs->debug)
2277 fuse_log(FUSE_LOG_DEBUG, "ioctl[%llu] 0x%x flags: 0x%x\n",
2278 (unsigned long long) fi->fh, cmd, flags);
2279
2280 return fs->op.ioctl(path, cmd, arg, fi, flags, data);
2281 } else
2282 return -ENOSYS;
2283}
2284
2285int fuse_fs_poll(struct fuse_fs *fs, const char *path,
2286 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
2287 unsigned *reventsp)
2288{
2289 fuse_get_context()->private_data = fs->user_data;
2290 if (fs->op.poll) {
2291 int res;
2292
2293 if (fs->debug)
2294 fuse_log(FUSE_LOG_DEBUG, "poll[%llu] ph: %p, events 0x%x\n",
2295 (unsigned long long) fi->fh, ph,
2296 fi->poll_events);
2297
2298 res = fs->op.poll(path, fi, ph, reventsp);
2299
2300 if (fs->debug && !res)
2301 fuse_log(FUSE_LOG_DEBUG, " poll[%llu] revents: 0x%x\n",
2302 (unsigned long long) fi->fh, *reventsp);
2303
2304 return res;
2305 } else
2306 return -ENOSYS;
2307}
2308
2309int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
2310 off_t offset, off_t length, struct fuse_file_info *fi)
2311{
2312 fuse_get_context()->private_data = fs->user_data;
2313 if (fs->op.fallocate) {
2314 if (fs->debug)
2315 fuse_log(FUSE_LOG_DEBUG, "fallocate %s mode %x, offset: %llu, length: %llu\n",
2316 path,
2317 mode,
2318 (unsigned long long) offset,
2319 (unsigned long long) length);
2320
2321 return fs->op.fallocate(path, mode, offset, length, fi);
2322 } else
2323 return -ENOSYS;
2324}
2325
2326ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
2327 struct fuse_file_info *fi_in, off_t off_in,
2328 const char *path_out,
2329 struct fuse_file_info *fi_out, off_t off_out,
2330 size_t len, int flags)
2331{
2332 fuse_get_context()->private_data = fs->user_data;
2333 if (fs->op.copy_file_range) {
2334 if (fs->debug)
2335 fuse_log(FUSE_LOG_DEBUG, "copy_file_range from %s:%llu to "
2336 "%s:%llu, length: %llu\n",
2337 path_in,
2338 (unsigned long long) off_in,
2339 path_out,
2340 (unsigned long long) off_out,
2341 (unsigned long long) len);
2342
2343 return fs->op.copy_file_range(path_in, fi_in, off_in, path_out,
2344 fi_out, off_out, len, flags);
2345 } else
2346 return -ENOSYS;
2347}
2348
2349off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
2350 struct fuse_file_info *fi)
2351{
2352 fuse_get_context()->private_data = fs->user_data;
2353 if (fs->op.lseek) {
2354 if (fs->debug) {
2355 char buf[10];
2356 fuse_log(FUSE_LOG_DEBUG, "lseek[%s] %llu %d\n",
2357 file_info_string(fi, buf, sizeof(buf)),
2358 (unsigned long long) off, whence);
2359 }
2360 return fs->op.lseek(path, off, whence, fi);
2361 } else {
2362 return -ENOSYS;
2363 }
2364}
2365
2366static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
2367{
2368 struct node *node;
2369 int isopen = 0;
2370 pthread_mutex_lock(&f->lock);
2371 node = lookup_node(f, dir, name);
2372 if (node && node->open_count > 0)
2373 isopen = 1;
2374 pthread_mutex_unlock(&f->lock);
2375 return isopen;
2376}
2377
2378static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname,
2379 char *newname, size_t bufsize)
2380{
2381 struct stat buf;
2382 struct node *node;
2383 struct node *newnode;
2384 char *newpath;
2385 int res;
2386 int failctr = 10;
2387
2388 do {
2389 pthread_mutex_lock(&f->lock);
2390 node = lookup_node(f, dir, oldname);
2391 if (node == NULL) {
2392 pthread_mutex_unlock(&f->lock);
2393 return NULL;
2394 }
2395 do {
2396 f->hidectr ++;
2397 snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
2398 (unsigned int) node->nodeid, f->hidectr);
2399 newnode = lookup_node(f, dir, newname);
2400 } while(newnode);
2401
2402 res = try_get_path(f, dir, newname, &newpath, NULL, false);
2403 pthread_mutex_unlock(&f->lock);
2404 if (res)
2405 break;
2406
2407 memset(&buf, 0, sizeof(buf));
2408 res = fuse_fs_getattr(f->fs, newpath, &buf, NULL);
2409 if (res == -ENOENT)
2410 break;
2411 free(newpath);
2412 newpath = NULL;
2413 } while(res == 0 && --failctr);
2414
2415 return newpath;
2416}
2417
2418static int hide_node(struct fuse *f, const char *oldpath,
2419 fuse_ino_t dir, const char *oldname)
2420{
2421 char newname[64];
2422 char *newpath;
2423 int err = -EBUSY;
2424
2425 newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
2426 if (newpath) {
2427 err = fuse_fs_rename(f->fs, oldpath, newpath, 0);
2428 if (!err)
2429 err = rename_node(f, dir, oldname, dir, newname, 1);
2430 free(newpath);
2431 }
2432 return err;
2433}
2434
2435static int mtime_eq(const struct stat *stbuf, const struct timespec *ts)
2436{
2437 return stbuf->st_mtime == ts->tv_sec &&
2438 ST_MTIM_NSEC(stbuf) == ts->tv_nsec;
2439}
2440
2441#ifndef CLOCK_MONOTONIC
2442#define CLOCK_MONOTONIC CLOCK_REALTIME
2443#endif
2444
2445static void curr_time(struct timespec *now)
2446{
2447 static clockid_t clockid = CLOCK_MONOTONIC;
2448 int res = clock_gettime(clockid, now);
2449 if (res == -1 && errno == EINVAL) {
2450 clockid = CLOCK_REALTIME;
2451 res = clock_gettime(clockid, now);
2452 }
2453 if (res == -1) {
2454 perror("fuse: clock_gettime");
2455 abort();
2456 }
2457}
2458
2459static void update_stat(struct node *node, const struct stat *stbuf)
2460{
2461 if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) ||
2462 stbuf->st_size != node->size))
2463 node->cache_valid = 0;
2464 node->mtime.tv_sec = stbuf->st_mtime;
2465 node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf);
2466 node->size = stbuf->st_size;
2467 curr_time(&node->stat_updated);
2468}
2469
2470static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name,
2471 struct fuse_entry_param *e)
2472{
2473 struct node *node;
2474
2475 node = find_node(f, nodeid, name);
2476 if (node == NULL)
2477 return -ENOMEM;
2478
2479 e->ino = node->nodeid;
2480 e->generation = node->generation;
2481 e->entry_timeout = f->conf.entry_timeout;
2482 e->attr_timeout = f->conf.attr_timeout;
2483 if (f->conf.auto_cache) {
2484 pthread_mutex_lock(&f->lock);
2485 update_stat(node, &e->attr);
2486 pthread_mutex_unlock(&f->lock);
2487 }
2488 set_stat(f, e->ino, &e->attr);
2489 return 0;
2490}
2491
2492static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
2493 const char *name, const char *path,
2494 struct fuse_entry_param *e, struct fuse_file_info *fi)
2495{
2496 int res;
2497
2498 memset(e, 0, sizeof(struct fuse_entry_param));
2499 res = fuse_fs_getattr(f->fs, path, &e->attr, fi);
2500 if (res == 0) {
2501 res = do_lookup(f, nodeid, name, e);
2502 if (res == 0 && f->conf.debug) {
2503 fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu\n",
2504 (unsigned long long) e->ino);
2505 }
2506 }
2507 return res;
2508}
2509
2510static struct fuse_context_i *fuse_get_context_internal(void)
2511{
2512 return (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
2513}
2514
2515static struct fuse_context_i *fuse_create_context(struct fuse *f)
2516{
2517 struct fuse_context_i *c = fuse_get_context_internal();
2518 if (c == NULL) {
2519 c = (struct fuse_context_i *)
2520 calloc(1, sizeof(struct fuse_context_i));
2521 if (c == NULL) {
2522 /* This is hard to deal with properly, so just
2523 abort. If memory is so low that the
2524 context cannot be allocated, there's not
2525 much hope for the filesystem anyway */
2526 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate thread specific data\n");
2527 abort();
2528 }
2529 pthread_setspecific(fuse_context_key, c);
2530 } else {
2531 memset(c, 0, sizeof(*c));
2532 }
2533 c->ctx.fuse = f;
2534
2535 return c;
2536}
2537
2538static void fuse_freecontext(void *data)
2539{
2540 free(data);
2541}
2542
2543static int fuse_create_context_key(void)
2544{
2545 int err = 0;
2546 pthread_mutex_lock(&fuse_context_lock);
2547 if (!fuse_context_ref) {
2548 err = pthread_key_create(&fuse_context_key, fuse_freecontext);
2549 if (err) {
2550 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
2551 strerror(err));
2552 pthread_mutex_unlock(&fuse_context_lock);
2553 return -1;
2554 }
2555 }
2556 fuse_context_ref++;
2557 pthread_mutex_unlock(&fuse_context_lock);
2558 return 0;
2559}
2560
2561static void fuse_delete_context_key(void)
2562{
2563 pthread_mutex_lock(&fuse_context_lock);
2564 fuse_context_ref--;
2565 if (!fuse_context_ref) {
2566 free(pthread_getspecific(fuse_context_key));
2567 pthread_key_delete(fuse_context_key);
2568 }
2569 pthread_mutex_unlock(&fuse_context_lock);
2570}
2571
2572static struct fuse *req_fuse_prepare(fuse_req_t req)
2573{
2574 struct fuse_context_i *c = fuse_create_context(req_fuse(req));
2575 const struct fuse_ctx *ctx = fuse_req_ctx(req);
2576 c->req = req;
2577 c->ctx.uid = ctx->uid;
2578 c->ctx.gid = ctx->gid;
2579 c->ctx.pid = ctx->pid;
2580 c->ctx.umask = ctx->umask;
2581 return c->ctx.fuse;
2582}
2583
2584static inline void reply_err(fuse_req_t req, int err)
2585{
2586 /* fuse_reply_err() uses non-negated errno values */
2587 fuse_reply_err(req, -err);
2588}
2589
2590static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
2591 int err)
2592{
2593 if (!err) {
2594 struct fuse *f = req_fuse(req);
2595 if (fuse_reply_entry(req, e) == -ENOENT) {
2596 /* Skip forget for negative result */
2597 if (e->ino != 0)
2598 forget_node(f, e->ino, 1);
2599 }
2600 } else
2601 reply_err(req, err);
2602}
2603
2604void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
2605 struct fuse_config *cfg)
2606{
2607 fuse_get_context()->private_data = fs->user_data;
2608 if (!fs->op.write_buf)
2610 if (!fs->op.lock)
2612 if (!fs->op.flock)
2614 if (fs->op.init)
2615 fs->user_data = fs->op.init(conn, cfg);
2616}
2617
2618static int fuse_init_intr_signal(int signum, int *installed);
2619
2620static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
2621{
2622 struct fuse *f = (struct fuse *) data;
2623
2624 fuse_create_context(f);
2626 fuse_fs_init(f->fs, conn, &f->conf);
2627
2628 if (f->conf.intr) {
2629 if (fuse_init_intr_signal(f->conf.intr_signal,
2630 &f->intr_installed) == -1)
2631 fuse_log(FUSE_LOG_ERR, "fuse: failed to init interrupt signal\n");
2632 } else {
2633 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
2634 conn->no_interrupt = 1;
2635 }
2636}
2637
2638void fuse_fs_destroy(struct fuse_fs *fs)
2639{
2640 fuse_get_context()->private_data = fs->user_data;
2641 if (fs->op.destroy)
2642 fs->op.destroy(fs->user_data);
2643}
2644
2645static void fuse_lib_destroy(void *data)
2646{
2647 struct fuse *f = (struct fuse *) data;
2648
2649 fuse_create_context(f);
2650 fuse_fs_destroy(f->fs);
2651}
2652
2653static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
2654 const char *name)
2655{
2656 struct fuse *f = req_fuse_prepare(req);
2657 struct fuse_entry_param e;
2658 char *path;
2659 int err;
2660 struct node *dot = NULL;
2661
2662 if (name[0] == '.') {
2663 int len = strlen(name);
2664
2665 if (len == 1 || (name[1] == '.' && len == 2)) {
2666 pthread_mutex_lock(&f->lock);
2667 if (len == 1) {
2668 if (f->conf.debug)
2669 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOT\n");
2670 dot = get_node_nocheck(f, parent);
2671 if (dot == NULL) {
2672 pthread_mutex_unlock(&f->lock);
2673 reply_entry(req, &e, -ESTALE);
2674 return;
2675 }
2676 dot->refctr++;
2677 } else {
2678 if (f->conf.debug)
2679 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOTDOT\n");
2680 parent = get_node(f, parent)->parent->nodeid;
2681 }
2682 pthread_mutex_unlock(&f->lock);
2683 name = NULL;
2684 }
2685 }
2686
2687 err = get_path_name(f, parent, name, &path);
2688 if (!err) {
2689 struct fuse_intr_data d;
2690 if (f->conf.debug)
2691 fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path);
2692 fuse_prepare_interrupt(f, req, &d);
2693 err = lookup_path(f, parent, name, path, &e, NULL);
2694 if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
2695 e.ino = 0;
2696 e.entry_timeout = f->conf.negative_timeout;
2697 err = 0;
2698 }
2699 fuse_finish_interrupt(f, req, &d);
2700 free_path(f, parent, path);
2701 }
2702 if (dot) {
2703 pthread_mutex_lock(&f->lock);
2704 unref_node(f, dot);
2705 pthread_mutex_unlock(&f->lock);
2706 }
2707 reply_entry(req, &e, err);
2708}
2709
2710static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
2711{
2712 if (f->conf.debug)
2713 fuse_log(FUSE_LOG_DEBUG, "FORGET %llu/%llu\n", (unsigned long long)ino,
2714 (unsigned long long) nlookup);
2715 forget_node(f, ino, nlookup);
2716}
2717
2718static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
2719{
2720 do_forget(req_fuse(req), ino, nlookup);
2721 fuse_reply_none(req);
2722}
2723
2724static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
2725 struct fuse_forget_data *forgets)
2726{
2727 struct fuse *f = req_fuse(req);
2728 size_t i;
2729
2730 for (i = 0; i < count; i++)
2731 do_forget(f, forgets[i].ino, forgets[i].nlookup);
2732
2733 fuse_reply_none(req);
2734}
2735
2736
2737static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
2738 struct fuse_file_info *fi)
2739{
2740 struct fuse *f = req_fuse_prepare(req);
2741 struct stat buf;
2742 char *path;
2743 int err;
2744
2745 memset(&buf, 0, sizeof(buf));
2746
2747 if (fi != NULL)
2748 err = get_path_nullok(f, ino, &path);
2749 else
2750 err = get_path(f, ino, &path);
2751 if (!err) {
2752 struct fuse_intr_data d;
2753 fuse_prepare_interrupt(f, req, &d);
2754 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2755 fuse_finish_interrupt(f, req, &d);
2756 free_path(f, ino, path);
2757 }
2758 if (!err) {
2759 struct node *node;
2760
2761 pthread_mutex_lock(&f->lock);
2762 node = get_node(f, ino);
2763 if (node->is_hidden && buf.st_nlink > 0)
2764 buf.st_nlink--;
2765 if (f->conf.auto_cache)
2766 update_stat(node, &buf);
2767 pthread_mutex_unlock(&f->lock);
2768 set_stat(f, ino, &buf);
2769 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2770 } else
2771 reply_err(req, err);
2772}
2773
2774int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
2775 struct fuse_file_info *fi)
2776{
2777 fuse_get_context()->private_data = fs->user_data;
2778 if (fs->op.chmod) {
2779 if (fs->debug) {
2780 char buf[10];
2781 fuse_log(FUSE_LOG_DEBUG, "chmod[%s] %s %llo\n",
2782 file_info_string(fi, buf, sizeof(buf)),
2783 path, (unsigned long long) mode);
2784 }
2785 return fs->op.chmod(path, mode, fi);
2786 }
2787 else
2788 return -ENOSYS;
2789}
2790
2791static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
2792 int valid, struct fuse_file_info *fi)
2793{
2794 struct fuse *f = req_fuse_prepare(req);
2795 struct stat buf;
2796 char *path;
2797 int err;
2798
2799 memset(&buf, 0, sizeof(buf));
2800 if (fi != NULL)
2801 err = get_path_nullok(f, ino, &path);
2802 else
2803 err = get_path(f, ino, &path);
2804 if (!err) {
2805 struct fuse_intr_data d;
2806 fuse_prepare_interrupt(f, req, &d);
2807 err = 0;
2808 if (!err && (valid & FUSE_SET_ATTR_MODE))
2809 err = fuse_fs_chmod(f->fs, path, attr->st_mode, fi);
2810 if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) {
2811 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
2812 attr->st_uid : (uid_t) -1;
2813 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
2814 attr->st_gid : (gid_t) -1;
2815 err = fuse_fs_chown(f->fs, path, uid, gid, fi);
2816 }
2817 if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
2818 err = fuse_fs_truncate(f->fs, path,
2819 attr->st_size, fi);
2820 }
2821#ifdef HAVE_UTIMENSAT
2822 if (!err &&
2823 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
2824 struct timespec tv[2];
2825
2826 tv[0].tv_sec = 0;
2827 tv[1].tv_sec = 0;
2828 tv[0].tv_nsec = UTIME_OMIT;
2829 tv[1].tv_nsec = UTIME_OMIT;
2830
2831 if (valid & FUSE_SET_ATTR_ATIME_NOW)
2832 tv[0].tv_nsec = UTIME_NOW;
2833 else if (valid & FUSE_SET_ATTR_ATIME)
2834 tv[0] = attr->st_atim;
2835
2836 if (valid & FUSE_SET_ATTR_MTIME_NOW)
2837 tv[1].tv_nsec = UTIME_NOW;
2838 else if (valid & FUSE_SET_ATTR_MTIME)
2839 tv[1] = attr->st_mtim;
2840
2841 err = fuse_fs_utimens(f->fs, path, tv, fi);
2842 } else
2843#endif
2844 if (!err &&
2845 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
2846 (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
2847 struct timespec tv[2];
2848 tv[0].tv_sec = attr->st_atime;
2849 tv[0].tv_nsec = ST_ATIM_NSEC(attr);
2850 tv[1].tv_sec = attr->st_mtime;
2851 tv[1].tv_nsec = ST_MTIM_NSEC(attr);
2852 err = fuse_fs_utimens(f->fs, path, tv, fi);
2853 }
2854 if (!err) {
2855 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2856 }
2857 fuse_finish_interrupt(f, req, &d);
2858 free_path(f, ino, path);
2859 }
2860 if (!err) {
2861 if (f->conf.auto_cache) {
2862 pthread_mutex_lock(&f->lock);
2863 update_stat(get_node(f, ino), &buf);
2864 pthread_mutex_unlock(&f->lock);
2865 }
2866 set_stat(f, ino, &buf);
2867 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2868 } else
2869 reply_err(req, err);
2870}
2871
2872static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask)
2873{
2874 struct fuse *f = req_fuse_prepare(req);
2875 char *path;
2876 int err;
2877
2878 err = get_path(f, ino, &path);
2879 if (!err) {
2880 struct fuse_intr_data d;
2881
2882 fuse_prepare_interrupt(f, req, &d);
2883 err = fuse_fs_access(f->fs, path, mask);
2884 fuse_finish_interrupt(f, req, &d);
2885 free_path(f, ino, path);
2886 }
2887 reply_err(req, err);
2888}
2889
2890static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino)
2891{
2892 struct fuse *f = req_fuse_prepare(req);
2893 char linkname[PATH_MAX + 1];
2894 char *path;
2895 int err;
2896
2897 err = get_path(f, ino, &path);
2898 if (!err) {
2899 struct fuse_intr_data d;
2900 fuse_prepare_interrupt(f, req, &d);
2901 err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname));
2902 fuse_finish_interrupt(f, req, &d);
2903 free_path(f, ino, path);
2904 }
2905 if (!err) {
2906 linkname[PATH_MAX] = '\0';
2907 fuse_reply_readlink(req, linkname);
2908 } else
2909 reply_err(req, err);
2910}
2911
2912static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
2913 mode_t mode, dev_t rdev)
2914{
2915 struct fuse *f = req_fuse_prepare(req);
2916 struct fuse_entry_param e;
2917 char *path;
2918 int err;
2919
2920 err = get_path_name(f, parent, name, &path);
2921 if (!err) {
2922 struct fuse_intr_data d;
2923
2924 fuse_prepare_interrupt(f, req, &d);
2925 err = -ENOSYS;
2926 if (S_ISREG(mode)) {
2927 struct fuse_file_info fi;
2928
2929 memset(&fi, 0, sizeof(fi));
2930 fi.flags = O_CREAT | O_EXCL | O_WRONLY;
2931 err = fuse_fs_create(f->fs, path, mode, &fi);
2932 if (!err) {
2933 err = lookup_path(f, parent, name, path, &e,
2934 &fi);
2935 fuse_fs_release(f->fs, path, &fi);
2936 }
2937 }
2938 if (err == -ENOSYS) {
2939 err = fuse_fs_mknod(f->fs, path, mode, rdev);
2940 if (!err)
2941 err = lookup_path(f, parent, name, path, &e,
2942 NULL);
2943 }
2944 fuse_finish_interrupt(f, req, &d);
2945 free_path(f, parent, path);
2946 }
2947 reply_entry(req, &e, err);
2948}
2949
2950static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
2951 mode_t mode)
2952{
2953 struct fuse *f = req_fuse_prepare(req);
2954 struct fuse_entry_param e;
2955 char *path;
2956 int err;
2957
2958 err = get_path_name(f, parent, name, &path);
2959 if (!err) {
2960 struct fuse_intr_data d;
2961
2962 fuse_prepare_interrupt(f, req, &d);
2963 err = fuse_fs_mkdir(f->fs, path, mode);
2964 if (!err)
2965 err = lookup_path(f, parent, name, path, &e, NULL);
2966 fuse_finish_interrupt(f, req, &d);
2967 free_path(f, parent, path);
2968 }
2969 reply_entry(req, &e, err);
2970}
2971
2972static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
2973 const char *name)
2974{
2975 struct fuse *f = req_fuse_prepare(req);
2976 struct node *wnode;
2977 char *path;
2978 int err;
2979
2980 err = get_path_wrlock(f, parent, name, &path, &wnode);
2981 if (!err) {
2982 struct fuse_intr_data d;
2983
2984 fuse_prepare_interrupt(f, req, &d);
2985 if (!f->conf.hard_remove && is_open(f, parent, name)) {
2986 err = hide_node(f, path, parent, name);
2987 if (!err) {
2988 /* we have hidden the node so now check again under a lock in case it is not used any more */
2989 if (!is_open(f, parent, wnode->name)) {
2990 char *unlinkpath;
2991
2992 /* get the hidden file path, to unlink it */
2993 if (try_get_path(f, wnode->nodeid, NULL, &unlinkpath, NULL, false) == 0) {
2994 err = fuse_fs_unlink(f->fs, unlinkpath);
2995 if (!err)
2996 remove_node(f, parent, wnode->name);
2997 free(unlinkpath);
2998 }
2999 }
3000 }
3001 } else {
3002 err = fuse_fs_unlink(f->fs, path);
3003 if (!err)
3004 remove_node(f, parent, name);
3005 }
3006 fuse_finish_interrupt(f, req, &d);
3007 free_path_wrlock(f, parent, wnode, path);
3008 }
3009 reply_err(req, err);
3010}
3011
3012static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
3013{
3014 struct fuse *f = req_fuse_prepare(req);
3015 struct node *wnode;
3016 char *path;
3017 int err;
3018
3019 err = get_path_wrlock(f, parent, name, &path, &wnode);
3020 if (!err) {
3021 struct fuse_intr_data d;
3022
3023 fuse_prepare_interrupt(f, req, &d);
3024 err = fuse_fs_rmdir(f->fs, path);
3025 fuse_finish_interrupt(f, req, &d);
3026 if (!err)
3027 remove_node(f, parent, name);
3028 free_path_wrlock(f, parent, wnode, path);
3029 }
3030 reply_err(req, err);
3031}
3032
3033static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
3034 fuse_ino_t parent, const char *name)
3035{
3036 struct fuse *f = req_fuse_prepare(req);
3037 struct fuse_entry_param e;
3038 char *path;
3039 int err;
3040
3041 err = get_path_name(f, parent, name, &path);
3042 if (!err) {
3043 struct fuse_intr_data d;
3044
3045 fuse_prepare_interrupt(f, req, &d);
3046 err = fuse_fs_symlink(f->fs, linkname, path);
3047 if (!err)
3048 err = lookup_path(f, parent, name, path, &e, NULL);
3049 fuse_finish_interrupt(f, req, &d);
3050 free_path(f, parent, path);
3051 }
3052 reply_entry(req, &e, err);
3053}
3054
3055static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
3056 const char *oldname, fuse_ino_t newdir,
3057 const char *newname, unsigned int flags)
3058{
3059 struct fuse *f = req_fuse_prepare(req);
3060 char *oldpath;
3061 char *newpath;
3062 struct node *wnode1;
3063 struct node *wnode2;
3064 int err;
3065
3066 err = get_path2(f, olddir, oldname, newdir, newname,
3067 &oldpath, &newpath, &wnode1, &wnode2);
3068 if (!err) {
3069 struct fuse_intr_data d;
3070 err = 0;
3071 fuse_prepare_interrupt(f, req, &d);
3072 if (!f->conf.hard_remove && !(flags & RENAME_EXCHANGE) &&
3073 is_open(f, newdir, newname))
3074 err = hide_node(f, newpath, newdir, newname);
3075 if (!err) {
3076 err = fuse_fs_rename(f->fs, oldpath, newpath, flags);
3077 if (!err) {
3078 if (flags & RENAME_EXCHANGE) {
3079 err = exchange_node(f, olddir, oldname,
3080 newdir, newname);
3081 } else {
3082 err = rename_node(f, olddir, oldname,
3083 newdir, newname, 0);
3084 }
3085 }
3086 }
3087 fuse_finish_interrupt(f, req, &d);
3088 free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
3089 }
3090 reply_err(req, err);
3091}
3092
3093static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
3094 const char *newname)
3095{
3096 struct fuse *f = req_fuse_prepare(req);
3097 struct fuse_entry_param e;
3098 char *oldpath;
3099 char *newpath;
3100 int err;
3101
3102 err = get_path2(f, ino, NULL, newparent, newname,
3103 &oldpath, &newpath, NULL, NULL);
3104 if (!err) {
3105 struct fuse_intr_data d;
3106
3107 fuse_prepare_interrupt(f, req, &d);
3108 err = fuse_fs_link(f->fs, oldpath, newpath);
3109 if (!err)
3110 err = lookup_path(f, newparent, newname, newpath,
3111 &e, NULL);
3112 fuse_finish_interrupt(f, req, &d);
3113 free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath);
3114 }
3115 reply_entry(req, &e, err);
3116}
3117
3118static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
3119 struct fuse_file_info *fi)
3120{
3121 struct node *node;
3122 int unlink_hidden = 0;
3123
3124 fuse_fs_release(f->fs, path, fi);
3125
3126 pthread_mutex_lock(&f->lock);
3127 node = get_node(f, ino);
3128 assert(node->open_count > 0);
3129 --node->open_count;
3130 if (node->is_hidden && !node->open_count) {
3131 unlink_hidden = 1;
3132 node->is_hidden = 0;
3133 }
3134 pthread_mutex_unlock(&f->lock);
3135
3136 if(unlink_hidden) {
3137 if (path) {
3138 fuse_fs_unlink(f->fs, path);
3139 } else if (f->conf.nullpath_ok) {
3140 char *unlinkpath;
3141
3142 if (get_path(f, ino, &unlinkpath) == 0)
3143 fuse_fs_unlink(f->fs, unlinkpath);
3144
3145 free_path(f, ino, unlinkpath);
3146 }
3147 }
3148}
3149
3150static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
3151 const char *name, mode_t mode,
3152 struct fuse_file_info *fi)
3153{
3154 struct fuse *f = req_fuse_prepare(req);
3155 struct fuse_intr_data d;
3156 struct fuse_entry_param e;
3157 char *path;
3158 int err;
3159
3160 err = get_path_name(f, parent, name, &path);
3161 if (!err) {
3162 fuse_prepare_interrupt(f, req, &d);
3163 err = fuse_fs_create(f->fs, path, mode, fi);
3164 if (!err) {
3165 err = lookup_path(f, parent, name, path, &e, fi);
3166 if (err)
3167 fuse_fs_release(f->fs, path, fi);
3168 else if (!S_ISREG(e.attr.st_mode)) {
3169 err = -EIO;
3170 fuse_fs_release(f->fs, path, fi);
3171 forget_node(f, e.ino, 1);
3172 } else {
3173 if (f->conf.direct_io)
3174 fi->direct_io = 1;
3175 if (f->conf.kernel_cache)
3176 fi->keep_cache = 1;
3177 if (fi->direct_io &&
3178 f->conf.parallel_direct_writes)
3179 fi->parallel_direct_writes = 1;
3180 }
3181 }
3182 fuse_finish_interrupt(f, req, &d);
3183 }
3184 if (!err) {
3185 pthread_mutex_lock(&f->lock);
3186 get_node(f, e.ino)->open_count++;
3187 pthread_mutex_unlock(&f->lock);
3188 if (fuse_reply_create(req, &e, fi) == -ENOENT) {
3189 /* The open syscall was interrupted, so it
3190 must be cancelled */
3191 fuse_do_release(f, e.ino, path, fi);
3192 forget_node(f, e.ino, 1);
3193 }
3194 } else {
3195 reply_err(req, err);
3196 }
3197
3198 free_path(f, parent, path);
3199}
3200
3201static double diff_timespec(const struct timespec *t1,
3202 const struct timespec *t2)
3203{
3204 return (t1->tv_sec - t2->tv_sec) +
3205 ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0;
3206}
3207
3208static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path,
3209 struct fuse_file_info *fi)
3210{
3211 struct node *node;
3212
3213 pthread_mutex_lock(&f->lock);
3214 node = get_node(f, ino);
3215 if (node->cache_valid) {
3216 struct timespec now;
3217
3218 curr_time(&now);
3219 if (diff_timespec(&now, &node->stat_updated) >
3220 f->conf.ac_attr_timeout) {
3221 struct stat stbuf;
3222 int err;
3223 pthread_mutex_unlock(&f->lock);
3224 err = fuse_fs_getattr(f->fs, path, &stbuf, fi);
3225 pthread_mutex_lock(&f->lock);
3226 if (!err)
3227 update_stat(node, &stbuf);
3228 else
3229 node->cache_valid = 0;
3230 }
3231 }
3232 if (node->cache_valid)
3233 fi->keep_cache = 1;
3234
3235 node->cache_valid = 1;
3236 pthread_mutex_unlock(&f->lock);
3237}
3238
3239static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
3240 struct fuse_file_info *fi)
3241{
3242 struct fuse *f = req_fuse_prepare(req);
3243 struct fuse_intr_data d;
3244 char *path;
3245 int err;
3246
3247 err = get_path(f, ino, &path);
3248 if (!err) {
3249 fuse_prepare_interrupt(f, req, &d);
3250 err = fuse_fs_open(f->fs, path, fi);
3251 if (!err) {
3252 if (f->conf.direct_io)
3253 fi->direct_io = 1;
3254 if (f->conf.kernel_cache)
3255 fi->keep_cache = 1;
3256
3257 if (f->conf.auto_cache)
3258 open_auto_cache(f, ino, path, fi);
3259
3260 if (f->conf.no_rofd_flush &&
3261 (fi->flags & O_ACCMODE) == O_RDONLY)
3262 fi->noflush = 1;
3263
3264 if (fi->direct_io && f->conf.parallel_direct_writes)
3265 fi->parallel_direct_writes = 1;
3266
3267 }
3268 fuse_finish_interrupt(f, req, &d);
3269 }
3270 if (!err) {
3271 pthread_mutex_lock(&f->lock);
3272 get_node(f, ino)->open_count++;
3273 pthread_mutex_unlock(&f->lock);
3274 if (fuse_reply_open(req, fi) == -ENOENT) {
3275 /* The open syscall was interrupted, so it
3276 must be cancelled */
3277 fuse_do_release(f, ino, path, fi);
3278 }
3279 } else
3280 reply_err(req, err);
3281
3282 free_path(f, ino, path);
3283}
3284
3285static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
3286 off_t off, struct fuse_file_info *fi)
3287{
3288 struct fuse *f = req_fuse_prepare(req);
3289 struct fuse_bufvec *buf = NULL;
3290 char *path;
3291 int res;
3292
3293 res = get_path_nullok(f, ino, &path);
3294 if (res == 0) {
3295 struct fuse_intr_data d;
3296
3297 fuse_prepare_interrupt(f, req, &d);
3298 res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
3299 fuse_finish_interrupt(f, req, &d);
3300 free_path(f, ino, path);
3301 }
3302
3303 if (res == 0)
3305 else
3306 reply_err(req, res);
3307
3308 fuse_free_buf(buf);
3309}
3310
3311static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
3312 struct fuse_bufvec *buf, off_t off,
3313 struct fuse_file_info *fi)
3314{
3315 struct fuse *f = req_fuse_prepare(req);
3316 char *path;
3317 int res;
3318
3319 res = get_path_nullok(f, ino, &path);
3320 if (res == 0) {
3321 struct fuse_intr_data d;
3322
3323 fuse_prepare_interrupt(f, req, &d);
3324 res = fuse_fs_write_buf(f->fs, path, buf, off, fi);
3325 fuse_finish_interrupt(f, req, &d);
3326 free_path(f, ino, path);
3327 }
3328
3329 if (res >= 0)
3330 fuse_reply_write(req, res);
3331 else
3332 reply_err(req, res);
3333}
3334
3335static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
3336 struct fuse_file_info *fi)
3337{
3338 struct fuse *f = req_fuse_prepare(req);
3339 char *path;
3340 int err;
3341
3342 err = get_path_nullok(f, ino, &path);
3343 if (!err) {
3344 struct fuse_intr_data d;
3345
3346 fuse_prepare_interrupt(f, req, &d);
3347 err = fuse_fs_fsync(f->fs, path, datasync, fi);
3348 fuse_finish_interrupt(f, req, &d);
3349 free_path(f, ino, path);
3350 }
3351 reply_err(req, err);
3352}
3353
3354static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi,
3355 struct fuse_file_info *fi)
3356{
3357 struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh;
3358 memset(fi, 0, sizeof(struct fuse_file_info));
3359 fi->fh = dh->fh;
3360 return dh;
3361}
3362
3363static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
3364 struct fuse_file_info *llfi)
3365{
3366 struct fuse *f = req_fuse_prepare(req);
3367 struct fuse_intr_data d;
3368 struct fuse_dh *dh;
3369 struct fuse_file_info fi;
3370 char *path;
3371 int err;
3372
3373 dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
3374 if (dh == NULL) {
3375 reply_err(req, -ENOMEM);
3376 return;
3377 }
3378 memset(dh, 0, sizeof(struct fuse_dh));
3379 dh->fuse = f;
3380 dh->contents = NULL;
3381 dh->first = NULL;
3382 dh->len = 0;
3383 dh->filled = 0;
3384 dh->nodeid = ino;
3385 pthread_mutex_init(&dh->lock, NULL);
3386
3387 llfi->fh = (uintptr_t) dh;
3388
3389 memset(&fi, 0, sizeof(fi));
3390 fi.flags = llfi->flags;
3391
3392 err = get_path(f, ino, &path);
3393 if (!err) {
3394 fuse_prepare_interrupt(f, req, &d);
3395 err = fuse_fs_opendir(f->fs, path, &fi);
3396 fuse_finish_interrupt(f, req, &d);
3397 dh->fh = fi.fh;
3398 llfi->cache_readdir = fi.cache_readdir;
3399 llfi->keep_cache = fi.keep_cache;
3400 }
3401 if (!err) {
3402 if (fuse_reply_open(req, llfi) == -ENOENT) {
3403 /* The opendir syscall was interrupted, so it
3404 must be cancelled */
3405 fuse_fs_releasedir(f->fs, path, &fi);
3406 pthread_mutex_destroy(&dh->lock);
3407 free(dh);
3408 }
3409 } else {
3410 reply_err(req, err);
3411 pthread_mutex_destroy(&dh->lock);
3412 free(dh);
3413 }
3414 free_path(f, ino, path);
3415}
3416
3417static int extend_contents(struct fuse_dh *dh, unsigned minsize)
3418{
3419 if (minsize > dh->size) {
3420 char *newptr;
3421 unsigned newsize = dh->size;
3422 if (!newsize)
3423 newsize = 1024;
3424 while (newsize < minsize) {
3425 if (newsize >= 0x80000000)
3426 newsize = 0xffffffff;
3427 else
3428 newsize *= 2;
3429 }
3430
3431 newptr = (char *) realloc(dh->contents, newsize);
3432 if (!newptr) {
3433 dh->error = -ENOMEM;
3434 return -1;
3435 }
3436 dh->contents = newptr;
3437 dh->size = newsize;
3438 }
3439 return 0;
3440}
3441
3442static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name,
3443 struct stat *st, enum fuse_fill_dir_flags flags)
3444{
3445 struct fuse_direntry *de;
3446
3447 de = malloc(sizeof(struct fuse_direntry));
3448 if (!de) {
3449 dh->error = -ENOMEM;
3450 return -1;
3451 }
3452 de->name = strdup(name);
3453 if (!de->name) {
3454 dh->error = -ENOMEM;
3455 free(de);
3456 return -1;
3457 }
3458 de->flags = flags;
3459 de->stat = *st;
3460 de->next = NULL;
3461
3462 *dh->last = de;
3463 dh->last = &de->next;
3464
3465 return 0;
3466}
3467
3468static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent,
3469 const char *name)
3470{
3471 struct node *node;
3472 fuse_ino_t res = FUSE_UNKNOWN_INO;
3473
3474 pthread_mutex_lock(&f->lock);
3475 node = lookup_node(f, parent, name);
3476 if (node)
3477 res = node->nodeid;
3478 pthread_mutex_unlock(&f->lock);
3479
3480 return res;
3481}
3482
3483static int fill_dir(void *dh_, const char *name, const struct stat *statp,
3484 off_t off, enum fuse_fill_dir_flags flags)
3485{
3486 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3487 struct stat stbuf;
3488
3489 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3490 dh->error = -EIO;
3491 return 1;
3492 }
3493
3494 if (statp)
3495 stbuf = *statp;
3496 else {
3497 memset(&stbuf, 0, sizeof(stbuf));
3498 stbuf.st_ino = FUSE_UNKNOWN_INO;
3499 }
3500
3501 if (!dh->fuse->conf.use_ino) {
3502 stbuf.st_ino = FUSE_UNKNOWN_INO;
3503 if (dh->fuse->conf.readdir_ino) {
3504 stbuf.st_ino = (ino_t)
3505 lookup_nodeid(dh->fuse, dh->nodeid, name);
3506 }
3507 }
3508
3509 if (off) {
3510 size_t newlen;
3511
3512 if (dh->filled) {
3513 dh->error = -EIO;
3514 return 1;
3515 }
3516
3517 if (dh->first) {
3518 dh->error = -EIO;
3519 return 1;
3520 }
3521
3522 if (extend_contents(dh, dh->needlen) == -1)
3523 return 1;
3524
3525 newlen = dh->len +
3526 fuse_add_direntry(dh->req, dh->contents + dh->len,
3527 dh->needlen - dh->len, name,
3528 &stbuf, off);
3529 if (newlen > dh->needlen)
3530 return 1;
3531
3532 dh->len = newlen;
3533 } else {
3534 dh->filled = 1;
3535
3536 if (fuse_add_direntry_to_dh(dh, name, &stbuf, flags) == -1)
3537 return 1;
3538 }
3539 return 0;
3540}
3541
3542static int is_dot_or_dotdot(const char *name)
3543{
3544 return name[0] == '.' && (name[1] == '\0' ||
3545 (name[1] == '.' && name[2] == '\0'));
3546}
3547
3548static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
3549 off_t off, enum fuse_fill_dir_flags flags)
3550{
3551 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3552 struct fuse_entry_param e = {
3553 /* ino=0 tells the kernel to ignore readdirplus stat info */
3554 .ino = 0,
3555 };
3556 struct fuse *f = dh->fuse;
3557 int res;
3558
3559 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3560 dh->error = -EIO;
3561 return 1;
3562 }
3563
3564 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3565 e.attr = *statp;
3566 } else {
3567 e.attr.st_ino = FUSE_UNKNOWN_INO;
3568 if (statp) {
3569 e.attr.st_mode = statp->st_mode;
3570 if (f->conf.use_ino)
3571 e.attr.st_ino = statp->st_ino;
3572 }
3573 if (!f->conf.use_ino && f->conf.readdir_ino) {
3574 e.attr.st_ino = (ino_t)
3575 lookup_nodeid(f, dh->nodeid, name);
3576 }
3577 }
3578
3579 if (off) {
3580 size_t newlen;
3581
3582 if (dh->filled) {
3583 dh->error = -EIO;
3584 return 1;
3585 }
3586
3587 if (dh->first) {
3588 dh->error = -EIO;
3589 return 1;
3590 }
3591 if (extend_contents(dh, dh->needlen) == -1)
3592 return 1;
3593
3594 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3595 if (!is_dot_or_dotdot(name)) {
3596 res = do_lookup(f, dh->nodeid, name, &e);
3597 if (res) {
3598 dh->error = res;
3599 return 1;
3600 }
3601 }
3602 }
3603
3604 newlen = dh->len +
3605 fuse_add_direntry_plus(dh->req, dh->contents + dh->len,
3606 dh->needlen - dh->len, name,
3607 &e, off);
3608 if (newlen > dh->needlen)
3609 return 1;
3610 dh->len = newlen;
3611 } else {
3612 dh->filled = 1;
3613
3614 if (fuse_add_direntry_to_dh(dh, name, &e.attr, flags) == -1)
3615 return 1;
3616 }
3617
3618 return 0;
3619}
3620
3621static void free_direntries(struct fuse_direntry *de)
3622{
3623 while (de) {
3624 struct fuse_direntry *next = de->next;
3625 free(de->name);
3626 free(de);
3627 de = next;
3628 }
3629}
3630
3631static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3632 size_t size, off_t off, struct fuse_dh *dh,
3633 struct fuse_file_info *fi,
3634 enum fuse_readdir_flags flags)
3635{
3636 char *path;
3637 int err;
3638
3639 if (f->fs->op.readdir)
3640 err = get_path_nullok(f, ino, &path);
3641 else
3642 err = get_path(f, ino, &path);
3643 if (!err) {
3644 struct fuse_intr_data d;
3645 fuse_fill_dir_t filler = fill_dir;
3646
3647 if (flags & FUSE_READDIR_PLUS)
3648 filler = fill_dir_plus;
3649
3650 free_direntries(dh->first);
3651 dh->first = NULL;
3652 dh->last = &dh->first;
3653 dh->len = 0;
3654 dh->error = 0;
3655 dh->needlen = size;
3656 dh->filled = 0;
3657 dh->req = req;
3658 fuse_prepare_interrupt(f, req, &d);
3659 err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags);
3660 fuse_finish_interrupt(f, req, &d);
3661 dh->req = NULL;
3662 if (!err)
3663 err = dh->error;
3664 if (err)
3665 dh->filled = 0;
3666 free_path(f, ino, path);
3667 }
3668 return err;
3669}
3670
3671static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh,
3672 off_t off, enum fuse_readdir_flags flags)
3673{
3674 off_t pos;
3675 struct fuse_direntry *de = dh->first;
3676 int res;
3677
3678 dh->len = 0;
3679
3680 if (extend_contents(dh, dh->needlen) == -1)
3681 return dh->error;
3682
3683 for (pos = 0; pos < off; pos++) {
3684 if (!de)
3685 break;
3686
3687 de = de->next;
3688 }
3689 while (de) {
3690 char *p = dh->contents + dh->len;
3691 unsigned rem = dh->needlen - dh->len;
3692 unsigned thislen;
3693 unsigned newlen;
3694 pos++;
3695
3696 if (flags & FUSE_READDIR_PLUS) {
3697 struct fuse_entry_param e = {
3698 .ino = 0,
3699 .attr = de->stat,
3700 };
3701
3702 if (de->flags & FUSE_FILL_DIR_PLUS &&
3703 !is_dot_or_dotdot(de->name)) {
3704 res = do_lookup(dh->fuse, dh->nodeid,
3705 de->name, &e);
3706 if (res) {
3707 dh->error = res;
3708 return 1;
3709 }
3710 }
3711
3712 thislen = fuse_add_direntry_plus(req, p, rem,
3713 de->name, &e, pos);
3714 } else {
3715 thislen = fuse_add_direntry(req, p, rem,
3716 de->name, &de->stat, pos);
3717 }
3718 newlen = dh->len + thislen;
3719 if (newlen > dh->needlen)
3720 break;
3721 dh->len = newlen;
3722 de = de->next;
3723 }
3724 return 0;
3725}
3726
3727static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size,
3728 off_t off, struct fuse_file_info *llfi,
3729 enum fuse_readdir_flags flags)
3730{
3731 struct fuse *f = req_fuse_prepare(req);
3732 struct fuse_file_info fi;
3733 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3734 int err;
3735
3736 pthread_mutex_lock(&dh->lock);
3737 /* According to SUS, directory contents need to be refreshed on
3738 rewinddir() */
3739 if (!off)
3740 dh->filled = 0;
3741
3742 if (!dh->filled) {
3743 err = readdir_fill(f, req, ino, size, off, dh, &fi, flags);
3744 if (err) {
3745 reply_err(req, err);
3746 goto out;
3747 }
3748 }
3749 if (dh->filled) {
3750 dh->needlen = size;
3751 err = readdir_fill_from_list(req, dh, off, flags);
3752 if (err) {
3753 reply_err(req, err);
3754 goto out;
3755 }
3756 }
3757 fuse_reply_buf(req, dh->contents, dh->len);
3758out:
3759 pthread_mutex_unlock(&dh->lock);
3760}
3761
3762static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
3763 off_t off, struct fuse_file_info *llfi)
3764{
3765 fuse_readdir_common(req, ino, size, off, llfi, 0);
3766}
3767
3768static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
3769 off_t off, struct fuse_file_info *llfi)
3770{
3771 fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS);
3772}
3773
3774static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
3775 struct fuse_file_info *llfi)
3776{
3777 struct fuse *f = req_fuse_prepare(req);
3778 struct fuse_intr_data d;
3779 struct fuse_file_info fi;
3780 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3781 char *path;
3782
3783 get_path_nullok(f, ino, &path);
3784
3785 fuse_prepare_interrupt(f, req, &d);
3786 fuse_fs_releasedir(f->fs, path, &fi);
3787 fuse_finish_interrupt(f, req, &d);
3788 free_path(f, ino, path);
3789
3790 pthread_mutex_lock(&dh->lock);
3791 pthread_mutex_unlock(&dh->lock);
3792 pthread_mutex_destroy(&dh->lock);
3793 free_direntries(dh->first);
3794 free(dh->contents);
3795 free(dh);
3796 reply_err(req, 0);
3797}
3798
3799static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
3800 struct fuse_file_info *llfi)
3801{
3802 struct fuse *f = req_fuse_prepare(req);
3803 struct fuse_file_info fi;
3804 char *path;
3805 int err;
3806
3807 get_dirhandle(llfi, &fi);
3808
3809 err = get_path_nullok(f, ino, &path);
3810 if (!err) {
3811 struct fuse_intr_data d;
3812 fuse_prepare_interrupt(f, req, &d);
3813 err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi);
3814 fuse_finish_interrupt(f, req, &d);
3815 free_path(f, ino, path);
3816 }
3817 reply_err(req, err);
3818}
3819
3820static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
3821{
3822 struct fuse *f = req_fuse_prepare(req);
3823 struct statvfs buf;
3824 char *path = NULL;
3825 int err = 0;
3826
3827 memset(&buf, 0, sizeof(buf));
3828 if (ino)
3829 err = get_path(f, ino, &path);
3830
3831 if (!err) {
3832 struct fuse_intr_data d;
3833 fuse_prepare_interrupt(f, req, &d);
3834 err = fuse_fs_statfs(f->fs, path ? path : "/", &buf);
3835 fuse_finish_interrupt(f, req, &d);
3836 free_path(f, ino, path);
3837 }
3838
3839 if (!err)
3840 fuse_reply_statfs(req, &buf);
3841 else
3842 reply_err(req, err);
3843}
3844
3845static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3846 const char *value, size_t size, int flags)
3847{
3848 struct fuse *f = req_fuse_prepare(req);
3849 char *path;
3850 int err;
3851
3852 err = get_path(f, ino, &path);
3853 if (!err) {
3854 struct fuse_intr_data d;
3855 fuse_prepare_interrupt(f, req, &d);
3856 err = fuse_fs_setxattr(f->fs, path, name, value, size, flags);
3857 fuse_finish_interrupt(f, req, &d);
3858 free_path(f, ino, path);
3859 }
3860 reply_err(req, err);
3861}
3862
3863static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3864 const char *name, char *value, size_t size)
3865{
3866 int err;
3867 char *path;
3868
3869 err = get_path(f, ino, &path);
3870 if (!err) {
3871 struct fuse_intr_data d;
3872 fuse_prepare_interrupt(f, req, &d);
3873 err = fuse_fs_getxattr(f->fs, path, name, value, size);
3874 fuse_finish_interrupt(f, req, &d);
3875 free_path(f, ino, path);
3876 }
3877 return err;
3878}
3879
3880static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3881 size_t size)
3882{
3883 struct fuse *f = req_fuse_prepare(req);
3884 int res;
3885
3886 if (size) {
3887 char *value = (char *) malloc(size);
3888 if (value == NULL) {
3889 reply_err(req, -ENOMEM);
3890 return;
3891 }
3892 res = common_getxattr(f, req, ino, name, value, size);
3893 if (res > 0)
3894 fuse_reply_buf(req, value, res);
3895 else
3896 reply_err(req, res);
3897 free(value);
3898 } else {
3899 res = common_getxattr(f, req, ino, name, NULL, 0);
3900 if (res >= 0)
3901 fuse_reply_xattr(req, res);
3902 else
3903 reply_err(req, res);
3904 }
3905}
3906
3907static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3908 char *list, size_t size)
3909{
3910 char *path;
3911 int err;
3912
3913 err = get_path(f, ino, &path);
3914 if (!err) {
3915 struct fuse_intr_data d;
3916 fuse_prepare_interrupt(f, req, &d);
3917 err = fuse_fs_listxattr(f->fs, path, list, size);
3918 fuse_finish_interrupt(f, req, &d);
3919 free_path(f, ino, path);
3920 }
3921 return err;
3922}
3923
3924static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
3925{
3926 struct fuse *f = req_fuse_prepare(req);
3927 int res;
3928
3929 if (size) {
3930 char *list = (char *) malloc(size);
3931 if (list == NULL) {
3932 reply_err(req, -ENOMEM);
3933 return;
3934 }
3935 res = common_listxattr(f, req, ino, list, size);
3936 if (res > 0)
3937 fuse_reply_buf(req, list, res);
3938 else
3939 reply_err(req, res);
3940 free(list);
3941 } else {
3942 res = common_listxattr(f, req, ino, NULL, 0);
3943 if (res >= 0)
3944 fuse_reply_xattr(req, res);
3945 else
3946 reply_err(req, res);
3947 }
3948}
3949
3950static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
3951 const char *name)
3952{
3953 struct fuse *f = req_fuse_prepare(req);
3954 char *path;
3955 int err;
3956
3957 err = get_path(f, ino, &path);
3958 if (!err) {
3959 struct fuse_intr_data d;
3960 fuse_prepare_interrupt(f, req, &d);
3961 err = fuse_fs_removexattr(f->fs, path, name);
3962 fuse_finish_interrupt(f, req, &d);
3963 free_path(f, ino, path);
3964 }
3965 reply_err(req, err);
3966}
3967
3968static struct lock *locks_conflict(struct node *node, const struct lock *lock)
3969{
3970 struct lock *l;
3971
3972 for (l = node->locks; l; l = l->next)
3973 if (l->owner != lock->owner &&
3974 lock->start <= l->end && l->start <= lock->end &&
3975 (l->type == F_WRLCK || lock->type == F_WRLCK))
3976 break;
3977
3978 return l;
3979}
3980
3981static void delete_lock(struct lock **lockp)
3982{
3983 struct lock *l = *lockp;
3984 *lockp = l->next;
3985 free(l);
3986}
3987
3988static void insert_lock(struct lock **pos, struct lock *lock)
3989{
3990 lock->next = *pos;
3991 *pos = lock;
3992}
3993
3994static int locks_insert(struct node *node, struct lock *lock)
3995{
3996 struct lock **lp;
3997 struct lock *newl1 = NULL;
3998 struct lock *newl2 = NULL;
3999
4000 if (lock->type != F_UNLCK || lock->start != 0 ||
4001 lock->end != OFFSET_MAX) {
4002 newl1 = malloc(sizeof(struct lock));
4003 newl2 = malloc(sizeof(struct lock));
4004
4005 if (!newl1 || !newl2) {
4006 free(newl1);
4007 free(newl2);
4008 return -ENOLCK;
4009 }
4010 }
4011
4012 for (lp = &node->locks; *lp;) {
4013 struct lock *l = *lp;
4014 if (l->owner != lock->owner)
4015 goto skip;
4016
4017 if (lock->type == l->type) {
4018 if (l->end < lock->start - 1)
4019 goto skip;
4020 if (lock->end < l->start - 1)
4021 break;
4022 if (l->start <= lock->start && lock->end <= l->end)
4023 goto out;
4024 if (l->start < lock->start)
4025 lock->start = l->start;
4026 if (lock->end < l->end)
4027 lock->end = l->end;
4028 goto delete;
4029 } else {
4030 if (l->end < lock->start)
4031 goto skip;
4032 if (lock->end < l->start)
4033 break;
4034 if (lock->start <= l->start && l->end <= lock->end)
4035 goto delete;
4036 if (l->end <= lock->end) {
4037 l->end = lock->start - 1;
4038 goto skip;
4039 }
4040 if (lock->start <= l->start) {
4041 l->start = lock->end + 1;
4042 break;
4043 }
4044 *newl2 = *l;
4045 newl2->start = lock->end + 1;
4046 l->end = lock->start - 1;
4047 insert_lock(&l->next, newl2);
4048 newl2 = NULL;
4049 }
4050 skip:
4051 lp = &l->next;
4052 continue;
4053
4054 delete:
4055 delete_lock(lp);
4056 }
4057 if (lock->type != F_UNLCK) {
4058 *newl1 = *lock;
4059 insert_lock(lp, newl1);
4060 newl1 = NULL;
4061 }
4062out:
4063 free(newl1);
4064 free(newl2);
4065 return 0;
4066}
4067
4068static void flock_to_lock(struct flock *flock, struct lock *lock)
4069{
4070 memset(lock, 0, sizeof(struct lock));
4071 lock->type = flock->l_type;
4072 lock->start = flock->l_start;
4073 lock->end =
4074 flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
4075 lock->pid = flock->l_pid;
4076}
4077
4078static void lock_to_flock(struct lock *lock, struct flock *flock)
4079{
4080 flock->l_type = lock->type;
4081 flock->l_start = lock->start;
4082 flock->l_len =
4083 (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
4084 flock->l_pid = lock->pid;
4085}
4086
4087static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
4088 const char *path, struct fuse_file_info *fi)
4089{
4090 struct fuse_intr_data d;
4091 struct flock lock;
4092 struct lock l;
4093 int err;
4094 int errlock;
4095
4096 fuse_prepare_interrupt(f, req, &d);
4097 memset(&lock, 0, sizeof(lock));
4098 lock.l_type = F_UNLCK;
4099 lock.l_whence = SEEK_SET;
4100 err = fuse_fs_flush(f->fs, path, fi);
4101 errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock);
4102 fuse_finish_interrupt(f, req, &d);
4103
4104 if (errlock != -ENOSYS) {
4105 flock_to_lock(&lock, &l);
4106 l.owner = fi->lock_owner;
4107 pthread_mutex_lock(&f->lock);
4108 locks_insert(get_node(f, ino), &l);
4109 pthread_mutex_unlock(&f->lock);
4110
4111 /* if op.lock() is defined FLUSH is needed regardless
4112 of op.flush() */
4113 if (err == -ENOSYS)
4114 err = 0;
4115 }
4116 return err;
4117}
4118
4119static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino,
4120 struct fuse_file_info *fi)
4121{
4122 struct fuse *f = req_fuse_prepare(req);
4123 struct fuse_intr_data d;
4124 char *path;
4125 int err = 0;
4126
4127 get_path_nullok(f, ino, &path);
4128 if (fi->flush) {
4129 err = fuse_flush_common(f, req, ino, path, fi);
4130 if (err == -ENOSYS)
4131 err = 0;
4132 }
4133
4134 fuse_prepare_interrupt(f, req, &d);
4135 fuse_do_release(f, ino, path, fi);
4136 fuse_finish_interrupt(f, req, &d);
4137 free_path(f, ino, path);
4138
4139 reply_err(req, err);
4140}
4141
4142static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino,
4143 struct fuse_file_info *fi)
4144{
4145 struct fuse *f = req_fuse_prepare(req);
4146 char *path;
4147 int err;
4148
4149 get_path_nullok(f, ino, &path);
4150 err = fuse_flush_common(f, req, ino, path, fi);
4151 free_path(f, ino, path);
4152
4153 reply_err(req, err);
4154}
4155
4156static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
4157 struct fuse_file_info *fi, struct flock *lock,
4158 int cmd)
4159{
4160 struct fuse *f = req_fuse_prepare(req);
4161 char *path;
4162 int err;
4163
4164 err = get_path_nullok(f, ino, &path);
4165 if (!err) {
4166 struct fuse_intr_data d;
4167 fuse_prepare_interrupt(f, req, &d);
4168 err = fuse_fs_lock(f->fs, path, fi, cmd, lock);
4169 fuse_finish_interrupt(f, req, &d);
4170 free_path(f, ino, path);
4171 }
4172 return err;
4173}
4174
4175static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino,
4176 struct fuse_file_info *fi, struct flock *lock)
4177{
4178 int err;
4179 struct lock l;
4180 struct lock *conflict;
4181 struct fuse *f = req_fuse(req);
4182
4183 flock_to_lock(lock, &l);
4184 l.owner = fi->lock_owner;
4185 pthread_mutex_lock(&f->lock);
4186 conflict = locks_conflict(get_node(f, ino), &l);
4187 if (conflict)
4188 lock_to_flock(conflict, lock);
4189 pthread_mutex_unlock(&f->lock);
4190 if (!conflict)
4191 err = fuse_lock_common(req, ino, fi, lock, F_GETLK);
4192 else
4193 err = 0;
4194
4195 if (!err)
4196 fuse_reply_lock(req, lock);
4197 else
4198 reply_err(req, err);
4199}
4200
4201static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino,
4202 struct fuse_file_info *fi, struct flock *lock,
4203 int sleep)
4204{
4205 int err = fuse_lock_common(req, ino, fi, lock,
4206 sleep ? F_SETLKW : F_SETLK);
4207 if (!err) {
4208 struct fuse *f = req_fuse(req);
4209 struct lock l;
4210 flock_to_lock(lock, &l);
4211 l.owner = fi->lock_owner;
4212 pthread_mutex_lock(&f->lock);
4213 locks_insert(get_node(f, ino), &l);
4214 pthread_mutex_unlock(&f->lock);
4215 }
4216 reply_err(req, err);
4217}
4218
4219static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino,
4220 struct fuse_file_info *fi, int op)
4221{
4222 struct fuse *f = req_fuse_prepare(req);
4223 char *path;
4224 int err;
4225
4226 err = get_path_nullok(f, ino, &path);
4227 if (err == 0) {
4228 struct fuse_intr_data d;
4229 fuse_prepare_interrupt(f, req, &d);
4230 err = fuse_fs_flock(f->fs, path, fi, op);
4231 fuse_finish_interrupt(f, req, &d);
4232 free_path(f, ino, path);
4233 }
4234 reply_err(req, err);
4235}
4236
4237static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
4238 uint64_t idx)
4239{
4240 struct fuse *f = req_fuse_prepare(req);
4241 struct fuse_intr_data d;
4242 char *path;
4243 int err;
4244
4245 err = get_path(f, ino, &path);
4246 if (!err) {
4247 fuse_prepare_interrupt(f, req, &d);
4248 err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
4249 fuse_finish_interrupt(f, req, &d);
4250 free_path(f, ino, path);
4251 }
4252 if (!err)
4253 fuse_reply_bmap(req, idx);
4254 else
4255 reply_err(req, err);
4256}
4257
4258static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
4259 void *arg, struct fuse_file_info *llfi,
4260 unsigned int flags, const void *in_buf,
4261 size_t in_bufsz, size_t out_bufsz)
4262{
4263 struct fuse *f = req_fuse_prepare(req);
4264 struct fuse_intr_data d;
4265 struct fuse_file_info fi;
4266 char *path, *out_buf = NULL;
4267 int err;
4268
4269 err = -EPERM;
4270 if (flags & FUSE_IOCTL_UNRESTRICTED)
4271 goto err;
4272
4273 if (flags & FUSE_IOCTL_DIR)
4274 get_dirhandle(llfi, &fi);
4275 else
4276 fi = *llfi;
4277
4278 if (out_bufsz) {
4279 err = -ENOMEM;
4280 out_buf = malloc(out_bufsz);
4281 if (!out_buf)
4282 goto err;
4283 }
4284
4285 assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
4286 if (out_buf && in_bufsz)
4287 memcpy(out_buf, in_buf, in_bufsz);
4288
4289 err = get_path_nullok(f, ino, &path);
4290 if (err)
4291 goto err;
4292
4293 fuse_prepare_interrupt(f, req, &d);
4294
4295 err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags,
4296 out_buf ? out_buf : (void *)in_buf);
4297
4298 fuse_finish_interrupt(f, req, &d);
4299 free_path(f, ino, path);
4300
4301 if (err < 0)
4302 goto err;
4303 fuse_reply_ioctl(req, err, out_buf, out_bufsz);
4304 goto out;
4305err:
4306 reply_err(req, err);
4307out:
4308 free(out_buf);
4309}
4310
4311static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
4312 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
4313{
4314 struct fuse *f = req_fuse_prepare(req);
4315 struct fuse_intr_data d;
4316 char *path;
4317 int err;
4318 unsigned revents = 0;
4319
4320 err = get_path_nullok(f, ino, &path);
4321 if (!err) {
4322 fuse_prepare_interrupt(f, req, &d);
4323 err = fuse_fs_poll(f->fs, path, fi, ph, &revents);
4324 fuse_finish_interrupt(f, req, &d);
4325 free_path(f, ino, path);
4326 }
4327 if (!err)
4328 fuse_reply_poll(req, revents);
4329 else
4330 reply_err(req, err);
4331}
4332
4333static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
4334 off_t offset, off_t length, struct fuse_file_info *fi)
4335{
4336 struct fuse *f = req_fuse_prepare(req);
4337 struct fuse_intr_data d;
4338 char *path;
4339 int err;
4340
4341 err = get_path_nullok(f, ino, &path);
4342 if (!err) {
4343 fuse_prepare_interrupt(f, req, &d);
4344 err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi);
4345 fuse_finish_interrupt(f, req, &d);
4346 free_path(f, ino, path);
4347 }
4348 reply_err(req, err);
4349}
4350
4351static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
4352 off_t off_in, struct fuse_file_info *fi_in,
4353 fuse_ino_t nodeid_out, off_t off_out,
4354 struct fuse_file_info *fi_out, size_t len,
4355 int flags)
4356{
4357 struct fuse *f = req_fuse_prepare(req);
4358 struct fuse_intr_data d;
4359 char *path_in, *path_out;
4360 int err;
4361 ssize_t res;
4362
4363 err = get_path_nullok(f, nodeid_in, &path_in);
4364 if (err) {
4365 reply_err(req, err);
4366 return;
4367 }
4368
4369 err = get_path_nullok(f, nodeid_out, &path_out);
4370 if (err) {
4371 free_path(f, nodeid_in, path_in);
4372 reply_err(req, err);
4373 return;
4374 }
4375
4376 fuse_prepare_interrupt(f, req, &d);
4377 res = fuse_fs_copy_file_range(f->fs, path_in, fi_in, off_in, path_out,
4378 fi_out, off_out, len, flags);
4379 fuse_finish_interrupt(f, req, &d);
4380
4381 if (res >= 0)
4382 fuse_reply_write(req, res);
4383 else
4384 reply_err(req, res);
4385
4386 free_path(f, nodeid_in, path_in);
4387 free_path(f, nodeid_out, path_out);
4388}
4389
4390static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
4391 struct fuse_file_info *fi)
4392{
4393 struct fuse *f = req_fuse_prepare(req);
4394 struct fuse_intr_data d;
4395 char *path;
4396 int err;
4397 off_t res;
4398
4399 err = get_path(f, ino, &path);
4400 if (err) {
4401 reply_err(req, err);
4402 return;
4403 }
4404
4405 fuse_prepare_interrupt(f, req, &d);
4406 res = fuse_fs_lseek(f->fs, path, off, whence, fi);
4407 fuse_finish_interrupt(f, req, &d);
4408 free_path(f, ino, path);
4409 if (res >= 0)
4410 fuse_reply_lseek(req, res);
4411 else
4412 reply_err(req, res);
4413}
4414
4415static int clean_delay(struct fuse *f)
4416{
4417 /*
4418 * This is calculating the delay between clean runs. To
4419 * reduce the number of cleans we are doing them 10 times
4420 * within the remember window.
4421 */
4422 int min_sleep = 60;
4423 int max_sleep = 3600;
4424 int sleep_time = f->conf.remember / 10;
4425
4426 if (sleep_time > max_sleep)
4427 return max_sleep;
4428 if (sleep_time < min_sleep)
4429 return min_sleep;
4430 return sleep_time;
4431}
4432
4433int fuse_clean_cache(struct fuse *f)
4434{
4435 struct node_lru *lnode;
4436 struct list_head *curr, *next;
4437 struct node *node;
4438 struct timespec now;
4439
4440 pthread_mutex_lock(&f->lock);
4441
4442 curr_time(&now);
4443
4444 for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) {
4445 double age;
4446
4447 next = curr->next;
4448 lnode = list_entry(curr, struct node_lru, lru);
4449 node = &lnode->node;
4450
4451 age = diff_timespec(&now, &lnode->forget_time);
4452 if (age <= f->conf.remember)
4453 break;
4454
4455 assert(node->nlookup == 1);
4456
4457 /* Don't forget active directories */
4458 if (node->refctr > 1)
4459 continue;
4460
4461 node->nlookup = 0;
4462 unhash_name(f, node);
4463 unref_node(f, node);
4464 }
4465 pthread_mutex_unlock(&f->lock);
4466
4467 return clean_delay(f);
4468}
4469
4470static struct fuse_lowlevel_ops fuse_path_ops = {
4471 .init = fuse_lib_init,
4472 .destroy = fuse_lib_destroy,
4473 .lookup = fuse_lib_lookup,
4474 .forget = fuse_lib_forget,
4475 .forget_multi = fuse_lib_forget_multi,
4476 .getattr = fuse_lib_getattr,
4477 .setattr = fuse_lib_setattr,
4478 .access = fuse_lib_access,
4479 .readlink = fuse_lib_readlink,
4480 .mknod = fuse_lib_mknod,
4481 .mkdir = fuse_lib_mkdir,
4482 .unlink = fuse_lib_unlink,
4483 .rmdir = fuse_lib_rmdir,
4484 .symlink = fuse_lib_symlink,
4485 .rename = fuse_lib_rename,
4486 .link = fuse_lib_link,
4487 .create = fuse_lib_create,
4488 .open = fuse_lib_open,
4489 .read = fuse_lib_read,
4490 .write_buf = fuse_lib_write_buf,
4491 .flush = fuse_lib_flush,
4492 .release = fuse_lib_release,
4493 .fsync = fuse_lib_fsync,
4494 .opendir = fuse_lib_opendir,
4495 .readdir = fuse_lib_readdir,
4496 .readdirplus = fuse_lib_readdirplus,
4497 .releasedir = fuse_lib_releasedir,
4498 .fsyncdir = fuse_lib_fsyncdir,
4499 .statfs = fuse_lib_statfs,
4500 .setxattr = fuse_lib_setxattr,
4501 .getxattr = fuse_lib_getxattr,
4502 .listxattr = fuse_lib_listxattr,
4503 .removexattr = fuse_lib_removexattr,
4504 .getlk = fuse_lib_getlk,
4505 .setlk = fuse_lib_setlk,
4506 .flock = fuse_lib_flock,
4507 .bmap = fuse_lib_bmap,
4508 .ioctl = fuse_lib_ioctl,
4509 .poll = fuse_lib_poll,
4510 .fallocate = fuse_lib_fallocate,
4511 .copy_file_range = fuse_lib_copy_file_range,
4512 .lseek = fuse_lib_lseek,
4513};
4514
4515int fuse_notify_poll(struct fuse_pollhandle *ph)
4516{
4517 return fuse_lowlevel_notify_poll(ph);
4518}
4519
4520struct fuse_session *fuse_get_session(struct fuse *f)
4521{
4522 return f->se;
4523}
4524
4525static int fuse_session_loop_remember(struct fuse *f)
4526{
4527 struct fuse_session *se = f->se;
4528 int res = 0;
4529 struct timespec now;
4530 time_t next_clean;
4531 struct pollfd fds = {
4532 .fd = se->fd,
4533 .events = POLLIN
4534 };
4535 struct fuse_buf fbuf = {
4536 .mem = NULL,
4537 };
4538
4539 curr_time(&now);
4540 next_clean = now.tv_sec;
4541 while (!fuse_session_exited(se)) {
4542 unsigned timeout;
4543
4544 curr_time(&now);
4545 if (now.tv_sec < next_clean)
4546 timeout = next_clean - now.tv_sec;
4547 else
4548 timeout = 0;
4549
4550 res = poll(&fds, 1, timeout * 1000);
4551 if (res == -1) {
4552 if (errno == EINTR)
4553 continue;
4554 else
4555 break;
4556 } else if (res > 0) {
4557 res = fuse_session_receive_buf_internal(se, &fbuf,
4558 NULL);
4559 if (res == -EINTR)
4560 continue;
4561 if (res <= 0)
4562 break;
4563
4564 fuse_session_process_buf_internal(se, &fbuf, NULL);
4565 } else {
4566 timeout = fuse_clean_cache(f);
4567 curr_time(&now);
4568 next_clean = now.tv_sec + timeout;
4569 }
4570 }
4571
4572 free(fbuf.mem);
4574 return res < 0 ? -1 : 0;
4575}
4576
4577int fuse_loop(struct fuse *f)
4578{
4579 if (!f)
4580 return -1;
4581
4582 if (lru_enabled(f))
4583 return fuse_session_loop_remember(f);
4584
4585 return fuse_session_loop(f->se);
4586}
4587
4588FUSE_SYMVER("fuse_loop_mt_312", "fuse_loop_mt@@FUSE_3.12")
4589int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config)
4590{
4591 if (f == NULL)
4592 return -1;
4593
4594 int res = fuse_start_cleanup_thread(f);
4595 if (res)
4596 return -1;
4597
4598 res = fuse_session_loop_mt_312(fuse_get_session(f), config);
4600 return res;
4601}
4602
4603int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1);
4604FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@FUSE_3.2")
4605int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1)
4606{
4607 struct fuse_loop_config *config = fuse_loop_cfg_create();
4608 if (config == NULL)
4609 return ENOMEM;
4610
4611 fuse_loop_cfg_convert(config, config_v1);
4612
4613 int res = fuse_loop_mt_312(f, config);
4614
4615 fuse_loop_cfg_destroy(config);
4616
4617 return res;
4618}
4619
4620int fuse_loop_mt_31(struct fuse *f, int clone_fd);
4621FUSE_SYMVER("fuse_loop_mt_31", "fuse_loop_mt@FUSE_3.0")
4622int fuse_loop_mt_31(struct fuse *f, int clone_fd)
4623{
4624 int err;
4625 struct fuse_loop_config *config = fuse_loop_cfg_create();
4626
4627 if (config == NULL)
4628 return ENOMEM;
4629
4630 fuse_loop_cfg_set_clone_fd(config, clone_fd);
4631
4632 err = fuse_loop_mt_312(f, config);
4633
4634 fuse_loop_cfg_destroy(config);
4635
4636 return err;
4637}
4638
4639void fuse_exit(struct fuse *f)
4640{
4641 fuse_session_exit(f->se);
4642}
4643
4645{
4646 struct fuse_context_i *c = fuse_get_context_internal();
4647
4648 if (c)
4649 return &c->ctx;
4650 else
4651 return NULL;
4652}
4653
4654int fuse_getgroups(int size, gid_t list[])
4655{
4656 struct fuse_context_i *c = fuse_get_context_internal();
4657 if (!c)
4658 return -EINVAL;
4659
4660 return fuse_req_getgroups(c->req, size, list);
4661}
4662
4664{
4665 struct fuse_context_i *c = fuse_get_context_internal();
4666
4667 if (c)
4668 return fuse_req_interrupted(c->req);
4669 else
4670 return 0;
4671}
4672
4673int fuse_invalidate_path(struct fuse *f, const char *path) {
4674 fuse_ino_t ino;
4675 int err = lookup_path_in_cache(f, path, &ino);
4676 if (err) {
4677 return err;
4678 }
4679
4680 return fuse_lowlevel_notify_inval_inode(f->se, ino, 0, 0);
4681}
4682
4683#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
4684
4685static const struct fuse_opt fuse_lib_opts[] = {
4688 FUSE_LIB_OPT("debug", debug, 1),
4689 FUSE_LIB_OPT("-d", debug, 1),
4690 FUSE_LIB_OPT("kernel_cache", kernel_cache, 1),
4691 FUSE_LIB_OPT("auto_cache", auto_cache, 1),
4692 FUSE_LIB_OPT("noauto_cache", auto_cache, 0),
4693 FUSE_LIB_OPT("no_rofd_flush", no_rofd_flush, 1),
4694 FUSE_LIB_OPT("umask=", set_mode, 1),
4695 FUSE_LIB_OPT("umask=%o", umask, 0),
4696 FUSE_LIB_OPT("fmask=", set_mode, 1),
4697 FUSE_LIB_OPT("fmask=%o", fmask, 0),
4698 FUSE_LIB_OPT("dmask=", set_mode, 1),
4699 FUSE_LIB_OPT("dmask=%o", dmask, 0),
4700 FUSE_LIB_OPT("uid=", set_uid, 1),
4701 FUSE_LIB_OPT("uid=%d", uid, 0),
4702 FUSE_LIB_OPT("gid=", set_gid, 1),
4703 FUSE_LIB_OPT("gid=%d", gid, 0),
4704 FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0),
4705 FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0),
4706 FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0),
4707 FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1),
4708 FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0),
4709 FUSE_LIB_OPT("noforget", remember, -1),
4710 FUSE_LIB_OPT("remember=%u", remember, 0),
4711 FUSE_LIB_OPT("modules=%s", modules, 0),
4712 FUSE_LIB_OPT("parallel_direct_write=%d", parallel_direct_writes, 0),
4714};
4715
4716static int fuse_lib_opt_proc(void *data, const char *arg, int key,
4717 struct fuse_args *outargs)
4718{
4719 (void) arg; (void) outargs; (void) data; (void) key;
4720
4721 /* Pass through unknown options */
4722 return 1;
4723}
4724
4725
4726static const struct fuse_opt fuse_help_opts[] = {
4727 FUSE_LIB_OPT("modules=%s", modules, 1),
4728 FUSE_OPT_KEY("modules=%s", FUSE_OPT_KEY_KEEP),
4730};
4731
4732static void print_module_help(const char *name,
4734{
4735 struct fuse_args a = FUSE_ARGS_INIT(0, NULL);
4736 if (fuse_opt_add_arg(&a, "") == -1 ||
4737 fuse_opt_add_arg(&a, "-h") == -1)
4738 return;
4739 printf("\nOptions for %s module:\n", name);
4740 (*fac)(&a, NULL);
4742}
4743
4744void fuse_lib_help(struct fuse_args *args)
4745{
4746 /* These are not all options, but only the ones that
4747 may be of interest to an end-user */
4748 printf(
4749" -o kernel_cache cache files in kernel\n"
4750" -o [no]auto_cache enable caching based on modification times (off)\n"
4751" -o no_rofd_flush disable flushing of read-only fd on close (off)\n"
4752" -o umask=M set file permissions (octal)\n"
4753" -o fmask=M set file permissions (octal)\n"
4754" -o dmask=M set dir permissions (octal)\n"
4755" -o uid=N set file owner\n"
4756" -o gid=N set file group\n"
4757" -o entry_timeout=T cache timeout for names (1.0s)\n"
4758" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
4759" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
4760" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n"
4761" -o noforget never forget cached inodes\n"
4762" -o remember=T remember cached inodes for T seconds (0s)\n"
4763" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n");
4764
4765
4766 /* Print low-level help */
4768
4769 /* Print help for builtin modules */
4770 print_module_help("subdir", &fuse_module_subdir_factory);
4771#ifdef HAVE_ICONV
4772 print_module_help("iconv", &fuse_module_iconv_factory);
4773#endif
4774
4775 /* Parse command line options in case we need to
4776 activate more modules */
4777 struct fuse_config conf = { .modules = NULL };
4778 if (fuse_opt_parse(args, &conf, fuse_help_opts,
4779 fuse_lib_opt_proc) == -1
4780 || !conf.modules)
4781 return;
4782
4783 char *module;
4784 char *next;
4785 struct fuse_module *m;
4786
4787 // Iterate over all modules
4788 for (module = conf.modules; module; module = next) {
4789 char *p;
4790 for (p = module; *p && *p != ':'; p++);
4791 next = *p ? p + 1 : NULL;
4792 *p = '\0';
4793
4794 m = fuse_get_module(module);
4795 if (m)
4796 print_module_help(module, &m->factory);
4797 }
4798}
4799
4800static int fuse_init_intr_signal(int signum, int *installed)
4801{
4802 struct sigaction old_sa;
4803
4804 if (sigaction(signum, NULL, &old_sa) == -1) {
4805 perror("fuse: cannot get old signal handler");
4806 return -1;
4807 }
4808
4809 if (old_sa.sa_handler == SIG_DFL) {
4810 struct sigaction sa;
4811
4812 memset(&sa, 0, sizeof(struct sigaction));
4813 sa.sa_handler = fuse_intr_sighandler;
4814 sigemptyset(&sa.sa_mask);
4815
4816 if (sigaction(signum, &sa, NULL) == -1) {
4817 perror("fuse: cannot set interrupt signal handler");
4818 return -1;
4819 }
4820 *installed = 1;
4821 }
4822 return 0;
4823}
4824
4825static void fuse_restore_intr_signal(int signum)
4826{
4827 struct sigaction sa;
4828
4829 memset(&sa, 0, sizeof(struct sigaction));
4830 sa.sa_handler = SIG_DFL;
4831 sigaction(signum, &sa, NULL);
4832}
4833
4834
4835static int fuse_push_module(struct fuse *f, const char *module,
4836 struct fuse_args *args)
4837{
4838 struct fuse_fs *fs[2] = { f->fs, NULL };
4839 struct fuse_fs *newfs;
4840 struct fuse_module *m = fuse_get_module(module);
4841
4842 if (!m)
4843 return -1;
4844
4845 newfs = m->factory(args, fs);
4846 if (!newfs) {
4847 fuse_put_module(m);
4848 return -1;
4849 }
4850 f->fs = newfs;
4851 return 0;
4852}
4853
4854struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
4855 void *user_data)
4856{
4857 struct fuse_fs *fs;
4858
4859 if (sizeof(struct fuse_operations) < op_size) {
4860 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not not work\n");
4861 op_size = sizeof(struct fuse_operations);
4862 }
4863
4864 fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
4865 if (!fs) {
4866 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse_fs object\n");
4867 return NULL;
4868 }
4869
4870 fs->user_data = user_data;
4871 if (op)
4872 memcpy(&fs->op, op, op_size);
4873 return fs;
4874}
4875
4876static int node_table_init(struct node_table *t)
4877{
4878 t->size = NODE_TABLE_MIN_SIZE;
4879 t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
4880 if (t->array == NULL) {
4881 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
4882 return -1;
4883 }
4884 t->use = 0;
4885 t->split = 0;
4886
4887 return 0;
4888}
4889
4890static void *fuse_prune_nodes(void *fuse)
4891{
4892 struct fuse *f = fuse;
4893 int sleep_time;
4894
4895#ifdef HAVE_PTHREAD_SETNAME_NP
4896 pthread_setname_np(pthread_self(), "fuse_prune_nodes");
4897#endif
4898
4899 while(1) {
4900 sleep_time = fuse_clean_cache(f);
4901 sleep(sleep_time);
4902 }
4903 return NULL;
4904}
4905
4906int fuse_start_cleanup_thread(struct fuse *f)
4907{
4908 if (lru_enabled(f))
4909 return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
4910
4911 return 0;
4912}
4913
4914void fuse_stop_cleanup_thread(struct fuse *f)
4915{
4916 if (lru_enabled(f)) {
4917 pthread_mutex_lock(&f->lock);
4918 pthread_cancel(f->prune_thread);
4919 pthread_mutex_unlock(&f->lock);
4920 pthread_join(f->prune_thread, NULL);
4921 }
4922}
4923
4924/*
4925 * Not supposed to be called directly, but supposed to be called
4926 * through the fuse_new macro
4927 */
4928struct fuse *_fuse_new_31(struct fuse_args *args,
4929 const struct fuse_operations *op, size_t op_size,
4930 struct libfuse_version *version, void *user_data)
4931{
4932 struct fuse *f;
4933 struct node *root;
4934 struct fuse_fs *fs;
4935 struct fuse_lowlevel_ops llop = fuse_path_ops;
4936
4937 f = (struct fuse *) calloc(1, sizeof(struct fuse));
4938 if (f == NULL) {
4939 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
4940 goto out;
4941 }
4942
4943 f->conf.entry_timeout = 1.0;
4944 f->conf.attr_timeout = 1.0;
4945 f->conf.negative_timeout = 0.0;
4946 f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
4947
4948 /* Parse options */
4949 if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
4950 fuse_lib_opt_proc) == -1)
4951 goto out_free;
4952
4953 pthread_mutex_lock(&fuse_context_lock);
4954 static int builtin_modules_registered = 0;
4955 /* Have the builtin modules already been registered? */
4956 if (builtin_modules_registered == 0) {
4957 /* If not, register them. */
4958 fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
4959#ifdef HAVE_ICONV
4960 fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
4961#endif
4962 builtin_modules_registered= 1;
4963 }
4964 pthread_mutex_unlock(&fuse_context_lock);
4965
4966 if (fuse_create_context_key() == -1)
4967 goto out_free;
4968
4969 fs = fuse_fs_new(op, op_size, user_data);
4970 if (!fs)
4971 goto out_delete_context_key;
4972
4973 f->fs = fs;
4974
4975 /* Oh f**k, this is ugly! */
4976 if (!fs->op.lock) {
4977 llop.getlk = NULL;
4978 llop.setlk = NULL;
4979 }
4980
4981 f->pagesize = getpagesize();
4982 init_list_head(&f->partial_slabs);
4983 init_list_head(&f->full_slabs);
4984 init_list_head(&f->lru_table);
4985
4986 if (f->conf.modules) {
4987 char *module;
4988 char *next;
4989
4990 for (module = f->conf.modules; module; module = next) {
4991 char *p;
4992 for (p = module; *p && *p != ':'; p++);
4993 next = *p ? p + 1 : NULL;
4994 *p = '\0';
4995 if (module[0] &&
4996 fuse_push_module(f, module, args) == -1)
4997 goto out_free_fs;
4998 }
4999 }
5000
5001 if (!f->conf.ac_attr_timeout_set)
5002 f->conf.ac_attr_timeout = f->conf.attr_timeout;
5003
5004#if defined(__FreeBSD__) || defined(__NetBSD__)
5005 /*
5006 * In FreeBSD, we always use these settings as inode numbers
5007 * are needed to make getcwd(3) work.
5008 */
5009 f->conf.readdir_ino = 1;
5010#endif
5011
5012 /* not declared globally, to restrict usage of this function */
5013 struct fuse_session *fuse_session_new_versioned(
5014 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
5015 size_t op_size, struct libfuse_version *version,
5016 void *userdata);
5017 f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version,
5018 f);
5019 if (f->se == NULL)
5020 goto out_free_fs;
5021
5022 if (f->conf.debug) {
5023 fuse_log(FUSE_LOG_DEBUG, "nullpath_ok: %i\n", f->conf.nullpath_ok);
5024 }
5025
5026 /* Trace topmost layer by default */
5027 f->fs->debug = f->conf.debug;
5028 f->ctr = 0;
5029 f->generation = 0;
5030 if (node_table_init(&f->name_table) == -1)
5031 goto out_free_session;
5032
5033 if (node_table_init(&f->id_table) == -1)
5034 goto out_free_name_table;
5035
5036 pthread_mutex_init(&f->lock, NULL);
5037
5038 root = alloc_node(f);
5039 if (root == NULL) {
5040 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
5041 goto out_free_id_table;
5042 }
5043 if (lru_enabled(f)) {
5044 struct node_lru *lnode = node_lru(root);
5045 init_list_head(&lnode->lru);
5046 }
5047
5048 strcpy(root->inline_name, "/");
5049 root->name = root->inline_name;
5050 root->parent = NULL;
5051 root->nodeid = FUSE_ROOT_ID;
5052 inc_nlookup(root);
5053 hash_id(f, root);
5054
5055 return f;
5056
5057out_free_id_table:
5058 free(f->id_table.array);
5059out_free_name_table:
5060 free(f->name_table.array);
5061out_free_session:
5062 fuse_session_destroy(f->se);
5063out_free_fs:
5064 free(f->fs);
5065 free(f->conf.modules);
5066out_delete_context_key:
5067 fuse_delete_context_key();
5068out_free:
5069 free(f);
5070out:
5071 return NULL;
5072}
5073
5074/* Emulates 3.0-style fuse_new(), which processes --help */
5075FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
5076struct fuse *_fuse_new_30(struct fuse_args *args,
5077 const struct fuse_operations *op,
5078 size_t op_size,
5079 struct libfuse_version *version,
5080 void *user_data)
5081{
5082 struct fuse_config conf = {0};
5083
5084 const struct fuse_opt opts[] = {
5085 FUSE_LIB_OPT("-h", show_help, 1),
5086 FUSE_LIB_OPT("--help", show_help, 1),
5088 };
5089
5090 if (fuse_opt_parse(args, &conf, opts,
5091 fuse_lib_opt_proc) == -1)
5092 return NULL;
5093
5094 if (conf.show_help) {
5095 fuse_lib_help(args);
5096 return NULL;
5097 } else
5098 return _fuse_new_31(args, op, op_size, version, user_data);
5099}
5100
5101/* ABI compat version */
5102struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
5103 size_t op_size, void *user_data);
5104FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
5105struct fuse *fuse_new_31(struct fuse_args *args,
5106 const struct fuse_operations *op,
5107 size_t op_size, void *user_data)
5108{
5109 /* unknown version */
5110 struct libfuse_version version = { 0 };
5111
5112 return _fuse_new_31(args, op, op_size, &version, user_data);
5113}
5114
5115/*
5116 * ABI compat version
5117 * Emulates 3.0-style fuse_new(), which processes --help
5118 */
5119struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
5120 size_t op_size, void *user_data);
5121FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
5122struct fuse *fuse_new_30(struct fuse_args *args,
5123 const struct fuse_operations *op,
5124 size_t op_size, void *user_data)
5125{
5126 struct fuse_config conf = {0};
5127
5128 const struct fuse_opt opts[] = {
5129 FUSE_LIB_OPT("-h", show_help, 1),
5130 FUSE_LIB_OPT("--help", show_help, 1),
5132 };
5133
5134 if (fuse_opt_parse(args, &conf, opts,
5135 fuse_lib_opt_proc) == -1)
5136 return NULL;
5137
5138 if (conf.show_help) {
5139 fuse_lib_help(args);
5140 return NULL;
5141 } else
5142 return fuse_new_31(args, op, op_size, user_data);
5143}
5144
5145
5146void fuse_destroy(struct fuse *f)
5147{
5148 size_t i;
5149
5150 if (f->conf.intr && f->intr_installed)
5151 fuse_restore_intr_signal(f->conf.intr_signal);
5152
5153 if (f->fs) {
5154 fuse_create_context(f);
5155
5156 for (i = 0; i < f->id_table.size; i++) {
5157 struct node *node;
5158
5159 for (node = f->id_table.array[i]; node != NULL;
5160 node = node->id_next) {
5161 if (node->is_hidden) {
5162 char *path;
5163 if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
5164 fuse_fs_unlink(f->fs, path);
5165 free(path);
5166 }
5167 }
5168 }
5169 }
5170 }
5171 for (i = 0; i < f->id_table.size; i++) {
5172 struct node *node;
5173 struct node *next;
5174
5175 for (node = f->id_table.array[i]; node != NULL; node = next) {
5176 next = node->id_next;
5177 free_node(f, node);
5178 f->id_table.use--;
5179 }
5180 }
5181 assert(list_empty(&f->partial_slabs));
5182 assert(list_empty(&f->full_slabs));
5183
5184 while (fuse_modules) {
5185 fuse_put_module(fuse_modules);
5186 }
5187 free(f->id_table.array);
5188 free(f->name_table.array);
5189 pthread_mutex_destroy(&f->lock);
5190 fuse_session_destroy(f->se);
5191 free(f->fs);
5192 free(f->conf.modules);
5193 free(f);
5194 fuse_delete_context_key();
5195}
5196
5197int fuse_mount(struct fuse *f, const char *mountpoint) {
5198 return fuse_session_mount(fuse_get_session(f), mountpoint);
5199}
5200
5201
5202void fuse_unmount(struct fuse *f) {
5204}
5205
5207{
5208 return FUSE_VERSION;
5209}
5210
5211const char *fuse_pkgversion(void)
5212{
5213 return PACKAGE_VERSION;
5214}
int fuse_getgroups(int size, gid_t list[])
Definition fuse.c:4654
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
int fuse_interrupted(void)
Definition fuse.c:4663
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_start_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4906
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
Definition fuse.h:1383
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_exit(struct fuse *f)
Definition fuse.c:4639
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4433
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4914
fuse_fill_dir_flags
Definition fuse.h:58
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_SPLICE_READ
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_IS_FD
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
int fuse_version(void)
Definition fuse.c:5206
@ FUSE_BUF_SPLICE_MOVE
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_session_exited(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
void fuse_session_reset(struct fuse_session *se)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_reply_xattr(fuse_req_t req, size_t count)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
enum fuse_buf_flags flags
void * mem
size_t size
struct fuse_buf buf[1]
int32_t show_help
Definition fuse.h:279
uint32_t no_interrupt
void * private_data
Definition fuse.h:874
mode_t umask
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t noflush
Definition fuse_common.h:93
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
void(* init)(void *userdata, struct fuse_conn_info *conn)
void(* setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse_8c_source.html0000644000175000017500000330527315156613443022141 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse.c Source File
libfuse
fuse.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the high-level FUSE API on top of the low-level
6 API.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#define _GNU_SOURCE
13#include "fuse.h"
14#include <pthread.h>
15
16#include "fuse_config.h"
17#include "fuse_i.h"
18#include "fuse_lowlevel.h"
19#include "fuse_opt.h"
20#include "fuse_misc.h"
21#include "fuse_kernel.h"
22#include "util.h"
23
24#include <stdint.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <stddef.h>
29#include <stdbool.h>
30#include <unistd.h>
31#include <time.h>
32#include <fcntl.h>
33#include <limits.h>
34#include <errno.h>
35#include <signal.h>
36#include <dlfcn.h>
37#include <assert.h>
38#include <poll.h>
39#include <sys/param.h>
40#include <sys/uio.h>
41#include <sys/time.h>
42#include <sys/mman.h>
43#include <sys/file.h>
44
45#define FUSE_NODE_SLAB 1
46
47#ifndef MAP_ANONYMOUS
48#undef FUSE_NODE_SLAB
49#endif
50
51#ifndef RENAME_EXCHANGE
52#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
53#endif
54
55#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
56
57#define FUSE_UNKNOWN_INO 0xffffffff
58#define OFFSET_MAX 0x7fffffffffffffffLL
59
60#define NODE_TABLE_MIN_SIZE 8192
61
62struct fuse_fs {
63 struct fuse_operations op;
64 void *user_data;
65 int debug;
66};
67
68struct fusemod_so {
69 void *handle;
70 int ctr;
71};
72
73struct lock_queue_element {
74 struct lock_queue_element *next;
75 pthread_cond_t cond;
76 fuse_ino_t nodeid1;
77 const char *name1;
78 char **path1;
79 struct node **wnode1;
80 fuse_ino_t nodeid2;
81 const char *name2;
82 char **path2;
83 struct node **wnode2;
84 int err;
85 bool done : 1;
86};
87
88struct node_table {
89 struct node **array;
90 size_t use;
91 size_t size;
92 size_t split;
93};
94
95#define list_entry(ptr, type, member) \
96 container_of(ptr, type, member)
97
98struct list_head {
99 struct list_head *next;
100 struct list_head *prev;
101};
102
103struct node_slab {
104 struct list_head list; /* must be the first member */
105 struct list_head freelist;
106 int used;
107};
108
109struct fuse {
110 struct fuse_session *se;
111 struct node_table name_table;
112 struct node_table id_table;
113 struct list_head lru_table;
114 fuse_ino_t ctr;
115 unsigned int generation;
116 unsigned int hidectr;
117 pthread_mutex_t lock;
118 struct fuse_config conf;
119 int intr_installed;
120 struct fuse_fs *fs;
121 struct lock_queue_element *lockq;
122 int pagesize;
123 struct list_head partial_slabs;
124 struct list_head full_slabs;
125 pthread_t prune_thread;
126};
127
128struct lock {
129 int type;
130 off_t start;
131 off_t end;
132 pid_t pid;
133 uint64_t owner;
134 struct lock *next;
135};
136
137struct node {
138 struct node *name_next;
139 struct node *id_next;
140 fuse_ino_t nodeid;
141 unsigned int generation;
142 int refctr;
143 struct node *parent;
144 char *name;
145 uint64_t nlookup;
146 int open_count;
147 struct timespec stat_updated;
148 struct timespec mtime;
149 off_t size;
150 struct lock *locks;
151 unsigned int is_hidden : 1;
152 unsigned int cache_valid : 1;
153 int treelock;
154 char inline_name[32];
155};
156
157#define TREELOCK_WRITE -1
158#define TREELOCK_WAIT_OFFSET INT_MIN
159
160struct node_lru {
161 struct node node;
162 struct list_head lru;
163 struct timespec forget_time;
164};
165
166struct fuse_direntry {
167 struct stat stat;
168 enum fuse_fill_dir_flags flags;
169 char *name;
170 struct fuse_direntry *next;
171};
172
173struct fuse_dh {
174 pthread_mutex_t lock;
175 struct fuse *fuse;
176 fuse_req_t req;
177 char *contents;
178 struct fuse_direntry *first;
179 struct fuse_direntry **last;
180 unsigned len;
181 unsigned size;
182 unsigned needlen;
183 int filled;
184 uint64_t fh;
185 int error;
186 fuse_ino_t nodeid;
187};
188
189struct fuse_context_i {
190 struct fuse_context ctx;
191 fuse_req_t req;
192};
193
194/* Defined by FUSE_REGISTER_MODULE() in lib/modules/subdir.c and iconv.c. */
195extern fuse_module_factory_t fuse_module_subdir_factory;
196#ifdef HAVE_ICONV
197extern fuse_module_factory_t fuse_module_iconv_factory;
198#endif
199
200static pthread_key_t fuse_context_key;
201static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
202static int fuse_context_ref;
203static struct fuse_module *fuse_modules = NULL;
204
205static int fuse_register_module(const char *name,
206 fuse_module_factory_t factory,
207 struct fusemod_so *so)
208{
209 struct fuse_module *mod;
210
211 mod = calloc(1, sizeof(struct fuse_module));
212 if (!mod) {
213 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module\n");
214 return -1;
215 }
216 mod->name = strdup(name);
217 if (!mod->name) {
218 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module name\n");
219 free(mod);
220 return -1;
221 }
222 mod->factory = factory;
223 mod->ctr = 0;
224 mod->so = so;
225 if (mod->so)
226 mod->so->ctr++;
227 mod->next = fuse_modules;
228 fuse_modules = mod;
229
230 return 0;
231}
232
233static void fuse_unregister_module(struct fuse_module *m)
234{
235 struct fuse_module **mp;
236 for (mp = &fuse_modules; *mp; mp = &(*mp)->next) {
237 if (*mp == m) {
238 *mp = (*mp)->next;
239 break;
240 }
241 }
242 free(m->name);
243 free(m);
244}
245
246static int fuse_load_so_module(const char *module)
247{
248 int ret = -1;
249 char *tmp;
250 struct fusemod_so *so;
251 fuse_module_factory_t *factory;
252
253 tmp = malloc(strlen(module) + 64);
254 if (!tmp) {
255 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
256 return -1;
257 }
258 sprintf(tmp, "libfusemod_%s.so", module);
259 so = calloc(1, sizeof(struct fusemod_so));
260 if (!so) {
261 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module so\n");
262 goto out;
263 }
264
265 so->handle = dlopen(tmp, RTLD_NOW);
266 if (so->handle == NULL) {
267 fuse_log(FUSE_LOG_ERR, "fuse: dlopen(%s) failed: %s\n",
268 tmp, dlerror());
269 goto out_free_so;
270 }
271
272 sprintf(tmp, "fuse_module_%s_factory", module);
273 factory = (fuse_module_factory_t*)dlsym(so->handle, tmp);
274 if (factory == NULL) {
275 fuse_log(FUSE_LOG_ERR, "fuse: symbol <%s> not found in module: %s\n",
276 tmp, dlerror());
277 goto out_dlclose;
278 }
279 ret = fuse_register_module(module, *factory, so);
280 if (ret)
281 goto out_dlclose;
282
283out:
284 free(tmp);
285 return ret;
286
287out_dlclose:
288 dlclose(so->handle);
289out_free_so:
290 free(so);
291 goto out;
292}
293
294static struct fuse_module *fuse_find_module(const char *module)
295{
296 struct fuse_module *m;
297 for (m = fuse_modules; m; m = m->next) {
298 if (strcmp(module, m->name) == 0) {
299 m->ctr++;
300 break;
301 }
302 }
303 return m;
304}
305
306static struct fuse_module *fuse_get_module(const char *module)
307{
308 struct fuse_module *m;
309
310 pthread_mutex_lock(&fuse_context_lock);
311 m = fuse_find_module(module);
312 if (!m) {
313 int err = fuse_load_so_module(module);
314 if (!err)
315 m = fuse_find_module(module);
316 }
317 pthread_mutex_unlock(&fuse_context_lock);
318 return m;
319}
320
321static void fuse_put_module(struct fuse_module *m)
322{
323 pthread_mutex_lock(&fuse_context_lock);
324 if (m->so)
325 assert(m->ctr > 0);
326 /* Builtin modules may already have m->ctr == 0 */
327 if (m->ctr > 0)
328 m->ctr--;
329 if (!m->ctr && m->so) {
330 struct fusemod_so *so = m->so;
331 assert(so->ctr > 0);
332 so->ctr--;
333 if (!so->ctr) {
334 struct fuse_module **mp;
335 for (mp = &fuse_modules; *mp;) {
336 if ((*mp)->so == so)
337 fuse_unregister_module(*mp);
338 else
339 mp = &(*mp)->next;
340 }
341 dlclose(so->handle);
342 free(so);
343 }
344 } else if (!m->ctr) {
345 fuse_unregister_module(m);
346 }
347 pthread_mutex_unlock(&fuse_context_lock);
348}
349
350static void init_list_head(struct list_head *list)
351{
352 list->next = list;
353 list->prev = list;
354}
355
356static int list_empty(const struct list_head *head)
357{
358 return head->next == head;
359}
360
361static void list_add(struct list_head *new, struct list_head *prev,
362 struct list_head *next)
363{
364 next->prev = new;
365 new->next = next;
366 new->prev = prev;
367 prev->next = new;
368}
369
370static inline void list_add_head(struct list_head *new, struct list_head *head)
371{
372 list_add(new, head, head->next);
373}
374
375static inline void list_add_tail(struct list_head *new, struct list_head *head)
376{
377 list_add(new, head->prev, head);
378}
379
380static inline void list_del(struct list_head *entry)
381{
382 struct list_head *prev = entry->prev;
383 struct list_head *next = entry->next;
384
385 next->prev = prev;
386 prev->next = next;
387}
388
389static inline int lru_enabled(struct fuse *f)
390{
391 return f->conf.remember > 0;
392}
393
394static struct node_lru *node_lru(struct node *node)
395{
396 return (struct node_lru *) node;
397}
398
399static size_t get_node_size(struct fuse *f)
400{
401 if (lru_enabled(f))
402 return sizeof(struct node_lru);
403 else
404 return sizeof(struct node);
405}
406
407#ifdef FUSE_NODE_SLAB
408static struct node_slab *list_to_slab(struct list_head *head)
409{
410 return (struct node_slab *) head;
411}
412
413static struct node_slab *node_to_slab(struct fuse *f, struct node *node)
414{
415 return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1));
416}
417
418static int alloc_slab(struct fuse *f)
419{
420 void *mem;
421 struct node_slab *slab;
422 char *start;
423 size_t num;
424 size_t i;
425 size_t node_size = get_node_size(f);
426
427 mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE,
428 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
429
430 if (mem == MAP_FAILED)
431 return -1;
432
433 slab = mem;
434 init_list_head(&slab->freelist);
435 slab->used = 0;
436 num = (f->pagesize - sizeof(struct node_slab)) / node_size;
437
438 start = (char *) mem + f->pagesize - num * node_size;
439 for (i = 0; i < num; i++) {
440 struct list_head *n;
441
442 n = (struct list_head *) (start + i * node_size);
443 list_add_tail(n, &slab->freelist);
444 }
445 list_add_tail(&slab->list, &f->partial_slabs);
446
447 return 0;
448}
449
450static struct node *alloc_node(struct fuse *f)
451{
452 struct node_slab *slab;
453 struct list_head *node;
454
455 if (list_empty(&f->partial_slabs)) {
456 int res = alloc_slab(f);
457 if (res != 0)
458 return NULL;
459 }
460 slab = list_to_slab(f->partial_slabs.next);
461 slab->used++;
462 node = slab->freelist.next;
463 list_del(node);
464 if (list_empty(&slab->freelist)) {
465 list_del(&slab->list);
466 list_add_tail(&slab->list, &f->full_slabs);
467 }
468 memset(node, 0, sizeof(struct node));
469
470 return (struct node *) node;
471}
472
473static void free_slab(struct fuse *f, struct node_slab *slab)
474{
475 int res;
476
477 list_del(&slab->list);
478 res = munmap(slab, f->pagesize);
479 if (res == -1)
480 fuse_log(FUSE_LOG_WARNING, "fuse warning: munmap(%p) failed\n",
481 slab);
482}
483
484static void free_node_mem(struct fuse *f, struct node *node)
485{
486 struct node_slab *slab = node_to_slab(f, node);
487 struct list_head *n = (struct list_head *) node;
488
489 slab->used--;
490 if (slab->used) {
491 if (list_empty(&slab->freelist)) {
492 list_del(&slab->list);
493 list_add_tail(&slab->list, &f->partial_slabs);
494 }
495 list_add_head(n, &slab->freelist);
496 } else {
497 free_slab(f, slab);
498 }
499}
500#else
501static struct node *alloc_node(struct fuse *f)
502{
503 return (struct node *) calloc(1, get_node_size(f));
504}
505
506static void free_node_mem(struct fuse *f, struct node *node)
507{
508 (void) f;
509 free(node);
510}
511#endif
512
513static size_t id_hash(struct fuse *f, fuse_ino_t ino)
514{
515 uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size;
516 uint64_t oldhash = hash % (f->id_table.size / 2);
517
518 if (oldhash >= f->id_table.split)
519 return oldhash;
520 else
521 return hash;
522}
523
524static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
525{
526 size_t hash = id_hash(f, nodeid);
527 struct node *node;
528
529 for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
530 if (node->nodeid == nodeid)
531 return node;
532
533 return NULL;
534}
535
536static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
537{
538 struct node *node = get_node_nocheck(f, nodeid);
539 if (!node) {
540 fuse_log(FUSE_LOG_ERR, "fuse internal error: node %llu not found\n",
541 (unsigned long long) nodeid);
542 abort();
543 }
544 return node;
545}
546
547static void curr_time(struct timespec *now);
548static double diff_timespec(const struct timespec *t1,
549 const struct timespec *t2);
550
551static void remove_node_lru(struct node *node)
552{
553 struct node_lru *lnode = node_lru(node);
554 list_del(&lnode->lru);
555 init_list_head(&lnode->lru);
556}
557
558static void set_forget_time(struct fuse *f, struct node *node)
559{
560 struct node_lru *lnode = node_lru(node);
561
562 list_del(&lnode->lru);
563 list_add_tail(&lnode->lru, &f->lru_table);
564 curr_time(&lnode->forget_time);
565}
566
567static void free_node(struct fuse *f, struct node *node)
568{
569 if (node->name != node->inline_name)
570 free(node->name);
571 free_node_mem(f, node);
572}
573
574static void node_table_reduce(struct node_table *t)
575{
576 size_t newsize = t->size / 2;
577 void *newarray;
578
579 if (newsize < NODE_TABLE_MIN_SIZE)
580 return;
581
582 newarray = realloc(t->array, sizeof(struct node *) * newsize);
583 if (newarray != NULL)
584 t->array = newarray;
585
586 t->size = newsize;
587 t->split = t->size / 2;
588}
589
590static void remerge_id(struct fuse *f)
591{
592 struct node_table *t = &f->id_table;
593 int iter;
594
595 if (t->split == 0)
596 node_table_reduce(t);
597
598 for (iter = 8; t->split > 0 && iter; iter--) {
599 struct node **upper;
600
601 t->split--;
602 upper = &t->array[t->split + t->size / 2];
603 if (*upper) {
604 struct node **nodep;
605
606 for (nodep = &t->array[t->split]; *nodep;
607 nodep = &(*nodep)->id_next);
608
609 *nodep = *upper;
610 *upper = NULL;
611 break;
612 }
613 }
614}
615
616static void unhash_id(struct fuse *f, struct node *node)
617{
618 struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
619
620 for (; *nodep != NULL; nodep = &(*nodep)->id_next)
621 if (*nodep == node) {
622 *nodep = node->id_next;
623 f->id_table.use--;
624
625 if(f->id_table.use < f->id_table.size / 4)
626 remerge_id(f);
627 return;
628 }
629}
630
631static int node_table_resize(struct node_table *t)
632{
633 size_t newsize = t->size * 2;
634 void *newarray;
635
636 newarray = realloc(t->array, sizeof(struct node *) * newsize);
637 if (newarray == NULL)
638 return -1;
639
640 t->array = newarray;
641 memset(t->array + t->size, 0, t->size * sizeof(struct node *));
642 t->size = newsize;
643 t->split = 0;
644
645 return 0;
646}
647
648static void rehash_id(struct fuse *f)
649{
650 struct node_table *t = &f->id_table;
651 struct node **nodep;
652 struct node **next;
653 size_t hash;
654
655 if (t->split == t->size / 2)
656 return;
657
658 hash = t->split;
659 t->split++;
660 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
661 struct node *node = *nodep;
662 size_t newhash = id_hash(f, node->nodeid);
663
664 if (newhash != hash) {
665 next = nodep;
666 *nodep = node->id_next;
667 node->id_next = t->array[newhash];
668 t->array[newhash] = node;
669 } else {
670 next = &node->id_next;
671 }
672 }
673 if (t->split == t->size / 2)
674 node_table_resize(t);
675}
676
677static void hash_id(struct fuse *f, struct node *node)
678{
679 size_t hash = id_hash(f, node->nodeid);
680 node->id_next = f->id_table.array[hash];
681 f->id_table.array[hash] = node;
682 f->id_table.use++;
683
684 if (f->id_table.use >= f->id_table.size / 2)
685 rehash_id(f);
686}
687
688static size_t name_hash(struct fuse *f, fuse_ino_t parent,
689 const char *name)
690{
691 uint64_t hash = parent;
692 uint64_t oldhash;
693
694 for (; *name; name++)
695 hash = hash * 31 + (unsigned char) *name;
696
697 hash %= f->name_table.size;
698 oldhash = hash % (f->name_table.size / 2);
699 if (oldhash >= f->name_table.split)
700 return oldhash;
701 else
702 return hash;
703}
704
705static void unref_node(struct fuse *f, struct node *node);
706
707static void remerge_name(struct fuse *f)
708{
709 struct node_table *t = &f->name_table;
710 int iter;
711
712 if (t->split == 0)
713 node_table_reduce(t);
714
715 for (iter = 8; t->split > 0 && iter; iter--) {
716 struct node **upper;
717
718 t->split--;
719 upper = &t->array[t->split + t->size / 2];
720 if (*upper) {
721 struct node **nodep;
722
723 for (nodep = &t->array[t->split]; *nodep;
724 nodep = &(*nodep)->name_next);
725
726 *nodep = *upper;
727 *upper = NULL;
728 break;
729 }
730 }
731}
732
733static void unhash_name(struct fuse *f, struct node *node)
734{
735 if (node->name) {
736 size_t hash = name_hash(f, node->parent->nodeid, node->name);
737 struct node **nodep = &f->name_table.array[hash];
738
739 for (; *nodep != NULL; nodep = &(*nodep)->name_next)
740 if (*nodep == node) {
741 *nodep = node->name_next;
742 node->name_next = NULL;
743 unref_node(f, node->parent);
744 if (node->name != node->inline_name)
745 free(node->name);
746 node->name = NULL;
747 node->parent = NULL;
748 f->name_table.use--;
749
750 if (f->name_table.use < f->name_table.size / 4)
751 remerge_name(f);
752 return;
753 }
754 fuse_log(FUSE_LOG_ERR,
755 "fuse internal error: unable to unhash node: %llu\n",
756 (unsigned long long) node->nodeid);
757 abort();
758 }
759}
760
761static void rehash_name(struct fuse *f)
762{
763 struct node_table *t = &f->name_table;
764 struct node **nodep;
765 struct node **next;
766 size_t hash;
767
768 if (t->split == t->size / 2)
769 return;
770
771 hash = t->split;
772 t->split++;
773 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
774 struct node *node = *nodep;
775 size_t newhash = name_hash(f, node->parent->nodeid, node->name);
776
777 if (newhash != hash) {
778 next = nodep;
779 *nodep = node->name_next;
780 node->name_next = t->array[newhash];
781 t->array[newhash] = node;
782 } else {
783 next = &node->name_next;
784 }
785 }
786 if (t->split == t->size / 2)
787 node_table_resize(t);
788}
789
790static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
791 const char *name)
792{
793 size_t hash = name_hash(f, parentid, name);
794 struct node *parent = get_node(f, parentid);
795 if (strlen(name) < sizeof(node->inline_name)) {
796 strcpy(node->inline_name, name);
797 node->name = node->inline_name;
798 } else {
799 node->name = strdup(name);
800 if (node->name == NULL)
801 return -1;
802 }
803
804 parent->refctr ++;
805 node->parent = parent;
806 node->name_next = f->name_table.array[hash];
807 f->name_table.array[hash] = node;
808 f->name_table.use++;
809
810 if (f->name_table.use >= f->name_table.size / 2)
811 rehash_name(f);
812
813 return 0;
814}
815
816static void delete_node(struct fuse *f, struct node *node)
817{
818 if (f->conf.debug)
819 fuse_log(FUSE_LOG_DEBUG, "DELETE: %llu\n",
820 (unsigned long long) node->nodeid);
821
822 assert(node->treelock == 0);
823 unhash_name(f, node);
824 if (lru_enabled(f))
825 remove_node_lru(node);
826 unhash_id(f, node);
827 free_node(f, node);
828}
829
830static void unref_node(struct fuse *f, struct node *node)
831{
832 assert(node->refctr > 0);
833 node->refctr --;
834 if (!node->refctr)
835 delete_node(f, node);
836}
837
838static fuse_ino_t next_id(struct fuse *f)
839{
840 do {
841 f->ctr = (f->ctr + 1) & 0xffffffff;
842 if (!f->ctr)
843 f->generation ++;
844 } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
845 get_node_nocheck(f, f->ctr) != NULL);
846 return f->ctr;
847}
848
849static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
850 const char *name)
851{
852 size_t hash = name_hash(f, parent, name);
853 struct node *node;
854
855 for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
856 if (node->parent->nodeid == parent &&
857 strcmp(node->name, name) == 0)
858 return node;
859
860 return NULL;
861}
862
863static void inc_nlookup(struct node *node)
864{
865 if (!node->nlookup)
866 node->refctr++;
867 node->nlookup++;
868}
869
870static struct node *find_node(struct fuse *f, fuse_ino_t parent,
871 const char *name)
872{
873 struct node *node;
874
875 pthread_mutex_lock(&f->lock);
876 if (!name)
877 node = get_node(f, parent);
878 else
879 node = lookup_node(f, parent, name);
880 if (node == NULL) {
881 node = alloc_node(f);
882 if (node == NULL)
883 goto out_err;
884
885 node->nodeid = next_id(f);
886 node->generation = f->generation;
887 if (f->conf.remember)
888 inc_nlookup(node);
889
890 if (hash_name(f, node, parent, name) == -1) {
891 free_node(f, node);
892 node = NULL;
893 goto out_err;
894 }
895 hash_id(f, node);
896 if (lru_enabled(f)) {
897 struct node_lru *lnode = node_lru(node);
898 init_list_head(&lnode->lru);
899 }
900 } else if (lru_enabled(f) && node->nlookup == 1) {
901 remove_node_lru(node);
902 }
903 inc_nlookup(node);
904out_err:
905 pthread_mutex_unlock(&f->lock);
906 return node;
907}
908
909static int lookup_path_in_cache(struct fuse *f,
910 const char *path, fuse_ino_t *inop)
911{
912 char *tmp = strdup(path);
913 if (!tmp)
914 return -ENOMEM;
915
916 pthread_mutex_lock(&f->lock);
917 fuse_ino_t ino = FUSE_ROOT_ID;
918
919 int err = 0;
920 char *save_ptr;
921 char *path_element = strtok_r(tmp, "/", &save_ptr);
922 while (path_element != NULL) {
923 struct node *node = lookup_node(f, ino, path_element);
924 if (node == NULL) {
925 err = -ENOENT;
926 break;
927 }
928 ino = node->nodeid;
929 path_element = strtok_r(NULL, "/", &save_ptr);
930 }
931 pthread_mutex_unlock(&f->lock);
932 free(tmp);
933
934 if (!err)
935 *inop = ino;
936 return err;
937}
938
939static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name)
940{
941 size_t len = strlen(name);
942
943 if (s - len <= *buf) {
944 unsigned pathlen = *bufsize - (s - *buf);
945 unsigned newbufsize = *bufsize;
946 char *newbuf;
947
948 while (newbufsize < pathlen + len + 1) {
949 if (newbufsize >= 0x80000000)
950 newbufsize = 0xffffffff;
951 else
952 newbufsize *= 2;
953 }
954
955 newbuf = realloc(*buf, newbufsize);
956 if (newbuf == NULL)
957 return NULL;
958
959 *buf = newbuf;
960 s = newbuf + newbufsize - pathlen;
961 memmove(s, newbuf + *bufsize - pathlen, pathlen);
962 *bufsize = newbufsize;
963 }
964 s -= len;
965 memcpy(s, name, len);
966 s--;
967 *s = '/';
968
969 return s;
970}
971
972static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
973 struct node *end)
974{
975 struct node *node;
976
977 if (wnode) {
978 assert(wnode->treelock == TREELOCK_WRITE);
979 wnode->treelock = 0;
980 }
981
982 for (node = get_node(f, nodeid);
983 node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) {
984 assert(node->treelock != 0);
985 assert(node->treelock != TREELOCK_WAIT_OFFSET);
986 assert(node->treelock != TREELOCK_WRITE);
987 node->treelock--;
988 if (node->treelock == TREELOCK_WAIT_OFFSET)
989 node->treelock = 0;
990 }
991}
992
993static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
994 char **path, struct node **wnodep, bool need_lock)
995{
996 unsigned bufsize = 256;
997 char *buf;
998 char *s;
999 struct node *node;
1000 struct node *wnode = NULL;
1001 int err;
1002
1003 *path = NULL;
1004
1005 err = -ENOMEM;
1006 buf = malloc(bufsize);
1007 if (buf == NULL)
1008 goto out_err;
1009
1010 s = buf + bufsize - 1;
1011 *s = '\0';
1012
1013 if (name != NULL) {
1014 s = add_name(&buf, &bufsize, s, name);
1015 err = -ENOMEM;
1016 if (s == NULL)
1017 goto out_free;
1018 }
1019
1020 if (wnodep) {
1021 assert(need_lock);
1022 wnode = lookup_node(f, nodeid, name);
1023 if (wnode) {
1024 if (wnode->treelock != 0) {
1025 if (wnode->treelock > 0)
1026 wnode->treelock += TREELOCK_WAIT_OFFSET;
1027 err = -EAGAIN;
1028 goto out_free;
1029 }
1030 wnode->treelock = TREELOCK_WRITE;
1031 }
1032 }
1033
1034 for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
1035 node = node->parent) {
1036 err = -ESTALE;
1037 if (node->name == NULL || node->parent == NULL)
1038 goto out_unlock;
1039
1040 err = -ENOMEM;
1041 s = add_name(&buf, &bufsize, s, node->name);
1042 if (s == NULL)
1043 goto out_unlock;
1044
1045 if (need_lock) {
1046 err = -EAGAIN;
1047 if (node->treelock < 0)
1048 goto out_unlock;
1049
1050 node->treelock++;
1051 }
1052 }
1053
1054 if (s[0])
1055 memmove(buf, s, bufsize - (s - buf));
1056 else
1057 strcpy(buf, "/");
1058
1059 *path = buf;
1060 if (wnodep)
1061 *wnodep = wnode;
1062
1063 return 0;
1064
1065 out_unlock:
1066 if (need_lock)
1067 unlock_path(f, nodeid, wnode, node);
1068 out_free:
1069 free(buf);
1070
1071 out_err:
1072 return err;
1073}
1074
1075static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1076 fuse_ino_t nodeid2, const char *name2,
1077 char **path1, char **path2,
1078 struct node **wnode1, struct node **wnode2)
1079{
1080 int err;
1081
1082 /* FIXME: locking two paths needs deadlock checking */
1083 err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
1084 if (!err) {
1085 err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
1086 if (err) {
1087 struct node *wn1 = wnode1 ? *wnode1 : NULL;
1088
1089 unlock_path(f, nodeid1, wn1, NULL);
1090 free(*path1);
1091 }
1092 }
1093 return err;
1094}
1095
1096static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
1097{
1098 int err;
1099
1100 if (!qe->path1) {
1101 /* Just waiting for it to be unlocked */
1102 if (get_node(f, qe->nodeid1)->treelock == 0)
1103 pthread_cond_signal(&qe->cond);
1104
1105 return;
1106 }
1107
1108 if (qe->done)
1109 return; // Don't try to double-lock the element
1110
1111 if (!qe->path2) {
1112 err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
1113 qe->wnode1, true);
1114 } else {
1115 err = try_get_path2(f, qe->nodeid1, qe->name1, qe->nodeid2,
1116 qe->name2, qe->path1, qe->path2, qe->wnode1,
1117 qe->wnode2);
1118 }
1119
1120 if (err == -EAGAIN)
1121 return; /* keep trying */
1122
1123 qe->err = err;
1124 qe->done = true;
1125 pthread_cond_signal(&qe->cond);
1126}
1127
1128static void wake_up_queued(struct fuse *f)
1129{
1130 struct lock_queue_element *qe;
1131
1132 for (qe = f->lockq; qe != NULL; qe = qe->next)
1133 queue_element_wakeup(f, qe);
1134}
1135
1136static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid,
1137 const char *name, bool wr)
1138{
1139 if (f->conf.debug) {
1140 struct node *wnode = NULL;
1141
1142 if (wr)
1143 wnode = lookup_node(f, nodeid, name);
1144
1145 if (wnode) {
1146 fuse_log(FUSE_LOG_DEBUG, "%s %llu (w)\n",
1147 msg, (unsigned long long) wnode->nodeid);
1148 } else {
1149 fuse_log(FUSE_LOG_DEBUG, "%s %llu\n",
1150 msg, (unsigned long long) nodeid);
1151 }
1152 }
1153}
1154
1155static void queue_path(struct fuse *f, struct lock_queue_element *qe)
1156{
1157 struct lock_queue_element **qp;
1158
1159 qe->done = false;
1160 pthread_cond_init(&qe->cond, NULL);
1161 qe->next = NULL;
1162 for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
1163 *qp = qe;
1164}
1165
1166static void dequeue_path(struct fuse *f, struct lock_queue_element *qe)
1167{
1168 struct lock_queue_element **qp;
1169
1170 pthread_cond_destroy(&qe->cond);
1171 for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next);
1172 *qp = qe->next;
1173}
1174
1175static int wait_path(struct fuse *f, struct lock_queue_element *qe)
1176{
1177 queue_path(f, qe);
1178
1179 do {
1180 pthread_cond_wait(&qe->cond, &f->lock);
1181 } while (!qe->done);
1182
1183 dequeue_path(f, qe);
1184
1185 return qe->err;
1186}
1187
1188static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name,
1189 char **path, struct node **wnode)
1190{
1191 int err;
1192
1193 pthread_mutex_lock(&f->lock);
1194 err = try_get_path(f, nodeid, name, path, wnode, true);
1195 if (err == -EAGAIN) {
1196 struct lock_queue_element qe = {
1197 .nodeid1 = nodeid,
1198 .name1 = name,
1199 .path1 = path,
1200 .wnode1 = wnode,
1201 };
1202 debug_path(f, "QUEUE PATH", nodeid, name, !!wnode);
1203 err = wait_path(f, &qe);
1204 debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode);
1205 }
1206 pthread_mutex_unlock(&f->lock);
1207
1208 return err;
1209}
1210
1211static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
1212{
1213 return get_path_common(f, nodeid, NULL, path, NULL);
1214}
1215
1216static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
1217{
1218 int err = 0;
1219
1220 if (f->conf.nullpath_ok) {
1221 *path = NULL;
1222 } else {
1223 err = get_path_common(f, nodeid, NULL, path, NULL);
1224 if (err == -ESTALE)
1225 err = 0;
1226 }
1227
1228 return err;
1229}
1230
1231static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
1232 char **path)
1233{
1234 return get_path_common(f, nodeid, name, path, NULL);
1235}
1236
1237static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
1238 char **path, struct node **wnode)
1239{
1240 return get_path_common(f, nodeid, name, path, wnode);
1241}
1242
1243#if defined(__FreeBSD__)
1244#define CHECK_DIR_LOOP
1245#endif
1246
1247#if defined(CHECK_DIR_LOOP)
1248static int check_dir_loop(struct fuse *f,
1249 fuse_ino_t nodeid1, const char *name1,
1250 fuse_ino_t nodeid2, const char *name2)
1251{
1252 struct node *node, *node1, *node2;
1253 fuse_ino_t id1, id2;
1254
1255 node1 = lookup_node(f, nodeid1, name1);
1256 id1 = node1 ? node1->nodeid : nodeid1;
1257
1258 node2 = lookup_node(f, nodeid2, name2);
1259 id2 = node2 ? node2->nodeid : nodeid2;
1260
1261 for (node = get_node(f, id2); node->nodeid != FUSE_ROOT_ID;
1262 node = node->parent) {
1263 if (node->name == NULL || node->parent == NULL)
1264 break;
1265
1266 if (node->nodeid != id2 && node->nodeid == id1)
1267 return -EINVAL;
1268 }
1269
1270 if (node2)
1271 {
1272 for (node = get_node(f, id1); node->nodeid != FUSE_ROOT_ID;
1273 node = node->parent) {
1274 if (node->name == NULL || node->parent == NULL)
1275 break;
1276
1277 if (node->nodeid != id1 && node->nodeid == id2)
1278 return -ENOTEMPTY;
1279 }
1280 }
1281
1282 return 0;
1283}
1284#endif
1285
1286static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1287 fuse_ino_t nodeid2, const char *name2,
1288 char **path1, char **path2,
1289 struct node **wnode1, struct node **wnode2)
1290{
1291 int err;
1292
1293 pthread_mutex_lock(&f->lock);
1294
1295#if defined(CHECK_DIR_LOOP)
1296 if (name1)
1297 {
1298 // called during rename; perform dir loop check
1299 err = check_dir_loop(f, nodeid1, name1, nodeid2, name2);
1300 if (err)
1301 goto out_unlock;
1302 }
1303#endif
1304
1305 err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
1306 path1, path2, wnode1, wnode2);
1307 if (err == -EAGAIN) {
1308 struct lock_queue_element qe = {
1309 .nodeid1 = nodeid1,
1310 .name1 = name1,
1311 .path1 = path1,
1312 .wnode1 = wnode1,
1313 .nodeid2 = nodeid2,
1314 .name2 = name2,
1315 .path2 = path2,
1316 .wnode2 = wnode2,
1317 };
1318
1319 debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1);
1320 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1321 err = wait_path(f, &qe);
1322 debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
1323 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1324 }
1325
1326#if defined(CHECK_DIR_LOOP)
1327out_unlock:
1328#endif
1329 pthread_mutex_unlock(&f->lock);
1330
1331 return err;
1332}
1333
1334static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid,
1335 struct node *wnode, char *path)
1336{
1337 pthread_mutex_lock(&f->lock);
1338 unlock_path(f, nodeid, wnode, NULL);
1339 if (f->lockq)
1340 wake_up_queued(f);
1341 pthread_mutex_unlock(&f->lock);
1342 free(path);
1343}
1344
1345static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path)
1346{
1347 if (path)
1348 free_path_wrlock(f, nodeid, NULL, path);
1349}
1350
1351static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2,
1352 struct node *wnode1, struct node *wnode2,
1353 char *path1, char *path2)
1354{
1355 pthread_mutex_lock(&f->lock);
1356 unlock_path(f, nodeid1, wnode1, NULL);
1357 unlock_path(f, nodeid2, wnode2, NULL);
1358 wake_up_queued(f);
1359 pthread_mutex_unlock(&f->lock);
1360 free(path1);
1361 free(path2);
1362}
1363
1364static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
1365{
1366 struct node *node;
1367 if (nodeid == FUSE_ROOT_ID)
1368 return;
1369 pthread_mutex_lock(&f->lock);
1370 node = get_node(f, nodeid);
1371
1372 /*
1373 * Node may still be locked due to interrupt idiocy in open,
1374 * create and opendir
1375 */
1376 while (node->nlookup == nlookup && node->treelock) {
1377 struct lock_queue_element qe = {
1378 .nodeid1 = nodeid,
1379 };
1380
1381 debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false);
1382 queue_path(f, &qe);
1383
1384 do {
1385 pthread_cond_wait(&qe.cond, &f->lock);
1386 } while (node->nlookup == nlookup && node->treelock);
1387
1388 dequeue_path(f, &qe);
1389 debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false);
1390 }
1391
1392 assert(node->nlookup >= nlookup);
1393 node->nlookup -= nlookup;
1394 if (!node->nlookup) {
1395 unref_node(f, node);
1396 } else if (lru_enabled(f) && node->nlookup == 1) {
1397 set_forget_time(f, node);
1398 }
1399 pthread_mutex_unlock(&f->lock);
1400}
1401
1402static void unlink_node(struct fuse *f, struct node *node)
1403{
1404 if (f->conf.remember) {
1405 assert(node->nlookup > 1);
1406 node->nlookup--;
1407 }
1408 unhash_name(f, node);
1409}
1410
1411static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
1412{
1413 struct node *node;
1414
1415 pthread_mutex_lock(&f->lock);
1416 node = lookup_node(f, dir, name);
1417 if (node != NULL)
1418 unlink_node(f, node);
1419 pthread_mutex_unlock(&f->lock);
1420}
1421
1422static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1423 fuse_ino_t newdir, const char *newname, int hide)
1424{
1425 struct node *node;
1426 struct node *newnode;
1427 int err = 0;
1428
1429 pthread_mutex_lock(&f->lock);
1430 node = lookup_node(f, olddir, oldname);
1431 newnode = lookup_node(f, newdir, newname);
1432 if (node == NULL)
1433 goto out;
1434
1435 if (newnode != NULL) {
1436 if (hide) {
1437 fuse_log(FUSE_LOG_ERR, "fuse: hidden file got created during hiding\n");
1438 err = -EBUSY;
1439 goto out;
1440 }
1441 unlink_node(f, newnode);
1442 }
1443
1444 unhash_name(f, node);
1445 if (hash_name(f, node, newdir, newname) == -1) {
1446 err = -ENOMEM;
1447 goto out;
1448 }
1449
1450 if (hide)
1451 node->is_hidden = 1;
1452
1453out:
1454 pthread_mutex_unlock(&f->lock);
1455 return err;
1456}
1457
1458static int exchange_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1459 fuse_ino_t newdir, const char *newname)
1460{
1461 struct node *oldnode;
1462 struct node *newnode;
1463 int err;
1464
1465 pthread_mutex_lock(&f->lock);
1466 oldnode = lookup_node(f, olddir, oldname);
1467 newnode = lookup_node(f, newdir, newname);
1468
1469 if (oldnode)
1470 unhash_name(f, oldnode);
1471 if (newnode)
1472 unhash_name(f, newnode);
1473
1474 err = -ENOMEM;
1475 if (oldnode) {
1476 if (hash_name(f, oldnode, newdir, newname) == -1)
1477 goto out;
1478 }
1479 if (newnode) {
1480 if (hash_name(f, newnode, olddir, oldname) == -1)
1481 goto out;
1482 }
1483 err = 0;
1484out:
1485 pthread_mutex_unlock(&f->lock);
1486 return err;
1487}
1488
1489static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
1490{
1491 if (!f->conf.use_ino)
1492 stbuf->st_ino = nodeid;
1493 if (f->conf.set_mode) {
1494 if (f->conf.dmask && S_ISDIR(stbuf->st_mode))
1495 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1496 (0777 & ~f->conf.dmask);
1497 else if (f->conf.fmask)
1498 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1499 (0777 & ~f->conf.fmask);
1500 else
1501 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1502 (0777 & ~f->conf.umask);
1503 }
1504 if (f->conf.set_uid)
1505 stbuf->st_uid = f->conf.uid;
1506 if (f->conf.set_gid)
1507 stbuf->st_gid = f->conf.gid;
1508}
1509
1510#ifdef HAVE_STATX
1511static void set_statx(struct fuse *f, fuse_ino_t nodeid, struct statx *stxbuf)
1512{
1513 if (!f->conf.use_ino)
1514 stxbuf->stx_ino = nodeid;
1515 if (f->conf.set_mode) {
1516 if (f->conf.dmask && S_ISDIR(stxbuf->stx_mode))
1517 stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
1518 (0777 & ~f->conf.dmask);
1519 else if (f->conf.fmask)
1520 stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
1521 (0777 & ~f->conf.fmask);
1522 else
1523 stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
1524 (0777 & ~f->conf.umask);
1525 }
1526 if (f->conf.set_uid)
1527 stxbuf->stx_uid = f->conf.uid;
1528 if (f->conf.set_gid)
1529 stxbuf->stx_gid = f->conf.gid;
1530}
1531#endif
1532
1533static struct fuse *req_fuse(fuse_req_t req)
1534{
1535 return (struct fuse *) fuse_req_userdata(req);
1536}
1537
1538static void fuse_intr_sighandler(int sig)
1539{
1540 (void) sig;
1541 /* Nothing to do */
1542}
1543
1544struct fuse_intr_data {
1545 pthread_t id;
1546 pthread_cond_t cond;
1547 int finished;
1548};
1549
1550static void fuse_interrupt(fuse_req_t req, void *d_)
1551{
1552 struct fuse_intr_data *d = d_;
1553 struct fuse *f = req_fuse(req);
1554
1555 if (d->id == pthread_self())
1556 return;
1557
1558 pthread_mutex_lock(&f->lock);
1559 while (!d->finished) {
1560 struct timeval now;
1561 struct timespec timeout;
1562
1563 pthread_kill(d->id, f->conf.intr_signal);
1564 gettimeofday(&now, NULL);
1565 timeout.tv_sec = now.tv_sec + 1;
1566 timeout.tv_nsec = now.tv_usec * 1000;
1567 pthread_cond_timedwait(&d->cond, &f->lock, &timeout);
1568 }
1569 pthread_mutex_unlock(&f->lock);
1570}
1571
1572static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req,
1573 struct fuse_intr_data *d)
1574{
1575 pthread_mutex_lock(&f->lock);
1576 d->finished = 1;
1577 pthread_cond_broadcast(&d->cond);
1578 pthread_mutex_unlock(&f->lock);
1579 fuse_req_interrupt_func(req, NULL, NULL);
1580 pthread_cond_destroy(&d->cond);
1581}
1582
1583static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d)
1584{
1585 d->id = pthread_self();
1586 pthread_cond_init(&d->cond, NULL);
1587 d->finished = 0;
1588 fuse_req_interrupt_func(req, fuse_interrupt, d);
1589}
1590
1591static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req,
1592 struct fuse_intr_data *d)
1593{
1594 if (f->conf.intr)
1595 fuse_do_finish_interrupt(f, req, d);
1596}
1597
1598static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
1599 struct fuse_intr_data *d)
1600{
1601 if (f->conf.intr)
1602 fuse_do_prepare_interrupt(req, d);
1603}
1604
1605static const char* file_info_string(struct fuse_file_info *fi,
1606 char* buf, size_t len)
1607{
1608 if(fi == NULL)
1609 return "NULL";
1610 snprintf(buf, len, "%llu", (unsigned long long) fi->fh);
1611 return buf;
1612}
1613
1614int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
1615 struct fuse_file_info *fi)
1616{
1617 fuse_get_context()->private_data = fs->user_data;
1618 if (!fs->op.getattr)
1619 return -ENOSYS;
1620
1621 if (fs->debug) {
1622 char buf[10];
1623
1624 fuse_log(FUSE_LOG_DEBUG, "getattr[%s] %s\n",
1625 file_info_string(fi, buf, sizeof(buf)),
1626 path);
1627 }
1628 return fs->op.getattr(path, buf, fi);
1629}
1630
1631int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
1632 const char *newpath, unsigned int flags)
1633{
1634 fuse_get_context()->private_data = fs->user_data;
1635 if (!fs->op.rename)
1636 return -ENOSYS;
1637 if (fs->debug)
1638 fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath,
1639 flags);
1640
1641 return fs->op.rename(oldpath, newpath, flags);
1642}
1643
1644int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
1645{
1646 fuse_get_context()->private_data = fs->user_data;
1647 if (!fs->op.unlink)
1648 return -ENOSYS;
1649 if (fs->debug)
1650 fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path);
1651
1652 return fs->op.unlink(path);
1653}
1654
1655int fuse_fs_rmdir(struct fuse_fs *fs, const char *path)
1656{
1657 fuse_get_context()->private_data = fs->user_data;
1658 if (!fs->op.rmdir)
1659 return -ENOSYS;
1660 if (fs->debug)
1661 fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path);
1662
1663 return fs->op.rmdir(path);
1664}
1665
1666int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path)
1667{
1668 fuse_get_context()->private_data = fs->user_data;
1669 if (!fs->op.symlink)
1670 return -ENOSYS;
1671 if (fs->debug)
1672 fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path);
1673
1674 return fs->op.symlink(linkname, path);
1675}
1676
1677int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath)
1678{
1679 fuse_get_context()->private_data = fs->user_data;
1680 if (!fs->op.link)
1681 return -ENOSYS;
1682 if (fs->debug)
1683 fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath);
1684
1685 return fs->op.link(oldpath, newpath);
1686}
1687
1688int fuse_fs_release(struct fuse_fs *fs, const char *path,
1689 struct fuse_file_info *fi)
1690{
1691 fuse_get_context()->private_data = fs->user_data;
1692 if (!fs->op.release)
1693 return 0;
1694
1695 if (fs->debug)
1696 fuse_log(FUSE_LOG_DEBUG, "release%s[%llu] flags: 0x%x\n",
1697 fi->flush ? "+flush" : "",
1698 (unsigned long long) fi->fh, fi->flags);
1699
1700 return fs->op.release(path, fi);
1701}
1702
1703int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
1704 struct fuse_file_info *fi)
1705{
1706 int err;
1707
1708 fuse_get_context()->private_data = fs->user_data;
1709 if (!fs->op.opendir)
1710 return 0;
1711
1712 if (fs->debug)
1713 fuse_log(FUSE_LOG_DEBUG, "opendir flags: 0x%x %s\n", fi->flags,
1714 path);
1715
1716 err = fs->op.opendir(path, fi);
1717
1718 if (fs->debug && !err)
1719 fuse_log(FUSE_LOG_DEBUG, " opendir[%llu] flags: 0x%x %s\n",
1720 (unsigned long long) fi->fh, fi->flags, path);
1721
1722 return err;
1723}
1724
1725int fuse_fs_open(struct fuse_fs *fs, const char *path,
1726 struct fuse_file_info *fi)
1727{
1728 int err;
1729
1730 fuse_get_context()->private_data = fs->user_data;
1731 if (!fs->op.open)
1732 return 0;
1733
1734 if (fs->debug)
1735 fuse_log(FUSE_LOG_DEBUG, "open flags: 0x%x %s\n", fi->flags,
1736 path);
1737
1738 err = fs->op.open(path, fi);
1739
1740 if (fs->debug && !err)
1741 fuse_log(FUSE_LOG_DEBUG, " open[%llu] flags: 0x%x %s\n",
1742 (unsigned long long) fi->fh, fi->flags, path);
1743
1744 return err;
1745}
1746
1747static void fuse_free_buf(struct fuse_bufvec *buf)
1748{
1749 if (buf != NULL) {
1750 size_t i;
1751
1752 for (i = 0; i < buf->count; i++)
1753 if (!(buf->buf[i].flags & FUSE_BUF_IS_FD))
1754 free(buf->buf[i].mem);
1755 free(buf);
1756 }
1757}
1758
1759int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
1760 struct fuse_bufvec **bufp, size_t size, off_t off,
1761 struct fuse_file_info *fi)
1762{
1763 int res;
1764
1765 fuse_get_context()->private_data = fs->user_data;
1766 if (!fs->op.read && !fs->op.read_buf)
1767 return -ENOSYS;
1768
1769 if (fs->debug)
1770 fuse_log(FUSE_LOG_DEBUG,
1771 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1772 (unsigned long long) fi->fh,
1773 size, (unsigned long long) off, fi->flags);
1774
1775 if (fs->op.read_buf) {
1776 res = fs->op.read_buf(path, bufp, size, off, fi);
1777 } else {
1778 struct fuse_bufvec *buf;
1779 void *mem;
1780
1781 buf = malloc(sizeof(struct fuse_bufvec));
1782 if (buf == NULL)
1783 return -ENOMEM;
1784
1785 mem = malloc(size);
1786 if (mem == NULL) {
1787 free(buf);
1788 return -ENOMEM;
1789 }
1790 *buf = FUSE_BUFVEC_INIT(size);
1791 buf->buf[0].mem = mem;
1792 *bufp = buf;
1793
1794 res = fs->op.read(path, mem, size, off, fi);
1795 if (res >= 0)
1796 buf->buf[0].size = res;
1797 }
1798
1799 if (fs->debug && res >= 0)
1800 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %zu bytes from %llu\n",
1801 (unsigned long long) fi->fh,
1802 fuse_buf_size(*bufp),
1803 (unsigned long long) off);
1804 if (res >= 0 && fuse_buf_size(*bufp) > size)
1805 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1806
1807 if (res < 0)
1808 return res;
1809
1810 return 0;
1811}
1812
1813int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
1814 off_t off, struct fuse_file_info *fi)
1815{
1816 int res;
1817
1818 fuse_get_context()->private_data = fs->user_data;
1819 if (!fs->op.read && !fs->op.read_buf)
1820 return -ENOSYS;
1821
1822 if (fs->debug)
1823 fuse_log(FUSE_LOG_DEBUG,
1824 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1825 (unsigned long long) fi->fh,
1826 size, (unsigned long long) off, fi->flags);
1827
1828 if (fs->op.read_buf) {
1829 struct fuse_bufvec *buf = NULL;
1830
1831 res = fs->op.read_buf(path, &buf, size, off, fi);
1832 if (res == 0) {
1833 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
1834
1835 dst.buf[0].mem = mem;
1836 res = fuse_buf_copy(&dst, buf, 0);
1837 }
1838 fuse_free_buf(buf);
1839 } else {
1840 res = fs->op.read(path, mem, size, off, fi);
1841 }
1842
1843 if (fs->debug && res >= 0)
1844 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %u bytes from %llu\n",
1845 (unsigned long long) fi->fh,
1846 res,
1847 (unsigned long long) off);
1848 if (res >= 0 && res > (int) size)
1849 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1850
1851 return res;
1852}
1853
1854int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
1855 struct fuse_bufvec *buf, off_t off,
1856 struct fuse_file_info *fi)
1857{
1858 int res;
1859 size_t size;
1860
1861 fuse_get_context()->private_data = fs->user_data;
1862 if (!fs->op.write_buf && !fs->op.write)
1863 return -ENOSYS;
1864
1865 size = fuse_buf_size(buf);
1866 assert(buf->idx == 0 && buf->off == 0);
1867 if (fs->debug)
1868 fuse_log(FUSE_LOG_DEBUG,
1869 "write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
1870 fi->writepage ? "page" : "",
1871 (unsigned long long) fi->fh,
1872 size,
1873 (unsigned long long) off,
1874 fi->flags);
1875
1876 if (fs->op.write_buf) {
1877 res = fs->op.write_buf(path, buf, off, fi);
1878 } else {
1879 void *mem = NULL;
1880 struct fuse_buf *flatbuf;
1881 struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size);
1882
1883 if (buf->count == 1 &&
1884 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
1885 flatbuf = &buf->buf[0];
1886 } else {
1887 res = -ENOMEM;
1888 mem = malloc(size);
1889 if (mem == NULL)
1890 goto out;
1891
1892 tmp.buf[0].mem = mem;
1893 res = fuse_buf_copy(&tmp, buf, 0);
1894 if (res <= 0)
1895 goto out_free;
1896
1897 tmp.buf[0].size = res;
1898 flatbuf = &tmp.buf[0];
1899 }
1900
1901 res = fs->op.write(path, flatbuf->mem, flatbuf->size,
1902 off, fi);
1903out_free:
1904 free(mem);
1905 }
1906out:
1907 if (fs->debug && res >= 0)
1908 fuse_log(FUSE_LOG_DEBUG, " write%s[%llu] %u bytes to %llu\n",
1909 fi->writepage ? "page" : "",
1910 (unsigned long long) fi->fh, res,
1911 (unsigned long long) off);
1912 if (res > (int) size)
1913 fuse_log(FUSE_LOG_ERR, "fuse: wrote too many bytes\n");
1914
1915 return res;
1916}
1917
1918int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem,
1919 size_t size, off_t off, struct fuse_file_info *fi)
1920{
1921 struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size);
1922
1923 bufv.buf[0].mem = (void *) mem;
1924
1925 return fuse_fs_write_buf(fs, path, &bufv, off, fi);
1926}
1927
1928int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
1929 struct fuse_file_info *fi)
1930{
1931 fuse_get_context()->private_data = fs->user_data;
1932 if (!fs->op.fsync)
1933 return -ENOSYS;
1934
1935 if (fs->debug)
1936 fuse_log(FUSE_LOG_DEBUG, "fsync[%llu] datasync: %i\n",
1937 (unsigned long long) fi->fh, datasync);
1938
1939 return fs->op.fsync(path, datasync, fi);
1940}
1941
1942int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
1943 struct fuse_file_info *fi)
1944{
1945 fuse_get_context()->private_data = fs->user_data;
1946 if (!fs->op.fsyncdir)
1947 return -ENOSYS;
1948
1949 if (fs->debug)
1950 fuse_log(FUSE_LOG_DEBUG, "fsyncdir[%llu] datasync: %i\n",
1951 (unsigned long long) fi->fh, datasync);
1952
1953 return fs->op.fsyncdir(path, datasync, fi);
1954}
1955
1956int fuse_fs_flush(struct fuse_fs *fs, const char *path,
1957 struct fuse_file_info *fi)
1958{
1959 fuse_get_context()->private_data = fs->user_data;
1960 if (!fs->op.flush)
1961 return -ENOSYS;
1962 if (fs->debug)
1963 fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n",
1964 (unsigned long long) fi->fh);
1965
1966 return fs->op.flush(path, fi);
1967}
1968
1969int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf)
1970{
1971 fuse_get_context()->private_data = fs->user_data;
1972 if (fs->op.statfs) {
1973 if (fs->debug)
1974 fuse_log(FUSE_LOG_DEBUG, "statfs %s\n", path);
1975
1976 return fs->op.statfs(path, buf);
1977 } else {
1978 buf->f_namemax = 255;
1979 buf->f_bsize = 512;
1980 return 0;
1981 }
1982}
1983
1984int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
1985 struct fuse_file_info *fi)
1986{
1987 fuse_get_context()->private_data = fs->user_data;
1988 if (!fs->op.releasedir)
1989 return 0;
1990
1991 if (fs->debug)
1992 fuse_log(FUSE_LOG_DEBUG, "releasedir[%llu] flags: 0x%x\n",
1993 (unsigned long long) fi->fh, fi->flags);
1994
1995 return fs->op.releasedir(path, fi);
1996}
1997
1998int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
1999 fuse_fill_dir_t filler, off_t off,
2000 struct fuse_file_info *fi,
2001 enum fuse_readdir_flags flags)
2002{
2003 fuse_get_context()->private_data = fs->user_data;
2004 if (!fs->op.readdir)
2005 return -ENOSYS;
2006 if (fs->debug) {
2007 fuse_log(FUSE_LOG_DEBUG, "readdir%s[%llu] from %llu\n",
2008 (flags & FUSE_READDIR_PLUS) ? "plus" : "",
2009 (unsigned long long) fi->fh,
2010 (unsigned long long) off);
2011 }
2012
2013 return fs->op.readdir(path, buf, filler, off, fi, flags);
2014}
2015
2016int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
2017 struct fuse_file_info *fi)
2018{
2019 int err;
2020
2021 fuse_get_context()->private_data = fs->user_data;
2022 if (!fs->op.create)
2023 return -ENOSYS;
2024
2025 if (fs->debug)
2026 fuse_log(FUSE_LOG_DEBUG,
2027 "create flags: 0x%x %s 0%o umask=0%03o\n",
2028 fi->flags, path, mode,
2029 fuse_get_context()->umask);
2030
2031 err = fs->op.create(path, mode, fi);
2032
2033 if (fs->debug && !err)
2034 fuse_log(FUSE_LOG_DEBUG, " create[%llu] flags: 0x%x %s\n",
2035 (unsigned long long) fi->fh, fi->flags, path);
2036
2037 return err;
2038}
2039
2040int fuse_fs_lock(struct fuse_fs *fs, const char *path,
2041 struct fuse_file_info *fi, int cmd, struct flock *lock)
2042{
2043 fuse_get_context()->private_data = fs->user_data;
2044 if (!fs->op.lock)
2045 return -ENOSYS;
2046
2047 if (fs->debug)
2048 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
2049 (unsigned long long) fi->fh,
2050 (cmd == F_GETLK ? "F_GETLK" :
2051 (cmd == F_SETLK ? "F_SETLK" :
2052 (cmd == F_SETLKW ? "F_SETLKW" : "???"))),
2053 (lock->l_type == F_RDLCK ? "F_RDLCK" :
2054 (lock->l_type == F_WRLCK ? "F_WRLCK" :
2055 (lock->l_type == F_UNLCK ? "F_UNLCK" :
2056 "???"))),
2057 (unsigned long long) lock->l_start,
2058 (unsigned long long) lock->l_len,
2059 (unsigned long long) lock->l_pid);
2060
2061 return fs->op.lock(path, fi, cmd, lock);
2062}
2063
2064int fuse_fs_flock(struct fuse_fs *fs, const char *path,
2065 struct fuse_file_info *fi, int op)
2066{
2067 fuse_get_context()->private_data = fs->user_data;
2068 if (!fs->op.flock)
2069 return -ENOSYS;
2070
2071 if (fs->debug) {
2072 int xop = op & ~LOCK_NB;
2073
2074 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s%s\n",
2075 (unsigned long long) fi->fh,
2076 xop == LOCK_SH ? "LOCK_SH" :
2077 (xop == LOCK_EX ? "LOCK_EX" :
2078 (xop == LOCK_UN ? "LOCK_UN" : "???")),
2079 (op & LOCK_NB) ? "|LOCK_NB" : "");
2080 }
2081 return fs->op.flock(path, fi, op);
2082}
2083
2084int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid,
2085 gid_t gid, struct fuse_file_info *fi)
2086{
2087 fuse_get_context()->private_data = fs->user_data;
2088 if (!fs->op.chown)
2089 return -ENOSYS;
2090 if (fs->debug) {
2091 char buf[10];
2092
2093 fuse_log(FUSE_LOG_DEBUG, "chown[%s] %s %lu %lu\n",
2094 file_info_string(fi, buf, sizeof(buf)),
2095 path, (unsigned long) uid, (unsigned long) gid);
2096 }
2097 return fs->op.chown(path, uid, gid, fi);
2098}
2099
2100int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
2101 struct fuse_file_info *fi)
2102{
2103 fuse_get_context()->private_data = fs->user_data;
2104 if (!fs->op.truncate)
2105 return -ENOSYS;
2106 if (fs->debug) {
2107 char buf[10];
2108
2109 fuse_log(FUSE_LOG_DEBUG, "truncate[%s] %llu\n",
2110 file_info_string(fi, buf, sizeof(buf)),
2111 (unsigned long long) size);
2112 }
2113 return fs->op.truncate(path, size, fi);
2114}
2115
2116int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
2117 const struct timespec tv[2], struct fuse_file_info *fi)
2118{
2119 fuse_get_context()->private_data = fs->user_data;
2120 if (!fs->op.utimens)
2121 return -ENOSYS;
2122 if (fs->debug) {
2123 char buf[10];
2124
2125 fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %jd.%09ld %jd.%09ld\n",
2126 file_info_string(fi, buf, sizeof(buf)),
2127 path, (intmax_t)tv[0].tv_sec, tv[0].tv_nsec,
2128 (intmax_t)tv[1].tv_sec, tv[1].tv_nsec);
2129 }
2130 return fs->op.utimens(path, tv, fi);
2131}
2132
2133int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask)
2134{
2135 fuse_get_context()->private_data = fs->user_data;
2136 if (!fs->op.access)
2137 return -ENOSYS;
2138 if (fs->debug)
2139 fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask);
2140
2141 return fs->op.access(path, mask);
2142}
2143
2144int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
2145 size_t len)
2146{
2147 fuse_get_context()->private_data = fs->user_data;
2148 if (!fs->op.readlink)
2149 return -ENOSYS;
2150 if (fs->debug)
2151 fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path,
2152 (unsigned long) len);
2153
2154 return fs->op.readlink(path, buf, len);
2155}
2156
2157int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
2158 dev_t rdev)
2159{
2160 fuse_get_context()->private_data = fs->user_data;
2161 if (!fs->op.mknod)
2162 return -ENOSYS;
2163 if (fs->debug)
2164 fuse_log(FUSE_LOG_DEBUG, "mknod %s 0%o 0x%llx umask=0%03o\n",
2165 path, mode, (unsigned long long) rdev,
2166 fuse_get_context()->umask);
2167
2168 return fs->op.mknod(path, mode, rdev);
2169}
2170
2171int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode)
2172{
2173 fuse_get_context()->private_data = fs->user_data;
2174 if (!fs->op.mkdir)
2175 return -ENOSYS;
2176 if (fs->debug)
2177 fuse_log(FUSE_LOG_DEBUG, "mkdir %s 0%o umask=0%03o\n",
2178 path, mode, fuse_get_context()->umask);
2179
2180 return fs->op.mkdir(path, mode);
2181}
2182
2183int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
2184 const char *value, size_t size, int flags)
2185{
2186 fuse_get_context()->private_data = fs->user_data;
2187 if (!fs->op.setxattr)
2188 return -ENOSYS;
2189 if (fs->debug)
2190 fuse_log(FUSE_LOG_DEBUG, "setxattr %s %s %lu 0x%x\n",
2191 path, name, (unsigned long) size, flags);
2192
2193 return fs->op.setxattr(path, name, value, size, flags);
2194}
2195
2196int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
2197 char *value, size_t size)
2198{
2199 fuse_get_context()->private_data = fs->user_data;
2200 if (!fs->op.getxattr)
2201 return -ENOSYS;
2202 if (fs->debug)
2203 fuse_log(FUSE_LOG_DEBUG, "getxattr %s %s %lu\n",
2204 path, name, (unsigned long) size);
2205
2206 return fs->op.getxattr(path, name, value, size);
2207}
2208
2209int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
2210 size_t size)
2211{
2212 fuse_get_context()->private_data = fs->user_data;
2213 if (!fs->op.listxattr)
2214 return -ENOSYS;
2215 if (fs->debug)
2216 fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n",
2217 path, (unsigned long) size);
2218
2219 return fs->op.listxattr(path, list, size);
2220}
2221
2222int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
2223 uint64_t *idx)
2224{
2225 fuse_get_context()->private_data = fs->user_data;
2226 if (!fs->op.bmap)
2227 return -ENOSYS;
2228 if (fs->debug)
2229 fuse_log(FUSE_LOG_DEBUG, "bmap %s blocksize: %lu index: %llu\n",
2230 path, (unsigned long) blocksize,
2231 (unsigned long long) *idx);
2232
2233 return fs->op.bmap(path, blocksize, idx);
2234}
2235
2236int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
2237{
2238 fuse_get_context()->private_data = fs->user_data;
2239 if (!fs->op.removexattr)
2240 return -ENOSYS;
2241 if (fs->debug)
2242 fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name);
2243
2244 return fs->op.removexattr(path, name);
2245}
2246
2247int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
2248 void *arg, struct fuse_file_info *fi, unsigned int flags,
2249 void *data)
2250{
2251 fuse_get_context()->private_data = fs->user_data;
2252 if (!fs->op.ioctl)
2253 return -ENOSYS;
2254 if (fs->debug)
2255 fuse_log(FUSE_LOG_DEBUG, "ioctl[%llu] 0x%x flags: 0x%x\n",
2256 (unsigned long long) fi->fh, cmd, flags);
2257
2258 return fs->op.ioctl(path, cmd, arg, fi, flags, data);
2259}
2260
2261int fuse_fs_poll(struct fuse_fs *fs, const char *path,
2262 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
2263 unsigned *reventsp)
2264{
2265 int res;
2266
2267 fuse_get_context()->private_data = fs->user_data;
2268 if (!fs->op.poll)
2269 return -ENOSYS;
2270 if (fs->debug)
2271 fuse_log(FUSE_LOG_DEBUG, "poll[%llu] ph: %p, events 0x%x\n",
2272 (unsigned long long) fi->fh, ph,
2273 fi->poll_events);
2274
2275 res = fs->op.poll(path, fi, ph, reventsp);
2276
2277 if (fs->debug && !res)
2278 fuse_log(FUSE_LOG_DEBUG, " poll[%llu] revents: 0x%x\n",
2279 (unsigned long long) fi->fh, *reventsp);
2280
2281 return res;
2282}
2283
2284int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
2285 off_t offset, off_t length, struct fuse_file_info *fi)
2286{
2287 fuse_get_context()->private_data = fs->user_data;
2288 if (!fs->op.fallocate)
2289 return -ENOSYS;
2290 if (fs->debug)
2291 fuse_log(FUSE_LOG_DEBUG, "fallocate %s mode %x, offset: %llu, length: %llu\n",
2292 path,
2293 mode,
2294 (unsigned long long) offset,
2295 (unsigned long long) length);
2296
2297 return fs->op.fallocate(path, mode, offset, length, fi);
2298}
2299
2300ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
2301 struct fuse_file_info *fi_in, off_t off_in,
2302 const char *path_out,
2303 struct fuse_file_info *fi_out, off_t off_out,
2304 size_t len, int flags)
2305{
2306 fuse_get_context()->private_data = fs->user_data;
2307 if (!fs->op.copy_file_range)
2308 return -ENOSYS;
2309 if (fs->debug)
2310 fuse_log(FUSE_LOG_DEBUG, "copy_file_range from %s:%llu to "
2311 "%s:%llu, length: %llu\n",
2312 path_in,
2313 (unsigned long long) off_in,
2314 path_out,
2315 (unsigned long long) off_out,
2316 (unsigned long long) len);
2317
2318 return fs->op.copy_file_range(path_in, fi_in, off_in, path_out,
2319 fi_out, off_out, len, flags);
2320}
2321
2322off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
2323 struct fuse_file_info *fi)
2324{
2325 fuse_get_context()->private_data = fs->user_data;
2326 if (!fs->op.lseek)
2327 return -ENOSYS;
2328 if (fs->debug) {
2329 char buf[10];
2330
2331 fuse_log(FUSE_LOG_DEBUG, "lseek[%s] %llu %d\n",
2332 file_info_string(fi, buf, sizeof(buf)),
2333 (unsigned long long) off, whence);
2334 }
2335 return fs->op.lseek(path, off, whence, fi);
2336}
2337
2338#ifdef HAVE_STATX
2339int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
2340 struct statx *stxbuf, struct fuse_file_info *fi)
2341{
2342 fuse_get_context()->private_data = fs->user_data;
2343 if (fs->op.statx) {
2344 if (fs->debug) {
2345 char buf[10];
2346
2347 fuse_log(FUSE_LOG_DEBUG, "statx[%s] %s %d %d\n",
2348 file_info_string(fi, buf, sizeof(buf)), path,
2349 flags, mask);
2350 }
2351 return fs->op.statx(path, flags, mask, stxbuf, fi);
2352 }
2353
2354 return -ENOSYS;
2355}
2356#else
2357int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
2358 struct statx *stxbuf, struct fuse_file_info *fi)
2359{
2360 (void)fs;
2361 (void)path;
2362 (void)flags;
2363 (void)mask;
2364 (void)stxbuf;
2365 (void)fi;
2366
2367 return -ENOSYS;
2368}
2369#endif
2370
2371static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
2372{
2373 struct node *node;
2374 int isopen = 0;
2375 pthread_mutex_lock(&f->lock);
2376 node = lookup_node(f, dir, name);
2377 if (node && node->open_count > 0)
2378 isopen = 1;
2379 pthread_mutex_unlock(&f->lock);
2380 return isopen;
2381}
2382
2383static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname,
2384 char *newname, size_t bufsize)
2385{
2386 struct stat buf;
2387 struct node *node;
2388 struct node *newnode;
2389 char *newpath;
2390 int res;
2391 int failctr = 10;
2392
2393 do {
2394 pthread_mutex_lock(&f->lock);
2395 node = lookup_node(f, dir, oldname);
2396 if (node == NULL) {
2397 pthread_mutex_unlock(&f->lock);
2398 return NULL;
2399 }
2400 do {
2401 f->hidectr ++;
2402 snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
2403 (unsigned int) node->nodeid, f->hidectr);
2404 newnode = lookup_node(f, dir, newname);
2405 } while(newnode);
2406
2407 res = try_get_path(f, dir, newname, &newpath, NULL, false);
2408 pthread_mutex_unlock(&f->lock);
2409 if (res)
2410 break;
2411
2412 memset(&buf, 0, sizeof(buf));
2413 res = fuse_fs_getattr(f->fs, newpath, &buf, NULL);
2414 if (res == -ENOENT)
2415 break;
2416 free(newpath);
2417 newpath = NULL;
2418 } while(res == 0 && --failctr);
2419
2420 return newpath;
2421}
2422
2423static int hide_node(struct fuse *f, const char *oldpath,
2424 fuse_ino_t dir, const char *oldname)
2425{
2426 char newname[64];
2427 char *newpath;
2428 int err = -EBUSY;
2429
2430 newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
2431 if (newpath) {
2432 err = fuse_fs_rename(f->fs, oldpath, newpath, 0);
2433 if (!err)
2434 err = rename_node(f, dir, oldname, dir, newname, 1);
2435 free(newpath);
2436 }
2437 return err;
2438}
2439
2440static int mtime_eq(const struct stat *stbuf, const struct timespec *ts)
2441{
2442 return stbuf->st_mtime == ts->tv_sec &&
2443 ST_MTIM_NSEC(stbuf) == ts->tv_nsec;
2444}
2445
2446#ifndef CLOCK_MONOTONIC
2447#define CLOCK_MONOTONIC CLOCK_REALTIME
2448#endif
2449
2450static void curr_time(struct timespec *now)
2451{
2452 static clockid_t clockid = CLOCK_MONOTONIC;
2453 int res = clock_gettime(clockid, now);
2454 if (res == -1 && errno == EINVAL) {
2455 clockid = CLOCK_REALTIME;
2456 res = clock_gettime(clockid, now);
2457 }
2458 if (res == -1) {
2459 perror("fuse: clock_gettime");
2460 abort();
2461 }
2462}
2463
2464static void update_stat(struct node *node, const struct stat *stbuf)
2465{
2466 if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) ||
2467 stbuf->st_size != node->size))
2468 node->cache_valid = 0;
2469 node->mtime.tv_sec = stbuf->st_mtime;
2470 node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf);
2471 node->size = stbuf->st_size;
2472 curr_time(&node->stat_updated);
2473}
2474
2475static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name,
2476 struct fuse_entry_param *e)
2477{
2478 struct node *node;
2479
2480 node = find_node(f, nodeid, name);
2481 if (node == NULL)
2482 return -ENOMEM;
2483
2484 e->ino = node->nodeid;
2485 e->generation = node->generation;
2486 e->entry_timeout = f->conf.entry_timeout;
2487 e->attr_timeout = f->conf.attr_timeout;
2488 if (f->conf.auto_cache) {
2489 pthread_mutex_lock(&f->lock);
2490 update_stat(node, &e->attr);
2491 pthread_mutex_unlock(&f->lock);
2492 }
2493 set_stat(f, e->ino, &e->attr);
2494 return 0;
2495}
2496
2497static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
2498 const char *name, const char *path,
2499 struct fuse_entry_param *e, struct fuse_file_info *fi)
2500{
2501 int res;
2502
2503 memset(e, 0, sizeof(struct fuse_entry_param));
2504 res = fuse_fs_getattr(f->fs, path, &e->attr, fi);
2505 if (res == 0) {
2506 res = do_lookup(f, nodeid, name, e);
2507 if (res == 0 && f->conf.debug) {
2508 fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu\n",
2509 (unsigned long long) e->ino);
2510 }
2511 }
2512 return res;
2513}
2514
2515static struct fuse_context_i *fuse_get_context_internal(void)
2516{
2517 return (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
2518}
2519
2520static struct fuse_context_i *fuse_create_context(struct fuse *f)
2521{
2522 struct fuse_context_i *c = fuse_get_context_internal();
2523 if (c == NULL) {
2524 c = (struct fuse_context_i *)
2525 calloc(1, sizeof(struct fuse_context_i));
2526 if (c == NULL) {
2527 /* This is hard to deal with properly, so just
2528 abort. If memory is so low that the
2529 context cannot be allocated, there's not
2530 much hope for the filesystem anyway */
2531 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate thread specific data\n");
2532 abort();
2533 }
2534 pthread_setspecific(fuse_context_key, c);
2535 } else {
2536 memset(c, 0, sizeof(*c));
2537 }
2538 c->ctx.fuse = f;
2539
2540 return c;
2541}
2542
2543static void fuse_freecontext(void *data)
2544{
2545 free(data);
2546}
2547
2548static int fuse_create_context_key(void)
2549{
2550 int err = 0;
2551 pthread_mutex_lock(&fuse_context_lock);
2552 if (!fuse_context_ref) {
2553 err = pthread_key_create(&fuse_context_key, fuse_freecontext);
2554 if (err) {
2555 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
2556 strerror(err));
2557 pthread_mutex_unlock(&fuse_context_lock);
2558 return -1;
2559 }
2560 }
2561 fuse_context_ref++;
2562 pthread_mutex_unlock(&fuse_context_lock);
2563 return 0;
2564}
2565
2566static void fuse_delete_context_key(void)
2567{
2568 pthread_mutex_lock(&fuse_context_lock);
2569 fuse_context_ref--;
2570 if (!fuse_context_ref) {
2571 free(pthread_getspecific(fuse_context_key));
2572 pthread_key_delete(fuse_context_key);
2573 }
2574 pthread_mutex_unlock(&fuse_context_lock);
2575}
2576
2577static struct fuse *req_fuse_prepare(fuse_req_t req)
2578{
2579 struct fuse_context_i *c = fuse_create_context(req_fuse(req));
2580 const struct fuse_ctx *ctx = fuse_req_ctx(req);
2581 c->req = req;
2582 c->ctx.uid = ctx->uid;
2583 c->ctx.gid = ctx->gid;
2584 c->ctx.pid = ctx->pid;
2585 c->ctx.umask = ctx->umask;
2586 return c->ctx.fuse;
2587}
2588
2589static inline void reply_err(fuse_req_t req, int err)
2590{
2591 /* fuse_reply_err() uses non-negated errno values */
2592 fuse_reply_err(req, -err);
2593}
2594
2595static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
2596 int err)
2597{
2598 if (!err) {
2599 struct fuse *f = req_fuse(req);
2600 if (fuse_reply_entry(req, e) == -ENOENT) {
2601 /* Skip forget for negative result */
2602 if (e->ino != 0)
2603 forget_node(f, e->ino, 1);
2604 }
2605 } else
2606 reply_err(req, err);
2607}
2608
2609void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
2610 struct fuse_config *cfg)
2611{
2612 fuse_get_context()->private_data = fs->user_data;
2613 if (!fs->op.write_buf)
2615 if (!fs->op.lock)
2617 if (!fs->op.flock)
2619 if (fs->op.init)
2620 fs->user_data = fs->op.init(conn, cfg);
2621}
2622
2623static int fuse_init_intr_signal(int signum, int *installed);
2624
2625static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
2626{
2627 struct fuse *f = (struct fuse *) data;
2628
2629 fuse_create_context(f);
2631 fuse_fs_init(f->fs, conn, &f->conf);
2632
2633 if (f->conf.intr) {
2634 if (fuse_init_intr_signal(f->conf.intr_signal,
2635 &f->intr_installed) == -1)
2636 fuse_log(FUSE_LOG_ERR, "fuse: failed to init interrupt signal\n");
2637 } else {
2638 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
2639 conn->no_interrupt = 1;
2640 }
2641}
2642
2643void fuse_fs_destroy(struct fuse_fs *fs)
2644{
2645 fuse_get_context()->private_data = fs->user_data;
2646 if (fs->op.destroy)
2647 fs->op.destroy(fs->user_data);
2648}
2649
2650static void fuse_lib_destroy(void *data)
2651{
2652 struct fuse *f = (struct fuse *) data;
2653
2654 fuse_create_context(f);
2655 fuse_fs_destroy(f->fs);
2656}
2657
2658static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
2659 const char *name)
2660{
2661 struct fuse *f = req_fuse_prepare(req);
2662 struct fuse_entry_param e = { .ino = 0 }; /* invalid ino */
2663 char *path;
2664 int err;
2665 struct node *dot = NULL;
2666
2667 if (name[0] == '.') {
2668 int len = strlen(name);
2669
2670 if (len == 1 || (name[1] == '.' && len == 2)) {
2671 pthread_mutex_lock(&f->lock);
2672 if (len == 1) {
2673 if (f->conf.debug)
2674 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOT\n");
2675 dot = get_node_nocheck(f, parent);
2676 if (dot == NULL) {
2677 pthread_mutex_unlock(&f->lock);
2678 reply_entry(req, &e, -ESTALE);
2679 return;
2680 }
2681 dot->refctr++;
2682 } else {
2683 if (f->conf.debug)
2684 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOTDOT\n");
2685 parent = get_node(f, parent)->parent->nodeid;
2686 }
2687 pthread_mutex_unlock(&f->lock);
2688 name = NULL;
2689 }
2690 }
2691
2692 err = get_path_name(f, parent, name, &path);
2693 if (!err) {
2694 struct fuse_intr_data d;
2695 if (f->conf.debug)
2696 fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path);
2697 fuse_prepare_interrupt(f, req, &d);
2698 err = lookup_path(f, parent, name, path, &e, NULL);
2699 if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
2700 e.ino = 0;
2701 e.entry_timeout = f->conf.negative_timeout;
2702 err = 0;
2703 }
2704 fuse_finish_interrupt(f, req, &d);
2705 free_path(f, parent, path);
2706 }
2707 if (dot) {
2708 pthread_mutex_lock(&f->lock);
2709 unref_node(f, dot);
2710 pthread_mutex_unlock(&f->lock);
2711 }
2712 reply_entry(req, &e, err);
2713}
2714
2715static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
2716{
2717 if (f->conf.debug)
2718 fuse_log(FUSE_LOG_DEBUG, "FORGET %llu/%llu\n", (unsigned long long)ino,
2719 (unsigned long long) nlookup);
2720 forget_node(f, ino, nlookup);
2721}
2722
2723static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
2724{
2725 do_forget(req_fuse(req), ino, nlookup);
2726 fuse_reply_none(req);
2727}
2728
2729static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
2730 struct fuse_forget_data *forgets)
2731{
2732 struct fuse *f = req_fuse(req);
2733 size_t i;
2734
2735 for (i = 0; i < count; i++)
2736 do_forget(f, forgets[i].ino, forgets[i].nlookup);
2737
2738 fuse_reply_none(req);
2739}
2740
2741
2742static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
2743 struct fuse_file_info *fi)
2744{
2745 struct fuse *f = req_fuse_prepare(req);
2746 struct stat buf;
2747 char *path;
2748 int err;
2749
2750 memset(&buf, 0, sizeof(buf));
2751
2752 if (fi != NULL)
2753 err = get_path_nullok(f, ino, &path);
2754 else
2755 err = get_path(f, ino, &path);
2756 if (!err) {
2757 struct fuse_intr_data d;
2758 fuse_prepare_interrupt(f, req, &d);
2759 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2760 fuse_finish_interrupt(f, req, &d);
2761 free_path(f, ino, path);
2762 }
2763 if (!err) {
2764 struct node *node;
2765
2766 pthread_mutex_lock(&f->lock);
2767 node = get_node(f, ino);
2768 if (node->is_hidden && buf.st_nlink > 0)
2769 buf.st_nlink--;
2770 if (f->conf.auto_cache)
2771 update_stat(node, &buf);
2772 pthread_mutex_unlock(&f->lock);
2773 set_stat(f, ino, &buf);
2774 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2775 } else
2776 reply_err(req, err);
2777}
2778
2779int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
2780 struct fuse_file_info *fi)
2781{
2782 fuse_get_context()->private_data = fs->user_data;
2783 if (!fs->op.chmod)
2784 return -ENOSYS;
2785
2786 if (fs->debug) {
2787 char buf[10];
2788
2789 fuse_log(FUSE_LOG_DEBUG, "chmod[%s] %s %llo\n",
2790 file_info_string(fi, buf, sizeof(buf)),
2791 path, (unsigned long long) mode);
2792 }
2793 return fs->op.chmod(path, mode, fi);
2794}
2795
2796static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
2797 int valid, struct fuse_file_info *fi)
2798{
2799 struct fuse *f = req_fuse_prepare(req);
2800 struct stat buf;
2801 char *path;
2802 int err;
2803
2804 memset(&buf, 0, sizeof(buf));
2805 if (fi != NULL)
2806 err = get_path_nullok(f, ino, &path);
2807 else
2808 err = get_path(f, ino, &path);
2809 if (!err) {
2810 struct fuse_intr_data d;
2811 fuse_prepare_interrupt(f, req, &d);
2812 err = 0;
2813 if (!err && (valid & FUSE_SET_ATTR_MODE))
2814 err = fuse_fs_chmod(f->fs, path, attr->st_mode, fi);
2815 if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) {
2816 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
2817 attr->st_uid : (uid_t) -1;
2818 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
2819 attr->st_gid : (gid_t) -1;
2820 err = fuse_fs_chown(f->fs, path, uid, gid, fi);
2821 }
2822 if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
2823 err = fuse_fs_truncate(f->fs, path,
2824 attr->st_size, fi);
2825 }
2826#ifdef HAVE_UTIMENSAT
2827 if (!err &&
2828 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
2829 struct timespec tv[2];
2830
2831 tv[0].tv_sec = 0;
2832 tv[1].tv_sec = 0;
2833 tv[0].tv_nsec = UTIME_OMIT;
2834 tv[1].tv_nsec = UTIME_OMIT;
2835
2836 if (valid & FUSE_SET_ATTR_ATIME_NOW)
2837 tv[0].tv_nsec = UTIME_NOW;
2838 else if (valid & FUSE_SET_ATTR_ATIME)
2839 tv[0] = attr->st_atim;
2840
2841 if (valid & FUSE_SET_ATTR_MTIME_NOW)
2842 tv[1].tv_nsec = UTIME_NOW;
2843 else if (valid & FUSE_SET_ATTR_MTIME)
2844 tv[1] = attr->st_mtim;
2845
2846 err = fuse_fs_utimens(f->fs, path, tv, fi);
2847 } else
2848#endif
2849 if (!err &&
2850 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
2851 (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
2852 struct timespec tv[2];
2853 tv[0].tv_sec = attr->st_atime;
2854 tv[0].tv_nsec = ST_ATIM_NSEC(attr);
2855 tv[1].tv_sec = attr->st_mtime;
2856 tv[1].tv_nsec = ST_MTIM_NSEC(attr);
2857 err = fuse_fs_utimens(f->fs, path, tv, fi);
2858 }
2859 if (!err) {
2860 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2861 }
2862 fuse_finish_interrupt(f, req, &d);
2863 free_path(f, ino, path);
2864 }
2865 if (!err) {
2866 if (f->conf.auto_cache) {
2867 pthread_mutex_lock(&f->lock);
2868 update_stat(get_node(f, ino), &buf);
2869 pthread_mutex_unlock(&f->lock);
2870 }
2871 set_stat(f, ino, &buf);
2872 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2873 } else
2874 reply_err(req, err);
2875}
2876
2877static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask)
2878{
2879 struct fuse *f = req_fuse_prepare(req);
2880 char *path;
2881 int err;
2882
2883 err = get_path(f, ino, &path);
2884 if (!err) {
2885 struct fuse_intr_data d;
2886
2887 fuse_prepare_interrupt(f, req, &d);
2888 err = fuse_fs_access(f->fs, path, mask);
2889 fuse_finish_interrupt(f, req, &d);
2890 free_path(f, ino, path);
2891 }
2892 reply_err(req, err);
2893}
2894
2895static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino)
2896{
2897 struct fuse *f = req_fuse_prepare(req);
2898 char linkname[PATH_MAX + 1];
2899 char *path;
2900 int err;
2901
2902 err = get_path(f, ino, &path);
2903 if (!err) {
2904 struct fuse_intr_data d;
2905 fuse_prepare_interrupt(f, req, &d);
2906 err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname));
2907 fuse_finish_interrupt(f, req, &d);
2908 free_path(f, ino, path);
2909 }
2910 if (!err) {
2911 linkname[PATH_MAX] = '\0';
2912 fuse_reply_readlink(req, linkname);
2913 } else
2914 reply_err(req, err);
2915}
2916
2917static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
2918 mode_t mode, dev_t rdev)
2919{
2920 struct fuse *f = req_fuse_prepare(req);
2921 struct fuse_entry_param e;
2922 char *path;
2923 int err;
2924
2925 err = get_path_name(f, parent, name, &path);
2926 if (!err) {
2927 struct fuse_intr_data d;
2928
2929 fuse_prepare_interrupt(f, req, &d);
2930 err = -ENOSYS;
2931 if (S_ISREG(mode)) {
2932 struct fuse_file_info fi;
2933
2934 memset(&fi, 0, sizeof(fi));
2935 fi.flags = O_CREAT | O_EXCL | O_WRONLY;
2936 err = fuse_fs_create(f->fs, path, mode, &fi);
2937 if (!err) {
2938 err = lookup_path(f, parent, name, path, &e,
2939 &fi);
2940 fuse_fs_release(f->fs, path, &fi);
2941 }
2942 }
2943 if (err == -ENOSYS) {
2944 err = fuse_fs_mknod(f->fs, path, mode, rdev);
2945 if (!err)
2946 err = lookup_path(f, parent, name, path, &e,
2947 NULL);
2948 }
2949 fuse_finish_interrupt(f, req, &d);
2950 free_path(f, parent, path);
2951 }
2952 reply_entry(req, &e, err);
2953}
2954
2955static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
2956 mode_t mode)
2957{
2958 struct fuse *f = req_fuse_prepare(req);
2959 struct fuse_entry_param e;
2960 char *path;
2961 int err;
2962
2963 err = get_path_name(f, parent, name, &path);
2964 if (!err) {
2965 struct fuse_intr_data d;
2966
2967 fuse_prepare_interrupt(f, req, &d);
2968 err = fuse_fs_mkdir(f->fs, path, mode);
2969 if (!err)
2970 err = lookup_path(f, parent, name, path, &e, NULL);
2971 fuse_finish_interrupt(f, req, &d);
2972 free_path(f, parent, path);
2973 }
2974 reply_entry(req, &e, err);
2975}
2976
2977static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
2978 const char *name)
2979{
2980 struct fuse *f = req_fuse_prepare(req);
2981 struct node *wnode;
2982 char *path;
2983 int err;
2984
2985 err = get_path_wrlock(f, parent, name, &path, &wnode);
2986 if (!err) {
2987 struct fuse_intr_data d;
2988
2989 fuse_prepare_interrupt(f, req, &d);
2990 if (!f->conf.hard_remove && is_open(f, parent, name)) {
2991 err = hide_node(f, path, parent, name);
2992 if (!err) {
2993 /* we have hidden the node so now check again under a lock in case it is not used any more */
2994 if (!is_open(f, parent, wnode->name)) {
2995 char *unlinkpath;
2996
2997 /* get the hidden file path, to unlink it */
2998 if (try_get_path(f, wnode->nodeid, NULL, &unlinkpath, NULL, false) == 0) {
2999 err = fuse_fs_unlink(f->fs, unlinkpath);
3000 if (!err)
3001 remove_node(f, parent, wnode->name);
3002 free(unlinkpath);
3003 }
3004 }
3005 }
3006 } else {
3007 err = fuse_fs_unlink(f->fs, path);
3008 if (!err)
3009 remove_node(f, parent, name);
3010 }
3011 fuse_finish_interrupt(f, req, &d);
3012 free_path_wrlock(f, parent, wnode, path);
3013 }
3014 reply_err(req, err);
3015}
3016
3017static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
3018{
3019 struct fuse *f = req_fuse_prepare(req);
3020 struct node *wnode;
3021 char *path;
3022 int err;
3023
3024 err = get_path_wrlock(f, parent, name, &path, &wnode);
3025 if (!err) {
3026 struct fuse_intr_data d;
3027
3028 fuse_prepare_interrupt(f, req, &d);
3029 err = fuse_fs_rmdir(f->fs, path);
3030 fuse_finish_interrupt(f, req, &d);
3031 if (!err)
3032 remove_node(f, parent, name);
3033 free_path_wrlock(f, parent, wnode, path);
3034 }
3035 reply_err(req, err);
3036}
3037
3038static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
3039 fuse_ino_t parent, const char *name)
3040{
3041 struct fuse *f = req_fuse_prepare(req);
3042 struct fuse_entry_param e;
3043 char *path;
3044 int err;
3045
3046 err = get_path_name(f, parent, name, &path);
3047 if (!err) {
3048 struct fuse_intr_data d;
3049
3050 fuse_prepare_interrupt(f, req, &d);
3051 err = fuse_fs_symlink(f->fs, linkname, path);
3052 if (!err)
3053 err = lookup_path(f, parent, name, path, &e, NULL);
3054 fuse_finish_interrupt(f, req, &d);
3055 free_path(f, parent, path);
3056 }
3057 reply_entry(req, &e, err);
3058}
3059
3060static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
3061 const char *oldname, fuse_ino_t newdir,
3062 const char *newname, unsigned int flags)
3063{
3064 struct fuse *f = req_fuse_prepare(req);
3065 char *oldpath;
3066 char *newpath;
3067 struct node *wnode1;
3068 struct node *wnode2;
3069 int err;
3070
3071 err = get_path2(f, olddir, oldname, newdir, newname,
3072 &oldpath, &newpath, &wnode1, &wnode2);
3073 if (!err) {
3074 struct fuse_intr_data d;
3075 err = 0;
3076 fuse_prepare_interrupt(f, req, &d);
3077 if (!f->conf.hard_remove && !(flags & RENAME_EXCHANGE) &&
3078 is_open(f, newdir, newname))
3079 err = hide_node(f, newpath, newdir, newname);
3080 if (!err) {
3081 err = fuse_fs_rename(f->fs, oldpath, newpath, flags);
3082 if (!err) {
3083 if (flags & RENAME_EXCHANGE) {
3084 err = exchange_node(f, olddir, oldname,
3085 newdir, newname);
3086 } else {
3087 err = rename_node(f, olddir, oldname,
3088 newdir, newname, 0);
3089 }
3090 }
3091 }
3092 fuse_finish_interrupt(f, req, &d);
3093 free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
3094 }
3095 reply_err(req, err);
3096}
3097
3098static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
3099 const char *newname)
3100{
3101 struct fuse *f = req_fuse_prepare(req);
3102 struct fuse_entry_param e;
3103 char *oldpath;
3104 char *newpath;
3105 int err;
3106
3107 err = get_path2(f, ino, NULL, newparent, newname,
3108 &oldpath, &newpath, NULL, NULL);
3109 if (!err) {
3110 struct fuse_intr_data d;
3111
3112 fuse_prepare_interrupt(f, req, &d);
3113 err = fuse_fs_link(f->fs, oldpath, newpath);
3114 if (!err)
3115 err = lookup_path(f, newparent, newname, newpath,
3116 &e, NULL);
3117 fuse_finish_interrupt(f, req, &d);
3118 free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath);
3119 }
3120 reply_entry(req, &e, err);
3121}
3122
3123static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
3124 struct fuse_file_info *fi)
3125{
3126 struct node *node;
3127 int unlink_hidden = 0;
3128
3129 fuse_fs_release(f->fs, path, fi);
3130
3131 pthread_mutex_lock(&f->lock);
3132 node = get_node(f, ino);
3133 assert(node->open_count > 0);
3134 --node->open_count;
3135 if (node->is_hidden && !node->open_count) {
3136 unlink_hidden = 1;
3137 node->is_hidden = 0;
3138 }
3139 pthread_mutex_unlock(&f->lock);
3140
3141 if(unlink_hidden) {
3142 if (path) {
3143 fuse_fs_unlink(f->fs, path);
3144 } else if (f->conf.nullpath_ok) {
3145 char *unlinkpath;
3146
3147 if (get_path(f, ino, &unlinkpath) == 0)
3148 fuse_fs_unlink(f->fs, unlinkpath);
3149
3150 free_path(f, ino, unlinkpath);
3151 }
3152 }
3153}
3154
3155static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
3156 const char *name, mode_t mode,
3157 struct fuse_file_info *fi)
3158{
3159 struct fuse *f = req_fuse_prepare(req);
3160 struct fuse_intr_data d;
3161 struct fuse_entry_param e;
3162 char *path;
3163 int err;
3164
3165 err = get_path_name(f, parent, name, &path);
3166 if (!err) {
3167 fuse_prepare_interrupt(f, req, &d);
3168 err = fuse_fs_create(f->fs, path, mode, fi);
3169 if (!err) {
3170 err = lookup_path(f, parent, name, path, &e, fi);
3171 if (err)
3172 fuse_fs_release(f->fs, path, fi);
3173 else if (!S_ISREG(e.attr.st_mode)) {
3174 err = -EIO;
3175 fuse_fs_release(f->fs, path, fi);
3176 forget_node(f, e.ino, 1);
3177 } else {
3178 if (f->conf.direct_io)
3179 fi->direct_io = 1;
3180 if (f->conf.kernel_cache)
3181 fi->keep_cache = 1;
3182 if (fi->direct_io &&
3183 f->conf.parallel_direct_writes)
3184 fi->parallel_direct_writes = 1;
3185 }
3186 }
3187 fuse_finish_interrupt(f, req, &d);
3188 }
3189 if (!err) {
3190 pthread_mutex_lock(&f->lock);
3191 get_node(f, e.ino)->open_count++;
3192 pthread_mutex_unlock(&f->lock);
3193 if (fuse_reply_create(req, &e, fi) == -ENOENT) {
3194 /* The open syscall was interrupted, so it
3195 must be cancelled */
3196 fuse_do_release(f, e.ino, path, fi);
3197 forget_node(f, e.ino, 1);
3198 }
3199 } else {
3200 reply_err(req, err);
3201 }
3202
3203 free_path(f, parent, path);
3204}
3205
3206static double diff_timespec(const struct timespec *t1,
3207 const struct timespec *t2)
3208{
3209 return (t1->tv_sec - t2->tv_sec) +
3210 ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0;
3211}
3212
3213static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path,
3214 struct fuse_file_info *fi)
3215{
3216 struct node *node;
3217
3218 pthread_mutex_lock(&f->lock);
3219 node = get_node(f, ino);
3220 if (node->cache_valid) {
3221 struct timespec now;
3222
3223 curr_time(&now);
3224 if (diff_timespec(&now, &node->stat_updated) >
3225 f->conf.ac_attr_timeout) {
3226 struct stat stbuf;
3227 int err;
3228 pthread_mutex_unlock(&f->lock);
3229 err = fuse_fs_getattr(f->fs, path, &stbuf, fi);
3230 pthread_mutex_lock(&f->lock);
3231 if (!err)
3232 update_stat(node, &stbuf);
3233 else
3234 node->cache_valid = 0;
3235 }
3236 }
3237 if (node->cache_valid)
3238 fi->keep_cache = 1;
3239
3240 node->cache_valid = 1;
3241 pthread_mutex_unlock(&f->lock);
3242}
3243
3244static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
3245 struct fuse_file_info *fi)
3246{
3247 struct fuse *f = req_fuse_prepare(req);
3248 struct fuse_intr_data d;
3249 char *path;
3250 int err;
3251
3252 err = get_path(f, ino, &path);
3253 if (!err) {
3254 fuse_prepare_interrupt(f, req, &d);
3255 err = fuse_fs_open(f->fs, path, fi);
3256 if (!err) {
3257 if (f->conf.direct_io)
3258 fi->direct_io = 1;
3259 if (f->conf.kernel_cache)
3260 fi->keep_cache = 1;
3261
3262 if (f->conf.auto_cache)
3263 open_auto_cache(f, ino, path, fi);
3264
3265 if (f->conf.no_rofd_flush &&
3266 (fi->flags & O_ACCMODE) == O_RDONLY)
3267 fi->noflush = 1;
3268
3269 if (fi->direct_io && f->conf.parallel_direct_writes)
3270 fi->parallel_direct_writes = 1;
3271
3272 }
3273 fuse_finish_interrupt(f, req, &d);
3274 }
3275 if (!err) {
3276 pthread_mutex_lock(&f->lock);
3277 get_node(f, ino)->open_count++;
3278 pthread_mutex_unlock(&f->lock);
3279 if (fuse_reply_open(req, fi) == -ENOENT) {
3280 /* The open syscall was interrupted, so it
3281 must be cancelled */
3282 fuse_do_release(f, ino, path, fi);
3283 }
3284 } else
3285 reply_err(req, err);
3286
3287 free_path(f, ino, path);
3288}
3289
3290static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
3291 off_t off, struct fuse_file_info *fi)
3292{
3293 struct fuse *f = req_fuse_prepare(req);
3294 struct fuse_bufvec *buf = NULL;
3295 char *path;
3296 int res;
3297
3298 res = get_path_nullok(f, ino, &path);
3299 if (res == 0) {
3300 struct fuse_intr_data d;
3301
3302 fuse_prepare_interrupt(f, req, &d);
3303 res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
3304 fuse_finish_interrupt(f, req, &d);
3305 free_path(f, ino, path);
3306 }
3307
3308 if (res == 0)
3310 else
3311 reply_err(req, res);
3312
3313 fuse_free_buf(buf);
3314}
3315
3316static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
3317 struct fuse_bufvec *buf, off_t off,
3318 struct fuse_file_info *fi)
3319{
3320 struct fuse *f = req_fuse_prepare(req);
3321 char *path;
3322 int res;
3323
3324 res = get_path_nullok(f, ino, &path);
3325 if (res == 0) {
3326 struct fuse_intr_data d;
3327
3328 fuse_prepare_interrupt(f, req, &d);
3329 res = fuse_fs_write_buf(f->fs, path, buf, off, fi);
3330 fuse_finish_interrupt(f, req, &d);
3331 free_path(f, ino, path);
3332 }
3333
3334 if (res >= 0)
3335 fuse_reply_write(req, res);
3336 else
3337 reply_err(req, res);
3338}
3339
3340static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
3341 struct fuse_file_info *fi)
3342{
3343 struct fuse *f = req_fuse_prepare(req);
3344 char *path;
3345 int err;
3346
3347 err = get_path_nullok(f, ino, &path);
3348 if (!err) {
3349 struct fuse_intr_data d;
3350
3351 fuse_prepare_interrupt(f, req, &d);
3352 err = fuse_fs_fsync(f->fs, path, datasync, fi);
3353 fuse_finish_interrupt(f, req, &d);
3354 free_path(f, ino, path);
3355 }
3356 reply_err(req, err);
3357}
3358
3359static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi,
3360 struct fuse_file_info *fi)
3361{
3362 struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh;
3363 memset(fi, 0, sizeof(struct fuse_file_info));
3364 fi->fh = dh->fh;
3365 return dh;
3366}
3367
3368static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
3369 struct fuse_file_info *llfi)
3370{
3371 struct fuse *f = req_fuse_prepare(req);
3372 struct fuse_intr_data d;
3373 struct fuse_dh *dh;
3374 struct fuse_file_info fi;
3375 char *path;
3376 int err;
3377
3378 dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
3379 if (dh == NULL) {
3380 reply_err(req, -ENOMEM);
3381 return;
3382 }
3383 memset(dh, 0, sizeof(struct fuse_dh));
3384 dh->fuse = f;
3385 dh->contents = NULL;
3386 dh->first = NULL;
3387 dh->len = 0;
3388 dh->filled = 0;
3389 dh->nodeid = ino;
3390 pthread_mutex_init(&dh->lock, NULL);
3391
3392 llfi->fh = (uintptr_t) dh;
3393
3394 memset(&fi, 0, sizeof(fi));
3395 fi.flags = llfi->flags;
3396
3397 err = get_path(f, ino, &path);
3398 if (!err) {
3399 fuse_prepare_interrupt(f, req, &d);
3400 err = fuse_fs_opendir(f->fs, path, &fi);
3401 fuse_finish_interrupt(f, req, &d);
3402 dh->fh = fi.fh;
3403 llfi->cache_readdir = fi.cache_readdir;
3404 llfi->keep_cache = fi.keep_cache;
3405 }
3406 if (!err) {
3407 if (fuse_reply_open(req, llfi) == -ENOENT) {
3408 /* The opendir syscall was interrupted, so it
3409 must be cancelled */
3410 fuse_fs_releasedir(f->fs, path, &fi);
3411 pthread_mutex_destroy(&dh->lock);
3412 free(dh);
3413 }
3414 } else {
3415 reply_err(req, err);
3416 pthread_mutex_destroy(&dh->lock);
3417 free(dh);
3418 }
3419 free_path(f, ino, path);
3420}
3421
3422static int extend_contents(struct fuse_dh *dh, unsigned minsize)
3423{
3424 if (minsize > dh->size) {
3425 char *newptr;
3426 unsigned newsize = dh->size;
3427 if (!newsize)
3428 newsize = 1024;
3429 while (newsize < minsize) {
3430 if (newsize >= 0x80000000)
3431 newsize = 0xffffffff;
3432 else
3433 newsize *= 2;
3434 }
3435
3436 newptr = (char *) realloc(dh->contents, newsize);
3437 if (!newptr) {
3438 dh->error = -ENOMEM;
3439 return -1;
3440 }
3441 dh->contents = newptr;
3442 dh->size = newsize;
3443 }
3444 return 0;
3445}
3446
3447static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name,
3448 struct stat *st, enum fuse_fill_dir_flags flags)
3449{
3450 struct fuse_direntry *de;
3451
3452 de = malloc(sizeof(struct fuse_direntry));
3453 if (!de) {
3454 dh->error = -ENOMEM;
3455 return -1;
3456 }
3457 de->name = strdup(name);
3458 if (!de->name) {
3459 dh->error = -ENOMEM;
3460 free(de);
3461 return -1;
3462 }
3463 de->flags = flags;
3464 de->stat = *st;
3465 de->next = NULL;
3466
3467 *dh->last = de;
3468 dh->last = &de->next;
3469
3470 return 0;
3471}
3472
3473static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent,
3474 const char *name)
3475{
3476 struct node *node;
3477 fuse_ino_t res = FUSE_UNKNOWN_INO;
3478
3479 pthread_mutex_lock(&f->lock);
3480 node = lookup_node(f, parent, name);
3481 if (node)
3482 res = node->nodeid;
3483 pthread_mutex_unlock(&f->lock);
3484
3485 return res;
3486}
3487
3488static int fill_dir(void *dh_, const char *name, const struct stat *statp,
3489 off_t off, enum fuse_fill_dir_flags flags)
3490{
3491 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3492 struct stat stbuf;
3493
3494 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3495 dh->error = -EIO;
3496 return 1;
3497 }
3498
3499 if (statp)
3500 stbuf = *statp;
3501 else {
3502 memset(&stbuf, 0, sizeof(stbuf));
3503 stbuf.st_ino = FUSE_UNKNOWN_INO;
3504 }
3505
3506 if (!dh->fuse->conf.use_ino) {
3507 stbuf.st_ino = FUSE_UNKNOWN_INO;
3508 if (dh->fuse->conf.readdir_ino) {
3509 stbuf.st_ino = (ino_t)
3510 lookup_nodeid(dh->fuse, dh->nodeid, name);
3511 }
3512 }
3513
3514 if (off) {
3515 size_t newlen;
3516
3517 if (dh->filled) {
3518 dh->error = -EIO;
3519 return 1;
3520 }
3521
3522 if (dh->first) {
3523 dh->error = -EIO;
3524 return 1;
3525 }
3526
3527 if (extend_contents(dh, dh->needlen) == -1)
3528 return 1;
3529
3530 newlen = dh->len +
3531 fuse_add_direntry(dh->req, dh->contents + dh->len,
3532 dh->needlen - dh->len, name,
3533 &stbuf, off);
3534 if (newlen > dh->needlen)
3535 return 1;
3536
3537 dh->len = newlen;
3538 } else {
3539 dh->filled = 1;
3540
3541 if (fuse_add_direntry_to_dh(dh, name, &stbuf, flags) == -1)
3542 return 1;
3543 }
3544 return 0;
3545}
3546
3547static int is_dot_or_dotdot(const char *name)
3548{
3549 return name[0] == '.' && (name[1] == '\0' ||
3550 (name[1] == '.' && name[2] == '\0'));
3551}
3552
3553static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
3554 off_t off, enum fuse_fill_dir_flags flags)
3555{
3556 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3557 struct fuse_entry_param e = {
3558 /* ino=0 tells the kernel to ignore readdirplus stat info */
3559 .ino = 0,
3560 };
3561 struct fuse *f = dh->fuse;
3562 int res;
3563
3564 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3565 dh->error = -EIO;
3566 return 1;
3567 }
3568
3569 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3570 e.attr = *statp;
3571 }
3572
3573 e.attr.st_ino = FUSE_UNKNOWN_INO;
3574 if (statp) {
3575 e.attr.st_mode = statp->st_mode;
3576 if (f->conf.use_ino)
3577 e.attr.st_ino = statp->st_ino;
3578 }
3579 if (!f->conf.use_ino && f->conf.readdir_ino) {
3580 e.attr.st_ino = (ino_t)
3581 lookup_nodeid(f, dh->nodeid, name);
3582 }
3583
3584 if (off) {
3585 size_t newlen;
3586
3587 if (dh->filled) {
3588 dh->error = -EIO;
3589 return 1;
3590 }
3591
3592 if (dh->first) {
3593 dh->error = -EIO;
3594 return 1;
3595 }
3596 if (extend_contents(dh, dh->needlen) == -1)
3597 return 1;
3598
3599 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3600 if (!is_dot_or_dotdot(name)) {
3601 res = do_lookup(f, dh->nodeid, name, &e);
3602 if (res) {
3603 dh->error = res;
3604 return 1;
3605 }
3606 }
3607 }
3608
3609 newlen = dh->len +
3610 fuse_add_direntry_plus(dh->req, dh->contents + dh->len,
3611 dh->needlen - dh->len, name,
3612 &e, off);
3613 if (newlen > dh->needlen)
3614 return 1;
3615 dh->len = newlen;
3616 } else {
3617 dh->filled = 1;
3618
3619 if (fuse_add_direntry_to_dh(dh, name, &e.attr, flags) == -1)
3620 return 1;
3621 }
3622
3623 return 0;
3624}
3625
3626static void free_direntries(struct fuse_direntry *de)
3627{
3628 while (de) {
3629 struct fuse_direntry *next = de->next;
3630 free(de->name);
3631 free(de);
3632 de = next;
3633 }
3634}
3635
3636static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3637 size_t size, off_t off, struct fuse_dh *dh,
3638 struct fuse_file_info *fi,
3639 enum fuse_readdir_flags flags)
3640{
3641 char *path;
3642 int err;
3643
3644 if (f->fs->op.readdir)
3645 err = get_path_nullok(f, ino, &path);
3646 else
3647 err = get_path(f, ino, &path);
3648 if (!err) {
3649 struct fuse_intr_data d;
3650 fuse_fill_dir_t filler = fill_dir;
3651
3652 if (flags & FUSE_READDIR_PLUS)
3653 filler = fill_dir_plus;
3654
3655 free_direntries(dh->first);
3656 dh->first = NULL;
3657 dh->last = &dh->first;
3658 dh->len = 0;
3659 dh->error = 0;
3660 dh->needlen = size;
3661 dh->filled = 0;
3662 dh->req = req;
3663 fuse_prepare_interrupt(f, req, &d);
3664 err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags);
3665 fuse_finish_interrupt(f, req, &d);
3666 dh->req = NULL;
3667 if (!err)
3668 err = dh->error;
3669 if (err)
3670 dh->filled = 0;
3671 free_path(f, ino, path);
3672 }
3673 return err;
3674}
3675
3676static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh,
3677 off_t off, enum fuse_readdir_flags flags)
3678{
3679 off_t pos;
3680 struct fuse_direntry *de = dh->first;
3681 int res;
3682
3683 dh->len = 0;
3684
3685 if (extend_contents(dh, dh->needlen) == -1)
3686 return dh->error;
3687
3688 for (pos = 0; pos < off; pos++) {
3689 if (!de)
3690 break;
3691
3692 de = de->next;
3693 }
3694 while (de) {
3695 char *p = dh->contents + dh->len;
3696 unsigned rem = dh->needlen - dh->len;
3697 unsigned thislen;
3698 unsigned newlen;
3699 pos++;
3700
3701 if (flags & FUSE_READDIR_PLUS) {
3702 struct fuse_entry_param e = {
3703 .ino = 0,
3704 .attr = de->stat,
3705 };
3706
3707 if (de->flags & FUSE_FILL_DIR_PLUS &&
3708 !is_dot_or_dotdot(de->name)) {
3709 res = do_lookup(dh->fuse, dh->nodeid,
3710 de->name, &e);
3711 if (res) {
3712 dh->error = res;
3713 return 1;
3714 }
3715 }
3716
3717 thislen = fuse_add_direntry_plus(req, p, rem,
3718 de->name, &e, pos);
3719 } else {
3720 thislen = fuse_add_direntry(req, p, rem,
3721 de->name, &de->stat, pos);
3722 }
3723 newlen = dh->len + thislen;
3724 if (newlen > dh->needlen)
3725 break;
3726 dh->len = newlen;
3727 de = de->next;
3728 }
3729 return 0;
3730}
3731
3732static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size,
3733 off_t off, struct fuse_file_info *llfi,
3734 enum fuse_readdir_flags flags)
3735{
3736 struct fuse *f = req_fuse_prepare(req);
3737 struct fuse_file_info fi;
3738 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3739 int err;
3740
3741 pthread_mutex_lock(&dh->lock);
3742 /* According to SUS, directory contents need to be refreshed on
3743 rewinddir() */
3744 if (!off)
3745 dh->filled = 0;
3746
3747 if (!dh->filled) {
3748 err = readdir_fill(f, req, ino, size, off, dh, &fi, flags);
3749 if (err) {
3750 reply_err(req, err);
3751 goto out;
3752 }
3753 }
3754 if (dh->filled) {
3755 dh->needlen = size;
3756 err = readdir_fill_from_list(req, dh, off, flags);
3757 if (err) {
3758 reply_err(req, err);
3759 goto out;
3760 }
3761 }
3762 fuse_reply_buf(req, dh->contents, dh->len);
3763out:
3764 pthread_mutex_unlock(&dh->lock);
3765}
3766
3767static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
3768 off_t off, struct fuse_file_info *llfi)
3769{
3770 fuse_readdir_common(req, ino, size, off, llfi, 0);
3771}
3772
3773static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
3774 off_t off, struct fuse_file_info *llfi)
3775{
3776 fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS);
3777}
3778
3779static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
3780 struct fuse_file_info *llfi)
3781{
3782 struct fuse *f = req_fuse_prepare(req);
3783 struct fuse_intr_data d;
3784 struct fuse_file_info fi;
3785 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3786 char *path;
3787
3788 get_path_nullok(f, ino, &path);
3789
3790 fuse_prepare_interrupt(f, req, &d);
3791 fuse_fs_releasedir(f->fs, path, &fi);
3792 fuse_finish_interrupt(f, req, &d);
3793 free_path(f, ino, path);
3794
3795 pthread_mutex_lock(&dh->lock);
3796 pthread_mutex_unlock(&dh->lock);
3797 pthread_mutex_destroy(&dh->lock);
3798 free_direntries(dh->first);
3799 free(dh->contents);
3800 free(dh);
3801 reply_err(req, 0);
3802}
3803
3804static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
3805 struct fuse_file_info *llfi)
3806{
3807 struct fuse *f = req_fuse_prepare(req);
3808 struct fuse_file_info fi;
3809 char *path;
3810 int err;
3811
3812 get_dirhandle(llfi, &fi);
3813
3814 err = get_path_nullok(f, ino, &path);
3815 if (!err) {
3816 struct fuse_intr_data d;
3817 fuse_prepare_interrupt(f, req, &d);
3818 err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi);
3819 fuse_finish_interrupt(f, req, &d);
3820 free_path(f, ino, path);
3821 }
3822 reply_err(req, err);
3823}
3824
3825static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
3826{
3827 struct fuse *f = req_fuse_prepare(req);
3828 struct statvfs buf;
3829 char *path = NULL;
3830 int err = 0;
3831
3832 memset(&buf, 0, sizeof(buf));
3833 if (ino)
3834 err = get_path(f, ino, &path);
3835
3836 if (!err) {
3837 struct fuse_intr_data d;
3838 fuse_prepare_interrupt(f, req, &d);
3839 err = fuse_fs_statfs(f->fs, path ? path : "/", &buf);
3840 fuse_finish_interrupt(f, req, &d);
3841 free_path(f, ino, path);
3842 }
3843
3844 if (!err)
3845 fuse_reply_statfs(req, &buf);
3846 else
3847 reply_err(req, err);
3848}
3849
3850static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3851 const char *value, size_t size, int flags)
3852{
3853 struct fuse *f = req_fuse_prepare(req);
3854 char *path;
3855 int err;
3856
3857 err = get_path(f, ino, &path);
3858 if (!err) {
3859 struct fuse_intr_data d;
3860 fuse_prepare_interrupt(f, req, &d);
3861 err = fuse_fs_setxattr(f->fs, path, name, value, size, flags);
3862 fuse_finish_interrupt(f, req, &d);
3863 free_path(f, ino, path);
3864 }
3865 reply_err(req, err);
3866}
3867
3868static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3869 const char *name, char *value, size_t size)
3870{
3871 int err;
3872 char *path;
3873
3874 err = get_path(f, ino, &path);
3875 if (!err) {
3876 struct fuse_intr_data d;
3877 fuse_prepare_interrupt(f, req, &d);
3878 err = fuse_fs_getxattr(f->fs, path, name, value, size);
3879 fuse_finish_interrupt(f, req, &d);
3880 free_path(f, ino, path);
3881 }
3882 return err;
3883}
3884
3885static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3886 size_t size)
3887{
3888 struct fuse *f = req_fuse_prepare(req);
3889 int res;
3890
3891 if (size) {
3892 char *value = (char *) malloc(size);
3893 if (value == NULL) {
3894 reply_err(req, -ENOMEM);
3895 return;
3896 }
3897 res = common_getxattr(f, req, ino, name, value, size);
3898 if (res > 0)
3899 fuse_reply_buf(req, value, res);
3900 else
3901 reply_err(req, res);
3902 free(value);
3903 } else {
3904 res = common_getxattr(f, req, ino, name, NULL, 0);
3905 if (res >= 0)
3906 fuse_reply_xattr(req, res);
3907 else
3908 reply_err(req, res);
3909 }
3910}
3911
3912static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3913 char *list, size_t size)
3914{
3915 char *path;
3916 int err;
3917
3918 err = get_path(f, ino, &path);
3919 if (!err) {
3920 struct fuse_intr_data d;
3921 fuse_prepare_interrupt(f, req, &d);
3922 err = fuse_fs_listxattr(f->fs, path, list, size);
3923 fuse_finish_interrupt(f, req, &d);
3924 free_path(f, ino, path);
3925 }
3926 return err;
3927}
3928
3929static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
3930{
3931 struct fuse *f = req_fuse_prepare(req);
3932 int res;
3933
3934 if (size) {
3935 char *list = (char *) malloc(size);
3936 if (list == NULL) {
3937 reply_err(req, -ENOMEM);
3938 return;
3939 }
3940 res = common_listxattr(f, req, ino, list, size);
3941 if (res > 0)
3942 fuse_reply_buf(req, list, res);
3943 else
3944 reply_err(req, res);
3945 free(list);
3946 } else {
3947 res = common_listxattr(f, req, ino, NULL, 0);
3948 if (res >= 0)
3949 fuse_reply_xattr(req, res);
3950 else
3951 reply_err(req, res);
3952 }
3953}
3954
3955static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
3956 const char *name)
3957{
3958 struct fuse *f = req_fuse_prepare(req);
3959 char *path;
3960 int err;
3961
3962 err = get_path(f, ino, &path);
3963 if (!err) {
3964 struct fuse_intr_data d;
3965 fuse_prepare_interrupt(f, req, &d);
3966 err = fuse_fs_removexattr(f->fs, path, name);
3967 fuse_finish_interrupt(f, req, &d);
3968 free_path(f, ino, path);
3969 }
3970 reply_err(req, err);
3971}
3972
3973static struct lock *locks_conflict(struct node *node, const struct lock *lock)
3974{
3975 struct lock *l;
3976
3977 for (l = node->locks; l; l = l->next)
3978 if (l->owner != lock->owner &&
3979 lock->start <= l->end && l->start <= lock->end &&
3980 (l->type == F_WRLCK || lock->type == F_WRLCK))
3981 break;
3982
3983 return l;
3984}
3985
3986static void delete_lock(struct lock **lockp)
3987{
3988 struct lock *l = *lockp;
3989 *lockp = l->next;
3990 free(l);
3991}
3992
3993static void insert_lock(struct lock **pos, struct lock *lock)
3994{
3995 lock->next = *pos;
3996 *pos = lock;
3997}
3998
3999static int locks_insert(struct node *node, struct lock *lock)
4000{
4001 struct lock **lp;
4002 struct lock *newl1 = NULL;
4003 struct lock *newl2 = NULL;
4004
4005 if (lock->type != F_UNLCK || lock->start != 0 ||
4006 lock->end != OFFSET_MAX) {
4007 newl1 = malloc(sizeof(struct lock));
4008 newl2 = malloc(sizeof(struct lock));
4009
4010 if (!newl1 || !newl2) {
4011 free(newl1);
4012 free(newl2);
4013 return -ENOLCK;
4014 }
4015 }
4016
4017 for (lp = &node->locks; *lp;) {
4018 struct lock *l = *lp;
4019 if (l->owner != lock->owner)
4020 goto skip;
4021
4022 if (lock->type == l->type) {
4023 if (l->end < lock->start - 1)
4024 goto skip;
4025 if (lock->end < l->start - 1)
4026 break;
4027 if (l->start <= lock->start && lock->end <= l->end)
4028 goto out;
4029 if (l->start < lock->start)
4030 lock->start = l->start;
4031 if (lock->end < l->end)
4032 lock->end = l->end;
4033 goto delete;
4034 } else {
4035 if (l->end < lock->start)
4036 goto skip;
4037 if (lock->end < l->start)
4038 break;
4039 if (lock->start <= l->start && l->end <= lock->end)
4040 goto delete;
4041 if (l->end <= lock->end) {
4042 l->end = lock->start - 1;
4043 goto skip;
4044 }
4045 if (lock->start <= l->start) {
4046 l->start = lock->end + 1;
4047 break;
4048 }
4049 *newl2 = *l;
4050 newl2->start = lock->end + 1;
4051 l->end = lock->start - 1;
4052 insert_lock(&l->next, newl2);
4053 newl2 = NULL;
4054 }
4055 skip:
4056 lp = &l->next;
4057 continue;
4058
4059 delete:
4060 delete_lock(lp);
4061 }
4062 if (lock->type != F_UNLCK) {
4063 *newl1 = *lock;
4064 insert_lock(lp, newl1);
4065 newl1 = NULL;
4066 }
4067out:
4068 free(newl1);
4069 free(newl2);
4070 return 0;
4071}
4072
4073static void flock_to_lock(struct flock *flock, struct lock *lock)
4074{
4075 memset(lock, 0, sizeof(struct lock));
4076 lock->type = flock->l_type;
4077 lock->start = flock->l_start;
4078 lock->end =
4079 flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
4080 lock->pid = flock->l_pid;
4081}
4082
4083static void lock_to_flock(struct lock *lock, struct flock *flock)
4084{
4085 flock->l_type = lock->type;
4086 flock->l_start = lock->start;
4087 flock->l_len =
4088 (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
4089 flock->l_pid = lock->pid;
4090}
4091
4092static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
4093 const char *path, struct fuse_file_info *fi)
4094{
4095 struct fuse_intr_data d;
4096 struct flock lock;
4097 struct lock l;
4098 int err;
4099 int errlock;
4100
4101 fuse_prepare_interrupt(f, req, &d);
4102 memset(&lock, 0, sizeof(lock));
4103 lock.l_type = F_UNLCK;
4104 lock.l_whence = SEEK_SET;
4105 err = fuse_fs_flush(f->fs, path, fi);
4106 errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock);
4107 fuse_finish_interrupt(f, req, &d);
4108
4109 if (errlock != -ENOSYS) {
4110 flock_to_lock(&lock, &l);
4111 l.owner = fi->lock_owner;
4112 pthread_mutex_lock(&f->lock);
4113 locks_insert(get_node(f, ino), &l);
4114 pthread_mutex_unlock(&f->lock);
4115
4116 /* if op.lock() is defined FLUSH is needed regardless
4117 of op.flush() */
4118 if (err == -ENOSYS)
4119 err = 0;
4120 }
4121 return err;
4122}
4123
4124static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino,
4125 struct fuse_file_info *fi)
4126{
4127 struct fuse *f = req_fuse_prepare(req);
4128 struct fuse_intr_data d;
4129 char *path;
4130 int err = 0;
4131
4132 get_path_nullok(f, ino, &path);
4133 if (fi->flush) {
4134 err = fuse_flush_common(f, req, ino, path, fi);
4135 if (err == -ENOSYS)
4136 err = 0;
4137 }
4138
4139 fuse_prepare_interrupt(f, req, &d);
4140 fuse_do_release(f, ino, path, fi);
4141 fuse_finish_interrupt(f, req, &d);
4142 free_path(f, ino, path);
4143
4144 reply_err(req, err);
4145}
4146
4147static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino,
4148 struct fuse_file_info *fi)
4149{
4150 struct fuse *f = req_fuse_prepare(req);
4151 char *path;
4152 int err;
4153
4154 get_path_nullok(f, ino, &path);
4155 err = fuse_flush_common(f, req, ino, path, fi);
4156 free_path(f, ino, path);
4157
4158 reply_err(req, err);
4159}
4160
4161static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
4162 struct fuse_file_info *fi, struct flock *lock,
4163 int cmd)
4164{
4165 struct fuse *f = req_fuse_prepare(req);
4166 char *path;
4167 int err;
4168
4169 err = get_path_nullok(f, ino, &path);
4170 if (!err) {
4171 struct fuse_intr_data d;
4172 fuse_prepare_interrupt(f, req, &d);
4173 err = fuse_fs_lock(f->fs, path, fi, cmd, lock);
4174 fuse_finish_interrupt(f, req, &d);
4175 free_path(f, ino, path);
4176 }
4177 return err;
4178}
4179
4180static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino,
4181 struct fuse_file_info *fi, struct flock *lock)
4182{
4183 int err;
4184 struct lock l;
4185 struct lock *conflict;
4186 struct fuse *f = req_fuse(req);
4187
4188 flock_to_lock(lock, &l);
4189 l.owner = fi->lock_owner;
4190 pthread_mutex_lock(&f->lock);
4191 conflict = locks_conflict(get_node(f, ino), &l);
4192 if (conflict)
4193 lock_to_flock(conflict, lock);
4194 pthread_mutex_unlock(&f->lock);
4195 if (!conflict)
4196 err = fuse_lock_common(req, ino, fi, lock, F_GETLK);
4197 else
4198 err = 0;
4199
4200 if (!err)
4201 fuse_reply_lock(req, lock);
4202 else
4203 reply_err(req, err);
4204}
4205
4206static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino,
4207 struct fuse_file_info *fi, struct flock *lock,
4208 int sleep)
4209{
4210 int err = fuse_lock_common(req, ino, fi, lock,
4211 sleep ? F_SETLKW : F_SETLK);
4212 if (!err) {
4213 struct fuse *f = req_fuse(req);
4214 struct lock l;
4215 flock_to_lock(lock, &l);
4216 l.owner = fi->lock_owner;
4217 pthread_mutex_lock(&f->lock);
4218 locks_insert(get_node(f, ino), &l);
4219 pthread_mutex_unlock(&f->lock);
4220 }
4221 reply_err(req, err);
4222}
4223
4224static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino,
4225 struct fuse_file_info *fi, int op)
4226{
4227 struct fuse *f = req_fuse_prepare(req);
4228 char *path;
4229 int err;
4230
4231 err = get_path_nullok(f, ino, &path);
4232 if (err == 0) {
4233 struct fuse_intr_data d;
4234 fuse_prepare_interrupt(f, req, &d);
4235 err = fuse_fs_flock(f->fs, path, fi, op);
4236 fuse_finish_interrupt(f, req, &d);
4237 free_path(f, ino, path);
4238 }
4239 reply_err(req, err);
4240}
4241
4242static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
4243 uint64_t idx)
4244{
4245 struct fuse *f = req_fuse_prepare(req);
4246 struct fuse_intr_data d;
4247 char *path;
4248 int err;
4249
4250 err = get_path(f, ino, &path);
4251 if (!err) {
4252 fuse_prepare_interrupt(f, req, &d);
4253 err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
4254 fuse_finish_interrupt(f, req, &d);
4255 free_path(f, ino, path);
4256 }
4257 if (!err)
4258 fuse_reply_bmap(req, idx);
4259 else
4260 reply_err(req, err);
4261}
4262
4263static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
4264 void *arg, struct fuse_file_info *llfi,
4265 unsigned int flags, const void *in_buf,
4266 size_t in_bufsz, size_t out_bufsz)
4267{
4268 struct fuse *f = req_fuse_prepare(req);
4269 struct fuse_intr_data d;
4270 struct fuse_file_info fi;
4271 char *path, *out_buf = NULL;
4272 int err;
4273
4274 err = -EPERM;
4275 if (flags & FUSE_IOCTL_UNRESTRICTED)
4276 goto err;
4277
4278 if (flags & FUSE_IOCTL_DIR)
4279 get_dirhandle(llfi, &fi);
4280 else
4281 fi = *llfi;
4282
4283 if (out_bufsz) {
4284 err = -ENOMEM;
4285 out_buf = malloc(out_bufsz);
4286 if (!out_buf)
4287 goto err;
4288 }
4289
4290 assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
4291 if (out_buf && in_bufsz)
4292 memcpy(out_buf, in_buf, in_bufsz);
4293
4294 err = get_path_nullok(f, ino, &path);
4295 if (err)
4296 goto err;
4297
4298 fuse_prepare_interrupt(f, req, &d);
4299
4300 err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags,
4301 out_buf ? out_buf : (void *)in_buf);
4302
4303 fuse_finish_interrupt(f, req, &d);
4304 free_path(f, ino, path);
4305
4306 if (err < 0)
4307 goto err;
4308 fuse_reply_ioctl(req, err, out_buf, out_bufsz);
4309 goto out;
4310err:
4311 reply_err(req, err);
4312out:
4313 free(out_buf);
4314}
4315
4316static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
4317 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
4318{
4319 struct fuse *f = req_fuse_prepare(req);
4320 struct fuse_intr_data d;
4321 char *path;
4322 int err;
4323 unsigned revents = 0;
4324
4325 err = get_path_nullok(f, ino, &path);
4326 if (!err) {
4327 fuse_prepare_interrupt(f, req, &d);
4328 err = fuse_fs_poll(f->fs, path, fi, ph, &revents);
4329 fuse_finish_interrupt(f, req, &d);
4330 free_path(f, ino, path);
4331 }
4332 if (!err)
4333 fuse_reply_poll(req, revents);
4334 else
4335 reply_err(req, err);
4336}
4337
4338static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
4339 off_t offset, off_t length, struct fuse_file_info *fi)
4340{
4341 struct fuse *f = req_fuse_prepare(req);
4342 struct fuse_intr_data d;
4343 char *path;
4344 int err;
4345
4346 err = get_path_nullok(f, ino, &path);
4347 if (!err) {
4348 fuse_prepare_interrupt(f, req, &d);
4349 err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi);
4350 fuse_finish_interrupt(f, req, &d);
4351 free_path(f, ino, path);
4352 }
4353 reply_err(req, err);
4354}
4355
4356static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
4357 off_t off_in, struct fuse_file_info *fi_in,
4358 fuse_ino_t nodeid_out, off_t off_out,
4359 struct fuse_file_info *fi_out, size_t len,
4360 int flags)
4361{
4362 struct fuse *f = req_fuse_prepare(req);
4363 struct fuse_intr_data d;
4364 char *path_in, *path_out;
4365 int err;
4366 ssize_t res;
4367
4368 err = get_path_nullok(f, nodeid_in, &path_in);
4369 if (err) {
4370 reply_err(req, err);
4371 return;
4372 }
4373
4374 err = get_path_nullok(f, nodeid_out, &path_out);
4375 if (err) {
4376 free_path(f, nodeid_in, path_in);
4377 reply_err(req, err);
4378 return;
4379 }
4380
4381 fuse_prepare_interrupt(f, req, &d);
4382 res = fuse_fs_copy_file_range(f->fs, path_in, fi_in, off_in, path_out,
4383 fi_out, off_out, len, flags);
4384 fuse_finish_interrupt(f, req, &d);
4385
4386 if (res >= 0)
4387 fuse_reply_write(req, res);
4388 else
4389 reply_err(req, res);
4390
4391 free_path(f, nodeid_in, path_in);
4392 free_path(f, nodeid_out, path_out);
4393}
4394
4395static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
4396 struct fuse_file_info *fi)
4397{
4398 struct fuse *f = req_fuse_prepare(req);
4399 struct fuse_intr_data d;
4400 char *path;
4401 int err;
4402 off_t res;
4403
4404 err = get_path(f, ino, &path);
4405 if (err) {
4406 reply_err(req, err);
4407 return;
4408 }
4409
4410 fuse_prepare_interrupt(f, req, &d);
4411 res = fuse_fs_lseek(f->fs, path, off, whence, fi);
4412 fuse_finish_interrupt(f, req, &d);
4413 free_path(f, ino, path);
4414 if (res >= 0)
4415 fuse_reply_lseek(req, res);
4416 else
4417 reply_err(req, res);
4418}
4419
4420#ifdef HAVE_STATX
4421static void fuse_lib_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
4422 struct fuse_file_info *fi)
4423{
4424 struct fuse *f = req_fuse_prepare(req);
4425 struct statx stxbuf;
4426 char *path;
4427 int err;
4428
4429 memset(&stxbuf, 0, sizeof(stxbuf));
4430
4431 if (fi != NULL)
4432 err = get_path_nullok(f, ino, &path);
4433 else
4434 err = get_path(f, ino, &path);
4435
4436 if (!err) {
4437 struct fuse_intr_data d;
4438
4439 if (!path)
4440 flags |= AT_EMPTY_PATH;
4441 fuse_prepare_interrupt(f, req, &d);
4442 err = fuse_fs_statx(f->fs, path, flags, mask, &stxbuf, fi);
4443 fuse_finish_interrupt(f, req, &d);
4444 free_path(f, ino, path);
4445 }
4446 if (!err) {
4447 struct node *node;
4448
4449 pthread_mutex_lock(&f->lock);
4450 node = get_node(f, ino);
4451 if (node->is_hidden && stxbuf.stx_nlink > 0)
4452 stxbuf.stx_nlink--;
4453 if (f->conf.auto_cache) {
4454 struct stat stbuf;
4455
4456 stbuf.st_mtime = stxbuf.stx_mtime.tv_nsec;
4457 ST_MTIM_NSEC(&stbuf) = stxbuf.stx_mtime.tv_nsec;
4458 stbuf.st_size = stxbuf.stx_size;
4459 update_stat(node, &stbuf);
4460 }
4461 pthread_mutex_unlock(&f->lock);
4462 set_statx(f, ino, &stxbuf);
4463 fuse_reply_statx(req, 0, &stxbuf, f->conf.attr_timeout);
4464 } else
4465 reply_err(req, err);
4466}
4467#endif
4468
4469static int clean_delay(struct fuse *f)
4470{
4471 /*
4472 * This is calculating the delay between clean runs. To
4473 * reduce the number of cleans we are doing them 10 times
4474 * within the remember window.
4475 */
4476 int min_sleep = 60;
4477 int max_sleep = 3600;
4478 int sleep_time = f->conf.remember / 10;
4479
4480 if (sleep_time > max_sleep)
4481 return max_sleep;
4482 if (sleep_time < min_sleep)
4483 return min_sleep;
4484 return sleep_time;
4485}
4486
4487int fuse_clean_cache(struct fuse *f)
4488{
4489 struct node_lru *lnode;
4490 struct list_head *curr, *next;
4491 struct node *node;
4492 struct timespec now;
4493
4494 pthread_mutex_lock(&f->lock);
4495
4496 curr_time(&now);
4497
4498 for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) {
4499 double age;
4500
4501 next = curr->next;
4502 lnode = list_entry(curr, struct node_lru, lru);
4503 node = &lnode->node;
4504
4505 age = diff_timespec(&now, &lnode->forget_time);
4506 if (age <= f->conf.remember)
4507 break;
4508
4509 assert(node->nlookup == 1);
4510
4511 /* Don't forget active directories */
4512 if (node->refctr > 1)
4513 continue;
4514
4515 node->nlookup = 0;
4516 unhash_name(f, node);
4517 unref_node(f, node);
4518 }
4519 pthread_mutex_unlock(&f->lock);
4520
4521 return clean_delay(f);
4522}
4523
4524static struct fuse_lowlevel_ops fuse_path_ops = {
4525 .init = fuse_lib_init,
4526 .destroy = fuse_lib_destroy,
4527 .lookup = fuse_lib_lookup,
4528 .forget = fuse_lib_forget,
4529 .forget_multi = fuse_lib_forget_multi,
4530 .getattr = fuse_lib_getattr,
4531 .setattr = fuse_lib_setattr,
4532 .access = fuse_lib_access,
4533 .readlink = fuse_lib_readlink,
4534 .mknod = fuse_lib_mknod,
4535 .mkdir = fuse_lib_mkdir,
4536 .unlink = fuse_lib_unlink,
4537 .rmdir = fuse_lib_rmdir,
4538 .symlink = fuse_lib_symlink,
4539 .rename = fuse_lib_rename,
4540 .link = fuse_lib_link,
4541 .create = fuse_lib_create,
4542 .open = fuse_lib_open,
4543 .read = fuse_lib_read,
4544 .write_buf = fuse_lib_write_buf,
4545 .flush = fuse_lib_flush,
4546 .release = fuse_lib_release,
4547 .fsync = fuse_lib_fsync,
4548 .opendir = fuse_lib_opendir,
4549 .readdir = fuse_lib_readdir,
4550 .readdirplus = fuse_lib_readdirplus,
4551 .releasedir = fuse_lib_releasedir,
4552 .fsyncdir = fuse_lib_fsyncdir,
4553 .statfs = fuse_lib_statfs,
4554 .setxattr = fuse_lib_setxattr,
4555 .getxattr = fuse_lib_getxattr,
4556 .listxattr = fuse_lib_listxattr,
4557 .removexattr = fuse_lib_removexattr,
4558 .getlk = fuse_lib_getlk,
4559 .setlk = fuse_lib_setlk,
4560 .flock = fuse_lib_flock,
4561 .bmap = fuse_lib_bmap,
4562 .ioctl = fuse_lib_ioctl,
4563 .poll = fuse_lib_poll,
4564 .fallocate = fuse_lib_fallocate,
4565 .copy_file_range = fuse_lib_copy_file_range,
4566 .lseek = fuse_lib_lseek,
4567#ifdef HAVE_STATX
4568 .statx = fuse_lib_statx,
4569#endif
4570};
4571
4572int fuse_notify_poll(struct fuse_pollhandle *ph)
4573{
4574 return fuse_lowlevel_notify_poll(ph);
4575}
4576
4577struct fuse_session *fuse_get_session(struct fuse *f)
4578{
4579 return f->se;
4580}
4581
4582static int fuse_session_loop_remember(struct fuse *f)
4583{
4584 struct fuse_session *se = f->se;
4585 int res = 0;
4586 struct timespec now;
4587 time_t next_clean;
4588 struct pollfd fds = {
4589 .fd = se->fd,
4590 .events = POLLIN
4591 };
4592 struct fuse_buf fbuf = {
4593 .mem = NULL,
4594 };
4595
4596 curr_time(&now);
4597 next_clean = now.tv_sec;
4598 while (!fuse_session_exited(se)) {
4599 unsigned timeout;
4600
4601 curr_time(&now);
4602 if (now.tv_sec < next_clean)
4603 timeout = next_clean - now.tv_sec;
4604 else
4605 timeout = 0;
4606
4607 res = poll(&fds, 1, timeout * 1000);
4608 if (res == -1) {
4609 if (errno == EINTR)
4610 continue;
4611 else
4612 break;
4613 } else if (res > 0) {
4614 res = fuse_session_receive_buf_internal(se, &fbuf,
4615 NULL);
4616 if (res == -EINTR)
4617 continue;
4618 if (res <= 0)
4619 break;
4620
4621 fuse_session_process_buf_internal(se, &fbuf, NULL);
4622 } else {
4623 timeout = fuse_clean_cache(f);
4624 curr_time(&now);
4625 next_clean = now.tv_sec + timeout;
4626 }
4627 }
4628
4629 fuse_buf_free(&fbuf);
4630 return res < 0 ? -1 : 0;
4631}
4632
4633int fuse_loop(struct fuse *f)
4634{
4635 if (!f)
4636 return -1;
4637
4638 if (lru_enabled(f))
4639 return fuse_session_loop_remember(f);
4640
4641 return fuse_session_loop(f->se);
4642}
4643
4644FUSE_SYMVER("fuse_loop_mt_312", "fuse_loop_mt@@FUSE_3.12")
4645int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config)
4646{
4647 if (f == NULL)
4648 return -1;
4649
4650 int res = fuse_start_cleanup_thread(f);
4651 if (res)
4652 return -1;
4653
4654 res = fuse_session_loop_mt_312(fuse_get_session(f), config);
4656 return res;
4657}
4658
4659int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1);
4660FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@FUSE_3.2")
4661int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1)
4662{
4663 struct fuse_loop_config *config = fuse_loop_cfg_create();
4664 if (config == NULL)
4665 return ENOMEM;
4666
4667 fuse_loop_cfg_convert(config, config_v1);
4668
4669 int res = fuse_loop_mt_312(f, config);
4670
4671 fuse_loop_cfg_destroy(config);
4672
4673 return res;
4674}
4675
4676int fuse_loop_mt_31(struct fuse *f, int clone_fd);
4677FUSE_SYMVER("fuse_loop_mt_31", "fuse_loop_mt@FUSE_3.0")
4678int fuse_loop_mt_31(struct fuse *f, int clone_fd)
4679{
4680 int err;
4681 struct fuse_loop_config *config = fuse_loop_cfg_create();
4682
4683 if (config == NULL)
4684 return ENOMEM;
4685
4686 fuse_loop_cfg_set_clone_fd(config, clone_fd);
4687
4688 err = fuse_loop_mt_312(f, config);
4689
4690 fuse_loop_cfg_destroy(config);
4691
4692 return err;
4693}
4694
4695void fuse_exit(struct fuse *f)
4696{
4697 fuse_session_exit(f->se);
4698}
4699
4700struct fuse_context *fuse_get_context(void)
4701{
4702 struct fuse_context_i *c = fuse_get_context_internal();
4703
4704 if (c)
4705 return &c->ctx;
4706 else
4707 return NULL;
4708}
4709
4710int fuse_getgroups(int size, gid_t list[])
4711{
4712 struct fuse_context_i *c = fuse_get_context_internal();
4713 if (!c)
4714 return -EINVAL;
4715
4716 return fuse_req_getgroups(c->req, size, list);
4717}
4718
4719int fuse_interrupted(void)
4720{
4721 struct fuse_context_i *c = fuse_get_context_internal();
4722
4723 if (c)
4724 return fuse_req_interrupted(c->req);
4725 else
4726 return 0;
4727}
4728
4729int fuse_invalidate_path(struct fuse *f, const char *path) {
4730 fuse_ino_t ino;
4731 int err = lookup_path_in_cache(f, path, &ino);
4732 if (err) {
4733 return err;
4734 }
4735
4736 return fuse_lowlevel_notify_inval_inode(f->se, ino, 0, 0);
4737}
4738
4739#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
4740
4741static const struct fuse_opt fuse_lib_opts[] = {
4744 FUSE_LIB_OPT("debug", debug, 1),
4745 FUSE_LIB_OPT("-d", debug, 1),
4746 FUSE_LIB_OPT("kernel_cache", kernel_cache, 1),
4747 FUSE_LIB_OPT("auto_cache", auto_cache, 1),
4748 FUSE_LIB_OPT("noauto_cache", auto_cache, 0),
4749 FUSE_LIB_OPT("no_rofd_flush", no_rofd_flush, 1),
4750 FUSE_LIB_OPT("umask=", set_mode, 1),
4751 FUSE_LIB_OPT("umask=%o", umask, 0),
4752 FUSE_LIB_OPT("fmask=", set_mode, 1),
4753 FUSE_LIB_OPT("fmask=%o", fmask, 0),
4754 FUSE_LIB_OPT("dmask=", set_mode, 1),
4755 FUSE_LIB_OPT("dmask=%o", dmask, 0),
4756 FUSE_LIB_OPT("uid=", set_uid, 1),
4757 FUSE_LIB_OPT("uid=%d", uid, 0),
4758 FUSE_LIB_OPT("gid=", set_gid, 1),
4759 FUSE_LIB_OPT("gid=%d", gid, 0),
4760 FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0),
4761 FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0),
4762 FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0),
4763 FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1),
4764 FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0),
4765 FUSE_LIB_OPT("noforget", remember, -1),
4766 FUSE_LIB_OPT("remember=%u", remember, 0),
4767 FUSE_LIB_OPT("modules=%s", modules, 0),
4768 FUSE_LIB_OPT("parallel_direct_write=%d", parallel_direct_writes, 0),
4770};
4771
4772static int fuse_lib_opt_proc(void *data, const char *arg, int key,
4773 struct fuse_args *outargs)
4774{
4775 (void) arg; (void) outargs; (void) data; (void) key;
4776
4777 /* Pass through unknown options */
4778 return 1;
4779}
4780
4781
4782static const struct fuse_opt fuse_help_opts[] = {
4783 FUSE_LIB_OPT("modules=%s", modules, 1),
4784 FUSE_OPT_KEY("modules=%s", FUSE_OPT_KEY_KEEP),
4786};
4787
4788static void print_module_help(const char *name,
4790{
4791 struct fuse_args a = FUSE_ARGS_INIT(0, NULL);
4792 if (fuse_opt_add_arg(&a, "") == -1 ||
4793 fuse_opt_add_arg(&a, "-h") == -1)
4794 return;
4795 printf("\nOptions for %s module:\n", name);
4796 (*fac)(&a, NULL);
4798}
4799
4800void fuse_lib_help(struct fuse_args *args)
4801{
4802 /* These are not all options, but only the ones that
4803 may be of interest to an end-user */
4804 printf(
4805" -o kernel_cache cache files in kernel\n"
4806" -o [no]auto_cache enable caching based on modification times (off)\n"
4807" -o no_rofd_flush disable flushing of read-only fd on close (off)\n"
4808" -o umask=M set file permissions (octal)\n"
4809" -o fmask=M set file permissions (octal)\n"
4810" -o dmask=M set dir permissions (octal)\n"
4811" -o uid=N set file owner\n"
4812" -o gid=N set file group\n"
4813" -o entry_timeout=T cache timeout for names (1.0s)\n"
4814" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
4815" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
4816" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n"
4817" -o noforget never forget cached inodes\n"
4818" -o remember=T remember cached inodes for T seconds (0s)\n"
4819" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n");
4820
4821
4822 /* Print low-level help */
4824
4825 /* Print help for builtin modules */
4826 print_module_help("subdir", &fuse_module_subdir_factory);
4827#ifdef HAVE_ICONV
4828 print_module_help("iconv", &fuse_module_iconv_factory);
4829#endif
4830
4831 /* Parse command line options in case we need to
4832 activate more modules */
4833 struct fuse_config conf = { .modules = NULL };
4834 if (fuse_opt_parse(args, &conf, fuse_help_opts,
4835 fuse_lib_opt_proc) == -1
4836 || !conf.modules)
4837 return;
4838
4839 char *module;
4840 char *next;
4841 struct fuse_module *m;
4842
4843 // Iterate over all modules
4844 for (module = conf.modules; module; module = next) {
4845 char *p;
4846 for (p = module; *p && *p != ':'; p++);
4847 next = *p ? p + 1 : NULL;
4848 *p = '\0';
4849
4850 m = fuse_get_module(module);
4851 if (m)
4852 print_module_help(module, &m->factory);
4853 }
4854}
4855
4856static int fuse_init_intr_signal(int signum, int *installed)
4857{
4858 struct sigaction old_sa;
4859
4860 if (sigaction(signum, NULL, &old_sa) == -1) {
4861 perror("fuse: cannot get old signal handler");
4862 return -1;
4863 }
4864
4865 if (old_sa.sa_handler == SIG_DFL) {
4866 struct sigaction sa;
4867
4868 memset(&sa, 0, sizeof(struct sigaction));
4869 sa.sa_handler = fuse_intr_sighandler;
4870 sigemptyset(&sa.sa_mask);
4871
4872 if (sigaction(signum, &sa, NULL) == -1) {
4873 perror("fuse: cannot set interrupt signal handler");
4874 return -1;
4875 }
4876 *installed = 1;
4877 }
4878 return 0;
4879}
4880
4881static void fuse_restore_intr_signal(int signum)
4882{
4883 struct sigaction sa;
4884
4885 memset(&sa, 0, sizeof(struct sigaction));
4886 sa.sa_handler = SIG_DFL;
4887 sigaction(signum, &sa, NULL);
4888}
4889
4890
4891static int fuse_push_module(struct fuse *f, const char *module,
4892 struct fuse_args *args)
4893{
4894 struct fuse_fs *fs[2] = { f->fs, NULL };
4895 struct fuse_fs *newfs;
4896 struct fuse_module *m = fuse_get_module(module);
4897
4898 if (!m)
4899 return -1;
4900
4901 newfs = m->factory(args, fs);
4902 if (!newfs) {
4903 fuse_put_module(m);
4904 return -1;
4905 }
4906 f->fs = newfs;
4907 return 0;
4908}
4909
4910struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
4911 void *user_data)
4912{
4913 struct fuse_fs *fs;
4914
4915 if (sizeof(struct fuse_operations) < op_size) {
4916 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not not work\n");
4917 op_size = sizeof(struct fuse_operations);
4918 }
4919
4920 fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
4921 if (!fs) {
4922 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse_fs object\n");
4923 return NULL;
4924 }
4925
4926 fs->user_data = user_data;
4927 if (op)
4928 memcpy(&fs->op, op, op_size);
4929 return fs;
4930}
4931
4932static int node_table_init(struct node_table *t)
4933{
4934 t->size = NODE_TABLE_MIN_SIZE;
4935 t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
4936 if (t->array == NULL) {
4937 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
4938 return -1;
4939 }
4940 t->use = 0;
4941 t->split = 0;
4942
4943 return 0;
4944}
4945
4946static void *fuse_prune_nodes(void *fuse)
4947{
4948 struct fuse *f = fuse;
4949 int sleep_time;
4950
4951 fuse_set_thread_name("fuse_prune_nodes");
4952
4953 while(1) {
4954 sleep_time = fuse_clean_cache(f);
4955 sleep(sleep_time);
4956 }
4957 return NULL;
4958}
4959
4960int fuse_start_cleanup_thread(struct fuse *f)
4961{
4962 if (lru_enabled(f))
4963 return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
4964
4965 return 0;
4966}
4967
4968void fuse_stop_cleanup_thread(struct fuse *f)
4969{
4970 if (lru_enabled(f)) {
4971 pthread_mutex_lock(&f->lock);
4972 pthread_cancel(f->prune_thread);
4973 pthread_mutex_unlock(&f->lock);
4974 pthread_join(f->prune_thread, NULL);
4975 }
4976}
4977
4978/*
4979 * Not supposed to be called directly, but supposed to be called
4980 * through the fuse_new macro
4981 */
4982struct fuse *_fuse_new_31(struct fuse_args *args,
4983 const struct fuse_operations *op, size_t op_size,
4984 struct libfuse_version *version, void *user_data)
4985{
4986 struct fuse *f;
4987 struct node *root;
4988 struct fuse_fs *fs;
4989 struct fuse_lowlevel_ops llop = fuse_path_ops;
4990
4991 f = (struct fuse *) calloc(1, sizeof(struct fuse));
4992 if (f == NULL) {
4993 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
4994 goto out;
4995 }
4996
4997 f->conf.entry_timeout = 1.0;
4998 f->conf.attr_timeout = 1.0;
4999 f->conf.negative_timeout = 0.0;
5000 f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
5001
5002 /* Parse options */
5003 if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
5004 fuse_lib_opt_proc) == -1)
5005 goto out_free;
5006
5007 pthread_mutex_lock(&fuse_context_lock);
5008 static int builtin_modules_registered = 0;
5009 /* Have the builtin modules already been registered? */
5010 if (builtin_modules_registered == 0) {
5011 /* If not, register them. */
5012 fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
5013#ifdef HAVE_ICONV
5014 fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
5015#endif
5016 builtin_modules_registered= 1;
5017 }
5018 pthread_mutex_unlock(&fuse_context_lock);
5019
5020 if (fuse_create_context_key() == -1)
5021 goto out_free;
5022
5023 fs = fuse_fs_new(op, op_size, user_data);
5024 if (!fs)
5025 goto out_delete_context_key;
5026
5027 f->fs = fs;
5028
5029 /* Oh f**k, this is ugly! */
5030 if (!fs->op.lock) {
5031 llop.getlk = NULL;
5032 llop.setlk = NULL;
5033 }
5034
5035 f->pagesize = getpagesize();
5036 init_list_head(&f->partial_slabs);
5037 init_list_head(&f->full_slabs);
5038 init_list_head(&f->lru_table);
5039
5040 if (f->conf.modules) {
5041 char *module;
5042 char *next;
5043
5044 for (module = f->conf.modules; module; module = next) {
5045 char *p;
5046 for (p = module; *p && *p != ':'; p++);
5047 next = *p ? p + 1 : NULL;
5048 *p = '\0';
5049 if (module[0] &&
5050 fuse_push_module(f, module, args) == -1)
5051 goto out_free_fs;
5052 }
5053 }
5054
5055 if (!f->conf.ac_attr_timeout_set)
5056 f->conf.ac_attr_timeout = f->conf.attr_timeout;
5057
5058#if defined(__FreeBSD__) || defined(__NetBSD__)
5059 /*
5060 * In FreeBSD, we always use these settings as inode numbers
5061 * are needed to make getcwd(3) work.
5062 */
5063 f->conf.readdir_ino = 1;
5064#endif
5065
5066 /* not declared globally, to restrict usage of this function */
5067 struct fuse_session *fuse_session_new_versioned(
5068 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
5069 size_t op_size, struct libfuse_version *version,
5070 void *userdata);
5071 f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version,
5072 f);
5073 if (f->se == NULL)
5074 goto out_free_fs;
5075
5076 /* Trace topmost layer by default */
5077 f->fs->debug = f->conf.debug;
5078 f->ctr = 0;
5079 f->generation = 0;
5080 if (node_table_init(&f->name_table) == -1)
5081 goto out_free_session;
5082
5083 if (node_table_init(&f->id_table) == -1)
5084 goto out_free_name_table;
5085
5086 pthread_mutex_init(&f->lock, NULL);
5087
5088 root = alloc_node(f);
5089 if (root == NULL) {
5090 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
5091 goto out_free_id_table;
5092 }
5093 if (lru_enabled(f)) {
5094 struct node_lru *lnode = node_lru(root);
5095 init_list_head(&lnode->lru);
5096 }
5097
5098 strcpy(root->inline_name, "/");
5099 root->name = root->inline_name;
5100 root->parent = NULL;
5101 root->nodeid = FUSE_ROOT_ID;
5102 inc_nlookup(root);
5103 hash_id(f, root);
5104
5105 return f;
5106
5107out_free_id_table:
5108 free(f->id_table.array);
5109out_free_name_table:
5110 free(f->name_table.array);
5111out_free_session:
5112 fuse_session_destroy(f->se);
5113out_free_fs:
5114 free(f->fs);
5115 free(f->conf.modules);
5116out_delete_context_key:
5117 fuse_delete_context_key();
5118out_free:
5119 free(f);
5120out:
5121 return NULL;
5122}
5123
5124/* Emulates 3.0-style fuse_new(), which processes --help */
5125FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
5126struct fuse *_fuse_new_30(struct fuse_args *args,
5127 const struct fuse_operations *op,
5128 size_t op_size,
5129 struct libfuse_version *version,
5130 void *user_data)
5131{
5132 struct fuse_config conf = {0};
5133
5134 const struct fuse_opt opts[] = {
5135 FUSE_LIB_OPT("-h", show_help, 1),
5136 FUSE_LIB_OPT("--help", show_help, 1),
5138 };
5139
5140 if (fuse_opt_parse(args, &conf, opts,
5141 fuse_lib_opt_proc) == -1)
5142 return NULL;
5143
5144 if (conf.show_help) {
5145 fuse_lib_help(args);
5146 return NULL;
5147 } else
5148 return _fuse_new_31(args, op, op_size, version, user_data);
5149}
5150
5151/* ABI compat version */
5152struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
5153 size_t op_size, void *user_data);
5154FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
5155struct fuse *fuse_new_31(struct fuse_args *args,
5156 const struct fuse_operations *op,
5157 size_t op_size, void *user_data)
5158{
5159 /* unknown version */
5160 struct libfuse_version version = { 0 };
5161
5162 return _fuse_new_31(args, op, op_size, &version, user_data);
5163}
5164
5165/*
5166 * ABI compat version
5167 * Emulates 3.0-style fuse_new(), which processes --help
5168 */
5169struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
5170 size_t op_size, void *user_data);
5171FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
5172struct fuse *fuse_new_30(struct fuse_args *args,
5173 const struct fuse_operations *op,
5174 size_t op_size, void *user_data)
5175{
5176 struct fuse_config conf = {0};
5177
5178 const struct fuse_opt opts[] = {
5179 FUSE_LIB_OPT("-h", show_help, 1),
5180 FUSE_LIB_OPT("--help", show_help, 1),
5182 };
5183
5184 if (fuse_opt_parse(args, &conf, opts,
5185 fuse_lib_opt_proc) == -1)
5186 return NULL;
5187
5188 if (conf.show_help) {
5189 fuse_lib_help(args);
5190 return NULL;
5191 } else
5192 return fuse_new_31(args, op, op_size, user_data);
5193}
5194
5195
5196void fuse_destroy(struct fuse *f)
5197{
5198 size_t i;
5199
5200 if (f->conf.intr && f->intr_installed)
5201 fuse_restore_intr_signal(f->conf.intr_signal);
5202
5203 if (f->fs) {
5204 fuse_create_context(f);
5205
5206 for (i = 0; i < f->id_table.size; i++) {
5207 struct node *node;
5208
5209 for (node = f->id_table.array[i]; node != NULL;
5210 node = node->id_next) {
5211 if (node->is_hidden) {
5212 char *path;
5213 if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
5214 fuse_fs_unlink(f->fs, path);
5215 free(path);
5216 }
5217 }
5218 }
5219 }
5220 }
5221 for (i = 0; i < f->id_table.size; i++) {
5222 struct node *node;
5223 struct node *next;
5224
5225 for (node = f->id_table.array[i]; node != NULL; node = next) {
5226 next = node->id_next;
5227 free_node(f, node);
5228 f->id_table.use--;
5229 }
5230 }
5231 assert(list_empty(&f->partial_slabs));
5232 assert(list_empty(&f->full_slabs));
5233
5234 while (fuse_modules) {
5235 fuse_put_module(fuse_modules);
5236 }
5237 free(f->id_table.array);
5238 free(f->name_table.array);
5239 pthread_mutex_destroy(&f->lock);
5240 fuse_session_destroy(f->se);
5241 free(f->fs);
5242 free(f->conf.modules);
5243 free(f);
5244 fuse_delete_context_key();
5245}
5246
5247int fuse_mount(struct fuse *f, const char *mountpoint) {
5248 return fuse_session_mount(fuse_get_session(f), mountpoint);
5249}
5250
5251
5252void fuse_unmount(struct fuse *f) {
5254}
5255
5256int fuse_version(void)
5257{
5258 return FUSE_VERSION;
5259}
5260
5261const char *fuse_pkgversion(void)
5262{
5263 return PACKAGE_VERSION;
5264}
int fuse_getgroups(int size, gid_t list[])
Definition fuse.c:4654
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
int fuse_interrupted(void)
Definition fuse.c:4663
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_start_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4906
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
Definition fuse.h:1383
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_exit(struct fuse *f)
Definition fuse.c:4639
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4433
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4914
fuse_fill_dir_flags
Definition fuse.h:58
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_SPLICE_READ
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_IS_FD
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
int fuse_version(void)
Definition fuse.c:5206
@ FUSE_BUF_SPLICE_MOVE
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_session_exited(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_reply_xattr(fuse_req_t req, size_t count)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
enum fuse_buf_flags flags
void * mem
size_t size
struct fuse_buf buf[1]
int32_t show_help
Definition fuse.h:279
uint32_t no_interrupt
void * private_data
Definition fuse.h:874
mode_t umask
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t noflush
Definition fuse_common.h:93
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
void(* init)(void *userdata, struct fuse_conn_info *conn)
void(* setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)
fuse-3.18.2/doc/html/lib_2fuse_8c_source.html0000644000175000017500000330511015156613443017753 0ustar berndbernd libfuse: lib/fuse.c Source File
libfuse
fuse.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the high-level FUSE API on top of the low-level
6 API.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#define _GNU_SOURCE
13#include "fuse.h"
14#include <pthread.h>
15
16#include "fuse_config.h"
17#include "fuse_i.h"
18#include "fuse_lowlevel.h"
19#include "fuse_opt.h"
20#include "fuse_misc.h"
21#include "fuse_kernel.h"
22#include "util.h"
23
24#include <stdint.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <stddef.h>
29#include <stdbool.h>
30#include <unistd.h>
31#include <time.h>
32#include <fcntl.h>
33#include <limits.h>
34#include <errno.h>
35#include <signal.h>
36#include <dlfcn.h>
37#include <assert.h>
38#include <poll.h>
39#include <sys/param.h>
40#include <sys/uio.h>
41#include <sys/time.h>
42#include <sys/mman.h>
43#include <sys/file.h>
44
45#define FUSE_NODE_SLAB 1
46
47#ifndef MAP_ANONYMOUS
48#undef FUSE_NODE_SLAB
49#endif
50
51#ifndef RENAME_EXCHANGE
52#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
53#endif
54
55#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
56
57#define FUSE_UNKNOWN_INO 0xffffffff
58#define OFFSET_MAX 0x7fffffffffffffffLL
59
60#define NODE_TABLE_MIN_SIZE 8192
61
62struct fuse_fs {
63 struct fuse_operations op;
64 void *user_data;
65 int debug;
66};
67
68struct fusemod_so {
69 void *handle;
70 int ctr;
71};
72
73struct lock_queue_element {
74 struct lock_queue_element *next;
75 pthread_cond_t cond;
76 fuse_ino_t nodeid1;
77 const char *name1;
78 char **path1;
79 struct node **wnode1;
80 fuse_ino_t nodeid2;
81 const char *name2;
82 char **path2;
83 struct node **wnode2;
84 int err;
85 bool done : 1;
86};
87
88struct node_table {
89 struct node **array;
90 size_t use;
91 size_t size;
92 size_t split;
93};
94
95#define list_entry(ptr, type, member) \
96 container_of(ptr, type, member)
97
98struct list_head {
99 struct list_head *next;
100 struct list_head *prev;
101};
102
103struct node_slab {
104 struct list_head list; /* must be the first member */
105 struct list_head freelist;
106 int used;
107};
108
109struct fuse {
110 struct fuse_session *se;
111 struct node_table name_table;
112 struct node_table id_table;
113 struct list_head lru_table;
114 fuse_ino_t ctr;
115 unsigned int generation;
116 unsigned int hidectr;
117 pthread_mutex_t lock;
118 struct fuse_config conf;
119 int intr_installed;
120 struct fuse_fs *fs;
121 struct lock_queue_element *lockq;
122 int pagesize;
123 struct list_head partial_slabs;
124 struct list_head full_slabs;
125 pthread_t prune_thread;
126};
127
128struct lock {
129 int type;
130 off_t start;
131 off_t end;
132 pid_t pid;
133 uint64_t owner;
134 struct lock *next;
135};
136
137struct node {
138 struct node *name_next;
139 struct node *id_next;
140 fuse_ino_t nodeid;
141 unsigned int generation;
142 int refctr;
143 struct node *parent;
144 char *name;
145 uint64_t nlookup;
146 int open_count;
147 struct timespec stat_updated;
148 struct timespec mtime;
149 off_t size;
150 struct lock *locks;
151 unsigned int is_hidden : 1;
152 unsigned int cache_valid : 1;
153 int treelock;
154 char inline_name[32];
155};
156
157#define TREELOCK_WRITE -1
158#define TREELOCK_WAIT_OFFSET INT_MIN
159
160struct node_lru {
161 struct node node;
162 struct list_head lru;
163 struct timespec forget_time;
164};
165
166struct fuse_direntry {
167 struct stat stat;
168 enum fuse_fill_dir_flags flags;
169 char *name;
170 struct fuse_direntry *next;
171};
172
173struct fuse_dh {
174 pthread_mutex_t lock;
175 struct fuse *fuse;
176 fuse_req_t req;
177 char *contents;
178 struct fuse_direntry *first;
179 struct fuse_direntry **last;
180 unsigned len;
181 unsigned size;
182 unsigned needlen;
183 int filled;
184 uint64_t fh;
185 int error;
186 fuse_ino_t nodeid;
187};
188
189struct fuse_context_i {
190 struct fuse_context ctx;
191 fuse_req_t req;
192};
193
194/* Defined by FUSE_REGISTER_MODULE() in lib/modules/subdir.c and iconv.c. */
195extern fuse_module_factory_t fuse_module_subdir_factory;
196#ifdef HAVE_ICONV
197extern fuse_module_factory_t fuse_module_iconv_factory;
198#endif
199
200static pthread_key_t fuse_context_key;
201static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
202static int fuse_context_ref;
203static struct fuse_module *fuse_modules = NULL;
204
205static int fuse_register_module(const char *name,
206 fuse_module_factory_t factory,
207 struct fusemod_so *so)
208{
209 struct fuse_module *mod;
210
211 mod = calloc(1, sizeof(struct fuse_module));
212 if (!mod) {
213 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module\n");
214 return -1;
215 }
216 mod->name = strdup(name);
217 if (!mod->name) {
218 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module name\n");
219 free(mod);
220 return -1;
221 }
222 mod->factory = factory;
223 mod->ctr = 0;
224 mod->so = so;
225 if (mod->so)
226 mod->so->ctr++;
227 mod->next = fuse_modules;
228 fuse_modules = mod;
229
230 return 0;
231}
232
233static void fuse_unregister_module(struct fuse_module *m)
234{
235 struct fuse_module **mp;
236 for (mp = &fuse_modules; *mp; mp = &(*mp)->next) {
237 if (*mp == m) {
238 *mp = (*mp)->next;
239 break;
240 }
241 }
242 free(m->name);
243 free(m);
244}
245
246static int fuse_load_so_module(const char *module)
247{
248 int ret = -1;
249 char *tmp;
250 struct fusemod_so *so;
251 fuse_module_factory_t *factory;
252
253 tmp = malloc(strlen(module) + 64);
254 if (!tmp) {
255 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
256 return -1;
257 }
258 sprintf(tmp, "libfusemod_%s.so", module);
259 so = calloc(1, sizeof(struct fusemod_so));
260 if (!so) {
261 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module so\n");
262 goto out;
263 }
264
265 so->handle = dlopen(tmp, RTLD_NOW);
266 if (so->handle == NULL) {
267 fuse_log(FUSE_LOG_ERR, "fuse: dlopen(%s) failed: %s\n",
268 tmp, dlerror());
269 goto out_free_so;
270 }
271
272 sprintf(tmp, "fuse_module_%s_factory", module);
273 factory = (fuse_module_factory_t*)dlsym(so->handle, tmp);
274 if (factory == NULL) {
275 fuse_log(FUSE_LOG_ERR, "fuse: symbol <%s> not found in module: %s\n",
276 tmp, dlerror());
277 goto out_dlclose;
278 }
279 ret = fuse_register_module(module, *factory, so);
280 if (ret)
281 goto out_dlclose;
282
283out:
284 free(tmp);
285 return ret;
286
287out_dlclose:
288 dlclose(so->handle);
289out_free_so:
290 free(so);
291 goto out;
292}
293
294static struct fuse_module *fuse_find_module(const char *module)
295{
296 struct fuse_module *m;
297 for (m = fuse_modules; m; m = m->next) {
298 if (strcmp(module, m->name) == 0) {
299 m->ctr++;
300 break;
301 }
302 }
303 return m;
304}
305
306static struct fuse_module *fuse_get_module(const char *module)
307{
308 struct fuse_module *m;
309
310 pthread_mutex_lock(&fuse_context_lock);
311 m = fuse_find_module(module);
312 if (!m) {
313 int err = fuse_load_so_module(module);
314 if (!err)
315 m = fuse_find_module(module);
316 }
317 pthread_mutex_unlock(&fuse_context_lock);
318 return m;
319}
320
321static void fuse_put_module(struct fuse_module *m)
322{
323 pthread_mutex_lock(&fuse_context_lock);
324 if (m->so)
325 assert(m->ctr > 0);
326 /* Builtin modules may already have m->ctr == 0 */
327 if (m->ctr > 0)
328 m->ctr--;
329 if (!m->ctr && m->so) {
330 struct fusemod_so *so = m->so;
331 assert(so->ctr > 0);
332 so->ctr--;
333 if (!so->ctr) {
334 struct fuse_module **mp;
335 for (mp = &fuse_modules; *mp;) {
336 if ((*mp)->so == so)
337 fuse_unregister_module(*mp);
338 else
339 mp = &(*mp)->next;
340 }
341 dlclose(so->handle);
342 free(so);
343 }
344 } else if (!m->ctr) {
345 fuse_unregister_module(m);
346 }
347 pthread_mutex_unlock(&fuse_context_lock);
348}
349
350static void init_list_head(struct list_head *list)
351{
352 list->next = list;
353 list->prev = list;
354}
355
356static int list_empty(const struct list_head *head)
357{
358 return head->next == head;
359}
360
361static void list_add(struct list_head *new, struct list_head *prev,
362 struct list_head *next)
363{
364 next->prev = new;
365 new->next = next;
366 new->prev = prev;
367 prev->next = new;
368}
369
370static inline void list_add_head(struct list_head *new, struct list_head *head)
371{
372 list_add(new, head, head->next);
373}
374
375static inline void list_add_tail(struct list_head *new, struct list_head *head)
376{
377 list_add(new, head->prev, head);
378}
379
380static inline void list_del(struct list_head *entry)
381{
382 struct list_head *prev = entry->prev;
383 struct list_head *next = entry->next;
384
385 next->prev = prev;
386 prev->next = next;
387}
388
389static inline int lru_enabled(struct fuse *f)
390{
391 return f->conf.remember > 0;
392}
393
394static struct node_lru *node_lru(struct node *node)
395{
396 return (struct node_lru *) node;
397}
398
399static size_t get_node_size(struct fuse *f)
400{
401 if (lru_enabled(f))
402 return sizeof(struct node_lru);
403 else
404 return sizeof(struct node);
405}
406
407#ifdef FUSE_NODE_SLAB
408static struct node_slab *list_to_slab(struct list_head *head)
409{
410 return (struct node_slab *) head;
411}
412
413static struct node_slab *node_to_slab(struct fuse *f, struct node *node)
414{
415 return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1));
416}
417
418static int alloc_slab(struct fuse *f)
419{
420 void *mem;
421 struct node_slab *slab;
422 char *start;
423 size_t num;
424 size_t i;
425 size_t node_size = get_node_size(f);
426
427 mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE,
428 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
429
430 if (mem == MAP_FAILED)
431 return -1;
432
433 slab = mem;
434 init_list_head(&slab->freelist);
435 slab->used = 0;
436 num = (f->pagesize - sizeof(struct node_slab)) / node_size;
437
438 start = (char *) mem + f->pagesize - num * node_size;
439 for (i = 0; i < num; i++) {
440 struct list_head *n;
441
442 n = (struct list_head *) (start + i * node_size);
443 list_add_tail(n, &slab->freelist);
444 }
445 list_add_tail(&slab->list, &f->partial_slabs);
446
447 return 0;
448}
449
450static struct node *alloc_node(struct fuse *f)
451{
452 struct node_slab *slab;
453 struct list_head *node;
454
455 if (list_empty(&f->partial_slabs)) {
456 int res = alloc_slab(f);
457 if (res != 0)
458 return NULL;
459 }
460 slab = list_to_slab(f->partial_slabs.next);
461 slab->used++;
462 node = slab->freelist.next;
463 list_del(node);
464 if (list_empty(&slab->freelist)) {
465 list_del(&slab->list);
466 list_add_tail(&slab->list, &f->full_slabs);
467 }
468 memset(node, 0, sizeof(struct node));
469
470 return (struct node *) node;
471}
472
473static void free_slab(struct fuse *f, struct node_slab *slab)
474{
475 int res;
476
477 list_del(&slab->list);
478 res = munmap(slab, f->pagesize);
479 if (res == -1)
480 fuse_log(FUSE_LOG_WARNING, "fuse warning: munmap(%p) failed\n",
481 slab);
482}
483
484static void free_node_mem(struct fuse *f, struct node *node)
485{
486 struct node_slab *slab = node_to_slab(f, node);
487 struct list_head *n = (struct list_head *) node;
488
489 slab->used--;
490 if (slab->used) {
491 if (list_empty(&slab->freelist)) {
492 list_del(&slab->list);
493 list_add_tail(&slab->list, &f->partial_slabs);
494 }
495 list_add_head(n, &slab->freelist);
496 } else {
497 free_slab(f, slab);
498 }
499}
500#else
501static struct node *alloc_node(struct fuse *f)
502{
503 return (struct node *) calloc(1, get_node_size(f));
504}
505
506static void free_node_mem(struct fuse *f, struct node *node)
507{
508 (void) f;
509 free(node);
510}
511#endif
512
513static size_t id_hash(struct fuse *f, fuse_ino_t ino)
514{
515 uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size;
516 uint64_t oldhash = hash % (f->id_table.size / 2);
517
518 if (oldhash >= f->id_table.split)
519 return oldhash;
520 else
521 return hash;
522}
523
524static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
525{
526 size_t hash = id_hash(f, nodeid);
527 struct node *node;
528
529 for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
530 if (node->nodeid == nodeid)
531 return node;
532
533 return NULL;
534}
535
536static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
537{
538 struct node *node = get_node_nocheck(f, nodeid);
539 if (!node) {
540 fuse_log(FUSE_LOG_ERR, "fuse internal error: node %llu not found\n",
541 (unsigned long long) nodeid);
542 abort();
543 }
544 return node;
545}
546
547static void curr_time(struct timespec *now);
548static double diff_timespec(const struct timespec *t1,
549 const struct timespec *t2);
550
551static void remove_node_lru(struct node *node)
552{
553 struct node_lru *lnode = node_lru(node);
554 list_del(&lnode->lru);
555 init_list_head(&lnode->lru);
556}
557
558static void set_forget_time(struct fuse *f, struct node *node)
559{
560 struct node_lru *lnode = node_lru(node);
561
562 list_del(&lnode->lru);
563 list_add_tail(&lnode->lru, &f->lru_table);
564 curr_time(&lnode->forget_time);
565}
566
567static void free_node(struct fuse *f, struct node *node)
568{
569 if (node->name != node->inline_name)
570 free(node->name);
571 free_node_mem(f, node);
572}
573
574static void node_table_reduce(struct node_table *t)
575{
576 size_t newsize = t->size / 2;
577 void *newarray;
578
579 if (newsize < NODE_TABLE_MIN_SIZE)
580 return;
581
582 newarray = realloc(t->array, sizeof(struct node *) * newsize);
583 if (newarray != NULL)
584 t->array = newarray;
585
586 t->size = newsize;
587 t->split = t->size / 2;
588}
589
590static void remerge_id(struct fuse *f)
591{
592 struct node_table *t = &f->id_table;
593 int iter;
594
595 if (t->split == 0)
596 node_table_reduce(t);
597
598 for (iter = 8; t->split > 0 && iter; iter--) {
599 struct node **upper;
600
601 t->split--;
602 upper = &t->array[t->split + t->size / 2];
603 if (*upper) {
604 struct node **nodep;
605
606 for (nodep = &t->array[t->split]; *nodep;
607 nodep = &(*nodep)->id_next);
608
609 *nodep = *upper;
610 *upper = NULL;
611 break;
612 }
613 }
614}
615
616static void unhash_id(struct fuse *f, struct node *node)
617{
618 struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
619
620 for (; *nodep != NULL; nodep = &(*nodep)->id_next)
621 if (*nodep == node) {
622 *nodep = node->id_next;
623 f->id_table.use--;
624
625 if(f->id_table.use < f->id_table.size / 4)
626 remerge_id(f);
627 return;
628 }
629}
630
631static int node_table_resize(struct node_table *t)
632{
633 size_t newsize = t->size * 2;
634 void *newarray;
635
636 newarray = realloc(t->array, sizeof(struct node *) * newsize);
637 if (newarray == NULL)
638 return -1;
639
640 t->array = newarray;
641 memset(t->array + t->size, 0, t->size * sizeof(struct node *));
642 t->size = newsize;
643 t->split = 0;
644
645 return 0;
646}
647
648static void rehash_id(struct fuse *f)
649{
650 struct node_table *t = &f->id_table;
651 struct node **nodep;
652 struct node **next;
653 size_t hash;
654
655 if (t->split == t->size / 2)
656 return;
657
658 hash = t->split;
659 t->split++;
660 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
661 struct node *node = *nodep;
662 size_t newhash = id_hash(f, node->nodeid);
663
664 if (newhash != hash) {
665 next = nodep;
666 *nodep = node->id_next;
667 node->id_next = t->array[newhash];
668 t->array[newhash] = node;
669 } else {
670 next = &node->id_next;
671 }
672 }
673 if (t->split == t->size / 2)
674 node_table_resize(t);
675}
676
677static void hash_id(struct fuse *f, struct node *node)
678{
679 size_t hash = id_hash(f, node->nodeid);
680 node->id_next = f->id_table.array[hash];
681 f->id_table.array[hash] = node;
682 f->id_table.use++;
683
684 if (f->id_table.use >= f->id_table.size / 2)
685 rehash_id(f);
686}
687
688static size_t name_hash(struct fuse *f, fuse_ino_t parent,
689 const char *name)
690{
691 uint64_t hash = parent;
692 uint64_t oldhash;
693
694 for (; *name; name++)
695 hash = hash * 31 + (unsigned char) *name;
696
697 hash %= f->name_table.size;
698 oldhash = hash % (f->name_table.size / 2);
699 if (oldhash >= f->name_table.split)
700 return oldhash;
701 else
702 return hash;
703}
704
705static void unref_node(struct fuse *f, struct node *node);
706
707static void remerge_name(struct fuse *f)
708{
709 struct node_table *t = &f->name_table;
710 int iter;
711
712 if (t->split == 0)
713 node_table_reduce(t);
714
715 for (iter = 8; t->split > 0 && iter; iter--) {
716 struct node **upper;
717
718 t->split--;
719 upper = &t->array[t->split + t->size / 2];
720 if (*upper) {
721 struct node **nodep;
722
723 for (nodep = &t->array[t->split]; *nodep;
724 nodep = &(*nodep)->name_next);
725
726 *nodep = *upper;
727 *upper = NULL;
728 break;
729 }
730 }
731}
732
733static void unhash_name(struct fuse *f, struct node *node)
734{
735 if (node->name) {
736 size_t hash = name_hash(f, node->parent->nodeid, node->name);
737 struct node **nodep = &f->name_table.array[hash];
738
739 for (; *nodep != NULL; nodep = &(*nodep)->name_next)
740 if (*nodep == node) {
741 *nodep = node->name_next;
742 node->name_next = NULL;
743 unref_node(f, node->parent);
744 if (node->name != node->inline_name)
745 free(node->name);
746 node->name = NULL;
747 node->parent = NULL;
748 f->name_table.use--;
749
750 if (f->name_table.use < f->name_table.size / 4)
751 remerge_name(f);
752 return;
753 }
754 fuse_log(FUSE_LOG_ERR,
755 "fuse internal error: unable to unhash node: %llu\n",
756 (unsigned long long) node->nodeid);
757 abort();
758 }
759}
760
761static void rehash_name(struct fuse *f)
762{
763 struct node_table *t = &f->name_table;
764 struct node **nodep;
765 struct node **next;
766 size_t hash;
767
768 if (t->split == t->size / 2)
769 return;
770
771 hash = t->split;
772 t->split++;
773 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
774 struct node *node = *nodep;
775 size_t newhash = name_hash(f, node->parent->nodeid, node->name);
776
777 if (newhash != hash) {
778 next = nodep;
779 *nodep = node->name_next;
780 node->name_next = t->array[newhash];
781 t->array[newhash] = node;
782 } else {
783 next = &node->name_next;
784 }
785 }
786 if (t->split == t->size / 2)
787 node_table_resize(t);
788}
789
790static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
791 const char *name)
792{
793 size_t hash = name_hash(f, parentid, name);
794 struct node *parent = get_node(f, parentid);
795 if (strlen(name) < sizeof(node->inline_name)) {
796 strcpy(node->inline_name, name);
797 node->name = node->inline_name;
798 } else {
799 node->name = strdup(name);
800 if (node->name == NULL)
801 return -1;
802 }
803
804 parent->refctr ++;
805 node->parent = parent;
806 node->name_next = f->name_table.array[hash];
807 f->name_table.array[hash] = node;
808 f->name_table.use++;
809
810 if (f->name_table.use >= f->name_table.size / 2)
811 rehash_name(f);
812
813 return 0;
814}
815
816static void delete_node(struct fuse *f, struct node *node)
817{
818 if (f->conf.debug)
819 fuse_log(FUSE_LOG_DEBUG, "DELETE: %llu\n",
820 (unsigned long long) node->nodeid);
821
822 assert(node->treelock == 0);
823 unhash_name(f, node);
824 if (lru_enabled(f))
825 remove_node_lru(node);
826 unhash_id(f, node);
827 free_node(f, node);
828}
829
830static void unref_node(struct fuse *f, struct node *node)
831{
832 assert(node->refctr > 0);
833 node->refctr --;
834 if (!node->refctr)
835 delete_node(f, node);
836}
837
838static fuse_ino_t next_id(struct fuse *f)
839{
840 do {
841 f->ctr = (f->ctr + 1) & 0xffffffff;
842 if (!f->ctr)
843 f->generation ++;
844 } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
845 get_node_nocheck(f, f->ctr) != NULL);
846 return f->ctr;
847}
848
849static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
850 const char *name)
851{
852 size_t hash = name_hash(f, parent, name);
853 struct node *node;
854
855 for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
856 if (node->parent->nodeid == parent &&
857 strcmp(node->name, name) == 0)
858 return node;
859
860 return NULL;
861}
862
863static void inc_nlookup(struct node *node)
864{
865 if (!node->nlookup)
866 node->refctr++;
867 node->nlookup++;
868}
869
870static struct node *find_node(struct fuse *f, fuse_ino_t parent,
871 const char *name)
872{
873 struct node *node;
874
875 pthread_mutex_lock(&f->lock);
876 if (!name)
877 node = get_node(f, parent);
878 else
879 node = lookup_node(f, parent, name);
880 if (node == NULL) {
881 node = alloc_node(f);
882 if (node == NULL)
883 goto out_err;
884
885 node->nodeid = next_id(f);
886 node->generation = f->generation;
887 if (f->conf.remember)
888 inc_nlookup(node);
889
890 if (hash_name(f, node, parent, name) == -1) {
891 free_node(f, node);
892 node = NULL;
893 goto out_err;
894 }
895 hash_id(f, node);
896 if (lru_enabled(f)) {
897 struct node_lru *lnode = node_lru(node);
898 init_list_head(&lnode->lru);
899 }
900 } else if (lru_enabled(f) && node->nlookup == 1) {
901 remove_node_lru(node);
902 }
903 inc_nlookup(node);
904out_err:
905 pthread_mutex_unlock(&f->lock);
906 return node;
907}
908
909static int lookup_path_in_cache(struct fuse *f,
910 const char *path, fuse_ino_t *inop)
911{
912 char *tmp = strdup(path);
913 if (!tmp)
914 return -ENOMEM;
915
916 pthread_mutex_lock(&f->lock);
917 fuse_ino_t ino = FUSE_ROOT_ID;
918
919 int err = 0;
920 char *save_ptr;
921 char *path_element = strtok_r(tmp, "/", &save_ptr);
922 while (path_element != NULL) {
923 struct node *node = lookup_node(f, ino, path_element);
924 if (node == NULL) {
925 err = -ENOENT;
926 break;
927 }
928 ino = node->nodeid;
929 path_element = strtok_r(NULL, "/", &save_ptr);
930 }
931 pthread_mutex_unlock(&f->lock);
932 free(tmp);
933
934 if (!err)
935 *inop = ino;
936 return err;
937}
938
939static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name)
940{
941 size_t len = strlen(name);
942
943 if (s - len <= *buf) {
944 unsigned pathlen = *bufsize - (s - *buf);
945 unsigned newbufsize = *bufsize;
946 char *newbuf;
947
948 while (newbufsize < pathlen + len + 1) {
949 if (newbufsize >= 0x80000000)
950 newbufsize = 0xffffffff;
951 else
952 newbufsize *= 2;
953 }
954
955 newbuf = realloc(*buf, newbufsize);
956 if (newbuf == NULL)
957 return NULL;
958
959 *buf = newbuf;
960 s = newbuf + newbufsize - pathlen;
961 memmove(s, newbuf + *bufsize - pathlen, pathlen);
962 *bufsize = newbufsize;
963 }
964 s -= len;
965 memcpy(s, name, len);
966 s--;
967 *s = '/';
968
969 return s;
970}
971
972static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
973 struct node *end)
974{
975 struct node *node;
976
977 if (wnode) {
978 assert(wnode->treelock == TREELOCK_WRITE);
979 wnode->treelock = 0;
980 }
981
982 for (node = get_node(f, nodeid);
983 node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) {
984 assert(node->treelock != 0);
985 assert(node->treelock != TREELOCK_WAIT_OFFSET);
986 assert(node->treelock != TREELOCK_WRITE);
987 node->treelock--;
988 if (node->treelock == TREELOCK_WAIT_OFFSET)
989 node->treelock = 0;
990 }
991}
992
993static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
994 char **path, struct node **wnodep, bool need_lock)
995{
996 unsigned bufsize = 256;
997 char *buf;
998 char *s;
999 struct node *node;
1000 struct node *wnode = NULL;
1001 int err;
1002
1003 *path = NULL;
1004
1005 err = -ENOMEM;
1006 buf = malloc(bufsize);
1007 if (buf == NULL)
1008 goto out_err;
1009
1010 s = buf + bufsize - 1;
1011 *s = '\0';
1012
1013 if (name != NULL) {
1014 s = add_name(&buf, &bufsize, s, name);
1015 err = -ENOMEM;
1016 if (s == NULL)
1017 goto out_free;
1018 }
1019
1020 if (wnodep) {
1021 assert(need_lock);
1022 wnode = lookup_node(f, nodeid, name);
1023 if (wnode) {
1024 if (wnode->treelock != 0) {
1025 if (wnode->treelock > 0)
1026 wnode->treelock += TREELOCK_WAIT_OFFSET;
1027 err = -EAGAIN;
1028 goto out_free;
1029 }
1030 wnode->treelock = TREELOCK_WRITE;
1031 }
1032 }
1033
1034 for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
1035 node = node->parent) {
1036 err = -ESTALE;
1037 if (node->name == NULL || node->parent == NULL)
1038 goto out_unlock;
1039
1040 err = -ENOMEM;
1041 s = add_name(&buf, &bufsize, s, node->name);
1042 if (s == NULL)
1043 goto out_unlock;
1044
1045 if (need_lock) {
1046 err = -EAGAIN;
1047 if (node->treelock < 0)
1048 goto out_unlock;
1049
1050 node->treelock++;
1051 }
1052 }
1053
1054 if (s[0])
1055 memmove(buf, s, bufsize - (s - buf));
1056 else
1057 strcpy(buf, "/");
1058
1059 *path = buf;
1060 if (wnodep)
1061 *wnodep = wnode;
1062
1063 return 0;
1064
1065 out_unlock:
1066 if (need_lock)
1067 unlock_path(f, nodeid, wnode, node);
1068 out_free:
1069 free(buf);
1070
1071 out_err:
1072 return err;
1073}
1074
1075static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1076 fuse_ino_t nodeid2, const char *name2,
1077 char **path1, char **path2,
1078 struct node **wnode1, struct node **wnode2)
1079{
1080 int err;
1081
1082 /* FIXME: locking two paths needs deadlock checking */
1083 err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
1084 if (!err) {
1085 err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
1086 if (err) {
1087 struct node *wn1 = wnode1 ? *wnode1 : NULL;
1088
1089 unlock_path(f, nodeid1, wn1, NULL);
1090 free(*path1);
1091 }
1092 }
1093 return err;
1094}
1095
1096static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
1097{
1098 int err;
1099
1100 if (!qe->path1) {
1101 /* Just waiting for it to be unlocked */
1102 if (get_node(f, qe->nodeid1)->treelock == 0)
1103 pthread_cond_signal(&qe->cond);
1104
1105 return;
1106 }
1107
1108 if (qe->done)
1109 return; // Don't try to double-lock the element
1110
1111 if (!qe->path2) {
1112 err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
1113 qe->wnode1, true);
1114 } else {
1115 err = try_get_path2(f, qe->nodeid1, qe->name1, qe->nodeid2,
1116 qe->name2, qe->path1, qe->path2, qe->wnode1,
1117 qe->wnode2);
1118 }
1119
1120 if (err == -EAGAIN)
1121 return; /* keep trying */
1122
1123 qe->err = err;
1124 qe->done = true;
1125 pthread_cond_signal(&qe->cond);
1126}
1127
1128static void wake_up_queued(struct fuse *f)
1129{
1130 struct lock_queue_element *qe;
1131
1132 for (qe = f->lockq; qe != NULL; qe = qe->next)
1133 queue_element_wakeup(f, qe);
1134}
1135
1136static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid,
1137 const char *name, bool wr)
1138{
1139 if (f->conf.debug) {
1140 struct node *wnode = NULL;
1141
1142 if (wr)
1143 wnode = lookup_node(f, nodeid, name);
1144
1145 if (wnode) {
1146 fuse_log(FUSE_LOG_DEBUG, "%s %llu (w)\n",
1147 msg, (unsigned long long) wnode->nodeid);
1148 } else {
1149 fuse_log(FUSE_LOG_DEBUG, "%s %llu\n",
1150 msg, (unsigned long long) nodeid);
1151 }
1152 }
1153}
1154
1155static void queue_path(struct fuse *f, struct lock_queue_element *qe)
1156{
1157 struct lock_queue_element **qp;
1158
1159 qe->done = false;
1160 pthread_cond_init(&qe->cond, NULL);
1161 qe->next = NULL;
1162 for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
1163 *qp = qe;
1164}
1165
1166static void dequeue_path(struct fuse *f, struct lock_queue_element *qe)
1167{
1168 struct lock_queue_element **qp;
1169
1170 pthread_cond_destroy(&qe->cond);
1171 for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next);
1172 *qp = qe->next;
1173}
1174
1175static int wait_path(struct fuse *f, struct lock_queue_element *qe)
1176{
1177 queue_path(f, qe);
1178
1179 do {
1180 pthread_cond_wait(&qe->cond, &f->lock);
1181 } while (!qe->done);
1182
1183 dequeue_path(f, qe);
1184
1185 return qe->err;
1186}
1187
1188static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name,
1189 char **path, struct node **wnode)
1190{
1191 int err;
1192
1193 pthread_mutex_lock(&f->lock);
1194 err = try_get_path(f, nodeid, name, path, wnode, true);
1195 if (err == -EAGAIN) {
1196 struct lock_queue_element qe = {
1197 .nodeid1 = nodeid,
1198 .name1 = name,
1199 .path1 = path,
1200 .wnode1 = wnode,
1201 };
1202 debug_path(f, "QUEUE PATH", nodeid, name, !!wnode);
1203 err = wait_path(f, &qe);
1204 debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode);
1205 }
1206 pthread_mutex_unlock(&f->lock);
1207
1208 return err;
1209}
1210
1211static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
1212{
1213 return get_path_common(f, nodeid, NULL, path, NULL);
1214}
1215
1216static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
1217{
1218 int err = 0;
1219
1220 if (f->conf.nullpath_ok) {
1221 *path = NULL;
1222 } else {
1223 err = get_path_common(f, nodeid, NULL, path, NULL);
1224 if (err == -ESTALE)
1225 err = 0;
1226 }
1227
1228 return err;
1229}
1230
1231static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
1232 char **path)
1233{
1234 return get_path_common(f, nodeid, name, path, NULL);
1235}
1236
1237static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
1238 char **path, struct node **wnode)
1239{
1240 return get_path_common(f, nodeid, name, path, wnode);
1241}
1242
1243#if defined(__FreeBSD__)
1244#define CHECK_DIR_LOOP
1245#endif
1246
1247#if defined(CHECK_DIR_LOOP)
1248static int check_dir_loop(struct fuse *f,
1249 fuse_ino_t nodeid1, const char *name1,
1250 fuse_ino_t nodeid2, const char *name2)
1251{
1252 struct node *node, *node1, *node2;
1253 fuse_ino_t id1, id2;
1254
1255 node1 = lookup_node(f, nodeid1, name1);
1256 id1 = node1 ? node1->nodeid : nodeid1;
1257
1258 node2 = lookup_node(f, nodeid2, name2);
1259 id2 = node2 ? node2->nodeid : nodeid2;
1260
1261 for (node = get_node(f, id2); node->nodeid != FUSE_ROOT_ID;
1262 node = node->parent) {
1263 if (node->name == NULL || node->parent == NULL)
1264 break;
1265
1266 if (node->nodeid != id2 && node->nodeid == id1)
1267 return -EINVAL;
1268 }
1269
1270 if (node2)
1271 {
1272 for (node = get_node(f, id1); node->nodeid != FUSE_ROOT_ID;
1273 node = node->parent) {
1274 if (node->name == NULL || node->parent == NULL)
1275 break;
1276
1277 if (node->nodeid != id1 && node->nodeid == id2)
1278 return -ENOTEMPTY;
1279 }
1280 }
1281
1282 return 0;
1283}
1284#endif
1285
1286static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1287 fuse_ino_t nodeid2, const char *name2,
1288 char **path1, char **path2,
1289 struct node **wnode1, struct node **wnode2)
1290{
1291 int err;
1292
1293 pthread_mutex_lock(&f->lock);
1294
1295#if defined(CHECK_DIR_LOOP)
1296 if (name1)
1297 {
1298 // called during rename; perform dir loop check
1299 err = check_dir_loop(f, nodeid1, name1, nodeid2, name2);
1300 if (err)
1301 goto out_unlock;
1302 }
1303#endif
1304
1305 err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
1306 path1, path2, wnode1, wnode2);
1307 if (err == -EAGAIN) {
1308 struct lock_queue_element qe = {
1309 .nodeid1 = nodeid1,
1310 .name1 = name1,
1311 .path1 = path1,
1312 .wnode1 = wnode1,
1313 .nodeid2 = nodeid2,
1314 .name2 = name2,
1315 .path2 = path2,
1316 .wnode2 = wnode2,
1317 };
1318
1319 debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1);
1320 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1321 err = wait_path(f, &qe);
1322 debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
1323 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1324 }
1325
1326#if defined(CHECK_DIR_LOOP)
1327out_unlock:
1328#endif
1329 pthread_mutex_unlock(&f->lock);
1330
1331 return err;
1332}
1333
1334static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid,
1335 struct node *wnode, char *path)
1336{
1337 pthread_mutex_lock(&f->lock);
1338 unlock_path(f, nodeid, wnode, NULL);
1339 if (f->lockq)
1340 wake_up_queued(f);
1341 pthread_mutex_unlock(&f->lock);
1342 free(path);
1343}
1344
1345static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path)
1346{
1347 if (path)
1348 free_path_wrlock(f, nodeid, NULL, path);
1349}
1350
1351static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2,
1352 struct node *wnode1, struct node *wnode2,
1353 char *path1, char *path2)
1354{
1355 pthread_mutex_lock(&f->lock);
1356 unlock_path(f, nodeid1, wnode1, NULL);
1357 unlock_path(f, nodeid2, wnode2, NULL);
1358 wake_up_queued(f);
1359 pthread_mutex_unlock(&f->lock);
1360 free(path1);
1361 free(path2);
1362}
1363
1364static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
1365{
1366 struct node *node;
1367 if (nodeid == FUSE_ROOT_ID)
1368 return;
1369 pthread_mutex_lock(&f->lock);
1370 node = get_node(f, nodeid);
1371
1372 /*
1373 * Node may still be locked due to interrupt idiocy in open,
1374 * create and opendir
1375 */
1376 while (node->nlookup == nlookup && node->treelock) {
1377 struct lock_queue_element qe = {
1378 .nodeid1 = nodeid,
1379 };
1380
1381 debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false);
1382 queue_path(f, &qe);
1383
1384 do {
1385 pthread_cond_wait(&qe.cond, &f->lock);
1386 } while (node->nlookup == nlookup && node->treelock);
1387
1388 dequeue_path(f, &qe);
1389 debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false);
1390 }
1391
1392 assert(node->nlookup >= nlookup);
1393 node->nlookup -= nlookup;
1394 if (!node->nlookup) {
1395 unref_node(f, node);
1396 } else if (lru_enabled(f) && node->nlookup == 1) {
1397 set_forget_time(f, node);
1398 }
1399 pthread_mutex_unlock(&f->lock);
1400}
1401
1402static void unlink_node(struct fuse *f, struct node *node)
1403{
1404 if (f->conf.remember) {
1405 assert(node->nlookup > 1);
1406 node->nlookup--;
1407 }
1408 unhash_name(f, node);
1409}
1410
1411static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
1412{
1413 struct node *node;
1414
1415 pthread_mutex_lock(&f->lock);
1416 node = lookup_node(f, dir, name);
1417 if (node != NULL)
1418 unlink_node(f, node);
1419 pthread_mutex_unlock(&f->lock);
1420}
1421
1422static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1423 fuse_ino_t newdir, const char *newname, int hide)
1424{
1425 struct node *node;
1426 struct node *newnode;
1427 int err = 0;
1428
1429 pthread_mutex_lock(&f->lock);
1430 node = lookup_node(f, olddir, oldname);
1431 newnode = lookup_node(f, newdir, newname);
1432 if (node == NULL)
1433 goto out;
1434
1435 if (newnode != NULL) {
1436 if (hide) {
1437 fuse_log(FUSE_LOG_ERR, "fuse: hidden file got created during hiding\n");
1438 err = -EBUSY;
1439 goto out;
1440 }
1441 unlink_node(f, newnode);
1442 }
1443
1444 unhash_name(f, node);
1445 if (hash_name(f, node, newdir, newname) == -1) {
1446 err = -ENOMEM;
1447 goto out;
1448 }
1449
1450 if (hide)
1451 node->is_hidden = 1;
1452
1453out:
1454 pthread_mutex_unlock(&f->lock);
1455 return err;
1456}
1457
1458static int exchange_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1459 fuse_ino_t newdir, const char *newname)
1460{
1461 struct node *oldnode;
1462 struct node *newnode;
1463 int err;
1464
1465 pthread_mutex_lock(&f->lock);
1466 oldnode = lookup_node(f, olddir, oldname);
1467 newnode = lookup_node(f, newdir, newname);
1468
1469 if (oldnode)
1470 unhash_name(f, oldnode);
1471 if (newnode)
1472 unhash_name(f, newnode);
1473
1474 err = -ENOMEM;
1475 if (oldnode) {
1476 if (hash_name(f, oldnode, newdir, newname) == -1)
1477 goto out;
1478 }
1479 if (newnode) {
1480 if (hash_name(f, newnode, olddir, oldname) == -1)
1481 goto out;
1482 }
1483 err = 0;
1484out:
1485 pthread_mutex_unlock(&f->lock);
1486 return err;
1487}
1488
1489static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
1490{
1491 if (!f->conf.use_ino)
1492 stbuf->st_ino = nodeid;
1493 if (f->conf.set_mode) {
1494 if (f->conf.dmask && S_ISDIR(stbuf->st_mode))
1495 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1496 (0777 & ~f->conf.dmask);
1497 else if (f->conf.fmask)
1498 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1499 (0777 & ~f->conf.fmask);
1500 else
1501 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1502 (0777 & ~f->conf.umask);
1503 }
1504 if (f->conf.set_uid)
1505 stbuf->st_uid = f->conf.uid;
1506 if (f->conf.set_gid)
1507 stbuf->st_gid = f->conf.gid;
1508}
1509
1510#ifdef HAVE_STATX
1511static void set_statx(struct fuse *f, fuse_ino_t nodeid, struct statx *stxbuf)
1512{
1513 if (!f->conf.use_ino)
1514 stxbuf->stx_ino = nodeid;
1515 if (f->conf.set_mode) {
1516 if (f->conf.dmask && S_ISDIR(stxbuf->stx_mode))
1517 stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
1518 (0777 & ~f->conf.dmask);
1519 else if (f->conf.fmask)
1520 stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
1521 (0777 & ~f->conf.fmask);
1522 else
1523 stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
1524 (0777 & ~f->conf.umask);
1525 }
1526 if (f->conf.set_uid)
1527 stxbuf->stx_uid = f->conf.uid;
1528 if (f->conf.set_gid)
1529 stxbuf->stx_gid = f->conf.gid;
1530}
1531#endif
1532
1533static struct fuse *req_fuse(fuse_req_t req)
1534{
1535 return (struct fuse *) fuse_req_userdata(req);
1536}
1537
1538static void fuse_intr_sighandler(int sig)
1539{
1540 (void) sig;
1541 /* Nothing to do */
1542}
1543
1544struct fuse_intr_data {
1545 pthread_t id;
1546 pthread_cond_t cond;
1547 int finished;
1548};
1549
1550static void fuse_interrupt(fuse_req_t req, void *d_)
1551{
1552 struct fuse_intr_data *d = d_;
1553 struct fuse *f = req_fuse(req);
1554
1555 if (d->id == pthread_self())
1556 return;
1557
1558 pthread_mutex_lock(&f->lock);
1559 while (!d->finished) {
1560 struct timeval now;
1561 struct timespec timeout;
1562
1563 pthread_kill(d->id, f->conf.intr_signal);
1564 gettimeofday(&now, NULL);
1565 timeout.tv_sec = now.tv_sec + 1;
1566 timeout.tv_nsec = now.tv_usec * 1000;
1567 pthread_cond_timedwait(&d->cond, &f->lock, &timeout);
1568 }
1569 pthread_mutex_unlock(&f->lock);
1570}
1571
1572static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req,
1573 struct fuse_intr_data *d)
1574{
1575 pthread_mutex_lock(&f->lock);
1576 d->finished = 1;
1577 pthread_cond_broadcast(&d->cond);
1578 pthread_mutex_unlock(&f->lock);
1579 fuse_req_interrupt_func(req, NULL, NULL);
1580 pthread_cond_destroy(&d->cond);
1581}
1582
1583static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d)
1584{
1585 d->id = pthread_self();
1586 pthread_cond_init(&d->cond, NULL);
1587 d->finished = 0;
1588 fuse_req_interrupt_func(req, fuse_interrupt, d);
1589}
1590
1591static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req,
1592 struct fuse_intr_data *d)
1593{
1594 if (f->conf.intr)
1595 fuse_do_finish_interrupt(f, req, d);
1596}
1597
1598static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
1599 struct fuse_intr_data *d)
1600{
1601 if (f->conf.intr)
1602 fuse_do_prepare_interrupt(req, d);
1603}
1604
1605static const char* file_info_string(struct fuse_file_info *fi,
1606 char* buf, size_t len)
1607{
1608 if(fi == NULL)
1609 return "NULL";
1610 snprintf(buf, len, "%llu", (unsigned long long) fi->fh);
1611 return buf;
1612}
1613
1614int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
1615 struct fuse_file_info *fi)
1616{
1617 fuse_get_context()->private_data = fs->user_data;
1618 if (!fs->op.getattr)
1619 return -ENOSYS;
1620
1621 if (fs->debug) {
1622 char buf[10];
1623
1624 fuse_log(FUSE_LOG_DEBUG, "getattr[%s] %s\n",
1625 file_info_string(fi, buf, sizeof(buf)),
1626 path);
1627 }
1628 return fs->op.getattr(path, buf, fi);
1629}
1630
1631int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
1632 const char *newpath, unsigned int flags)
1633{
1634 fuse_get_context()->private_data = fs->user_data;
1635 if (!fs->op.rename)
1636 return -ENOSYS;
1637 if (fs->debug)
1638 fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath,
1639 flags);
1640
1641 return fs->op.rename(oldpath, newpath, flags);
1642}
1643
1644int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
1645{
1646 fuse_get_context()->private_data = fs->user_data;
1647 if (!fs->op.unlink)
1648 return -ENOSYS;
1649 if (fs->debug)
1650 fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path);
1651
1652 return fs->op.unlink(path);
1653}
1654
1655int fuse_fs_rmdir(struct fuse_fs *fs, const char *path)
1656{
1657 fuse_get_context()->private_data = fs->user_data;
1658 if (!fs->op.rmdir)
1659 return -ENOSYS;
1660 if (fs->debug)
1661 fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path);
1662
1663 return fs->op.rmdir(path);
1664}
1665
1666int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path)
1667{
1668 fuse_get_context()->private_data = fs->user_data;
1669 if (!fs->op.symlink)
1670 return -ENOSYS;
1671 if (fs->debug)
1672 fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path);
1673
1674 return fs->op.symlink(linkname, path);
1675}
1676
1677int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath)
1678{
1679 fuse_get_context()->private_data = fs->user_data;
1680 if (!fs->op.link)
1681 return -ENOSYS;
1682 if (fs->debug)
1683 fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath);
1684
1685 return fs->op.link(oldpath, newpath);
1686}
1687
1688int fuse_fs_release(struct fuse_fs *fs, const char *path,
1689 struct fuse_file_info *fi)
1690{
1691 fuse_get_context()->private_data = fs->user_data;
1692 if (!fs->op.release)
1693 return 0;
1694
1695 if (fs->debug)
1696 fuse_log(FUSE_LOG_DEBUG, "release%s[%llu] flags: 0x%x\n",
1697 fi->flush ? "+flush" : "",
1698 (unsigned long long) fi->fh, fi->flags);
1699
1700 return fs->op.release(path, fi);
1701}
1702
1703int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
1704 struct fuse_file_info *fi)
1705{
1706 int err;
1707
1708 fuse_get_context()->private_data = fs->user_data;
1709 if (!fs->op.opendir)
1710 return 0;
1711
1712 if (fs->debug)
1713 fuse_log(FUSE_LOG_DEBUG, "opendir flags: 0x%x %s\n", fi->flags,
1714 path);
1715
1716 err = fs->op.opendir(path, fi);
1717
1718 if (fs->debug && !err)
1719 fuse_log(FUSE_LOG_DEBUG, " opendir[%llu] flags: 0x%x %s\n",
1720 (unsigned long long) fi->fh, fi->flags, path);
1721
1722 return err;
1723}
1724
1725int fuse_fs_open(struct fuse_fs *fs, const char *path,
1726 struct fuse_file_info *fi)
1727{
1728 int err;
1729
1730 fuse_get_context()->private_data = fs->user_data;
1731 if (!fs->op.open)
1732 return 0;
1733
1734 if (fs->debug)
1735 fuse_log(FUSE_LOG_DEBUG, "open flags: 0x%x %s\n", fi->flags,
1736 path);
1737
1738 err = fs->op.open(path, fi);
1739
1740 if (fs->debug && !err)
1741 fuse_log(FUSE_LOG_DEBUG, " open[%llu] flags: 0x%x %s\n",
1742 (unsigned long long) fi->fh, fi->flags, path);
1743
1744 return err;
1745}
1746
1747static void fuse_free_buf(struct fuse_bufvec *buf)
1748{
1749 if (buf != NULL) {
1750 size_t i;
1751
1752 for (i = 0; i < buf->count; i++)
1753 if (!(buf->buf[i].flags & FUSE_BUF_IS_FD))
1754 free(buf->buf[i].mem);
1755 free(buf);
1756 }
1757}
1758
1759int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
1760 struct fuse_bufvec **bufp, size_t size, off_t off,
1761 struct fuse_file_info *fi)
1762{
1763 int res;
1764
1765 fuse_get_context()->private_data = fs->user_data;
1766 if (!fs->op.read && !fs->op.read_buf)
1767 return -ENOSYS;
1768
1769 if (fs->debug)
1770 fuse_log(FUSE_LOG_DEBUG,
1771 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1772 (unsigned long long) fi->fh,
1773 size, (unsigned long long) off, fi->flags);
1774
1775 if (fs->op.read_buf) {
1776 res = fs->op.read_buf(path, bufp, size, off, fi);
1777 } else {
1778 struct fuse_bufvec *buf;
1779 void *mem;
1780
1781 buf = malloc(sizeof(struct fuse_bufvec));
1782 if (buf == NULL)
1783 return -ENOMEM;
1784
1785 mem = malloc(size);
1786 if (mem == NULL) {
1787 free(buf);
1788 return -ENOMEM;
1789 }
1790 *buf = FUSE_BUFVEC_INIT(size);
1791 buf->buf[0].mem = mem;
1792 *bufp = buf;
1793
1794 res = fs->op.read(path, mem, size, off, fi);
1795 if (res >= 0)
1796 buf->buf[0].size = res;
1797 }
1798
1799 if (fs->debug && res >= 0)
1800 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %zu bytes from %llu\n",
1801 (unsigned long long) fi->fh,
1802 fuse_buf_size(*bufp),
1803 (unsigned long long) off);
1804 if (res >= 0 && fuse_buf_size(*bufp) > size)
1805 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1806
1807 if (res < 0)
1808 return res;
1809
1810 return 0;
1811}
1812
1813int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
1814 off_t off, struct fuse_file_info *fi)
1815{
1816 int res;
1817
1818 fuse_get_context()->private_data = fs->user_data;
1819 if (!fs->op.read && !fs->op.read_buf)
1820 return -ENOSYS;
1821
1822 if (fs->debug)
1823 fuse_log(FUSE_LOG_DEBUG,
1824 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1825 (unsigned long long) fi->fh,
1826 size, (unsigned long long) off, fi->flags);
1827
1828 if (fs->op.read_buf) {
1829 struct fuse_bufvec *buf = NULL;
1830
1831 res = fs->op.read_buf(path, &buf, size, off, fi);
1832 if (res == 0) {
1833 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
1834
1835 dst.buf[0].mem = mem;
1836 res = fuse_buf_copy(&dst, buf, 0);
1837 }
1838 fuse_free_buf(buf);
1839 } else {
1840 res = fs->op.read(path, mem, size, off, fi);
1841 }
1842
1843 if (fs->debug && res >= 0)
1844 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %u bytes from %llu\n",
1845 (unsigned long long) fi->fh,
1846 res,
1847 (unsigned long long) off);
1848 if (res >= 0 && res > (int) size)
1849 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1850
1851 return res;
1852}
1853
1854int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
1855 struct fuse_bufvec *buf, off_t off,
1856 struct fuse_file_info *fi)
1857{
1858 int res;
1859 size_t size;
1860
1861 fuse_get_context()->private_data = fs->user_data;
1862 if (!fs->op.write_buf && !fs->op.write)
1863 return -ENOSYS;
1864
1865 size = fuse_buf_size(buf);
1866 assert(buf->idx == 0 && buf->off == 0);
1867 if (fs->debug)
1868 fuse_log(FUSE_LOG_DEBUG,
1869 "write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
1870 fi->writepage ? "page" : "",
1871 (unsigned long long) fi->fh,
1872 size,
1873 (unsigned long long) off,
1874 fi->flags);
1875
1876 if (fs->op.write_buf) {
1877 res = fs->op.write_buf(path, buf, off, fi);
1878 } else {
1879 void *mem = NULL;
1880 struct fuse_buf *flatbuf;
1881 struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size);
1882
1883 if (buf->count == 1 &&
1884 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
1885 flatbuf = &buf->buf[0];
1886 } else {
1887 res = -ENOMEM;
1888 mem = malloc(size);
1889 if (mem == NULL)
1890 goto out;
1891
1892 tmp.buf[0].mem = mem;
1893 res = fuse_buf_copy(&tmp, buf, 0);
1894 if (res <= 0)
1895 goto out_free;
1896
1897 tmp.buf[0].size = res;
1898 flatbuf = &tmp.buf[0];
1899 }
1900
1901 res = fs->op.write(path, flatbuf->mem, flatbuf->size,
1902 off, fi);
1903out_free:
1904 free(mem);
1905 }
1906out:
1907 if (fs->debug && res >= 0)
1908 fuse_log(FUSE_LOG_DEBUG, " write%s[%llu] %u bytes to %llu\n",
1909 fi->writepage ? "page" : "",
1910 (unsigned long long) fi->fh, res,
1911 (unsigned long long) off);
1912 if (res > (int) size)
1913 fuse_log(FUSE_LOG_ERR, "fuse: wrote too many bytes\n");
1914
1915 return res;
1916}
1917
1918int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem,
1919 size_t size, off_t off, struct fuse_file_info *fi)
1920{
1921 struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size);
1922
1923 bufv.buf[0].mem = (void *) mem;
1924
1925 return fuse_fs_write_buf(fs, path, &bufv, off, fi);
1926}
1927
1928int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
1929 struct fuse_file_info *fi)
1930{
1931 fuse_get_context()->private_data = fs->user_data;
1932 if (!fs->op.fsync)
1933 return -ENOSYS;
1934
1935 if (fs->debug)
1936 fuse_log(FUSE_LOG_DEBUG, "fsync[%llu] datasync: %i\n",
1937 (unsigned long long) fi->fh, datasync);
1938
1939 return fs->op.fsync(path, datasync, fi);
1940}
1941
1942int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
1943 struct fuse_file_info *fi)
1944{
1945 fuse_get_context()->private_data = fs->user_data;
1946 if (!fs->op.fsyncdir)
1947 return -ENOSYS;
1948
1949 if (fs->debug)
1950 fuse_log(FUSE_LOG_DEBUG, "fsyncdir[%llu] datasync: %i\n",
1951 (unsigned long long) fi->fh, datasync);
1952
1953 return fs->op.fsyncdir(path, datasync, fi);
1954}
1955
1956int fuse_fs_flush(struct fuse_fs *fs, const char *path,
1957 struct fuse_file_info *fi)
1958{
1959 fuse_get_context()->private_data = fs->user_data;
1960 if (!fs->op.flush)
1961 return -ENOSYS;
1962 if (fs->debug)
1963 fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n",
1964 (unsigned long long) fi->fh);
1965
1966 return fs->op.flush(path, fi);
1967}
1968
1969int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf)
1970{
1971 fuse_get_context()->private_data = fs->user_data;
1972 if (fs->op.statfs) {
1973 if (fs->debug)
1974 fuse_log(FUSE_LOG_DEBUG, "statfs %s\n", path);
1975
1976 return fs->op.statfs(path, buf);
1977 } else {
1978 buf->f_namemax = 255;
1979 buf->f_bsize = 512;
1980 return 0;
1981 }
1982}
1983
1984int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
1985 struct fuse_file_info *fi)
1986{
1987 fuse_get_context()->private_data = fs->user_data;
1988 if (!fs->op.releasedir)
1989 return 0;
1990
1991 if (fs->debug)
1992 fuse_log(FUSE_LOG_DEBUG, "releasedir[%llu] flags: 0x%x\n",
1993 (unsigned long long) fi->fh, fi->flags);
1994
1995 return fs->op.releasedir(path, fi);
1996}
1997
1998int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
1999 fuse_fill_dir_t filler, off_t off,
2000 struct fuse_file_info *fi,
2001 enum fuse_readdir_flags flags)
2002{
2003 fuse_get_context()->private_data = fs->user_data;
2004 if (!fs->op.readdir)
2005 return -ENOSYS;
2006 if (fs->debug) {
2007 fuse_log(FUSE_LOG_DEBUG, "readdir%s[%llu] from %llu\n",
2008 (flags & FUSE_READDIR_PLUS) ? "plus" : "",
2009 (unsigned long long) fi->fh,
2010 (unsigned long long) off);
2011 }
2012
2013 return fs->op.readdir(path, buf, filler, off, fi, flags);
2014}
2015
2016int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
2017 struct fuse_file_info *fi)
2018{
2019 int err;
2020
2021 fuse_get_context()->private_data = fs->user_data;
2022 if (!fs->op.create)
2023 return -ENOSYS;
2024
2025 if (fs->debug)
2026 fuse_log(FUSE_LOG_DEBUG,
2027 "create flags: 0x%x %s 0%o umask=0%03o\n",
2028 fi->flags, path, mode,
2029 fuse_get_context()->umask);
2030
2031 err = fs->op.create(path, mode, fi);
2032
2033 if (fs->debug && !err)
2034 fuse_log(FUSE_LOG_DEBUG, " create[%llu] flags: 0x%x %s\n",
2035 (unsigned long long) fi->fh, fi->flags, path);
2036
2037 return err;
2038}
2039
2040int fuse_fs_lock(struct fuse_fs *fs, const char *path,
2041 struct fuse_file_info *fi, int cmd, struct flock *lock)
2042{
2043 fuse_get_context()->private_data = fs->user_data;
2044 if (!fs->op.lock)
2045 return -ENOSYS;
2046
2047 if (fs->debug)
2048 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
2049 (unsigned long long) fi->fh,
2050 (cmd == F_GETLK ? "F_GETLK" :
2051 (cmd == F_SETLK ? "F_SETLK" :
2052 (cmd == F_SETLKW ? "F_SETLKW" : "???"))),
2053 (lock->l_type == F_RDLCK ? "F_RDLCK" :
2054 (lock->l_type == F_WRLCK ? "F_WRLCK" :
2055 (lock->l_type == F_UNLCK ? "F_UNLCK" :
2056 "???"))),
2057 (unsigned long long) lock->l_start,
2058 (unsigned long long) lock->l_len,
2059 (unsigned long long) lock->l_pid);
2060
2061 return fs->op.lock(path, fi, cmd, lock);
2062}
2063
2064int fuse_fs_flock(struct fuse_fs *fs, const char *path,
2065 struct fuse_file_info *fi, int op)
2066{
2067 fuse_get_context()->private_data = fs->user_data;
2068 if (!fs->op.flock)
2069 return -ENOSYS;
2070
2071 if (fs->debug) {
2072 int xop = op & ~LOCK_NB;
2073
2074 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s%s\n",
2075 (unsigned long long) fi->fh,
2076 xop == LOCK_SH ? "LOCK_SH" :
2077 (xop == LOCK_EX ? "LOCK_EX" :
2078 (xop == LOCK_UN ? "LOCK_UN" : "???")),
2079 (op & LOCK_NB) ? "|LOCK_NB" : "");
2080 }
2081 return fs->op.flock(path, fi, op);
2082}
2083
2084int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid,
2085 gid_t gid, struct fuse_file_info *fi)
2086{
2087 fuse_get_context()->private_data = fs->user_data;
2088 if (!fs->op.chown)
2089 return -ENOSYS;
2090 if (fs->debug) {
2091 char buf[10];
2092
2093 fuse_log(FUSE_LOG_DEBUG, "chown[%s] %s %lu %lu\n",
2094 file_info_string(fi, buf, sizeof(buf)),
2095 path, (unsigned long) uid, (unsigned long) gid);
2096 }
2097 return fs->op.chown(path, uid, gid, fi);
2098}
2099
2100int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
2101 struct fuse_file_info *fi)
2102{
2103 fuse_get_context()->private_data = fs->user_data;
2104 if (!fs->op.truncate)
2105 return -ENOSYS;
2106 if (fs->debug) {
2107 char buf[10];
2108
2109 fuse_log(FUSE_LOG_DEBUG, "truncate[%s] %llu\n",
2110 file_info_string(fi, buf, sizeof(buf)),
2111 (unsigned long long) size);
2112 }
2113 return fs->op.truncate(path, size, fi);
2114}
2115
2116int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
2117 const struct timespec tv[2], struct fuse_file_info *fi)
2118{
2119 fuse_get_context()->private_data = fs->user_data;
2120 if (!fs->op.utimens)
2121 return -ENOSYS;
2122 if (fs->debug) {
2123 char buf[10];
2124
2125 fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %jd.%09ld %jd.%09ld\n",
2126 file_info_string(fi, buf, sizeof(buf)),
2127 path, (intmax_t)tv[0].tv_sec, tv[0].tv_nsec,
2128 (intmax_t)tv[1].tv_sec, tv[1].tv_nsec);
2129 }
2130 return fs->op.utimens(path, tv, fi);
2131}
2132
2133int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask)
2134{
2135 fuse_get_context()->private_data = fs->user_data;
2136 if (!fs->op.access)
2137 return -ENOSYS;
2138 if (fs->debug)
2139 fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask);
2140
2141 return fs->op.access(path, mask);
2142}
2143
2144int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
2145 size_t len)
2146{
2147 fuse_get_context()->private_data = fs->user_data;
2148 if (!fs->op.readlink)
2149 return -ENOSYS;
2150 if (fs->debug)
2151 fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path,
2152 (unsigned long) len);
2153
2154 return fs->op.readlink(path, buf, len);
2155}
2156
2157int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
2158 dev_t rdev)
2159{
2160 fuse_get_context()->private_data = fs->user_data;
2161 if (!fs->op.mknod)
2162 return -ENOSYS;
2163 if (fs->debug)
2164 fuse_log(FUSE_LOG_DEBUG, "mknod %s 0%o 0x%llx umask=0%03o\n",
2165 path, mode, (unsigned long long) rdev,
2166 fuse_get_context()->umask);
2167
2168 return fs->op.mknod(path, mode, rdev);
2169}
2170
2171int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode)
2172{
2173 fuse_get_context()->private_data = fs->user_data;
2174 if (!fs->op.mkdir)
2175 return -ENOSYS;
2176 if (fs->debug)
2177 fuse_log(FUSE_LOG_DEBUG, "mkdir %s 0%o umask=0%03o\n",
2178 path, mode, fuse_get_context()->umask);
2179
2180 return fs->op.mkdir(path, mode);
2181}
2182
2183int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
2184 const char *value, size_t size, int flags)
2185{
2186 fuse_get_context()->private_data = fs->user_data;
2187 if (!fs->op.setxattr)
2188 return -ENOSYS;
2189 if (fs->debug)
2190 fuse_log(FUSE_LOG_DEBUG, "setxattr %s %s %lu 0x%x\n",
2191 path, name, (unsigned long) size, flags);
2192
2193 return fs->op.setxattr(path, name, value, size, flags);
2194}
2195
2196int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
2197 char *value, size_t size)
2198{
2199 fuse_get_context()->private_data = fs->user_data;
2200 if (!fs->op.getxattr)
2201 return -ENOSYS;
2202 if (fs->debug)
2203 fuse_log(FUSE_LOG_DEBUG, "getxattr %s %s %lu\n",
2204 path, name, (unsigned long) size);
2205
2206 return fs->op.getxattr(path, name, value, size);
2207}
2208
2209int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
2210 size_t size)
2211{
2212 fuse_get_context()->private_data = fs->user_data;
2213 if (!fs->op.listxattr)
2214 return -ENOSYS;
2215 if (fs->debug)
2216 fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n",
2217 path, (unsigned long) size);
2218
2219 return fs->op.listxattr(path, list, size);
2220}
2221
2222int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
2223 uint64_t *idx)
2224{
2225 fuse_get_context()->private_data = fs->user_data;
2226 if (!fs->op.bmap)
2227 return -ENOSYS;
2228 if (fs->debug)
2229 fuse_log(FUSE_LOG_DEBUG, "bmap %s blocksize: %lu index: %llu\n",
2230 path, (unsigned long) blocksize,
2231 (unsigned long long) *idx);
2232
2233 return fs->op.bmap(path, blocksize, idx);
2234}
2235
2236int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
2237{
2238 fuse_get_context()->private_data = fs->user_data;
2239 if (!fs->op.removexattr)
2240 return -ENOSYS;
2241 if (fs->debug)
2242 fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name);
2243
2244 return fs->op.removexattr(path, name);
2245}
2246
2247int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
2248 void *arg, struct fuse_file_info *fi, unsigned int flags,
2249 void *data)
2250{
2251 fuse_get_context()->private_data = fs->user_data;
2252 if (!fs->op.ioctl)
2253 return -ENOSYS;
2254 if (fs->debug)
2255 fuse_log(FUSE_LOG_DEBUG, "ioctl[%llu] 0x%x flags: 0x%x\n",
2256 (unsigned long long) fi->fh, cmd, flags);
2257
2258 return fs->op.ioctl(path, cmd, arg, fi, flags, data);
2259}
2260
2261int fuse_fs_poll(struct fuse_fs *fs, const char *path,
2262 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
2263 unsigned *reventsp)
2264{
2265 int res;
2266
2267 fuse_get_context()->private_data = fs->user_data;
2268 if (!fs->op.poll)
2269 return -ENOSYS;
2270 if (fs->debug)
2271 fuse_log(FUSE_LOG_DEBUG, "poll[%llu] ph: %p, events 0x%x\n",
2272 (unsigned long long) fi->fh, ph,
2273 fi->poll_events);
2274
2275 res = fs->op.poll(path, fi, ph, reventsp);
2276
2277 if (fs->debug && !res)
2278 fuse_log(FUSE_LOG_DEBUG, " poll[%llu] revents: 0x%x\n",
2279 (unsigned long long) fi->fh, *reventsp);
2280
2281 return res;
2282}
2283
2284int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
2285 off_t offset, off_t length, struct fuse_file_info *fi)
2286{
2287 fuse_get_context()->private_data = fs->user_data;
2288 if (!fs->op.fallocate)
2289 return -ENOSYS;
2290 if (fs->debug)
2291 fuse_log(FUSE_LOG_DEBUG, "fallocate %s mode %x, offset: %llu, length: %llu\n",
2292 path,
2293 mode,
2294 (unsigned long long) offset,
2295 (unsigned long long) length);
2296
2297 return fs->op.fallocate(path, mode, offset, length, fi);
2298}
2299
2300ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
2301 struct fuse_file_info *fi_in, off_t off_in,
2302 const char *path_out,
2303 struct fuse_file_info *fi_out, off_t off_out,
2304 size_t len, int flags)
2305{
2306 fuse_get_context()->private_data = fs->user_data;
2307 if (!fs->op.copy_file_range)
2308 return -ENOSYS;
2309 if (fs->debug)
2310 fuse_log(FUSE_LOG_DEBUG, "copy_file_range from %s:%llu to "
2311 "%s:%llu, length: %llu\n",
2312 path_in,
2313 (unsigned long long) off_in,
2314 path_out,
2315 (unsigned long long) off_out,
2316 (unsigned long long) len);
2317
2318 return fs->op.copy_file_range(path_in, fi_in, off_in, path_out,
2319 fi_out, off_out, len, flags);
2320}
2321
2322off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
2323 struct fuse_file_info *fi)
2324{
2325 fuse_get_context()->private_data = fs->user_data;
2326 if (!fs->op.lseek)
2327 return -ENOSYS;
2328 if (fs->debug) {
2329 char buf[10];
2330
2331 fuse_log(FUSE_LOG_DEBUG, "lseek[%s] %llu %d\n",
2332 file_info_string(fi, buf, sizeof(buf)),
2333 (unsigned long long) off, whence);
2334 }
2335 return fs->op.lseek(path, off, whence, fi);
2336}
2337
2338#ifdef HAVE_STATX
2339int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
2340 struct statx *stxbuf, struct fuse_file_info *fi)
2341{
2342 fuse_get_context()->private_data = fs->user_data;
2343 if (fs->op.statx) {
2344 if (fs->debug) {
2345 char buf[10];
2346
2347 fuse_log(FUSE_LOG_DEBUG, "statx[%s] %s %d %d\n",
2348 file_info_string(fi, buf, sizeof(buf)), path,
2349 flags, mask);
2350 }
2351 return fs->op.statx(path, flags, mask, stxbuf, fi);
2352 }
2353
2354 return -ENOSYS;
2355}
2356#else
2357int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
2358 struct statx *stxbuf, struct fuse_file_info *fi)
2359{
2360 (void)fs;
2361 (void)path;
2362 (void)flags;
2363 (void)mask;
2364 (void)stxbuf;
2365 (void)fi;
2366
2367 return -ENOSYS;
2368}
2369#endif
2370
2371static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
2372{
2373 struct node *node;
2374 int isopen = 0;
2375 pthread_mutex_lock(&f->lock);
2376 node = lookup_node(f, dir, name);
2377 if (node && node->open_count > 0)
2378 isopen = 1;
2379 pthread_mutex_unlock(&f->lock);
2380 return isopen;
2381}
2382
2383static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname,
2384 char *newname, size_t bufsize)
2385{
2386 struct stat buf;
2387 struct node *node;
2388 struct node *newnode;
2389 char *newpath;
2390 int res;
2391 int failctr = 10;
2392
2393 do {
2394 pthread_mutex_lock(&f->lock);
2395 node = lookup_node(f, dir, oldname);
2396 if (node == NULL) {
2397 pthread_mutex_unlock(&f->lock);
2398 return NULL;
2399 }
2400 do {
2401 f->hidectr ++;
2402 snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
2403 (unsigned int) node->nodeid, f->hidectr);
2404 newnode = lookup_node(f, dir, newname);
2405 } while(newnode);
2406
2407 res = try_get_path(f, dir, newname, &newpath, NULL, false);
2408 pthread_mutex_unlock(&f->lock);
2409 if (res)
2410 break;
2411
2412 memset(&buf, 0, sizeof(buf));
2413 res = fuse_fs_getattr(f->fs, newpath, &buf, NULL);
2414 if (res == -ENOENT)
2415 break;
2416 free(newpath);
2417 newpath = NULL;
2418 } while(res == 0 && --failctr);
2419
2420 return newpath;
2421}
2422
2423static int hide_node(struct fuse *f, const char *oldpath,
2424 fuse_ino_t dir, const char *oldname)
2425{
2426 char newname[64];
2427 char *newpath;
2428 int err = -EBUSY;
2429
2430 newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
2431 if (newpath) {
2432 err = fuse_fs_rename(f->fs, oldpath, newpath, 0);
2433 if (!err)
2434 err = rename_node(f, dir, oldname, dir, newname, 1);
2435 free(newpath);
2436 }
2437 return err;
2438}
2439
2440static int mtime_eq(const struct stat *stbuf, const struct timespec *ts)
2441{
2442 return stbuf->st_mtime == ts->tv_sec &&
2443 ST_MTIM_NSEC(stbuf) == ts->tv_nsec;
2444}
2445
2446#ifndef CLOCK_MONOTONIC
2447#define CLOCK_MONOTONIC CLOCK_REALTIME
2448#endif
2449
2450static void curr_time(struct timespec *now)
2451{
2452 static clockid_t clockid = CLOCK_MONOTONIC;
2453 int res = clock_gettime(clockid, now);
2454 if (res == -1 && errno == EINVAL) {
2455 clockid = CLOCK_REALTIME;
2456 res = clock_gettime(clockid, now);
2457 }
2458 if (res == -1) {
2459 perror("fuse: clock_gettime");
2460 abort();
2461 }
2462}
2463
2464static void update_stat(struct node *node, const struct stat *stbuf)
2465{
2466 if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) ||
2467 stbuf->st_size != node->size))
2468 node->cache_valid = 0;
2469 node->mtime.tv_sec = stbuf->st_mtime;
2470 node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf);
2471 node->size = stbuf->st_size;
2472 curr_time(&node->stat_updated);
2473}
2474
2475static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name,
2476 struct fuse_entry_param *e)
2477{
2478 struct node *node;
2479
2480 node = find_node(f, nodeid, name);
2481 if (node == NULL)
2482 return -ENOMEM;
2483
2484 e->ino = node->nodeid;
2485 e->generation = node->generation;
2486 e->entry_timeout = f->conf.entry_timeout;
2487 e->attr_timeout = f->conf.attr_timeout;
2488 if (f->conf.auto_cache) {
2489 pthread_mutex_lock(&f->lock);
2490 update_stat(node, &e->attr);
2491 pthread_mutex_unlock(&f->lock);
2492 }
2493 set_stat(f, e->ino, &e->attr);
2494 return 0;
2495}
2496
2497static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
2498 const char *name, const char *path,
2499 struct fuse_entry_param *e, struct fuse_file_info *fi)
2500{
2501 int res;
2502
2503 memset(e, 0, sizeof(struct fuse_entry_param));
2504 res = fuse_fs_getattr(f->fs, path, &e->attr, fi);
2505 if (res == 0) {
2506 res = do_lookup(f, nodeid, name, e);
2507 if (res == 0 && f->conf.debug) {
2508 fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu\n",
2509 (unsigned long long) e->ino);
2510 }
2511 }
2512 return res;
2513}
2514
2515static struct fuse_context_i *fuse_get_context_internal(void)
2516{
2517 return (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
2518}
2519
2520static struct fuse_context_i *fuse_create_context(struct fuse *f)
2521{
2522 struct fuse_context_i *c = fuse_get_context_internal();
2523 if (c == NULL) {
2524 c = (struct fuse_context_i *)
2525 calloc(1, sizeof(struct fuse_context_i));
2526 if (c == NULL) {
2527 /* This is hard to deal with properly, so just
2528 abort. If memory is so low that the
2529 context cannot be allocated, there's not
2530 much hope for the filesystem anyway */
2531 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate thread specific data\n");
2532 abort();
2533 }
2534 pthread_setspecific(fuse_context_key, c);
2535 } else {
2536 memset(c, 0, sizeof(*c));
2537 }
2538 c->ctx.fuse = f;
2539
2540 return c;
2541}
2542
2543static void fuse_freecontext(void *data)
2544{
2545 free(data);
2546}
2547
2548static int fuse_create_context_key(void)
2549{
2550 int err = 0;
2551 pthread_mutex_lock(&fuse_context_lock);
2552 if (!fuse_context_ref) {
2553 err = pthread_key_create(&fuse_context_key, fuse_freecontext);
2554 if (err) {
2555 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
2556 strerror(err));
2557 pthread_mutex_unlock(&fuse_context_lock);
2558 return -1;
2559 }
2560 }
2561 fuse_context_ref++;
2562 pthread_mutex_unlock(&fuse_context_lock);
2563 return 0;
2564}
2565
2566static void fuse_delete_context_key(void)
2567{
2568 pthread_mutex_lock(&fuse_context_lock);
2569 fuse_context_ref--;
2570 if (!fuse_context_ref) {
2571 free(pthread_getspecific(fuse_context_key));
2572 pthread_key_delete(fuse_context_key);
2573 }
2574 pthread_mutex_unlock(&fuse_context_lock);
2575}
2576
2577static struct fuse *req_fuse_prepare(fuse_req_t req)
2578{
2579 struct fuse_context_i *c = fuse_create_context(req_fuse(req));
2580 const struct fuse_ctx *ctx = fuse_req_ctx(req);
2581 c->req = req;
2582 c->ctx.uid = ctx->uid;
2583 c->ctx.gid = ctx->gid;
2584 c->ctx.pid = ctx->pid;
2585 c->ctx.umask = ctx->umask;
2586 return c->ctx.fuse;
2587}
2588
2589static inline void reply_err(fuse_req_t req, int err)
2590{
2591 /* fuse_reply_err() uses non-negated errno values */
2592 fuse_reply_err(req, -err);
2593}
2594
2595static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
2596 int err)
2597{
2598 if (!err) {
2599 struct fuse *f = req_fuse(req);
2600 if (fuse_reply_entry(req, e) == -ENOENT) {
2601 /* Skip forget for negative result */
2602 if (e->ino != 0)
2603 forget_node(f, e->ino, 1);
2604 }
2605 } else
2606 reply_err(req, err);
2607}
2608
2609void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
2610 struct fuse_config *cfg)
2611{
2612 fuse_get_context()->private_data = fs->user_data;
2613 if (!fs->op.write_buf)
2615 if (!fs->op.lock)
2617 if (!fs->op.flock)
2619 if (fs->op.init)
2620 fs->user_data = fs->op.init(conn, cfg);
2621}
2622
2623static int fuse_init_intr_signal(int signum, int *installed);
2624
2625static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
2626{
2627 struct fuse *f = (struct fuse *) data;
2628
2629 fuse_create_context(f);
2631 fuse_fs_init(f->fs, conn, &f->conf);
2632
2633 if (f->conf.intr) {
2634 if (fuse_init_intr_signal(f->conf.intr_signal,
2635 &f->intr_installed) == -1)
2636 fuse_log(FUSE_LOG_ERR, "fuse: failed to init interrupt signal\n");
2637 } else {
2638 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
2639 conn->no_interrupt = 1;
2640 }
2641}
2642
2643void fuse_fs_destroy(struct fuse_fs *fs)
2644{
2645 fuse_get_context()->private_data = fs->user_data;
2646 if (fs->op.destroy)
2647 fs->op.destroy(fs->user_data);
2648}
2649
2650static void fuse_lib_destroy(void *data)
2651{
2652 struct fuse *f = (struct fuse *) data;
2653
2654 fuse_create_context(f);
2655 fuse_fs_destroy(f->fs);
2656}
2657
2658static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
2659 const char *name)
2660{
2661 struct fuse *f = req_fuse_prepare(req);
2662 struct fuse_entry_param e = { .ino = 0 }; /* invalid ino */
2663 char *path;
2664 int err;
2665 struct node *dot = NULL;
2666
2667 if (name[0] == '.') {
2668 int len = strlen(name);
2669
2670 if (len == 1 || (name[1] == '.' && len == 2)) {
2671 pthread_mutex_lock(&f->lock);
2672 if (len == 1) {
2673 if (f->conf.debug)
2674 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOT\n");
2675 dot = get_node_nocheck(f, parent);
2676 if (dot == NULL) {
2677 pthread_mutex_unlock(&f->lock);
2678 reply_entry(req, &e, -ESTALE);
2679 return;
2680 }
2681 dot->refctr++;
2682 } else {
2683 if (f->conf.debug)
2684 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOTDOT\n");
2685 parent = get_node(f, parent)->parent->nodeid;
2686 }
2687 pthread_mutex_unlock(&f->lock);
2688 name = NULL;
2689 }
2690 }
2691
2692 err = get_path_name(f, parent, name, &path);
2693 if (!err) {
2694 struct fuse_intr_data d;
2695 if (f->conf.debug)
2696 fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path);
2697 fuse_prepare_interrupt(f, req, &d);
2698 err = lookup_path(f, parent, name, path, &e, NULL);
2699 if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
2700 e.ino = 0;
2701 e.entry_timeout = f->conf.negative_timeout;
2702 err = 0;
2703 }
2704 fuse_finish_interrupt(f, req, &d);
2705 free_path(f, parent, path);
2706 }
2707 if (dot) {
2708 pthread_mutex_lock(&f->lock);
2709 unref_node(f, dot);
2710 pthread_mutex_unlock(&f->lock);
2711 }
2712 reply_entry(req, &e, err);
2713}
2714
2715static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
2716{
2717 if (f->conf.debug)
2718 fuse_log(FUSE_LOG_DEBUG, "FORGET %llu/%llu\n", (unsigned long long)ino,
2719 (unsigned long long) nlookup);
2720 forget_node(f, ino, nlookup);
2721}
2722
2723static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
2724{
2725 do_forget(req_fuse(req), ino, nlookup);
2726 fuse_reply_none(req);
2727}
2728
2729static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
2730 struct fuse_forget_data *forgets)
2731{
2732 struct fuse *f = req_fuse(req);
2733 size_t i;
2734
2735 for (i = 0; i < count; i++)
2736 do_forget(f, forgets[i].ino, forgets[i].nlookup);
2737
2738 fuse_reply_none(req);
2739}
2740
2741
2742static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
2743 struct fuse_file_info *fi)
2744{
2745 struct fuse *f = req_fuse_prepare(req);
2746 struct stat buf;
2747 char *path;
2748 int err;
2749
2750 memset(&buf, 0, sizeof(buf));
2751
2752 if (fi != NULL)
2753 err = get_path_nullok(f, ino, &path);
2754 else
2755 err = get_path(f, ino, &path);
2756 if (!err) {
2757 struct fuse_intr_data d;
2758 fuse_prepare_interrupt(f, req, &d);
2759 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2760 fuse_finish_interrupt(f, req, &d);
2761 free_path(f, ino, path);
2762 }
2763 if (!err) {
2764 struct node *node;
2765
2766 pthread_mutex_lock(&f->lock);
2767 node = get_node(f, ino);
2768 if (node->is_hidden && buf.st_nlink > 0)
2769 buf.st_nlink--;
2770 if (f->conf.auto_cache)
2771 update_stat(node, &buf);
2772 pthread_mutex_unlock(&f->lock);
2773 set_stat(f, ino, &buf);
2774 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2775 } else
2776 reply_err(req, err);
2777}
2778
2779int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
2780 struct fuse_file_info *fi)
2781{
2782 fuse_get_context()->private_data = fs->user_data;
2783 if (!fs->op.chmod)
2784 return -ENOSYS;
2785
2786 if (fs->debug) {
2787 char buf[10];
2788
2789 fuse_log(FUSE_LOG_DEBUG, "chmod[%s] %s %llo\n",
2790 file_info_string(fi, buf, sizeof(buf)),
2791 path, (unsigned long long) mode);
2792 }
2793 return fs->op.chmod(path, mode, fi);
2794}
2795
2796static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
2797 int valid, struct fuse_file_info *fi)
2798{
2799 struct fuse *f = req_fuse_prepare(req);
2800 struct stat buf;
2801 char *path;
2802 int err;
2803
2804 memset(&buf, 0, sizeof(buf));
2805 if (fi != NULL)
2806 err = get_path_nullok(f, ino, &path);
2807 else
2808 err = get_path(f, ino, &path);
2809 if (!err) {
2810 struct fuse_intr_data d;
2811 fuse_prepare_interrupt(f, req, &d);
2812 err = 0;
2813 if (!err && (valid & FUSE_SET_ATTR_MODE))
2814 err = fuse_fs_chmod(f->fs, path, attr->st_mode, fi);
2815 if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) {
2816 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
2817 attr->st_uid : (uid_t) -1;
2818 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
2819 attr->st_gid : (gid_t) -1;
2820 err = fuse_fs_chown(f->fs, path, uid, gid, fi);
2821 }
2822 if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
2823 err = fuse_fs_truncate(f->fs, path,
2824 attr->st_size, fi);
2825 }
2826#ifdef HAVE_UTIMENSAT
2827 if (!err &&
2828 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
2829 struct timespec tv[2];
2830
2831 tv[0].tv_sec = 0;
2832 tv[1].tv_sec = 0;
2833 tv[0].tv_nsec = UTIME_OMIT;
2834 tv[1].tv_nsec = UTIME_OMIT;
2835
2836 if (valid & FUSE_SET_ATTR_ATIME_NOW)
2837 tv[0].tv_nsec = UTIME_NOW;
2838 else if (valid & FUSE_SET_ATTR_ATIME)
2839 tv[0] = attr->st_atim;
2840
2841 if (valid & FUSE_SET_ATTR_MTIME_NOW)
2842 tv[1].tv_nsec = UTIME_NOW;
2843 else if (valid & FUSE_SET_ATTR_MTIME)
2844 tv[1] = attr->st_mtim;
2845
2846 err = fuse_fs_utimens(f->fs, path, tv, fi);
2847 } else
2848#endif
2849 if (!err &&
2850 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
2851 (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
2852 struct timespec tv[2];
2853 tv[0].tv_sec = attr->st_atime;
2854 tv[0].tv_nsec = ST_ATIM_NSEC(attr);
2855 tv[1].tv_sec = attr->st_mtime;
2856 tv[1].tv_nsec = ST_MTIM_NSEC(attr);
2857 err = fuse_fs_utimens(f->fs, path, tv, fi);
2858 }
2859 if (!err) {
2860 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2861 }
2862 fuse_finish_interrupt(f, req, &d);
2863 free_path(f, ino, path);
2864 }
2865 if (!err) {
2866 if (f->conf.auto_cache) {
2867 pthread_mutex_lock(&f->lock);
2868 update_stat(get_node(f, ino), &buf);
2869 pthread_mutex_unlock(&f->lock);
2870 }
2871 set_stat(f, ino, &buf);
2872 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2873 } else
2874 reply_err(req, err);
2875}
2876
2877static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask)
2878{
2879 struct fuse *f = req_fuse_prepare(req);
2880 char *path;
2881 int err;
2882
2883 err = get_path(f, ino, &path);
2884 if (!err) {
2885 struct fuse_intr_data d;
2886
2887 fuse_prepare_interrupt(f, req, &d);
2888 err = fuse_fs_access(f->fs, path, mask);
2889 fuse_finish_interrupt(f, req, &d);
2890 free_path(f, ino, path);
2891 }
2892 reply_err(req, err);
2893}
2894
2895static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino)
2896{
2897 struct fuse *f = req_fuse_prepare(req);
2898 char linkname[PATH_MAX + 1];
2899 char *path;
2900 int err;
2901
2902 err = get_path(f, ino, &path);
2903 if (!err) {
2904 struct fuse_intr_data d;
2905 fuse_prepare_interrupt(f, req, &d);
2906 err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname));
2907 fuse_finish_interrupt(f, req, &d);
2908 free_path(f, ino, path);
2909 }
2910 if (!err) {
2911 linkname[PATH_MAX] = '\0';
2912 fuse_reply_readlink(req, linkname);
2913 } else
2914 reply_err(req, err);
2915}
2916
2917static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
2918 mode_t mode, dev_t rdev)
2919{
2920 struct fuse *f = req_fuse_prepare(req);
2921 struct fuse_entry_param e;
2922 char *path;
2923 int err;
2924
2925 err = get_path_name(f, parent, name, &path);
2926 if (!err) {
2927 struct fuse_intr_data d;
2928
2929 fuse_prepare_interrupt(f, req, &d);
2930 err = -ENOSYS;
2931 if (S_ISREG(mode)) {
2932 struct fuse_file_info fi;
2933
2934 memset(&fi, 0, sizeof(fi));
2935 fi.flags = O_CREAT | O_EXCL | O_WRONLY;
2936 err = fuse_fs_create(f->fs, path, mode, &fi);
2937 if (!err) {
2938 err = lookup_path(f, parent, name, path, &e,
2939 &fi);
2940 fuse_fs_release(f->fs, path, &fi);
2941 }
2942 }
2943 if (err == -ENOSYS) {
2944 err = fuse_fs_mknod(f->fs, path, mode, rdev);
2945 if (!err)
2946 err = lookup_path(f, parent, name, path, &e,
2947 NULL);
2948 }
2949 fuse_finish_interrupt(f, req, &d);
2950 free_path(f, parent, path);
2951 }
2952 reply_entry(req, &e, err);
2953}
2954
2955static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
2956 mode_t mode)
2957{
2958 struct fuse *f = req_fuse_prepare(req);
2959 struct fuse_entry_param e;
2960 char *path;
2961 int err;
2962
2963 err = get_path_name(f, parent, name, &path);
2964 if (!err) {
2965 struct fuse_intr_data d;
2966
2967 fuse_prepare_interrupt(f, req, &d);
2968 err = fuse_fs_mkdir(f->fs, path, mode);
2969 if (!err)
2970 err = lookup_path(f, parent, name, path, &e, NULL);
2971 fuse_finish_interrupt(f, req, &d);
2972 free_path(f, parent, path);
2973 }
2974 reply_entry(req, &e, err);
2975}
2976
2977static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
2978 const char *name)
2979{
2980 struct fuse *f = req_fuse_prepare(req);
2981 struct node *wnode;
2982 char *path;
2983 int err;
2984
2985 err = get_path_wrlock(f, parent, name, &path, &wnode);
2986 if (!err) {
2987 struct fuse_intr_data d;
2988
2989 fuse_prepare_interrupt(f, req, &d);
2990 if (!f->conf.hard_remove && is_open(f, parent, name)) {
2991 err = hide_node(f, path, parent, name);
2992 if (!err) {
2993 /* we have hidden the node so now check again under a lock in case it is not used any more */
2994 if (!is_open(f, parent, wnode->name)) {
2995 char *unlinkpath;
2996
2997 /* get the hidden file path, to unlink it */
2998 if (try_get_path(f, wnode->nodeid, NULL, &unlinkpath, NULL, false) == 0) {
2999 err = fuse_fs_unlink(f->fs, unlinkpath);
3000 if (!err)
3001 remove_node(f, parent, wnode->name);
3002 free(unlinkpath);
3003 }
3004 }
3005 }
3006 } else {
3007 err = fuse_fs_unlink(f->fs, path);
3008 if (!err)
3009 remove_node(f, parent, name);
3010 }
3011 fuse_finish_interrupt(f, req, &d);
3012 free_path_wrlock(f, parent, wnode, path);
3013 }
3014 reply_err(req, err);
3015}
3016
3017static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
3018{
3019 struct fuse *f = req_fuse_prepare(req);
3020 struct node *wnode;
3021 char *path;
3022 int err;
3023
3024 err = get_path_wrlock(f, parent, name, &path, &wnode);
3025 if (!err) {
3026 struct fuse_intr_data d;
3027
3028 fuse_prepare_interrupt(f, req, &d);
3029 err = fuse_fs_rmdir(f->fs, path);
3030 fuse_finish_interrupt(f, req, &d);
3031 if (!err)
3032 remove_node(f, parent, name);
3033 free_path_wrlock(f, parent, wnode, path);
3034 }
3035 reply_err(req, err);
3036}
3037
3038static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
3039 fuse_ino_t parent, const char *name)
3040{
3041 struct fuse *f = req_fuse_prepare(req);
3042 struct fuse_entry_param e;
3043 char *path;
3044 int err;
3045
3046 err = get_path_name(f, parent, name, &path);
3047 if (!err) {
3048 struct fuse_intr_data d;
3049
3050 fuse_prepare_interrupt(f, req, &d);
3051 err = fuse_fs_symlink(f->fs, linkname, path);
3052 if (!err)
3053 err = lookup_path(f, parent, name, path, &e, NULL);
3054 fuse_finish_interrupt(f, req, &d);
3055 free_path(f, parent, path);
3056 }
3057 reply_entry(req, &e, err);
3058}
3059
3060static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
3061 const char *oldname, fuse_ino_t newdir,
3062 const char *newname, unsigned int flags)
3063{
3064 struct fuse *f = req_fuse_prepare(req);
3065 char *oldpath;
3066 char *newpath;
3067 struct node *wnode1;
3068 struct node *wnode2;
3069 int err;
3070
3071 err = get_path2(f, olddir, oldname, newdir, newname,
3072 &oldpath, &newpath, &wnode1, &wnode2);
3073 if (!err) {
3074 struct fuse_intr_data d;
3075 err = 0;
3076 fuse_prepare_interrupt(f, req, &d);
3077 if (!f->conf.hard_remove && !(flags & RENAME_EXCHANGE) &&
3078 is_open(f, newdir, newname))
3079 err = hide_node(f, newpath, newdir, newname);
3080 if (!err) {
3081 err = fuse_fs_rename(f->fs, oldpath, newpath, flags);
3082 if (!err) {
3083 if (flags & RENAME_EXCHANGE) {
3084 err = exchange_node(f, olddir, oldname,
3085 newdir, newname);
3086 } else {
3087 err = rename_node(f, olddir, oldname,
3088 newdir, newname, 0);
3089 }
3090 }
3091 }
3092 fuse_finish_interrupt(f, req, &d);
3093 free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
3094 }
3095 reply_err(req, err);
3096}
3097
3098static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
3099 const char *newname)
3100{
3101 struct fuse *f = req_fuse_prepare(req);
3102 struct fuse_entry_param e;
3103 char *oldpath;
3104 char *newpath;
3105 int err;
3106
3107 err = get_path2(f, ino, NULL, newparent, newname,
3108 &oldpath, &newpath, NULL, NULL);
3109 if (!err) {
3110 struct fuse_intr_data d;
3111
3112 fuse_prepare_interrupt(f, req, &d);
3113 err = fuse_fs_link(f->fs, oldpath, newpath);
3114 if (!err)
3115 err = lookup_path(f, newparent, newname, newpath,
3116 &e, NULL);
3117 fuse_finish_interrupt(f, req, &d);
3118 free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath);
3119 }
3120 reply_entry(req, &e, err);
3121}
3122
3123static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
3124 struct fuse_file_info *fi)
3125{
3126 struct node *node;
3127 int unlink_hidden = 0;
3128
3129 fuse_fs_release(f->fs, path, fi);
3130
3131 pthread_mutex_lock(&f->lock);
3132 node = get_node(f, ino);
3133 assert(node->open_count > 0);
3134 --node->open_count;
3135 if (node->is_hidden && !node->open_count) {
3136 unlink_hidden = 1;
3137 node->is_hidden = 0;
3138 }
3139 pthread_mutex_unlock(&f->lock);
3140
3141 if(unlink_hidden) {
3142 if (path) {
3143 fuse_fs_unlink(f->fs, path);
3144 } else if (f->conf.nullpath_ok) {
3145 char *unlinkpath;
3146
3147 if (get_path(f, ino, &unlinkpath) == 0)
3148 fuse_fs_unlink(f->fs, unlinkpath);
3149
3150 free_path(f, ino, unlinkpath);
3151 }
3152 }
3153}
3154
3155static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
3156 const char *name, mode_t mode,
3157 struct fuse_file_info *fi)
3158{
3159 struct fuse *f = req_fuse_prepare(req);
3160 struct fuse_intr_data d;
3161 struct fuse_entry_param e;
3162 char *path;
3163 int err;
3164
3165 err = get_path_name(f, parent, name, &path);
3166 if (!err) {
3167 fuse_prepare_interrupt(f, req, &d);
3168 err = fuse_fs_create(f->fs, path, mode, fi);
3169 if (!err) {
3170 err = lookup_path(f, parent, name, path, &e, fi);
3171 if (err)
3172 fuse_fs_release(f->fs, path, fi);
3173 else if (!S_ISREG(e.attr.st_mode)) {
3174 err = -EIO;
3175 fuse_fs_release(f->fs, path, fi);
3176 forget_node(f, e.ino, 1);
3177 } else {
3178 if (f->conf.direct_io)
3179 fi->direct_io = 1;
3180 if (f->conf.kernel_cache)
3181 fi->keep_cache = 1;
3182 if (fi->direct_io &&
3183 f->conf.parallel_direct_writes)
3184 fi->parallel_direct_writes = 1;
3185 }
3186 }
3187 fuse_finish_interrupt(f, req, &d);
3188 }
3189 if (!err) {
3190 pthread_mutex_lock(&f->lock);
3191 get_node(f, e.ino)->open_count++;
3192 pthread_mutex_unlock(&f->lock);
3193 if (fuse_reply_create(req, &e, fi) == -ENOENT) {
3194 /* The open syscall was interrupted, so it
3195 must be cancelled */
3196 fuse_do_release(f, e.ino, path, fi);
3197 forget_node(f, e.ino, 1);
3198 }
3199 } else {
3200 reply_err(req, err);
3201 }
3202
3203 free_path(f, parent, path);
3204}
3205
3206static double diff_timespec(const struct timespec *t1,
3207 const struct timespec *t2)
3208{
3209 return (t1->tv_sec - t2->tv_sec) +
3210 ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0;
3211}
3212
3213static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path,
3214 struct fuse_file_info *fi)
3215{
3216 struct node *node;
3217
3218 pthread_mutex_lock(&f->lock);
3219 node = get_node(f, ino);
3220 if (node->cache_valid) {
3221 struct timespec now;
3222
3223 curr_time(&now);
3224 if (diff_timespec(&now, &node->stat_updated) >
3225 f->conf.ac_attr_timeout) {
3226 struct stat stbuf;
3227 int err;
3228 pthread_mutex_unlock(&f->lock);
3229 err = fuse_fs_getattr(f->fs, path, &stbuf, fi);
3230 pthread_mutex_lock(&f->lock);
3231 if (!err)
3232 update_stat(node, &stbuf);
3233 else
3234 node->cache_valid = 0;
3235 }
3236 }
3237 if (node->cache_valid)
3238 fi->keep_cache = 1;
3239
3240 node->cache_valid = 1;
3241 pthread_mutex_unlock(&f->lock);
3242}
3243
3244static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
3245 struct fuse_file_info *fi)
3246{
3247 struct fuse *f = req_fuse_prepare(req);
3248 struct fuse_intr_data d;
3249 char *path;
3250 int err;
3251
3252 err = get_path(f, ino, &path);
3253 if (!err) {
3254 fuse_prepare_interrupt(f, req, &d);
3255 err = fuse_fs_open(f->fs, path, fi);
3256 if (!err) {
3257 if (f->conf.direct_io)
3258 fi->direct_io = 1;
3259 if (f->conf.kernel_cache)
3260 fi->keep_cache = 1;
3261
3262 if (f->conf.auto_cache)
3263 open_auto_cache(f, ino, path, fi);
3264
3265 if (f->conf.no_rofd_flush &&
3266 (fi->flags & O_ACCMODE) == O_RDONLY)
3267 fi->noflush = 1;
3268
3269 if (fi->direct_io && f->conf.parallel_direct_writes)
3270 fi->parallel_direct_writes = 1;
3271
3272 }
3273 fuse_finish_interrupt(f, req, &d);
3274 }
3275 if (!err) {
3276 pthread_mutex_lock(&f->lock);
3277 get_node(f, ino)->open_count++;
3278 pthread_mutex_unlock(&f->lock);
3279 if (fuse_reply_open(req, fi) == -ENOENT) {
3280 /* The open syscall was interrupted, so it
3281 must be cancelled */
3282 fuse_do_release(f, ino, path, fi);
3283 }
3284 } else
3285 reply_err(req, err);
3286
3287 free_path(f, ino, path);
3288}
3289
3290static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
3291 off_t off, struct fuse_file_info *fi)
3292{
3293 struct fuse *f = req_fuse_prepare(req);
3294 struct fuse_bufvec *buf = NULL;
3295 char *path;
3296 int res;
3297
3298 res = get_path_nullok(f, ino, &path);
3299 if (res == 0) {
3300 struct fuse_intr_data d;
3301
3302 fuse_prepare_interrupt(f, req, &d);
3303 res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
3304 fuse_finish_interrupt(f, req, &d);
3305 free_path(f, ino, path);
3306 }
3307
3308 if (res == 0)
3310 else
3311 reply_err(req, res);
3312
3313 fuse_free_buf(buf);
3314}
3315
3316static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
3317 struct fuse_bufvec *buf, off_t off,
3318 struct fuse_file_info *fi)
3319{
3320 struct fuse *f = req_fuse_prepare(req);
3321 char *path;
3322 int res;
3323
3324 res = get_path_nullok(f, ino, &path);
3325 if (res == 0) {
3326 struct fuse_intr_data d;
3327
3328 fuse_prepare_interrupt(f, req, &d);
3329 res = fuse_fs_write_buf(f->fs, path, buf, off, fi);
3330 fuse_finish_interrupt(f, req, &d);
3331 free_path(f, ino, path);
3332 }
3333
3334 if (res >= 0)
3335 fuse_reply_write(req, res);
3336 else
3337 reply_err(req, res);
3338}
3339
3340static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
3341 struct fuse_file_info *fi)
3342{
3343 struct fuse *f = req_fuse_prepare(req);
3344 char *path;
3345 int err;
3346
3347 err = get_path_nullok(f, ino, &path);
3348 if (!err) {
3349 struct fuse_intr_data d;
3350
3351 fuse_prepare_interrupt(f, req, &d);
3352 err = fuse_fs_fsync(f->fs, path, datasync, fi);
3353 fuse_finish_interrupt(f, req, &d);
3354 free_path(f, ino, path);
3355 }
3356 reply_err(req, err);
3357}
3358
3359static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi,
3360 struct fuse_file_info *fi)
3361{
3362 struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh;
3363 memset(fi, 0, sizeof(struct fuse_file_info));
3364 fi->fh = dh->fh;
3365 return dh;
3366}
3367
3368static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
3369 struct fuse_file_info *llfi)
3370{
3371 struct fuse *f = req_fuse_prepare(req);
3372 struct fuse_intr_data d;
3373 struct fuse_dh *dh;
3374 struct fuse_file_info fi;
3375 char *path;
3376 int err;
3377
3378 dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
3379 if (dh == NULL) {
3380 reply_err(req, -ENOMEM);
3381 return;
3382 }
3383 memset(dh, 0, sizeof(struct fuse_dh));
3384 dh->fuse = f;
3385 dh->contents = NULL;
3386 dh->first = NULL;
3387 dh->len = 0;
3388 dh->filled = 0;
3389 dh->nodeid = ino;
3390 pthread_mutex_init(&dh->lock, NULL);
3391
3392 llfi->fh = (uintptr_t) dh;
3393
3394 memset(&fi, 0, sizeof(fi));
3395 fi.flags = llfi->flags;
3396
3397 err = get_path(f, ino, &path);
3398 if (!err) {
3399 fuse_prepare_interrupt(f, req, &d);
3400 err = fuse_fs_opendir(f->fs, path, &fi);
3401 fuse_finish_interrupt(f, req, &d);
3402 dh->fh = fi.fh;
3403 llfi->cache_readdir = fi.cache_readdir;
3404 llfi->keep_cache = fi.keep_cache;
3405 }
3406 if (!err) {
3407 if (fuse_reply_open(req, llfi) == -ENOENT) {
3408 /* The opendir syscall was interrupted, so it
3409 must be cancelled */
3410 fuse_fs_releasedir(f->fs, path, &fi);
3411 pthread_mutex_destroy(&dh->lock);
3412 free(dh);
3413 }
3414 } else {
3415 reply_err(req, err);
3416 pthread_mutex_destroy(&dh->lock);
3417 free(dh);
3418 }
3419 free_path(f, ino, path);
3420}
3421
3422static int extend_contents(struct fuse_dh *dh, unsigned minsize)
3423{
3424 if (minsize > dh->size) {
3425 char *newptr;
3426 unsigned newsize = dh->size;
3427 if (!newsize)
3428 newsize = 1024;
3429 while (newsize < minsize) {
3430 if (newsize >= 0x80000000)
3431 newsize = 0xffffffff;
3432 else
3433 newsize *= 2;
3434 }
3435
3436 newptr = (char *) realloc(dh->contents, newsize);
3437 if (!newptr) {
3438 dh->error = -ENOMEM;
3439 return -1;
3440 }
3441 dh->contents = newptr;
3442 dh->size = newsize;
3443 }
3444 return 0;
3445}
3446
3447static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name,
3448 struct stat *st, enum fuse_fill_dir_flags flags)
3449{
3450 struct fuse_direntry *de;
3451
3452 de = malloc(sizeof(struct fuse_direntry));
3453 if (!de) {
3454 dh->error = -ENOMEM;
3455 return -1;
3456 }
3457 de->name = strdup(name);
3458 if (!de->name) {
3459 dh->error = -ENOMEM;
3460 free(de);
3461 return -1;
3462 }
3463 de->flags = flags;
3464 de->stat = *st;
3465 de->next = NULL;
3466
3467 *dh->last = de;
3468 dh->last = &de->next;
3469
3470 return 0;
3471}
3472
3473static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent,
3474 const char *name)
3475{
3476 struct node *node;
3477 fuse_ino_t res = FUSE_UNKNOWN_INO;
3478
3479 pthread_mutex_lock(&f->lock);
3480 node = lookup_node(f, parent, name);
3481 if (node)
3482 res = node->nodeid;
3483 pthread_mutex_unlock(&f->lock);
3484
3485 return res;
3486}
3487
3488static int fill_dir(void *dh_, const char *name, const struct stat *statp,
3489 off_t off, enum fuse_fill_dir_flags flags)
3490{
3491 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3492 struct stat stbuf;
3493
3494 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3495 dh->error = -EIO;
3496 return 1;
3497 }
3498
3499 if (statp)
3500 stbuf = *statp;
3501 else {
3502 memset(&stbuf, 0, sizeof(stbuf));
3503 stbuf.st_ino = FUSE_UNKNOWN_INO;
3504 }
3505
3506 if (!dh->fuse->conf.use_ino) {
3507 stbuf.st_ino = FUSE_UNKNOWN_INO;
3508 if (dh->fuse->conf.readdir_ino) {
3509 stbuf.st_ino = (ino_t)
3510 lookup_nodeid(dh->fuse, dh->nodeid, name);
3511 }
3512 }
3513
3514 if (off) {
3515 size_t newlen;
3516
3517 if (dh->filled) {
3518 dh->error = -EIO;
3519 return 1;
3520 }
3521
3522 if (dh->first) {
3523 dh->error = -EIO;
3524 return 1;
3525 }
3526
3527 if (extend_contents(dh, dh->needlen) == -1)
3528 return 1;
3529
3530 newlen = dh->len +
3531 fuse_add_direntry(dh->req, dh->contents + dh->len,
3532 dh->needlen - dh->len, name,
3533 &stbuf, off);
3534 if (newlen > dh->needlen)
3535 return 1;
3536
3537 dh->len = newlen;
3538 } else {
3539 dh->filled = 1;
3540
3541 if (fuse_add_direntry_to_dh(dh, name, &stbuf, flags) == -1)
3542 return 1;
3543 }
3544 return 0;
3545}
3546
3547static int is_dot_or_dotdot(const char *name)
3548{
3549 return name[0] == '.' && (name[1] == '\0' ||
3550 (name[1] == '.' && name[2] == '\0'));
3551}
3552
3553static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
3554 off_t off, enum fuse_fill_dir_flags flags)
3555{
3556 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3557 struct fuse_entry_param e = {
3558 /* ino=0 tells the kernel to ignore readdirplus stat info */
3559 .ino = 0,
3560 };
3561 struct fuse *f = dh->fuse;
3562 int res;
3563
3564 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3565 dh->error = -EIO;
3566 return 1;
3567 }
3568
3569 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3570 e.attr = *statp;
3571 }
3572
3573 e.attr.st_ino = FUSE_UNKNOWN_INO;
3574 if (statp) {
3575 e.attr.st_mode = statp->st_mode;
3576 if (f->conf.use_ino)
3577 e.attr.st_ino = statp->st_ino;
3578 }
3579 if (!f->conf.use_ino && f->conf.readdir_ino) {
3580 e.attr.st_ino = (ino_t)
3581 lookup_nodeid(f, dh->nodeid, name);
3582 }
3583
3584 if (off) {
3585 size_t newlen;
3586
3587 if (dh->filled) {
3588 dh->error = -EIO;
3589 return 1;
3590 }
3591
3592 if (dh->first) {
3593 dh->error = -EIO;
3594 return 1;
3595 }
3596 if (extend_contents(dh, dh->needlen) == -1)
3597 return 1;
3598
3599 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3600 if (!is_dot_or_dotdot(name)) {
3601 res = do_lookup(f, dh->nodeid, name, &e);
3602 if (res) {
3603 dh->error = res;
3604 return 1;
3605 }
3606 }
3607 }
3608
3609 newlen = dh->len +
3610 fuse_add_direntry_plus(dh->req, dh->contents + dh->len,
3611 dh->needlen - dh->len, name,
3612 &e, off);
3613 if (newlen > dh->needlen)
3614 return 1;
3615 dh->len = newlen;
3616 } else {
3617 dh->filled = 1;
3618
3619 if (fuse_add_direntry_to_dh(dh, name, &e.attr, flags) == -1)
3620 return 1;
3621 }
3622
3623 return 0;
3624}
3625
3626static void free_direntries(struct fuse_direntry *de)
3627{
3628 while (de) {
3629 struct fuse_direntry *next = de->next;
3630 free(de->name);
3631 free(de);
3632 de = next;
3633 }
3634}
3635
3636static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3637 size_t size, off_t off, struct fuse_dh *dh,
3638 struct fuse_file_info *fi,
3639 enum fuse_readdir_flags flags)
3640{
3641 char *path;
3642 int err;
3643
3644 if (f->fs->op.readdir)
3645 err = get_path_nullok(f, ino, &path);
3646 else
3647 err = get_path(f, ino, &path);
3648 if (!err) {
3649 struct fuse_intr_data d;
3650 fuse_fill_dir_t filler = fill_dir;
3651
3652 if (flags & FUSE_READDIR_PLUS)
3653 filler = fill_dir_plus;
3654
3655 free_direntries(dh->first);
3656 dh->first = NULL;
3657 dh->last = &dh->first;
3658 dh->len = 0;
3659 dh->error = 0;
3660 dh->needlen = size;
3661 dh->filled = 0;
3662 dh->req = req;
3663 fuse_prepare_interrupt(f, req, &d);
3664 err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags);
3665 fuse_finish_interrupt(f, req, &d);
3666 dh->req = NULL;
3667 if (!err)
3668 err = dh->error;
3669 if (err)
3670 dh->filled = 0;
3671 free_path(f, ino, path);
3672 }
3673 return err;
3674}
3675
3676static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh,
3677 off_t off, enum fuse_readdir_flags flags)
3678{
3679 off_t pos;
3680 struct fuse_direntry *de = dh->first;
3681 int res;
3682
3683 dh->len = 0;
3684
3685 if (extend_contents(dh, dh->needlen) == -1)
3686 return dh->error;
3687
3688 for (pos = 0; pos < off; pos++) {
3689 if (!de)
3690 break;
3691
3692 de = de->next;
3693 }
3694 while (de) {
3695 char *p = dh->contents + dh->len;
3696 unsigned rem = dh->needlen - dh->len;
3697 unsigned thislen;
3698 unsigned newlen;
3699 pos++;
3700
3701 if (flags & FUSE_READDIR_PLUS) {
3702 struct fuse_entry_param e = {
3703 .ino = 0,
3704 .attr = de->stat,
3705 };
3706
3707 if (de->flags & FUSE_FILL_DIR_PLUS &&
3708 !is_dot_or_dotdot(de->name)) {
3709 res = do_lookup(dh->fuse, dh->nodeid,
3710 de->name, &e);
3711 if (res) {
3712 dh->error = res;
3713 return 1;
3714 }
3715 }
3716
3717 thislen = fuse_add_direntry_plus(req, p, rem,
3718 de->name, &e, pos);
3719 } else {
3720 thislen = fuse_add_direntry(req, p, rem,
3721 de->name, &de->stat, pos);
3722 }
3723 newlen = dh->len + thislen;
3724 if (newlen > dh->needlen)
3725 break;
3726 dh->len = newlen;
3727 de = de->next;
3728 }
3729 return 0;
3730}
3731
3732static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size,
3733 off_t off, struct fuse_file_info *llfi,
3734 enum fuse_readdir_flags flags)
3735{
3736 struct fuse *f = req_fuse_prepare(req);
3737 struct fuse_file_info fi;
3738 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3739 int err;
3740
3741 pthread_mutex_lock(&dh->lock);
3742 /* According to SUS, directory contents need to be refreshed on
3743 rewinddir() */
3744 if (!off)
3745 dh->filled = 0;
3746
3747 if (!dh->filled) {
3748 err = readdir_fill(f, req, ino, size, off, dh, &fi, flags);
3749 if (err) {
3750 reply_err(req, err);
3751 goto out;
3752 }
3753 }
3754 if (dh->filled) {
3755 dh->needlen = size;
3756 err = readdir_fill_from_list(req, dh, off, flags);
3757 if (err) {
3758 reply_err(req, err);
3759 goto out;
3760 }
3761 }
3762 fuse_reply_buf(req, dh->contents, dh->len);
3763out:
3764 pthread_mutex_unlock(&dh->lock);
3765}
3766
3767static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
3768 off_t off, struct fuse_file_info *llfi)
3769{
3770 fuse_readdir_common(req, ino, size, off, llfi, 0);
3771}
3772
3773static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
3774 off_t off, struct fuse_file_info *llfi)
3775{
3776 fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS);
3777}
3778
3779static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
3780 struct fuse_file_info *llfi)
3781{
3782 struct fuse *f = req_fuse_prepare(req);
3783 struct fuse_intr_data d;
3784 struct fuse_file_info fi;
3785 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3786 char *path;
3787
3788 get_path_nullok(f, ino, &path);
3789
3790 fuse_prepare_interrupt(f, req, &d);
3791 fuse_fs_releasedir(f->fs, path, &fi);
3792 fuse_finish_interrupt(f, req, &d);
3793 free_path(f, ino, path);
3794
3795 pthread_mutex_lock(&dh->lock);
3796 pthread_mutex_unlock(&dh->lock);
3797 pthread_mutex_destroy(&dh->lock);
3798 free_direntries(dh->first);
3799 free(dh->contents);
3800 free(dh);
3801 reply_err(req, 0);
3802}
3803
3804static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
3805 struct fuse_file_info *llfi)
3806{
3807 struct fuse *f = req_fuse_prepare(req);
3808 struct fuse_file_info fi;
3809 char *path;
3810 int err;
3811
3812 get_dirhandle(llfi, &fi);
3813
3814 err = get_path_nullok(f, ino, &path);
3815 if (!err) {
3816 struct fuse_intr_data d;
3817 fuse_prepare_interrupt(f, req, &d);
3818 err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi);
3819 fuse_finish_interrupt(f, req, &d);
3820 free_path(f, ino, path);
3821 }
3822 reply_err(req, err);
3823}
3824
3825static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
3826{
3827 struct fuse *f = req_fuse_prepare(req);
3828 struct statvfs buf;
3829 char *path = NULL;
3830 int err = 0;
3831
3832 memset(&buf, 0, sizeof(buf));
3833 if (ino)
3834 err = get_path(f, ino, &path);
3835
3836 if (!err) {
3837 struct fuse_intr_data d;
3838 fuse_prepare_interrupt(f, req, &d);
3839 err = fuse_fs_statfs(f->fs, path ? path : "/", &buf);
3840 fuse_finish_interrupt(f, req, &d);
3841 free_path(f, ino, path);
3842 }
3843
3844 if (!err)
3845 fuse_reply_statfs(req, &buf);
3846 else
3847 reply_err(req, err);
3848}
3849
3850static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3851 const char *value, size_t size, int flags)
3852{
3853 struct fuse *f = req_fuse_prepare(req);
3854 char *path;
3855 int err;
3856
3857 err = get_path(f, ino, &path);
3858 if (!err) {
3859 struct fuse_intr_data d;
3860 fuse_prepare_interrupt(f, req, &d);
3861 err = fuse_fs_setxattr(f->fs, path, name, value, size, flags);
3862 fuse_finish_interrupt(f, req, &d);
3863 free_path(f, ino, path);
3864 }
3865 reply_err(req, err);
3866}
3867
3868static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3869 const char *name, char *value, size_t size)
3870{
3871 int err;
3872 char *path;
3873
3874 err = get_path(f, ino, &path);
3875 if (!err) {
3876 struct fuse_intr_data d;
3877 fuse_prepare_interrupt(f, req, &d);
3878 err = fuse_fs_getxattr(f->fs, path, name, value, size);
3879 fuse_finish_interrupt(f, req, &d);
3880 free_path(f, ino, path);
3881 }
3882 return err;
3883}
3884
3885static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3886 size_t size)
3887{
3888 struct fuse *f = req_fuse_prepare(req);
3889 int res;
3890
3891 if (size) {
3892 char *value = (char *) malloc(size);
3893 if (value == NULL) {
3894 reply_err(req, -ENOMEM);
3895 return;
3896 }
3897 res = common_getxattr(f, req, ino, name, value, size);
3898 if (res > 0)
3899 fuse_reply_buf(req, value, res);
3900 else
3901 reply_err(req, res);
3902 free(value);
3903 } else {
3904 res = common_getxattr(f, req, ino, name, NULL, 0);
3905 if (res >= 0)
3906 fuse_reply_xattr(req, res);
3907 else
3908 reply_err(req, res);
3909 }
3910}
3911
3912static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3913 char *list, size_t size)
3914{
3915 char *path;
3916 int err;
3917
3918 err = get_path(f, ino, &path);
3919 if (!err) {
3920 struct fuse_intr_data d;
3921 fuse_prepare_interrupt(f, req, &d);
3922 err = fuse_fs_listxattr(f->fs, path, list, size);
3923 fuse_finish_interrupt(f, req, &d);
3924 free_path(f, ino, path);
3925 }
3926 return err;
3927}
3928
3929static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
3930{
3931 struct fuse *f = req_fuse_prepare(req);
3932 int res;
3933
3934 if (size) {
3935 char *list = (char *) malloc(size);
3936 if (list == NULL) {
3937 reply_err(req, -ENOMEM);
3938 return;
3939 }
3940 res = common_listxattr(f, req, ino, list, size);
3941 if (res > 0)
3942 fuse_reply_buf(req, list, res);
3943 else
3944 reply_err(req, res);
3945 free(list);
3946 } else {
3947 res = common_listxattr(f, req, ino, NULL, 0);
3948 if (res >= 0)
3949 fuse_reply_xattr(req, res);
3950 else
3951 reply_err(req, res);
3952 }
3953}
3954
3955static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
3956 const char *name)
3957{
3958 struct fuse *f = req_fuse_prepare(req);
3959 char *path;
3960 int err;
3961
3962 err = get_path(f, ino, &path);
3963 if (!err) {
3964 struct fuse_intr_data d;
3965 fuse_prepare_interrupt(f, req, &d);
3966 err = fuse_fs_removexattr(f->fs, path, name);
3967 fuse_finish_interrupt(f, req, &d);
3968 free_path(f, ino, path);
3969 }
3970 reply_err(req, err);
3971}
3972
3973static struct lock *locks_conflict(struct node *node, const struct lock *lock)
3974{
3975 struct lock *l;
3976
3977 for (l = node->locks; l; l = l->next)
3978 if (l->owner != lock->owner &&
3979 lock->start <= l->end && l->start <= lock->end &&
3980 (l->type == F_WRLCK || lock->type == F_WRLCK))
3981 break;
3982
3983 return l;
3984}
3985
3986static void delete_lock(struct lock **lockp)
3987{
3988 struct lock *l = *lockp;
3989 *lockp = l->next;
3990 free(l);
3991}
3992
3993static void insert_lock(struct lock **pos, struct lock *lock)
3994{
3995 lock->next = *pos;
3996 *pos = lock;
3997}
3998
3999static int locks_insert(struct node *node, struct lock *lock)
4000{
4001 struct lock **lp;
4002 struct lock *newl1 = NULL;
4003 struct lock *newl2 = NULL;
4004
4005 if (lock->type != F_UNLCK || lock->start != 0 ||
4006 lock->end != OFFSET_MAX) {
4007 newl1 = malloc(sizeof(struct lock));
4008 newl2 = malloc(sizeof(struct lock));
4009
4010 if (!newl1 || !newl2) {
4011 free(newl1);
4012 free(newl2);
4013 return -ENOLCK;
4014 }
4015 }
4016
4017 for (lp = &node->locks; *lp;) {
4018 struct lock *l = *lp;
4019 if (l->owner != lock->owner)
4020 goto skip;
4021
4022 if (lock->type == l->type) {
4023 if (l->end < lock->start - 1)
4024 goto skip;
4025 if (lock->end < l->start - 1)
4026 break;
4027 if (l->start <= lock->start && lock->end <= l->end)
4028 goto out;
4029 if (l->start < lock->start)
4030 lock->start = l->start;
4031 if (lock->end < l->end)
4032 lock->end = l->end;
4033 goto delete;
4034 } else {
4035 if (l->end < lock->start)
4036 goto skip;
4037 if (lock->end < l->start)
4038 break;
4039 if (lock->start <= l->start && l->end <= lock->end)
4040 goto delete;
4041 if (l->end <= lock->end) {
4042 l->end = lock->start - 1;
4043 goto skip;
4044 }
4045 if (lock->start <= l->start) {
4046 l->start = lock->end + 1;
4047 break;
4048 }
4049 *newl2 = *l;
4050 newl2->start = lock->end + 1;
4051 l->end = lock->start - 1;
4052 insert_lock(&l->next, newl2);
4053 newl2 = NULL;
4054 }
4055 skip:
4056 lp = &l->next;
4057 continue;
4058
4059 delete:
4060 delete_lock(lp);
4061 }
4062 if (lock->type != F_UNLCK) {
4063 *newl1 = *lock;
4064 insert_lock(lp, newl1);
4065 newl1 = NULL;
4066 }
4067out:
4068 free(newl1);
4069 free(newl2);
4070 return 0;
4071}
4072
4073static void flock_to_lock(struct flock *flock, struct lock *lock)
4074{
4075 memset(lock, 0, sizeof(struct lock));
4076 lock->type = flock->l_type;
4077 lock->start = flock->l_start;
4078 lock->end =
4079 flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
4080 lock->pid = flock->l_pid;
4081}
4082
4083static void lock_to_flock(struct lock *lock, struct flock *flock)
4084{
4085 flock->l_type = lock->type;
4086 flock->l_start = lock->start;
4087 flock->l_len =
4088 (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
4089 flock->l_pid = lock->pid;
4090}
4091
4092static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
4093 const char *path, struct fuse_file_info *fi)
4094{
4095 struct fuse_intr_data d;
4096 struct flock lock;
4097 struct lock l;
4098 int err;
4099 int errlock;
4100
4101 fuse_prepare_interrupt(f, req, &d);
4102 memset(&lock, 0, sizeof(lock));
4103 lock.l_type = F_UNLCK;
4104 lock.l_whence = SEEK_SET;
4105 err = fuse_fs_flush(f->fs, path, fi);
4106 errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock);
4107 fuse_finish_interrupt(f, req, &d);
4108
4109 if (errlock != -ENOSYS) {
4110 flock_to_lock(&lock, &l);
4111 l.owner = fi->lock_owner;
4112 pthread_mutex_lock(&f->lock);
4113 locks_insert(get_node(f, ino), &l);
4114 pthread_mutex_unlock(&f->lock);
4115
4116 /* if op.lock() is defined FLUSH is needed regardless
4117 of op.flush() */
4118 if (err == -ENOSYS)
4119 err = 0;
4120 }
4121 return err;
4122}
4123
4124static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino,
4125 struct fuse_file_info *fi)
4126{
4127 struct fuse *f = req_fuse_prepare(req);
4128 struct fuse_intr_data d;
4129 char *path;
4130 int err = 0;
4131
4132 get_path_nullok(f, ino, &path);
4133 if (fi->flush) {
4134 err = fuse_flush_common(f, req, ino, path, fi);
4135 if (err == -ENOSYS)
4136 err = 0;
4137 }
4138
4139 fuse_prepare_interrupt(f, req, &d);
4140 fuse_do_release(f, ino, path, fi);
4141 fuse_finish_interrupt(f, req, &d);
4142 free_path(f, ino, path);
4143
4144 reply_err(req, err);
4145}
4146
4147static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino,
4148 struct fuse_file_info *fi)
4149{
4150 struct fuse *f = req_fuse_prepare(req);
4151 char *path;
4152 int err;
4153
4154 get_path_nullok(f, ino, &path);
4155 err = fuse_flush_common(f, req, ino, path, fi);
4156 free_path(f, ino, path);
4157
4158 reply_err(req, err);
4159}
4160
4161static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
4162 struct fuse_file_info *fi, struct flock *lock,
4163 int cmd)
4164{
4165 struct fuse *f = req_fuse_prepare(req);
4166 char *path;
4167 int err;
4168
4169 err = get_path_nullok(f, ino, &path);
4170 if (!err) {
4171 struct fuse_intr_data d;
4172 fuse_prepare_interrupt(f, req, &d);
4173 err = fuse_fs_lock(f->fs, path, fi, cmd, lock);
4174 fuse_finish_interrupt(f, req, &d);
4175 free_path(f, ino, path);
4176 }
4177 return err;
4178}
4179
4180static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino,
4181 struct fuse_file_info *fi, struct flock *lock)
4182{
4183 int err;
4184 struct lock l;
4185 struct lock *conflict;
4186 struct fuse *f = req_fuse(req);
4187
4188 flock_to_lock(lock, &l);
4189 l.owner = fi->lock_owner;
4190 pthread_mutex_lock(&f->lock);
4191 conflict = locks_conflict(get_node(f, ino), &l);
4192 if (conflict)
4193 lock_to_flock(conflict, lock);
4194 pthread_mutex_unlock(&f->lock);
4195 if (!conflict)
4196 err = fuse_lock_common(req, ino, fi, lock, F_GETLK);
4197 else
4198 err = 0;
4199
4200 if (!err)
4201 fuse_reply_lock(req, lock);
4202 else
4203 reply_err(req, err);
4204}
4205
4206static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino,
4207 struct fuse_file_info *fi, struct flock *lock,
4208 int sleep)
4209{
4210 int err = fuse_lock_common(req, ino, fi, lock,
4211 sleep ? F_SETLKW : F_SETLK);
4212 if (!err) {
4213 struct fuse *f = req_fuse(req);
4214 struct lock l;
4215 flock_to_lock(lock, &l);
4216 l.owner = fi->lock_owner;
4217 pthread_mutex_lock(&f->lock);
4218 locks_insert(get_node(f, ino), &l);
4219 pthread_mutex_unlock(&f->lock);
4220 }
4221 reply_err(req, err);
4222}
4223
4224static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino,
4225 struct fuse_file_info *fi, int op)
4226{
4227 struct fuse *f = req_fuse_prepare(req);
4228 char *path;
4229 int err;
4230
4231 err = get_path_nullok(f, ino, &path);
4232 if (err == 0) {
4233 struct fuse_intr_data d;
4234 fuse_prepare_interrupt(f, req, &d);
4235 err = fuse_fs_flock(f->fs, path, fi, op);
4236 fuse_finish_interrupt(f, req, &d);
4237 free_path(f, ino, path);
4238 }
4239 reply_err(req, err);
4240}
4241
4242static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
4243 uint64_t idx)
4244{
4245 struct fuse *f = req_fuse_prepare(req);
4246 struct fuse_intr_data d;
4247 char *path;
4248 int err;
4249
4250 err = get_path(f, ino, &path);
4251 if (!err) {
4252 fuse_prepare_interrupt(f, req, &d);
4253 err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
4254 fuse_finish_interrupt(f, req, &d);
4255 free_path(f, ino, path);
4256 }
4257 if (!err)
4258 fuse_reply_bmap(req, idx);
4259 else
4260 reply_err(req, err);
4261}
4262
4263static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
4264 void *arg, struct fuse_file_info *llfi,
4265 unsigned int flags, const void *in_buf,
4266 size_t in_bufsz, size_t out_bufsz)
4267{
4268 struct fuse *f = req_fuse_prepare(req);
4269 struct fuse_intr_data d;
4270 struct fuse_file_info fi;
4271 char *path, *out_buf = NULL;
4272 int err;
4273
4274 err = -EPERM;
4275 if (flags & FUSE_IOCTL_UNRESTRICTED)
4276 goto err;
4277
4278 if (flags & FUSE_IOCTL_DIR)
4279 get_dirhandle(llfi, &fi);
4280 else
4281 fi = *llfi;
4282
4283 if (out_bufsz) {
4284 err = -ENOMEM;
4285 out_buf = malloc(out_bufsz);
4286 if (!out_buf)
4287 goto err;
4288 }
4289
4290 assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
4291 if (out_buf && in_bufsz)
4292 memcpy(out_buf, in_buf, in_bufsz);
4293
4294 err = get_path_nullok(f, ino, &path);
4295 if (err)
4296 goto err;
4297
4298 fuse_prepare_interrupt(f, req, &d);
4299
4300 err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags,
4301 out_buf ? out_buf : (void *)in_buf);
4302
4303 fuse_finish_interrupt(f, req, &d);
4304 free_path(f, ino, path);
4305
4306 if (err < 0)
4307 goto err;
4308 fuse_reply_ioctl(req, err, out_buf, out_bufsz);
4309 goto out;
4310err:
4311 reply_err(req, err);
4312out:
4313 free(out_buf);
4314}
4315
4316static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
4317 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
4318{
4319 struct fuse *f = req_fuse_prepare(req);
4320 struct fuse_intr_data d;
4321 char *path;
4322 int err;
4323 unsigned revents = 0;
4324
4325 err = get_path_nullok(f, ino, &path);
4326 if (!err) {
4327 fuse_prepare_interrupt(f, req, &d);
4328 err = fuse_fs_poll(f->fs, path, fi, ph, &revents);
4329 fuse_finish_interrupt(f, req, &d);
4330 free_path(f, ino, path);
4331 }
4332 if (!err)
4333 fuse_reply_poll(req, revents);
4334 else
4335 reply_err(req, err);
4336}
4337
4338static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
4339 off_t offset, off_t length, struct fuse_file_info *fi)
4340{
4341 struct fuse *f = req_fuse_prepare(req);
4342 struct fuse_intr_data d;
4343 char *path;
4344 int err;
4345
4346 err = get_path_nullok(f, ino, &path);
4347 if (!err) {
4348 fuse_prepare_interrupt(f, req, &d);
4349 err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi);
4350 fuse_finish_interrupt(f, req, &d);
4351 free_path(f, ino, path);
4352 }
4353 reply_err(req, err);
4354}
4355
4356static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
4357 off_t off_in, struct fuse_file_info *fi_in,
4358 fuse_ino_t nodeid_out, off_t off_out,
4359 struct fuse_file_info *fi_out, size_t len,
4360 int flags)
4361{
4362 struct fuse *f = req_fuse_prepare(req);
4363 struct fuse_intr_data d;
4364 char *path_in, *path_out;
4365 int err;
4366 ssize_t res;
4367
4368 err = get_path_nullok(f, nodeid_in, &path_in);
4369 if (err) {
4370 reply_err(req, err);
4371 return;
4372 }
4373
4374 err = get_path_nullok(f, nodeid_out, &path_out);
4375 if (err) {
4376 free_path(f, nodeid_in, path_in);
4377 reply_err(req, err);
4378 return;
4379 }
4380
4381 fuse_prepare_interrupt(f, req, &d);
4382 res = fuse_fs_copy_file_range(f->fs, path_in, fi_in, off_in, path_out,
4383 fi_out, off_out, len, flags);
4384 fuse_finish_interrupt(f, req, &d);
4385
4386 if (res >= 0)
4387 fuse_reply_write(req, res);
4388 else
4389 reply_err(req, res);
4390
4391 free_path(f, nodeid_in, path_in);
4392 free_path(f, nodeid_out, path_out);
4393}
4394
4395static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
4396 struct fuse_file_info *fi)
4397{
4398 struct fuse *f = req_fuse_prepare(req);
4399 struct fuse_intr_data d;
4400 char *path;
4401 int err;
4402 off_t res;
4403
4404 err = get_path(f, ino, &path);
4405 if (err) {
4406 reply_err(req, err);
4407 return;
4408 }
4409
4410 fuse_prepare_interrupt(f, req, &d);
4411 res = fuse_fs_lseek(f->fs, path, off, whence, fi);
4412 fuse_finish_interrupt(f, req, &d);
4413 free_path(f, ino, path);
4414 if (res >= 0)
4415 fuse_reply_lseek(req, res);
4416 else
4417 reply_err(req, res);
4418}
4419
4420#ifdef HAVE_STATX
4421static void fuse_lib_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
4422 struct fuse_file_info *fi)
4423{
4424 struct fuse *f = req_fuse_prepare(req);
4425 struct statx stxbuf;
4426 char *path;
4427 int err;
4428
4429 memset(&stxbuf, 0, sizeof(stxbuf));
4430
4431 if (fi != NULL)
4432 err = get_path_nullok(f, ino, &path);
4433 else
4434 err = get_path(f, ino, &path);
4435
4436 if (!err) {
4437 struct fuse_intr_data d;
4438
4439 if (!path)
4440 flags |= AT_EMPTY_PATH;
4441 fuse_prepare_interrupt(f, req, &d);
4442 err = fuse_fs_statx(f->fs, path, flags, mask, &stxbuf, fi);
4443 fuse_finish_interrupt(f, req, &d);
4444 free_path(f, ino, path);
4445 }
4446 if (!err) {
4447 struct node *node;
4448
4449 pthread_mutex_lock(&f->lock);
4450 node = get_node(f, ino);
4451 if (node->is_hidden && stxbuf.stx_nlink > 0)
4452 stxbuf.stx_nlink--;
4453 if (f->conf.auto_cache) {
4454 struct stat stbuf;
4455
4456 stbuf.st_mtime = stxbuf.stx_mtime.tv_nsec;
4457 ST_MTIM_NSEC(&stbuf) = stxbuf.stx_mtime.tv_nsec;
4458 stbuf.st_size = stxbuf.stx_size;
4459 update_stat(node, &stbuf);
4460 }
4461 pthread_mutex_unlock(&f->lock);
4462 set_statx(f, ino, &stxbuf);
4463 fuse_reply_statx(req, 0, &stxbuf, f->conf.attr_timeout);
4464 } else
4465 reply_err(req, err);
4466}
4467#endif
4468
4469static int clean_delay(struct fuse *f)
4470{
4471 /*
4472 * This is calculating the delay between clean runs. To
4473 * reduce the number of cleans we are doing them 10 times
4474 * within the remember window.
4475 */
4476 int min_sleep = 60;
4477 int max_sleep = 3600;
4478 int sleep_time = f->conf.remember / 10;
4479
4480 if (sleep_time > max_sleep)
4481 return max_sleep;
4482 if (sleep_time < min_sleep)
4483 return min_sleep;
4484 return sleep_time;
4485}
4486
4487int fuse_clean_cache(struct fuse *f)
4488{
4489 struct node_lru *lnode;
4490 struct list_head *curr, *next;
4491 struct node *node;
4492 struct timespec now;
4493
4494 pthread_mutex_lock(&f->lock);
4495
4496 curr_time(&now);
4497
4498 for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) {
4499 double age;
4500
4501 next = curr->next;
4502 lnode = list_entry(curr, struct node_lru, lru);
4503 node = &lnode->node;
4504
4505 age = diff_timespec(&now, &lnode->forget_time);
4506 if (age <= f->conf.remember)
4507 break;
4508
4509 assert(node->nlookup == 1);
4510
4511 /* Don't forget active directories */
4512 if (node->refctr > 1)
4513 continue;
4514
4515 node->nlookup = 0;
4516 unhash_name(f, node);
4517 unref_node(f, node);
4518 }
4519 pthread_mutex_unlock(&f->lock);
4520
4521 return clean_delay(f);
4522}
4523
4524static struct fuse_lowlevel_ops fuse_path_ops = {
4525 .init = fuse_lib_init,
4526 .destroy = fuse_lib_destroy,
4527 .lookup = fuse_lib_lookup,
4528 .forget = fuse_lib_forget,
4529 .forget_multi = fuse_lib_forget_multi,
4530 .getattr = fuse_lib_getattr,
4531 .setattr = fuse_lib_setattr,
4532 .access = fuse_lib_access,
4533 .readlink = fuse_lib_readlink,
4534 .mknod = fuse_lib_mknod,
4535 .mkdir = fuse_lib_mkdir,
4536 .unlink = fuse_lib_unlink,
4537 .rmdir = fuse_lib_rmdir,
4538 .symlink = fuse_lib_symlink,
4539 .rename = fuse_lib_rename,
4540 .link = fuse_lib_link,
4541 .create = fuse_lib_create,
4542 .open = fuse_lib_open,
4543 .read = fuse_lib_read,
4544 .write_buf = fuse_lib_write_buf,
4545 .flush = fuse_lib_flush,
4546 .release = fuse_lib_release,
4547 .fsync = fuse_lib_fsync,
4548 .opendir = fuse_lib_opendir,
4549 .readdir = fuse_lib_readdir,
4550 .readdirplus = fuse_lib_readdirplus,
4551 .releasedir = fuse_lib_releasedir,
4552 .fsyncdir = fuse_lib_fsyncdir,
4553 .statfs = fuse_lib_statfs,
4554 .setxattr = fuse_lib_setxattr,
4555 .getxattr = fuse_lib_getxattr,
4556 .listxattr = fuse_lib_listxattr,
4557 .removexattr = fuse_lib_removexattr,
4558 .getlk = fuse_lib_getlk,
4559 .setlk = fuse_lib_setlk,
4560 .flock = fuse_lib_flock,
4561 .bmap = fuse_lib_bmap,
4562 .ioctl = fuse_lib_ioctl,
4563 .poll = fuse_lib_poll,
4564 .fallocate = fuse_lib_fallocate,
4565 .copy_file_range = fuse_lib_copy_file_range,
4566 .lseek = fuse_lib_lseek,
4567#ifdef HAVE_STATX
4568 .statx = fuse_lib_statx,
4569#endif
4570};
4571
4572int fuse_notify_poll(struct fuse_pollhandle *ph)
4573{
4574 return fuse_lowlevel_notify_poll(ph);
4575}
4576
4577struct fuse_session *fuse_get_session(struct fuse *f)
4578{
4579 return f->se;
4580}
4581
4582static int fuse_session_loop_remember(struct fuse *f)
4583{
4584 struct fuse_session *se = f->se;
4585 int res = 0;
4586 struct timespec now;
4587 time_t next_clean;
4588 struct pollfd fds = {
4589 .fd = se->fd,
4590 .events = POLLIN
4591 };
4592 struct fuse_buf fbuf = {
4593 .mem = NULL,
4594 };
4595
4596 curr_time(&now);
4597 next_clean = now.tv_sec;
4598 while (!fuse_session_exited(se)) {
4599 unsigned timeout;
4600
4601 curr_time(&now);
4602 if (now.tv_sec < next_clean)
4603 timeout = next_clean - now.tv_sec;
4604 else
4605 timeout = 0;
4606
4607 res = poll(&fds, 1, timeout * 1000);
4608 if (res == -1) {
4609 if (errno == EINTR)
4610 continue;
4611 else
4612 break;
4613 } else if (res > 0) {
4614 res = fuse_session_receive_buf_internal(se, &fbuf,
4615 NULL);
4616 if (res == -EINTR)
4617 continue;
4618 if (res <= 0)
4619 break;
4620
4621 fuse_session_process_buf_internal(se, &fbuf, NULL);
4622 } else {
4623 timeout = fuse_clean_cache(f);
4624 curr_time(&now);
4625 next_clean = now.tv_sec + timeout;
4626 }
4627 }
4628
4629 fuse_buf_free(&fbuf);
4630 return res < 0 ? -1 : 0;
4631}
4632
4633int fuse_loop(struct fuse *f)
4634{
4635 if (!f)
4636 return -1;
4637
4638 if (lru_enabled(f))
4639 return fuse_session_loop_remember(f);
4640
4641 return fuse_session_loop(f->se);
4642}
4643
4644FUSE_SYMVER("fuse_loop_mt_312", "fuse_loop_mt@@FUSE_3.12")
4645int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config)
4646{
4647 if (f == NULL)
4648 return -1;
4649
4650 int res = fuse_start_cleanup_thread(f);
4651 if (res)
4652 return -1;
4653
4654 res = fuse_session_loop_mt_312(fuse_get_session(f), config);
4656 return res;
4657}
4658
4659int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1);
4660FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@FUSE_3.2")
4661int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1)
4662{
4663 struct fuse_loop_config *config = fuse_loop_cfg_create();
4664 if (config == NULL)
4665 return ENOMEM;
4666
4667 fuse_loop_cfg_convert(config, config_v1);
4668
4669 int res = fuse_loop_mt_312(f, config);
4670
4671 fuse_loop_cfg_destroy(config);
4672
4673 return res;
4674}
4675
4676int fuse_loop_mt_31(struct fuse *f, int clone_fd);
4677FUSE_SYMVER("fuse_loop_mt_31", "fuse_loop_mt@FUSE_3.0")
4678int fuse_loop_mt_31(struct fuse *f, int clone_fd)
4679{
4680 int err;
4681 struct fuse_loop_config *config = fuse_loop_cfg_create();
4682
4683 if (config == NULL)
4684 return ENOMEM;
4685
4686 fuse_loop_cfg_set_clone_fd(config, clone_fd);
4687
4688 err = fuse_loop_mt_312(f, config);
4689
4690 fuse_loop_cfg_destroy(config);
4691
4692 return err;
4693}
4694
4695void fuse_exit(struct fuse *f)
4696{
4697 fuse_session_exit(f->se);
4698}
4699
4700struct fuse_context *fuse_get_context(void)
4701{
4702 struct fuse_context_i *c = fuse_get_context_internal();
4703
4704 if (c)
4705 return &c->ctx;
4706 else
4707 return NULL;
4708}
4709
4710int fuse_getgroups(int size, gid_t list[])
4711{
4712 struct fuse_context_i *c = fuse_get_context_internal();
4713 if (!c)
4714 return -EINVAL;
4715
4716 return fuse_req_getgroups(c->req, size, list);
4717}
4718
4719int fuse_interrupted(void)
4720{
4721 struct fuse_context_i *c = fuse_get_context_internal();
4722
4723 if (c)
4724 return fuse_req_interrupted(c->req);
4725 else
4726 return 0;
4727}
4728
4729int fuse_invalidate_path(struct fuse *f, const char *path) {
4730 fuse_ino_t ino;
4731 int err = lookup_path_in_cache(f, path, &ino);
4732 if (err) {
4733 return err;
4734 }
4735
4736 return fuse_lowlevel_notify_inval_inode(f->se, ino, 0, 0);
4737}
4738
4739#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
4740
4741static const struct fuse_opt fuse_lib_opts[] = {
4744 FUSE_LIB_OPT("debug", debug, 1),
4745 FUSE_LIB_OPT("-d", debug, 1),
4746 FUSE_LIB_OPT("kernel_cache", kernel_cache, 1),
4747 FUSE_LIB_OPT("auto_cache", auto_cache, 1),
4748 FUSE_LIB_OPT("noauto_cache", auto_cache, 0),
4749 FUSE_LIB_OPT("no_rofd_flush", no_rofd_flush, 1),
4750 FUSE_LIB_OPT("umask=", set_mode, 1),
4751 FUSE_LIB_OPT("umask=%o", umask, 0),
4752 FUSE_LIB_OPT("fmask=", set_mode, 1),
4753 FUSE_LIB_OPT("fmask=%o", fmask, 0),
4754 FUSE_LIB_OPT("dmask=", set_mode, 1),
4755 FUSE_LIB_OPT("dmask=%o", dmask, 0),
4756 FUSE_LIB_OPT("uid=", set_uid, 1),
4757 FUSE_LIB_OPT("uid=%d", uid, 0),
4758 FUSE_LIB_OPT("gid=", set_gid, 1),
4759 FUSE_LIB_OPT("gid=%d", gid, 0),
4760 FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0),
4761 FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0),
4762 FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0),
4763 FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1),
4764 FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0),
4765 FUSE_LIB_OPT("noforget", remember, -1),
4766 FUSE_LIB_OPT("remember=%u", remember, 0),
4767 FUSE_LIB_OPT("modules=%s", modules, 0),
4768 FUSE_LIB_OPT("parallel_direct_write=%d", parallel_direct_writes, 0),
4770};
4771
4772static int fuse_lib_opt_proc(void *data, const char *arg, int key,
4773 struct fuse_args *outargs)
4774{
4775 (void) arg; (void) outargs; (void) data; (void) key;
4776
4777 /* Pass through unknown options */
4778 return 1;
4779}
4780
4781
4782static const struct fuse_opt fuse_help_opts[] = {
4783 FUSE_LIB_OPT("modules=%s", modules, 1),
4784 FUSE_OPT_KEY("modules=%s", FUSE_OPT_KEY_KEEP),
4786};
4787
4788static void print_module_help(const char *name,
4790{
4791 struct fuse_args a = FUSE_ARGS_INIT(0, NULL);
4792 if (fuse_opt_add_arg(&a, "") == -1 ||
4793 fuse_opt_add_arg(&a, "-h") == -1)
4794 return;
4795 printf("\nOptions for %s module:\n", name);
4796 (*fac)(&a, NULL);
4798}
4799
4800void fuse_lib_help(struct fuse_args *args)
4801{
4802 /* These are not all options, but only the ones that
4803 may be of interest to an end-user */
4804 printf(
4805" -o kernel_cache cache files in kernel\n"
4806" -o [no]auto_cache enable caching based on modification times (off)\n"
4807" -o no_rofd_flush disable flushing of read-only fd on close (off)\n"
4808" -o umask=M set file permissions (octal)\n"
4809" -o fmask=M set file permissions (octal)\n"
4810" -o dmask=M set dir permissions (octal)\n"
4811" -o uid=N set file owner\n"
4812" -o gid=N set file group\n"
4813" -o entry_timeout=T cache timeout for names (1.0s)\n"
4814" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
4815" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
4816" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n"
4817" -o noforget never forget cached inodes\n"
4818" -o remember=T remember cached inodes for T seconds (0s)\n"
4819" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n");
4820
4821
4822 /* Print low-level help */
4824
4825 /* Print help for builtin modules */
4826 print_module_help("subdir", &fuse_module_subdir_factory);
4827#ifdef HAVE_ICONV
4828 print_module_help("iconv", &fuse_module_iconv_factory);
4829#endif
4830
4831 /* Parse command line options in case we need to
4832 activate more modules */
4833 struct fuse_config conf = { .modules = NULL };
4834 if (fuse_opt_parse(args, &conf, fuse_help_opts,
4835 fuse_lib_opt_proc) == -1
4836 || !conf.modules)
4837 return;
4838
4839 char *module;
4840 char *next;
4841 struct fuse_module *m;
4842
4843 // Iterate over all modules
4844 for (module = conf.modules; module; module = next) {
4845 char *p;
4846 for (p = module; *p && *p != ':'; p++);
4847 next = *p ? p + 1 : NULL;
4848 *p = '\0';
4849
4850 m = fuse_get_module(module);
4851 if (m)
4852 print_module_help(module, &m->factory);
4853 }
4854}
4855
4856static int fuse_init_intr_signal(int signum, int *installed)
4857{
4858 struct sigaction old_sa;
4859
4860 if (sigaction(signum, NULL, &old_sa) == -1) {
4861 perror("fuse: cannot get old signal handler");
4862 return -1;
4863 }
4864
4865 if (old_sa.sa_handler == SIG_DFL) {
4866 struct sigaction sa;
4867
4868 memset(&sa, 0, sizeof(struct sigaction));
4869 sa.sa_handler = fuse_intr_sighandler;
4870 sigemptyset(&sa.sa_mask);
4871
4872 if (sigaction(signum, &sa, NULL) == -1) {
4873 perror("fuse: cannot set interrupt signal handler");
4874 return -1;
4875 }
4876 *installed = 1;
4877 }
4878 return 0;
4879}
4880
4881static void fuse_restore_intr_signal(int signum)
4882{
4883 struct sigaction sa;
4884
4885 memset(&sa, 0, sizeof(struct sigaction));
4886 sa.sa_handler = SIG_DFL;
4887 sigaction(signum, &sa, NULL);
4888}
4889
4890
4891static int fuse_push_module(struct fuse *f, const char *module,
4892 struct fuse_args *args)
4893{
4894 struct fuse_fs *fs[2] = { f->fs, NULL };
4895 struct fuse_fs *newfs;
4896 struct fuse_module *m = fuse_get_module(module);
4897
4898 if (!m)
4899 return -1;
4900
4901 newfs = m->factory(args, fs);
4902 if (!newfs) {
4903 fuse_put_module(m);
4904 return -1;
4905 }
4906 f->fs = newfs;
4907 return 0;
4908}
4909
4910struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
4911 void *user_data)
4912{
4913 struct fuse_fs *fs;
4914
4915 if (sizeof(struct fuse_operations) < op_size) {
4916 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not not work\n");
4917 op_size = sizeof(struct fuse_operations);
4918 }
4919
4920 fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
4921 if (!fs) {
4922 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse_fs object\n");
4923 return NULL;
4924 }
4925
4926 fs->user_data = user_data;
4927 if (op)
4928 memcpy(&fs->op, op, op_size);
4929 return fs;
4930}
4931
4932static int node_table_init(struct node_table *t)
4933{
4934 t->size = NODE_TABLE_MIN_SIZE;
4935 t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
4936 if (t->array == NULL) {
4937 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
4938 return -1;
4939 }
4940 t->use = 0;
4941 t->split = 0;
4942
4943 return 0;
4944}
4945
4946static void *fuse_prune_nodes(void *fuse)
4947{
4948 struct fuse *f = fuse;
4949 int sleep_time;
4950
4951 fuse_set_thread_name("fuse_prune_nodes");
4952
4953 while(1) {
4954 sleep_time = fuse_clean_cache(f);
4955 sleep(sleep_time);
4956 }
4957 return NULL;
4958}
4959
4960int fuse_start_cleanup_thread(struct fuse *f)
4961{
4962 if (lru_enabled(f))
4963 return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
4964
4965 return 0;
4966}
4967
4968void fuse_stop_cleanup_thread(struct fuse *f)
4969{
4970 if (lru_enabled(f)) {
4971 pthread_mutex_lock(&f->lock);
4972 pthread_cancel(f->prune_thread);
4973 pthread_mutex_unlock(&f->lock);
4974 pthread_join(f->prune_thread, NULL);
4975 }
4976}
4977
4978/*
4979 * Not supposed to be called directly, but supposed to be called
4980 * through the fuse_new macro
4981 */
4982struct fuse *_fuse_new_31(struct fuse_args *args,
4983 const struct fuse_operations *op, size_t op_size,
4984 struct libfuse_version *version, void *user_data)
4985{
4986 struct fuse *f;
4987 struct node *root;
4988 struct fuse_fs *fs;
4989 struct fuse_lowlevel_ops llop = fuse_path_ops;
4990
4991 f = (struct fuse *) calloc(1, sizeof(struct fuse));
4992 if (f == NULL) {
4993 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
4994 goto out;
4995 }
4996
4997 f->conf.entry_timeout = 1.0;
4998 f->conf.attr_timeout = 1.0;
4999 f->conf.negative_timeout = 0.0;
5000 f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
5001
5002 /* Parse options */
5003 if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
5004 fuse_lib_opt_proc) == -1)
5005 goto out_free;
5006
5007 pthread_mutex_lock(&fuse_context_lock);
5008 static int builtin_modules_registered = 0;
5009 /* Have the builtin modules already been registered? */
5010 if (builtin_modules_registered == 0) {
5011 /* If not, register them. */
5012 fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
5013#ifdef HAVE_ICONV
5014 fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
5015#endif
5016 builtin_modules_registered= 1;
5017 }
5018 pthread_mutex_unlock(&fuse_context_lock);
5019
5020 if (fuse_create_context_key() == -1)
5021 goto out_free;
5022
5023 fs = fuse_fs_new(op, op_size, user_data);
5024 if (!fs)
5025 goto out_delete_context_key;
5026
5027 f->fs = fs;
5028
5029 /* Oh f**k, this is ugly! */
5030 if (!fs->op.lock) {
5031 llop.getlk = NULL;
5032 llop.setlk = NULL;
5033 }
5034
5035 f->pagesize = getpagesize();
5036 init_list_head(&f->partial_slabs);
5037 init_list_head(&f->full_slabs);
5038 init_list_head(&f->lru_table);
5039
5040 if (f->conf.modules) {
5041 char *module;
5042 char *next;
5043
5044 for (module = f->conf.modules; module; module = next) {
5045 char *p;
5046 for (p = module; *p && *p != ':'; p++);
5047 next = *p ? p + 1 : NULL;
5048 *p = '\0';
5049 if (module[0] &&
5050 fuse_push_module(f, module, args) == -1)
5051 goto out_free_fs;
5052 }
5053 }
5054
5055 if (!f->conf.ac_attr_timeout_set)
5056 f->conf.ac_attr_timeout = f->conf.attr_timeout;
5057
5058#if defined(__FreeBSD__) || defined(__NetBSD__)
5059 /*
5060 * In FreeBSD, we always use these settings as inode numbers
5061 * are needed to make getcwd(3) work.
5062 */
5063 f->conf.readdir_ino = 1;
5064#endif
5065
5066 /* not declared globally, to restrict usage of this function */
5067 struct fuse_session *fuse_session_new_versioned(
5068 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
5069 size_t op_size, struct libfuse_version *version,
5070 void *userdata);
5071 f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version,
5072 f);
5073 if (f->se == NULL)
5074 goto out_free_fs;
5075
5076 /* Trace topmost layer by default */
5077 f->fs->debug = f->conf.debug;
5078 f->ctr = 0;
5079 f->generation = 0;
5080 if (node_table_init(&f->name_table) == -1)
5081 goto out_free_session;
5082
5083 if (node_table_init(&f->id_table) == -1)
5084 goto out_free_name_table;
5085
5086 pthread_mutex_init(&f->lock, NULL);
5087
5088 root = alloc_node(f);
5089 if (root == NULL) {
5090 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
5091 goto out_free_id_table;
5092 }
5093 if (lru_enabled(f)) {
5094 struct node_lru *lnode = node_lru(root);
5095 init_list_head(&lnode->lru);
5096 }
5097
5098 strcpy(root->inline_name, "/");
5099 root->name = root->inline_name;
5100 root->parent = NULL;
5101 root->nodeid = FUSE_ROOT_ID;
5102 inc_nlookup(root);
5103 hash_id(f, root);
5104
5105 return f;
5106
5107out_free_id_table:
5108 free(f->id_table.array);
5109out_free_name_table:
5110 free(f->name_table.array);
5111out_free_session:
5112 fuse_session_destroy(f->se);
5113out_free_fs:
5114 free(f->fs);
5115 free(f->conf.modules);
5116out_delete_context_key:
5117 fuse_delete_context_key();
5118out_free:
5119 free(f);
5120out:
5121 return NULL;
5122}
5123
5124/* Emulates 3.0-style fuse_new(), which processes --help */
5125FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
5126struct fuse *_fuse_new_30(struct fuse_args *args,
5127 const struct fuse_operations *op,
5128 size_t op_size,
5129 struct libfuse_version *version,
5130 void *user_data)
5131{
5132 struct fuse_config conf = {0};
5133
5134 const struct fuse_opt opts[] = {
5135 FUSE_LIB_OPT("-h", show_help, 1),
5136 FUSE_LIB_OPT("--help", show_help, 1),
5138 };
5139
5140 if (fuse_opt_parse(args, &conf, opts,
5141 fuse_lib_opt_proc) == -1)
5142 return NULL;
5143
5144 if (conf.show_help) {
5145 fuse_lib_help(args);
5146 return NULL;
5147 } else
5148 return _fuse_new_31(args, op, op_size, version, user_data);
5149}
5150
5151/* ABI compat version */
5152struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
5153 size_t op_size, void *user_data);
5154FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
5155struct fuse *fuse_new_31(struct fuse_args *args,
5156 const struct fuse_operations *op,
5157 size_t op_size, void *user_data)
5158{
5159 /* unknown version */
5160 struct libfuse_version version = { 0 };
5161
5162 return _fuse_new_31(args, op, op_size, &version, user_data);
5163}
5164
5165/*
5166 * ABI compat version
5167 * Emulates 3.0-style fuse_new(), which processes --help
5168 */
5169struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
5170 size_t op_size, void *user_data);
5171FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
5172struct fuse *fuse_new_30(struct fuse_args *args,
5173 const struct fuse_operations *op,
5174 size_t op_size, void *user_data)
5175{
5176 struct fuse_config conf = {0};
5177
5178 const struct fuse_opt opts[] = {
5179 FUSE_LIB_OPT("-h", show_help, 1),
5180 FUSE_LIB_OPT("--help", show_help, 1),
5182 };
5183
5184 if (fuse_opt_parse(args, &conf, opts,
5185 fuse_lib_opt_proc) == -1)
5186 return NULL;
5187
5188 if (conf.show_help) {
5189 fuse_lib_help(args);
5190 return NULL;
5191 } else
5192 return fuse_new_31(args, op, op_size, user_data);
5193}
5194
5195
5196void fuse_destroy(struct fuse *f)
5197{
5198 size_t i;
5199
5200 if (f->conf.intr && f->intr_installed)
5201 fuse_restore_intr_signal(f->conf.intr_signal);
5202
5203 if (f->fs) {
5204 fuse_create_context(f);
5205
5206 for (i = 0; i < f->id_table.size; i++) {
5207 struct node *node;
5208
5209 for (node = f->id_table.array[i]; node != NULL;
5210 node = node->id_next) {
5211 if (node->is_hidden) {
5212 char *path;
5213 if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
5214 fuse_fs_unlink(f->fs, path);
5215 free(path);
5216 }
5217 }
5218 }
5219 }
5220 }
5221 for (i = 0; i < f->id_table.size; i++) {
5222 struct node *node;
5223 struct node *next;
5224
5225 for (node = f->id_table.array[i]; node != NULL; node = next) {
5226 next = node->id_next;
5227 free_node(f, node);
5228 f->id_table.use--;
5229 }
5230 }
5231 assert(list_empty(&f->partial_slabs));
5232 assert(list_empty(&f->full_slabs));
5233
5234 while (fuse_modules) {
5235 fuse_put_module(fuse_modules);
5236 }
5237 free(f->id_table.array);
5238 free(f->name_table.array);
5239 pthread_mutex_destroy(&f->lock);
5240 fuse_session_destroy(f->se);
5241 free(f->fs);
5242 free(f->conf.modules);
5243 free(f);
5244 fuse_delete_context_key();
5245}
5246
5247int fuse_mount(struct fuse *f, const char *mountpoint) {
5248 return fuse_session_mount(fuse_get_session(f), mountpoint);
5249}
5250
5251
5252void fuse_unmount(struct fuse *f) {
5254}
5255
5256int fuse_version(void)
5257{
5258 return FUSE_VERSION;
5259}
5260
5261const char *fuse_pkgversion(void)
5262{
5263 return PACKAGE_VERSION;
5264}
int fuse_getgroups(int size, gid_t list[])
Definition fuse.c:4654
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
int fuse_interrupted(void)
Definition fuse.c:4663
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_start_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4906
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
Definition fuse.h:1383
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_exit(struct fuse *f)
Definition fuse.c:4639
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4433
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4914
fuse_fill_dir_flags
Definition fuse.h:58
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_SPLICE_READ
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_IS_FD
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
int fuse_version(void)
Definition fuse.c:5206
@ FUSE_BUF_SPLICE_MOVE
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_session_exited(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_reply_xattr(fuse_req_t req, size_t count)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
enum fuse_buf_flags flags
void * mem
size_t size
struct fuse_buf buf[1]
int32_t show_help
Definition fuse.h:279
uint32_t no_interrupt
void * private_data
Definition fuse.h:874
mode_t umask
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t noflush
Definition fuse_common.h:93
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
void(* init)(void *userdata, struct fuse_conn_info *conn)
void(* setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2fuse__i_8h_source.html0000644000175000017500000011303715156613443022607 0ustar berndbernd libfuse: fuse-3.17.4/lib/fuse_i.h Source File
libfuse
fuse_i.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
7*/
8
9#include "fuse.h"
10#include "fuse_lowlevel.h"
11#include "util.h"
12
13#include <stdint.h>
14#include <stdbool.h>
15#include <errno.h>
16#include <stdatomic.h>
17
18#define MIN(a, b) \
19({ \
20 typeof(a) _a = (a); \
21 typeof(b) _b = (b); \
22 _a < _b ? _a : _b; \
23})
24
25struct mount_opts;
26
27struct fuse_req {
28 struct fuse_session *se;
29 uint64_t unique;
30 _Atomic int ref_cnt;
31 pthread_mutex_t lock;
32 struct fuse_ctx ctx;
33 struct fuse_chan *ch;
34 int interrupted;
35 unsigned int ioctl_64bit : 1;
36 union {
37 struct {
38 uint64_t unique;
39 } i;
40 struct {
42 void *data;
43 } ni;
44 } u;
45 struct fuse_req *next;
46 struct fuse_req *prev;
47};
48
49struct fuse_notify_req {
50 uint64_t unique;
51 void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
52 const void *, const struct fuse_buf *);
53 struct fuse_notify_req *next;
54 struct fuse_notify_req *prev;
55};
56
57struct fuse_session {
58 _Atomic(char *)mountpoint;
59 volatile int exited;
60 int fd;
61 struct fuse_custom_io *io;
62 struct mount_opts *mo;
63 int debug;
64 int deny_others;
65 struct fuse_lowlevel_ops op;
66 int got_init;
67 struct cuse_data *cuse_data;
68 void *userdata;
69 uid_t owner;
70 struct fuse_conn_info conn;
71 struct fuse_req list;
72 struct fuse_req interrupts;
73 pthread_mutex_t lock;
74 int got_destroy;
75 pthread_key_t pipe_key;
76 int broken_splice_nonblock;
77 uint64_t notify_ctr;
78 struct fuse_notify_req notify_list;
79 _Atomic size_t bufsize;
80 int error;
81
82 /* This is useful if any kind of ABI incompatibility is found at
83 * a later version, to 'fix' it at run time.
84 */
85 struct libfuse_version version;
86
87 /* true if reading requests from /dev/fuse are handled internally */
88 bool buf_reallocable;
89
90 /*
91 * conn->want and conn_want_ext options set by libfuse , needed
92 * to correctly convert want to want_ext
93 */
94 uint32_t conn_want;
95 uint64_t conn_want_ext;
96};
97
98struct fuse_chan {
99 pthread_mutex_t lock;
100 int ctr;
101 int fd;
102};
103
112 char *name;
113 fuse_module_factory_t factory;
114 struct fuse_module *next;
115 struct fusemod_so *so;
116 int ctr;
117};
118
127#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
128struct fuse_loop_config
129{
130 /* verififier that a correct struct was was passed. This is especially
131 * needed, as versions below (3, 12) were using a public struct
132 * (now called fuse_loop_config_v1), which was hard to extend with
133 * additional parameters, without risking that file system implementations
134 * would not have noticed and might either pass uninitialized members
135 * or even too small structs.
136 * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
137 * or 1. v2 or even higher version just need to set a value here
138 * which not conflicting and very unlikely as having been set by
139 * file system implementation.
140 */
141 int version_id;
142
147 int clone_fd;
160
166 unsigned int max_threads;
167};
168#endif
169
170/* ----------------------------------------------------------- *
171 * Channel interface (when using -o clone_fd) *
172 * ----------------------------------------------------------- */
173
180struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
181
187void fuse_chan_put(struct fuse_chan *ch);
188
189struct mount_opts *parse_mount_opts(struct fuse_args *args);
190void destroy_mount_opts(struct mount_opts *mo);
191void fuse_mount_version(void);
192unsigned get_max_read(struct mount_opts *o);
193void fuse_kern_unmount(const char *mountpoint, int fd);
194int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
195
196int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
197 int count);
198void fuse_free_req(fuse_req_t req);
199
200void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
201
202int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
203
204void fuse_buf_free(struct fuse_buf *buf);
205
206int fuse_session_receive_buf_internal(struct fuse_session *se,
207 struct fuse_buf *buf,
208 struct fuse_chan *ch);
209void fuse_session_process_buf_internal(struct fuse_session *se,
210 const struct fuse_buf *buf,
211 struct fuse_chan *ch);
212
213struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
214 size_t op_size, void *private_data);
215int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
216int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
217
223int fuse_loop_cfg_verify(struct fuse_loop_config *config);
224
225
226/*
227 * This can be changed dynamically on recent kernels through the
228 * /proc/sys/fs/fuse/max_pages_limit interface.
229 *
230 * Older kernels will always use the default value.
231 */
232#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
233#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
234
235/* room needed in buffer to accommodate header */
236#define FUSE_BUFFER_HEADER_SIZE 0x1000
237
struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
Definition fuse.h:1383
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
struct fuse_req * fuse_req_t
uint64_t fuse_ino_t
unsigned int max_threads
Definition fuse_i.h:166
int max_idle_threads
Definition fuse_i.h:159
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__i_8h_source.html0000644000175000017500000012410315156613443022601 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_i.h Source File
libfuse
fuse_i.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#ifndef LIB_FUSE_I_H_
10#define LIB_FUSE_I_H_
11
12#include "fuse.h"
13#include "fuse_lowlevel.h"
14#include "util.h"
15
16#include <pthread.h>
17#include <semaphore.h>
18#include <stdint.h>
19#include <stdbool.h>
20#include <errno.h>
21#include <stdatomic.h>
22
23#define MIN(a, b) \
24({ \
25 typeof(a) _a = (a); \
26 typeof(b) _b = (b); \
27 _a < _b ? _a : _b; \
28})
29
30struct mount_opts;
31struct fuse_ring_pool;
32
33struct fuse_req {
34 struct fuse_session *se;
35 uint64_t unique;
36 _Atomic int ref_cnt;
37 pthread_mutex_t lock;
38 struct fuse_ctx ctx;
39 struct fuse_chan *ch;
40 int interrupted;
41 struct {
42 unsigned int ioctl_64bit : 1;
43 unsigned int is_uring : 1;
44 unsigned int is_copy_file_range_64 : 1;
45 } flags;
46 union {
47 struct {
48 uint64_t unique;
49 } i;
50 struct {
52 void *data;
53 } ni;
54 } u;
55 struct fuse_req *next;
56 struct fuse_req *prev;
57};
58
59struct fuse_notify_req {
60 uint64_t unique;
61 void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
62 const void *, const struct fuse_buf *);
63 struct fuse_notify_req *next;
64 struct fuse_notify_req *prev;
65};
66
67struct fuse_session_uring {
68 bool enable;
69 unsigned int q_depth;
70 struct fuse_ring_pool *pool;
71};
72
73struct fuse_session {
74 _Atomic(char *)mountpoint;
75 int fd;
76 struct fuse_custom_io *io;
77 struct mount_opts *mo;
78 int debug;
79 int deny_others;
80 struct fuse_lowlevel_ops op;
81 int got_init;
82 struct cuse_data *cuse_data;
83 void *userdata;
84 uid_t owner;
85 struct fuse_conn_info conn;
86 struct fuse_req list;
87 struct fuse_req interrupts;
88 pthread_mutex_t lock;
89 int got_destroy;
90 pthread_key_t pipe_key;
91 int broken_splice_nonblock;
92 uint64_t notify_ctr;
93 struct fuse_notify_req notify_list;
94 _Atomic size_t bufsize;
95 int error;
96
97 /*
98 * This is useful if any kind of ABI incompatibility is found at
99 * a later version, to 'fix' it at run time.
100 */
101 struct libfuse_version version;
102
103 /* thread synchronization */
104 _Atomic bool mt_exited;
105 pthread_mutex_t mt_lock;
106 sem_t mt_finish;
107
108 /* true if reading requests from /dev/fuse are handled internally */
109 bool buf_reallocable;
110
111 /* io_uring */
112 struct fuse_session_uring uring;
113
114 /*
115 * conn->want and conn_want_ext options set by libfuse , needed
116 * to correctly convert want to want_ext
117 */
118 uint32_t conn_want;
119 uint64_t conn_want_ext;
120};
121
122struct fuse_chan {
123 pthread_mutex_t lock;
124 int ctr;
125 int fd;
126};
127
135struct fuse_module {
136 char *name;
137 fuse_module_factory_t factory;
138 struct fuse_module *next;
139 struct fusemod_so *so;
140 int ctr;
141};
142
151#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
152struct fuse_loop_config
153{
154 /* verififier that a correct struct was was passed. This is especially
155 * needed, as versions below (3, 12) were using a public struct
156 * (now called fuse_loop_config_v1), which was hard to extend with
157 * additional parameters, without risking that file system implementations
158 * would not have noticed and might either pass uninitialized members
159 * or even too small structs.
160 * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
161 * or 1. v2 or even higher version just need to set a value here
162 * which not conflicting and very unlikely as having been set by
163 * file system implementation.
164 */
165 int version_id;
166
171 int clone_fd;
184
190 unsigned int max_threads;
191};
192#endif
193
194/* ----------------------------------------------------------- *
195 * Channel interface (when using -o clone_fd) *
196 * ----------------------------------------------------------- */
197
204struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
205
211void fuse_chan_put(struct fuse_chan *ch);
212
213struct mount_opts *parse_mount_opts(struct fuse_args *args);
214void destroy_mount_opts(struct mount_opts *mo);
215void fuse_mount_version(void);
216unsigned get_max_read(struct mount_opts *o);
217void fuse_kern_unmount(const char *mountpoint, int fd);
218int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
219
220int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
221 int count);
222void fuse_free_req(fuse_req_t req);
223void list_init_req(struct fuse_req *req);
224
225void _cuse_lowlevel_init(fuse_req_t req, const fuse_ino_t nodeid,
226 const void *req_header, const void *req_payload);
227void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
228
229int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
230
231void fuse_buf_free(struct fuse_buf *buf);
232
233int fuse_session_receive_buf_internal(struct fuse_session *se,
234 struct fuse_buf *buf,
235 struct fuse_chan *ch);
236void fuse_session_process_buf_internal(struct fuse_session *se,
237 const struct fuse_buf *buf,
238 struct fuse_chan *ch);
239
240struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
241 size_t op_size, void *private_data);
242int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
243int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
244
250int fuse_loop_cfg_verify(struct fuse_loop_config *config);
251
252
253/*
254 * This can be changed dynamically on recent kernels through the
255 * /proc/sys/fs/fuse/max_pages_limit interface.
256 *
257 * Older kernels will always use the default value.
258 */
259#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
260#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
261
262/* room needed in buffer to accommodate header */
263#define FUSE_BUFFER_HEADER_SIZE 0x1000
264
265
266#endif /* LIB_FUSE_I_H_*/
struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
Definition fuse.h:1383
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
struct fuse_req * fuse_req_t
uint64_t fuse_ino_t
unsigned int max_threads
Definition fuse_i.h:166
unsigned int max_idle_threads
fuse-3.18.2/doc/html/lib_2fuse__i_8h_source.html0000644000175000017500000012372015156613443020431 0ustar berndbernd libfuse: lib/fuse_i.h Source File
libfuse
fuse_i.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#ifndef LIB_FUSE_I_H_
10#define LIB_FUSE_I_H_
11
12#include "fuse.h"
13#include "fuse_lowlevel.h"
14#include "util.h"
15
16#include <pthread.h>
17#include <semaphore.h>
18#include <stdint.h>
19#include <stdbool.h>
20#include <errno.h>
21#include <stdatomic.h>
22
23#define MIN(a, b) \
24({ \
25 typeof(a) _a = (a); \
26 typeof(b) _b = (b); \
27 _a < _b ? _a : _b; \
28})
29
30struct mount_opts;
31struct fuse_ring_pool;
32
33struct fuse_req {
34 struct fuse_session *se;
35 uint64_t unique;
36 _Atomic int ref_cnt;
37 pthread_mutex_t lock;
38 struct fuse_ctx ctx;
39 struct fuse_chan *ch;
40 int interrupted;
41 struct {
42 unsigned int ioctl_64bit : 1;
43 unsigned int is_uring : 1;
44 unsigned int is_copy_file_range_64 : 1;
45 } flags;
46 union {
47 struct {
48 uint64_t unique;
49 } i;
50 struct {
52 void *data;
53 } ni;
54 } u;
55 struct fuse_req *next;
56 struct fuse_req *prev;
57};
58
59struct fuse_notify_req {
60 uint64_t unique;
61 void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
62 const void *, const struct fuse_buf *);
63 struct fuse_notify_req *next;
64 struct fuse_notify_req *prev;
65};
66
67struct fuse_session_uring {
68 bool enable;
69 unsigned int q_depth;
70 struct fuse_ring_pool *pool;
71};
72
73struct fuse_session {
74 _Atomic(char *)mountpoint;
75 int fd;
76 struct fuse_custom_io *io;
77 struct mount_opts *mo;
78 int debug;
79 int deny_others;
80 struct fuse_lowlevel_ops op;
81 int got_init;
82 struct cuse_data *cuse_data;
83 void *userdata;
84 uid_t owner;
85 struct fuse_conn_info conn;
86 struct fuse_req list;
87 struct fuse_req interrupts;
88 pthread_mutex_t lock;
89 int got_destroy;
90 pthread_key_t pipe_key;
91 int broken_splice_nonblock;
92 uint64_t notify_ctr;
93 struct fuse_notify_req notify_list;
94 _Atomic size_t bufsize;
95 int error;
96
97 /*
98 * This is useful if any kind of ABI incompatibility is found at
99 * a later version, to 'fix' it at run time.
100 */
101 struct libfuse_version version;
102
103 /* thread synchronization */
104 _Atomic bool mt_exited;
105 pthread_mutex_t mt_lock;
106 sem_t mt_finish;
107
108 /* true if reading requests from /dev/fuse are handled internally */
109 bool buf_reallocable;
110
111 /* io_uring */
112 struct fuse_session_uring uring;
113
114 /*
115 * conn->want and conn_want_ext options set by libfuse , needed
116 * to correctly convert want to want_ext
117 */
118 uint32_t conn_want;
119 uint64_t conn_want_ext;
120};
121
122struct fuse_chan {
123 pthread_mutex_t lock;
124 int ctr;
125 int fd;
126};
127
135struct fuse_module {
136 char *name;
137 fuse_module_factory_t factory;
138 struct fuse_module *next;
139 struct fusemod_so *so;
140 int ctr;
141};
142
151#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
152struct fuse_loop_config
153{
154 /* verififier that a correct struct was was passed. This is especially
155 * needed, as versions below (3, 12) were using a public struct
156 * (now called fuse_loop_config_v1), which was hard to extend with
157 * additional parameters, without risking that file system implementations
158 * would not have noticed and might either pass uninitialized members
159 * or even too small structs.
160 * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
161 * or 1. v2 or even higher version just need to set a value here
162 * which not conflicting and very unlikely as having been set by
163 * file system implementation.
164 */
165 int version_id;
166
171 int clone_fd;
184
190 unsigned int max_threads;
191};
192#endif
193
194/* ----------------------------------------------------------- *
195 * Channel interface (when using -o clone_fd) *
196 * ----------------------------------------------------------- */
197
204struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
205
211void fuse_chan_put(struct fuse_chan *ch);
212
213struct mount_opts *parse_mount_opts(struct fuse_args *args);
214void destroy_mount_opts(struct mount_opts *mo);
215void fuse_mount_version(void);
216unsigned get_max_read(struct mount_opts *o);
217void fuse_kern_unmount(const char *mountpoint, int fd);
218int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
219
220int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
221 int count);
222void fuse_free_req(fuse_req_t req);
223void list_init_req(struct fuse_req *req);
224
225void _cuse_lowlevel_init(fuse_req_t req, const fuse_ino_t nodeid,
226 const void *req_header, const void *req_payload);
227void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
228
229int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
230
231void fuse_buf_free(struct fuse_buf *buf);
232
233int fuse_session_receive_buf_internal(struct fuse_session *se,
234 struct fuse_buf *buf,
235 struct fuse_chan *ch);
236void fuse_session_process_buf_internal(struct fuse_session *se,
237 const struct fuse_buf *buf,
238 struct fuse_chan *ch);
239
240struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
241 size_t op_size, void *private_data);
242int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
243int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
244
250int fuse_loop_cfg_verify(struct fuse_loop_config *config);
251
252
253/*
254 * This can be changed dynamically on recent kernels through the
255 * /proc/sys/fs/fuse/max_pages_limit interface.
256 *
257 * Older kernels will always use the default value.
258 */
259#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
260#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
261
262/* room needed in buffer to accommodate header */
263#define FUSE_BUFFER_HEADER_SIZE 0x1000
264
265
266#endif /* LIB_FUSE_I_H_*/
struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
Definition fuse.h:1383
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
struct fuse_req * fuse_req_t
uint64_t fuse_ino_t
unsigned int max_threads
Definition fuse_i.h:166
unsigned int max_idle_threads
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2fuse__log_8c_source.html0000644000175000017500000004723415156613443023140 0ustar berndbernd libfuse: fuse-3.17.4/lib/fuse_log.c Source File
libfuse
fuse_log.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2019 Red Hat, Inc.
4
5 Logging API.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB
9*/
10
11#include "fuse_log.h"
12
13#include <stdarg.h>
14#include <stdio.h>
15#include <stdbool.h>
16#include <syslog.h>
17
18#define MAX_SYSLOG_LINE_LEN 512
19
20static bool to_syslog = false;
21
22static void default_log_func(__attribute__((unused)) enum fuse_log_level level,
23 const char *fmt, va_list ap)
24{
25 if (to_syslog) {
26 int sys_log_level = LOG_ERR;
27
28 /*
29 * with glibc fuse_log_level has identical values as
30 * syslog levels, but we also support BSD - better we convert to
31 * be sure.
32 */
33 switch (level) {
34 case FUSE_LOG_DEBUG:
35 sys_log_level = LOG_DEBUG;
36 break;
37 case FUSE_LOG_INFO:
38 sys_log_level = LOG_INFO;
39 break;
40 case FUSE_LOG_NOTICE:
41 sys_log_level = LOG_NOTICE;
42 break;
43 case FUSE_LOG_WARNING:
44 sys_log_level = LOG_WARNING;
45 break;
46 case FUSE_LOG_ERR:
47 sys_log_level = LOG_ERR;
48 break;
49 case FUSE_LOG_CRIT:
50 sys_log_level = LOG_CRIT;
51 break;
52 case FUSE_LOG_ALERT:
53 sys_log_level = LOG_ALERT;
54 break;
55 case FUSE_LOG_EMERG:
56 sys_log_level = LOG_EMERG;
57 }
58
59 char log[MAX_SYSLOG_LINE_LEN];
60 vsnprintf(log, MAX_SYSLOG_LINE_LEN, fmt, ap);
61 syslog(sys_log_level, "%s", log);
62 } else {
63 vfprintf(stderr, fmt, ap);
64 }
65}
66
67static fuse_log_func_t log_func = default_log_func;
68
70{
71 if (!func)
72 func = default_log_func;
73
74 log_func = func;
75}
77void fuse_log(enum fuse_log_level level, const char *fmt, ...)
78{
79 va_list ap;
80
81 va_start(ap, fmt);
82 log_func(level, fmt, ap);
83 va_end(ap);
84}
85
86void fuse_log_enable_syslog(const char *ident, int option, int facility)
87{
88 to_syslog = true;
89
90 openlog(ident, option, facility);
91}
92
94{
95 closelog();
96}
void fuse_log_close_syslog(void)
Definition fuse_log.c:93
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void(* fuse_log_func_t)(enum fuse_log_level level, const char *fmt, va_list ap)
Definition fuse_log.h:52
void fuse_log_enable_syslog(const char *ident, int option, int facility)
Definition fuse_log.c:86
fuse_log_level
Definition fuse_log.h:28
void fuse_set_log_func(fuse_log_func_t func)
Definition fuse_log.c:69
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__log_8c_source.html0000644000175000017500000003256515156613443023137 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_log.c Source File
libfuse
fuse_log.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2019 Red Hat, Inc.
4
5 Logging API.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt
9*/
10
11#include "fuse_log.h"
12
13#include <stdio.h>
14#include <stdbool.h>
15#include <syslog.h>
16#include <stdarg.h>
17
18#define MAX_SYSLOG_LINE_LEN 512
19
20static bool to_syslog = false;
21
22static void default_log_func(enum fuse_log_level level, const char *fmt, va_list ap)
23{
24 if (to_syslog)
25 vsyslog(level, fmt, ap);
26 else
27 vfprintf(stderr, fmt, ap);
28}
29
30static fuse_log_func_t log_func = default_log_func;
31
33{
34 if (!func)
35 func = default_log_func;
36
37 log_func = func;
38}
39
40void fuse_log(enum fuse_log_level level, const char *fmt, ...)
41{
42 va_list ap;
43
44 va_start(ap, fmt);
45 log_func(level, fmt, ap);
46 va_end(ap);
47}
48
49void fuse_log_enable_syslog(const char *ident, int option, int facility)
50{
51 to_syslog = true;
52
53 openlog(ident, option, facility);
54}
55
56void fuse_log_close_syslog(void)
57{
58 closelog();
59}
void fuse_log_close_syslog(void)
Definition fuse_log.c:93
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void(* fuse_log_func_t)(enum fuse_log_level level, const char *fmt, va_list ap)
Definition fuse_log.h:52
void fuse_log_enable_syslog(const char *ident, int option, int facility)
Definition fuse_log.c:86
fuse_log_level
Definition fuse_log.h:28
void fuse_set_log_func(fuse_log_func_t func)
Definition fuse_log.c:69
fuse-3.18.2/doc/html/lib_2fuse__log_8c_source.html0000644000175000017500000003240215156613443020751 0ustar berndbernd libfuse: lib/fuse_log.c Source File
libfuse
fuse_log.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2019 Red Hat, Inc.
4
5 Logging API.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt
9*/
10
11#include "fuse_log.h"
12
13#include <stdio.h>
14#include <stdbool.h>
15#include <syslog.h>
16#include <stdarg.h>
17
18#define MAX_SYSLOG_LINE_LEN 512
19
20static bool to_syslog = false;
21
22static void default_log_func(enum fuse_log_level level, const char *fmt, va_list ap)
23{
24 if (to_syslog)
25 vsyslog(level, fmt, ap);
26 else
27 vfprintf(stderr, fmt, ap);
28}
29
30static fuse_log_func_t log_func = default_log_func;
31
33{
34 if (!func)
35 func = default_log_func;
36
37 log_func = func;
38}
39
40void fuse_log(enum fuse_log_level level, const char *fmt, ...)
41{
42 va_list ap;
43
44 va_start(ap, fmt);
45 log_func(level, fmt, ap);
46 va_end(ap);
47}
48
49void fuse_log_enable_syslog(const char *ident, int option, int facility)
50{
51 to_syslog = true;
52
53 openlog(ident, option, facility);
54}
55
56void fuse_log_close_syslog(void)
57{
58 closelog();
59}
void fuse_log_close_syslog(void)
Definition fuse_log.c:93
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void(* fuse_log_func_t)(enum fuse_log_level level, const char *fmt, va_list ap)
Definition fuse_log.h:52
void fuse_log_enable_syslog(const char *ident, int option, int facility)
Definition fuse_log.c:86
fuse_log_level
Definition fuse_log.h:28
void fuse_set_log_func(fuse_log_func_t func)
Definition fuse_log.c:69
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2fuse__loop_8c_source.html0000644000175000017500000002666215156613443023332 0ustar berndbernd libfuse: fuse-3.17.4/lib/fuse_loop.c Source File
libfuse
fuse_loop.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the single-threaded FUSE session loop.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB
9*/
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_i.h"
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <errno.h>
18
19int fuse_session_loop(struct fuse_session *se)
20{
21 int res = 0;
22 struct fuse_buf fbuf = {
23 .mem = NULL,
24 };
25
26 while (!fuse_session_exited(se)) {
27 res = fuse_session_receive_buf_internal(se, &fbuf, NULL);
28
29 if (res == -EINTR)
30 continue;
31 if (res <= 0)
32 break;
33
34 fuse_session_process_buf(se, &fbuf);
35 }
36
37 fuse_buf_free(&fbuf);
38 if(res > 0)
39 /* No error, just the length of the most recently read
40 request */
41 res = 0;
42 if(se->error != 0)
43 res = se->error;
45 return res;
46}
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void fuse_session_reset(struct fuse_session *se)
void * mem
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__loop_8c_source.html0000644000175000017500000002617215156613443023324 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_loop.c Source File
libfuse
fuse_loop.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the single-threaded FUSE session loop.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt
9*/
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_i.h"
14#include "fuse_uring_i.h"
15#include <stdio.h>
16#include <stdlib.h>
17#include <errno.h>
18
19int fuse_session_loop(struct fuse_session *se)
20{
21 int res = 0;
22 struct fuse_buf fbuf = {
23 .mem = NULL,
24 };
25
26 while (!fuse_session_exited(se)) {
27 res = fuse_session_receive_buf_internal(se, &fbuf, NULL);
28
29 if (res == -EINTR)
30 continue;
31 if (res <= 0)
32 break;
33
34 fuse_session_process_buf(se, &fbuf);
35 }
36
37 fuse_buf_free(&fbuf);
38 if(res > 0)
39 /* No error, just the length of the most recently read
40 request */
41 res = 0;
42 if(se->error != 0)
43 res = se->error;
44
45 if (se->uring.pool)
46 fuse_uring_stop(se);
47 return res;
48}
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void * mem
fuse-3.18.2/doc/html/lib_2fuse__loop_8c_source.html0000644000175000017500000002600715156613443021145 0ustar berndbernd libfuse: lib/fuse_loop.c Source File
libfuse
fuse_loop.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the single-threaded FUSE session loop.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt
9*/
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_i.h"
14#include "fuse_uring_i.h"
15#include <stdio.h>
16#include <stdlib.h>
17#include <errno.h>
18
19int fuse_session_loop(struct fuse_session *se)
20{
21 int res = 0;
22 struct fuse_buf fbuf = {
23 .mem = NULL,
24 };
25
26 while (!fuse_session_exited(se)) {
27 res = fuse_session_receive_buf_internal(se, &fbuf, NULL);
28
29 if (res == -EINTR)
30 continue;
31 if (res <= 0)
32 break;
33
34 fuse_session_process_buf(se, &fbuf);
35 }
36
37 fuse_buf_free(&fbuf);
38 if(res > 0)
39 /* No error, just the length of the most recently read
40 request */
41 res = 0;
42 if(se->error != 0)
43 res = se->error;
44
45 if (se->uring.pool)
46 fuse_uring_stop(se);
47 return res;
48}
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void * mem
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2fuse__loop__mt_8c_source.html0000644000175000017500000024543215156613443024167 0ustar berndbernd libfuse: fuse-3.17.4/lib/fuse_loop_mt.c Source File
libfuse
fuse_loop_mt.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the multi-threaded FUSE session loop.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB.
9*/
10
11#define _GNU_SOURCE
12
13#include "fuse_config.h"
14#include "fuse_lowlevel.h"
15#include "fuse_misc.h"
16#include "fuse_kernel.h"
17#include "fuse_i.h"
18#include "util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24#include <signal.h>
25#include <semaphore.h>
26#include <errno.h>
27#include <sys/time.h>
28#include <sys/ioctl.h>
29#include <assert.h>
30#include <limits.h>
31
32/* Environment var controlling the thread stack size */
33#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
34
35#define FUSE_LOOP_MT_V2_IDENTIFIER INT_MAX - 2
36#define FUSE_LOOP_MT_DEF_CLONE_FD 0
37#define FUSE_LOOP_MT_DEF_MAX_THREADS 10
38#define FUSE_LOOP_MT_DEF_IDLE_THREADS -1 /* thread destruction is disabled
39 * by default */
40
41/* an arbitrary large value that cannot be valid */
42#define FUSE_LOOP_MT_MAX_THREADS (100U * 1000)
43
44struct fuse_worker {
45 struct fuse_worker *prev;
46 struct fuse_worker *next;
47 pthread_t thread_id;
48
49 // We need to include fuse_buf so that we can properly free
50 // it when a thread is terminated by pthread_cancel().
51 struct fuse_buf fbuf;
52 struct fuse_chan *ch;
53 struct fuse_mt *mt;
54};
55
56struct fuse_mt {
57 pthread_mutex_t lock;
58 int numworker;
59 int numavail;
60 struct fuse_session *se;
61 struct fuse_worker main;
62 sem_t finish;
63 int exit;
64 int error;
65 int clone_fd;
66 int max_idle;
67 int max_threads;
68};
69
70static struct fuse_chan *fuse_chan_new(int fd)
71{
72 struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
73 if (ch == NULL) {
74 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate channel\n");
75 return NULL;
76 }
77
78 memset(ch, 0, sizeof(*ch));
79 ch->fd = fd;
80 ch->ctr = 1;
81 pthread_mutex_init(&ch->lock, NULL);
82
83 return ch;
84}
85
86struct fuse_chan *fuse_chan_get(struct fuse_chan *ch)
87{
88 assert(ch->ctr > 0);
89 pthread_mutex_lock(&ch->lock);
90 ch->ctr++;
91 pthread_mutex_unlock(&ch->lock);
92
93 return ch;
94}
95
96void fuse_chan_put(struct fuse_chan *ch)
97{
98 if (ch == NULL)
99 return;
100 pthread_mutex_lock(&ch->lock);
101 ch->ctr--;
102 if (!ch->ctr) {
103 pthread_mutex_unlock(&ch->lock);
104 close(ch->fd);
105 pthread_mutex_destroy(&ch->lock);
106 free(ch);
107 } else
108 pthread_mutex_unlock(&ch->lock);
109}
110
111static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
112{
113 struct fuse_worker *prev = next->prev;
114 w->next = next;
115 w->prev = prev;
116 prev->next = w;
117 next->prev = w;
118}
119
120static void list_del_worker(struct fuse_worker *w)
121{
122 struct fuse_worker *prev = w->prev;
123 struct fuse_worker *next = w->next;
124 prev->next = next;
125 next->prev = prev;
126}
127
128static int fuse_loop_start_thread(struct fuse_mt *mt);
129
130static void *fuse_do_work(void *data)
131{
132 struct fuse_worker *w = (struct fuse_worker *) data;
133 struct fuse_mt *mt = w->mt;
134
135#ifdef HAVE_PTHREAD_SETNAME_NP
136 pthread_setname_np(pthread_self(), "fuse_worker");
137#endif
138
139 while (!fuse_session_exited(mt->se)) {
140 int isforget = 0;
141 int res;
142
143 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
144 res = fuse_session_receive_buf_internal(mt->se, &w->fbuf,
145 w->ch);
146 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
147 if (res == -EINTR)
148 continue;
149 if (res <= 0) {
150 if (res < 0) {
151 fuse_session_exit(mt->se);
152 mt->error = res;
153 }
154 break;
155 }
156
157 pthread_mutex_lock(&mt->lock);
158 if (mt->exit) {
159 pthread_mutex_unlock(&mt->lock);
160 return NULL;
161 }
162
163 /*
164 * This disgusting hack is needed so that zillions of threads
165 * are not created on a burst of FORGET messages
166 */
167 if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) {
168 struct fuse_in_header *in = w->fbuf.mem;
169
170 if (in->opcode == FUSE_FORGET ||
171 in->opcode == FUSE_BATCH_FORGET)
172 isforget = 1;
173 }
174
175 if (!isforget)
176 mt->numavail--;
177 if (mt->numavail == 0 && mt->numworker < mt->max_threads)
178 fuse_loop_start_thread(mt);
179 pthread_mutex_unlock(&mt->lock);
180
181 fuse_session_process_buf_internal(mt->se, &w->fbuf, w->ch);
182
183 pthread_mutex_lock(&mt->lock);
184 if (!isforget)
185 mt->numavail++;
186
187 /* creating and destroying threads is rather expensive - and there is
188 * not much gain from destroying existing threads. It is therefore
189 * discouraged to set max_idle to anything else than -1. If there
190 * is indeed a good reason to destruct threads it should be done
191 * delayed, a moving average might be useful for that.
192 */
193 if (mt->max_idle != -1 && mt->numavail > mt->max_idle && mt->numworker > 1) {
194 if (mt->exit) {
195 pthread_mutex_unlock(&mt->lock);
196 return NULL;
197 }
198 list_del_worker(w);
199 mt->numavail--;
200 mt->numworker--;
201 pthread_mutex_unlock(&mt->lock);
202
203 pthread_detach(w->thread_id);
204 fuse_buf_free(&w->fbuf);
205 fuse_chan_put(w->ch);
206 free(w);
207 return NULL;
208 }
209 pthread_mutex_unlock(&mt->lock);
210 }
211
212 sem_post(&mt->finish);
213
214 return NULL;
215}
216
217int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
218{
219 sigset_t oldset;
220 sigset_t newset;
221 int res;
222 pthread_attr_t attr;
223 char *stack_size;
224
225 /* Override default stack size
226 * XXX: This should ideally be a parameter option. It is rather
227 * well hidden here.
228 */
229 pthread_attr_init(&attr);
230 stack_size = getenv(ENVNAME_THREAD_STACK);
231 if (stack_size) {
232 long size;
233
234 res = libfuse_strtol(stack_size, &size);
235 if (res)
236 fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n",
237 stack_size);
238 else if (pthread_attr_setstacksize(&attr, size))
239 fuse_log(FUSE_LOG_ERR, "fuse: could not set stack size: %ld\n",
240 size);
241 }
242
243 /* Disallow signal reception in worker threads */
244 sigemptyset(&newset);
245 sigaddset(&newset, SIGTERM);
246 sigaddset(&newset, SIGINT);
247 sigaddset(&newset, SIGHUP);
248 sigaddset(&newset, SIGQUIT);
249 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
250 res = pthread_create(thread_id, &attr, func, arg);
251 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
252 pthread_attr_destroy(&attr);
253 if (res != 0) {
254 fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n",
255 strerror(res));
256 return -1;
257 }
258
259 return 0;
260}
261
262static int fuse_clone_chan_fd_default(struct fuse_session *se)
263{
264 int res;
265 int clonefd;
266 uint32_t masterfd;
267 const char *devname = "/dev/fuse";
268
269#ifndef O_CLOEXEC
270#define O_CLOEXEC 0
271#endif
272 clonefd = open(devname, O_RDWR | O_CLOEXEC);
273 if (clonefd == -1) {
274 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname,
275 strerror(errno));
276 return -1;
277 }
278 if (!O_CLOEXEC) {
279 res = fcntl(clonefd, F_SETFD, FD_CLOEXEC);
280 if (res == -1) {
281 fuse_log(FUSE_LOG_ERR, "fuse: failed to set CLOEXEC: %s\n",
282 strerror(errno));
283 close(clonefd);
284 return -1;
285 }
286 }
287
288 masterfd = se->fd;
289 res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
290 if (res == -1) {
291 fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
292 strerror(errno));
293 close(clonefd);
294 return -1;
295 }
296 return clonefd;
297}
298
299static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
300{
301 int clonefd;
302 struct fuse_session *se = mt->se;
303 struct fuse_chan *newch;
304
305 if (se->io != NULL) {
306 if (se->io->clone_fd != NULL)
307 clonefd = se->io->clone_fd(se->fd);
308 else
309 return NULL;
310 } else {
311 clonefd = fuse_clone_chan_fd_default(se);
312 }
313 if (clonefd < 0)
314 return NULL;
315
316 newch = fuse_chan_new(clonefd);
317 if (newch == NULL)
318 close(clonefd);
319
320 return newch;
321}
322
323static int fuse_loop_start_thread(struct fuse_mt *mt)
324{
325 int res;
326
327 struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
328 if (!w) {
329 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
330 return -1;
331 }
332 memset(w, 0, sizeof(struct fuse_worker));
333 w->fbuf.mem = NULL;
334 w->mt = mt;
335
336 w->ch = NULL;
337 if (mt->clone_fd) {
338 w->ch = fuse_clone_chan(mt);
339 if(!w->ch) {
340 /* Don't attempt this again */
341 fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
342 "without -o clone_fd.\n");
343 mt->clone_fd = 0;
344 }
345 }
346
347 res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
348 if (res == -1) {
349 fuse_chan_put(w->ch);
350 free(w);
351 return -1;
352 }
353 list_add_worker(w, &mt->main);
354 mt->numavail ++;
355 mt->numworker ++;
356
357 return 0;
358}
359
360static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
361{
362 pthread_join(w->thread_id, NULL);
363 pthread_mutex_lock(&mt->lock);
364 list_del_worker(w);
365 pthread_mutex_unlock(&mt->lock);
366 fuse_buf_free(&w->fbuf);
367 fuse_chan_put(w->ch);
368 free(w);
369}
370
371int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
372FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
373int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
374{
375int err;
376 struct fuse_mt mt;
377 struct fuse_worker *w;
378 int created_config = 0;
379
380 if (config) {
381 err = fuse_loop_cfg_verify(config);
382 if (err)
383 return err;
384 } else {
385 /* The caller does not care about parameters - use the default */
386 config = fuse_loop_cfg_create();
387 created_config = 1;
388 }
389
390
391 memset(&mt, 0, sizeof(struct fuse_mt));
392 mt.se = se;
393 mt.clone_fd = config->clone_fd;
394 mt.error = 0;
395 mt.numworker = 0;
396 mt.numavail = 0;
397 mt.max_idle = config->max_idle_threads;
398 mt.max_threads = config->max_threads;
399 mt.main.thread_id = pthread_self();
400 mt.main.prev = mt.main.next = &mt.main;
401 sem_init(&mt.finish, 0, 0);
402 pthread_mutex_init(&mt.lock, NULL);
403
404 pthread_mutex_lock(&mt.lock);
405 err = fuse_loop_start_thread(&mt);
406 pthread_mutex_unlock(&mt.lock);
407 if (!err) {
408 /* sem_wait() is interruptible */
409 while (!fuse_session_exited(se))
410 sem_wait(&mt.finish);
411
412 pthread_mutex_lock(&mt.lock);
413 for (w = mt.main.next; w != &mt.main; w = w->next)
414 pthread_cancel(w->thread_id);
415 mt.exit = 1;
416 pthread_mutex_unlock(&mt.lock);
417
418 while (mt.main.next != &mt.main)
419 fuse_join_worker(&mt, mt.main.next);
420
421 err = mt.error;
422 }
423
424 pthread_mutex_destroy(&mt.lock);
425 sem_destroy(&mt.finish);
426 if(se->error != 0)
427 err = se->error;
429
430 if (created_config) {
431 fuse_loop_cfg_destroy(config);
432 config = NULL;
433 }
434
435 return err;
436}
437
438int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
439FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
440int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
441{
442 int err;
443 struct fuse_loop_config *config = NULL;
444
445 if (config_v1 != NULL) {
446 /* convert the given v1 config */
447 config = fuse_loop_cfg_create();
448 if (config == NULL)
449 return ENOMEM;
450
451 fuse_loop_cfg_convert(config, config_v1);
452 }
453
454 err = fuse_session_loop_mt_312(se, config);
455
456 fuse_loop_cfg_destroy(config);
457
458 return err;
459}
460
461
462int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
463FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
464int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
465{
466 int err;
467 struct fuse_loop_config *config = fuse_loop_cfg_create();
468 if (clone_fd > 0)
469 fuse_loop_cfg_set_clone_fd(config, clone_fd);
470 err = fuse_session_loop_mt_312(se, config);
471
472 fuse_loop_cfg_destroy(config);
473
474 return err;
475}
476
477struct fuse_loop_config *fuse_loop_cfg_create(void)
478{
479 struct fuse_loop_config *config = calloc(1, sizeof(*config));
480 if (config == NULL)
481 return NULL;
482
483 config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
484 config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
485 config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
486 config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
487
488 return config;
489}
490
491void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
492{
493 free(config);
494}
495
496int fuse_loop_cfg_verify(struct fuse_loop_config *config)
497{
498 if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
499 return -EINVAL;
500
501 return 0;
502}
503
504void fuse_loop_cfg_convert(struct fuse_loop_config *config,
505 struct fuse_loop_config_v1 *v1_conf)
506{
507 fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
508
509 fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
510}
511
512void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
513 unsigned int value)
514{
515 if (value > FUSE_LOOP_MT_MAX_THREADS) {
516 if (value != UINT_MAX)
517 fuse_log(FUSE_LOG_ERR,
518 "Ignoring invalid max threads value "
519 "%u > max (%u).\n", value,
520 FUSE_LOOP_MT_MAX_THREADS);
521 return;
522 }
523 config->max_idle_threads = value;
524}
525
526void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
527 unsigned int value)
528{
529 config->max_threads = value;
530}
531
532void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
533 unsigned int value)
534{
535 config->clone_fd = value;
536}
537
@ FUSE_BUF_IS_FD
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
int fuse_session_exited(struct fuse_session *se)
void fuse_session_reset(struct fuse_session *se)
unsigned int max_threads
Definition fuse_i.h:166
unsigned int max_idle_threads
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__loop__mt_8c_source.html0000644000175000017500000024526315156613443024167 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_loop_mt.c Source File
libfuse
fuse_loop_mt.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the multi-threaded FUSE session loop.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#define _GNU_SOURCE
12
13#include "fuse_config.h"
14#include "fuse_lowlevel.h"
15#include "fuse_misc.h"
16#include "fuse_kernel.h"
17#include "fuse_i.h"
18#include "fuse_uring_i.h"
19#include "util.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25#include <signal.h>
26#include <semaphore.h>
27#include <errno.h>
28#include <sys/time.h>
29#include <sys/ioctl.h>
30#include <assert.h>
31#include <limits.h>
32
33/* Environment var controlling the thread stack size */
34#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
35
36#define FUSE_LOOP_MT_V2_IDENTIFIER INT_MAX - 2
37#define FUSE_LOOP_MT_DEF_CLONE_FD 0
38#define FUSE_LOOP_MT_DEF_MAX_THREADS 10
39#define FUSE_LOOP_MT_DEF_IDLE_THREADS -1 /* thread destruction is disabled
40 * by default */
41
42/* an arbitrary large value that cannot be valid */
43#define FUSE_LOOP_MT_MAX_THREADS (100U * 1000)
44
45struct fuse_worker {
46 struct fuse_worker *prev;
47 struct fuse_worker *next;
48 pthread_t thread_id;
49
50 // We need to include fuse_buf so that we can properly free
51 // it when a thread is terminated by pthread_cancel().
52 struct fuse_buf fbuf;
53 struct fuse_chan *ch;
54 struct fuse_mt *mt;
55};
56
57/* synchronization via se->mt_lock */
58struct fuse_mt {
59 int numworker;
60 int numavail;
61 struct fuse_session *se;
62 struct fuse_worker main;
63 int error;
64 int clone_fd;
65 int max_idle;
66 int max_threads;
67};
68
69static struct fuse_chan *fuse_chan_new(int fd)
70{
71 struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
72 if (ch == NULL) {
73 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate channel\n");
74 return NULL;
75 }
76
77 memset(ch, 0, sizeof(*ch));
78 ch->fd = fd;
79 ch->ctr = 1;
80 pthread_mutex_init(&ch->lock, NULL);
81
82 return ch;
83}
84
85struct fuse_chan *fuse_chan_get(struct fuse_chan *ch)
86{
87 assert(ch->ctr > 0);
88 pthread_mutex_lock(&ch->lock);
89 ch->ctr++;
90 pthread_mutex_unlock(&ch->lock);
91
92 return ch;
93}
94
95void fuse_chan_put(struct fuse_chan *ch)
96{
97 if (ch == NULL)
98 return;
99 pthread_mutex_lock(&ch->lock);
100 ch->ctr--;
101 if (!ch->ctr) {
102 pthread_mutex_unlock(&ch->lock);
103 close(ch->fd);
104 pthread_mutex_destroy(&ch->lock);
105 free(ch);
106 } else
107 pthread_mutex_unlock(&ch->lock);
108}
109
110static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
111{
112 struct fuse_worker *prev = next->prev;
113 w->next = next;
114 w->prev = prev;
115 prev->next = w;
116 next->prev = w;
117}
118
119static void list_del_worker(struct fuse_worker *w)
120{
121 struct fuse_worker *prev = w->prev;
122 struct fuse_worker *next = w->next;
123 prev->next = next;
124 next->prev = prev;
125}
126
127static int fuse_loop_start_thread(struct fuse_mt *mt);
128
129static void *fuse_do_work(void *data)
130{
131 struct fuse_worker *w = (struct fuse_worker *) data;
132 struct fuse_mt *mt = w->mt;
133 struct fuse_session *se = mt->se;
134
135 fuse_set_thread_name("fuse_worker");
136
137 while (!fuse_session_exited(se)) {
138 int isforget = 0;
139 int res;
140
141 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
142 res = fuse_session_receive_buf_internal(se, &w->fbuf, w->ch);
143 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
144 if (res == -EINTR)
145 continue;
146 if (res <= 0) {
147 if (res < 0) {
149 mt->error = res;
150 }
151 break;
152 }
153
154 pthread_mutex_lock(&se->mt_lock);
155 if (fuse_session_exited(se)) {
156 pthread_mutex_unlock(&se->mt_lock);
157 return NULL;
158 }
159
160 /*
161 * This disgusting hack is needed so that zillions of threads
162 * are not created on a burst of FORGET messages
163 */
164 if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) {
165 struct fuse_in_header *in = w->fbuf.mem;
166
167 if (in->opcode == FUSE_FORGET ||
168 in->opcode == FUSE_BATCH_FORGET)
169 isforget = 1;
170 }
171
172 if (!isforget)
173 mt->numavail--;
174 if (mt->numavail == 0 && mt->numworker < mt->max_threads &&
175 likely(se->got_init))
176 fuse_loop_start_thread(mt);
177 pthread_mutex_unlock(&se->mt_lock);
178
179 fuse_session_process_buf_internal(se, &w->fbuf, w->ch);
180
181 pthread_mutex_lock(&se->mt_lock);
182 if (!isforget)
183 mt->numavail++;
184
185 /* creating and destroying threads is rather expensive - and there is
186 * not much gain from destroying existing threads. It is therefore
187 * discouraged to set max_idle to anything else than -1. If there
188 * is indeed a good reason to destruct threads it should be done
189 * delayed, a moving average might be useful for that.
190 */
191 if (mt->max_idle != -1 && mt->numavail > mt->max_idle && mt->numworker > 1) {
192 if (fuse_session_exited(se)) {
193 pthread_mutex_unlock(&se->mt_lock);
194 return NULL;
195 }
196 list_del_worker(w);
197 mt->numavail--;
198 mt->numworker--;
199 pthread_mutex_unlock(&se->mt_lock);
200
201 pthread_detach(w->thread_id);
202 fuse_buf_free(&w->fbuf);
203 fuse_chan_put(w->ch);
204 free(w);
205 return NULL;
206 }
207 pthread_mutex_unlock(&se->mt_lock);
208 }
209
210 sem_post(&se->mt_finish);
211 return NULL;
212}
213
214int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
215{
216 sigset_t oldset;
217 sigset_t newset;
218 int res;
219 pthread_attr_t attr;
220 char *stack_size;
221
222 /* Override default stack size
223 * XXX: This should ideally be a parameter option. It is rather
224 * well hidden here.
225 */
226 pthread_attr_init(&attr);
227 stack_size = getenv(ENVNAME_THREAD_STACK);
228 if (stack_size) {
229 long size;
230
231 res = libfuse_strtol(stack_size, &size);
232 if (res)
233 fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n",
234 stack_size);
235 else if (pthread_attr_setstacksize(&attr, size))
236 fuse_log(FUSE_LOG_ERR, "fuse: could not set stack size: %ld\n",
237 size);
238 }
239
240 /* Disallow signal reception in worker threads */
241 sigemptyset(&newset);
242 sigaddset(&newset, SIGTERM);
243 sigaddset(&newset, SIGINT);
244 sigaddset(&newset, SIGHUP);
245 sigaddset(&newset, SIGQUIT);
246 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
247 res = pthread_create(thread_id, &attr, func, arg);
248 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
249 pthread_attr_destroy(&attr);
250 if (res != 0) {
251 fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n",
252 strerror(res));
253 return -1;
254 }
255
256 return 0;
257}
258
259static int fuse_clone_chan_fd_default(struct fuse_session *se)
260{
261 int res;
262 int clonefd;
263 uint32_t masterfd;
264 const char *devname = "/dev/fuse";
265
266#ifndef O_CLOEXEC
267#define O_CLOEXEC 0
268#endif
269 clonefd = open(devname, O_RDWR | O_CLOEXEC);
270 if (clonefd == -1) {
271 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname,
272 strerror(errno));
273 return -1;
274 }
275 if (!O_CLOEXEC) {
276 res = fcntl(clonefd, F_SETFD, FD_CLOEXEC);
277 if (res == -1) {
278 fuse_log(FUSE_LOG_ERR, "fuse: failed to set CLOEXEC: %s\n",
279 strerror(errno));
280 close(clonefd);
281 return -1;
282 }
283 }
284
285 masterfd = se->fd;
286 res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
287 if (res == -1) {
288 fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
289 strerror(errno));
290 close(clonefd);
291 return -1;
292 }
293 return clonefd;
294}
295
296static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
297{
298 int clonefd;
299 struct fuse_session *se = mt->se;
300 struct fuse_chan *newch;
301
302 if (se->io != NULL) {
303 if (se->io->clone_fd != NULL)
304 clonefd = se->io->clone_fd(se->fd);
305 else
306 return NULL;
307 } else {
308 clonefd = fuse_clone_chan_fd_default(se);
309 }
310 if (clonefd < 0)
311 return NULL;
312
313 newch = fuse_chan_new(clonefd);
314 if (newch == NULL)
315 close(clonefd);
316
317 return newch;
318}
319
320static int fuse_loop_start_thread(struct fuse_mt *mt)
321{
322 int res;
323
324 struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
325 if (!w) {
326 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
327 return -1;
328 }
329 memset(w, 0, sizeof(struct fuse_worker));
330 w->fbuf.mem = NULL;
331 w->mt = mt;
332
333 w->ch = NULL;
334 if (mt->clone_fd) {
335 w->ch = fuse_clone_chan(mt);
336 if(!w->ch) {
337 /* Don't attempt this again */
338 fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
339 "without -o clone_fd.\n");
340 mt->clone_fd = 0;
341 }
342 }
343
344 res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
345 if (res == -1) {
346 fuse_chan_put(w->ch);
347 free(w);
348 return -1;
349 }
350 list_add_worker(w, &mt->main);
351 mt->numavail ++;
352 mt->numworker ++;
353
354 return 0;
355}
356
357static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
358{
359 pthread_join(w->thread_id, NULL);
360 pthread_mutex_lock(&mt->se->mt_lock);
361 list_del_worker(w);
362 pthread_mutex_unlock(&mt->se->mt_lock);
363 fuse_buf_free(&w->fbuf);
364 fuse_chan_put(w->ch);
365 free(w);
366}
367
368int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
369FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
370int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
371{
372int err;
373 struct fuse_mt mt;
374 struct fuse_worker *w;
375 int created_config = 0;
376
377 if (config) {
378 err = fuse_loop_cfg_verify(config);
379 if (err)
380 return err;
381 } else {
382 /* The caller does not care about parameters - use the default */
383 config = fuse_loop_cfg_create();
384 created_config = 1;
385 }
386
387
388 memset(&mt, 0, sizeof(struct fuse_mt));
389 mt.se = se;
390 mt.clone_fd = config->clone_fd;
391 mt.error = 0;
392 mt.numworker = 0;
393 mt.numavail = 0;
394 mt.max_idle = config->max_idle_threads;
395 mt.max_threads = config->max_threads;
396 mt.main.thread_id = pthread_self();
397 mt.main.prev = mt.main.next = &mt.main;
398
399 pthread_mutex_lock(&se->mt_lock);
400 err = fuse_loop_start_thread(&mt);
401 pthread_mutex_unlock(&se->mt_lock);
402 if (!err) {
403 while (!fuse_session_exited(se))
404 sem_wait(&se->mt_finish);
405 if (se->debug)
406 fuse_log(FUSE_LOG_DEBUG,
407 "fuse: session exited, terminating workers\n");
408
409 pthread_mutex_lock(&se->mt_lock);
410 for (w = mt.main.next; w != &mt.main; w = w->next)
411 pthread_cancel(w->thread_id);
412 pthread_mutex_unlock(&se->mt_lock);
413
414 while (mt.main.next != &mt.main)
415 fuse_join_worker(&mt, mt.main.next);
416
417 err = mt.error;
418
419 if (se->uring.pool)
420 fuse_uring_stop(se);
421 }
422
423 pthread_mutex_destroy(&se->mt_lock);
424 if(se->error != 0)
425 err = se->error;
426
427
428 if (created_config) {
429 fuse_loop_cfg_destroy(config);
430 config = NULL;
431 }
432
433 return err;
434}
435
436int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
437FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
438int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
439{
440 int err;
441 struct fuse_loop_config *config = NULL;
442
443 if (config_v1 != NULL) {
444 /* convert the given v1 config */
445 config = fuse_loop_cfg_create();
446 if (config == NULL)
447 return ENOMEM;
448
449 fuse_loop_cfg_convert(config, config_v1);
450 }
451
452 err = fuse_session_loop_mt_312(se, config);
453
454 fuse_loop_cfg_destroy(config);
455
456 return err;
457}
458
459
460int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
461FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
462int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
463{
464 int err;
465 struct fuse_loop_config *config = fuse_loop_cfg_create();
466 if (clone_fd > 0)
467 fuse_loop_cfg_set_clone_fd(config, clone_fd);
468 err = fuse_session_loop_mt_312(se, config);
469
470 fuse_loop_cfg_destroy(config);
471
472 return err;
473}
474
475struct fuse_loop_config *fuse_loop_cfg_create(void)
476{
477 struct fuse_loop_config *config = calloc(1, sizeof(*config));
478 if (config == NULL)
479 return NULL;
480
481 config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
482 config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
483 config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
484 config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
485
486 return config;
487}
488
489void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
490{
491 free(config);
492}
493
494int fuse_loop_cfg_verify(struct fuse_loop_config *config)
495{
496 if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
497 return -EINVAL;
498
499 return 0;
500}
501
502void fuse_loop_cfg_convert(struct fuse_loop_config *config,
503 struct fuse_loop_config_v1 *v1_conf)
504{
505 fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
506
507 fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
508}
509
510void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
511 unsigned int value)
512{
513 if (value > FUSE_LOOP_MT_MAX_THREADS) {
514 if (value != UINT_MAX)
515 fuse_log(FUSE_LOG_ERR,
516 "Ignoring invalid max threads value "
517 "%u > max (%u).\n", value,
518 FUSE_LOOP_MT_MAX_THREADS);
519 return;
520 }
521 config->max_idle_threads = value;
522}
523
524void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
525 unsigned int value)
526{
527 config->max_threads = value;
528}
529
530void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
531 unsigned int value)
532{
533 config->clone_fd = value;
534}
535
@ FUSE_BUF_IS_FD
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
int fuse_session_exited(struct fuse_session *se)
unsigned int max_threads
Definition fuse_i.h:166
unsigned int max_idle_threads
fuse-3.18.2/doc/html/lib_2fuse__loop__mt_8c_source.html0000644000175000017500000024510015156613443022001 0ustar berndbernd libfuse: lib/fuse_loop_mt.c Source File
libfuse
fuse_loop_mt.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the multi-threaded FUSE session loop.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#define _GNU_SOURCE
12
13#include "fuse_config.h"
14#include "fuse_lowlevel.h"
15#include "fuse_misc.h"
16#include "fuse_kernel.h"
17#include "fuse_i.h"
18#include "fuse_uring_i.h"
19#include "util.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25#include <signal.h>
26#include <semaphore.h>
27#include <errno.h>
28#include <sys/time.h>
29#include <sys/ioctl.h>
30#include <assert.h>
31#include <limits.h>
32
33/* Environment var controlling the thread stack size */
34#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
35
36#define FUSE_LOOP_MT_V2_IDENTIFIER INT_MAX - 2
37#define FUSE_LOOP_MT_DEF_CLONE_FD 0
38#define FUSE_LOOP_MT_DEF_MAX_THREADS 10
39#define FUSE_LOOP_MT_DEF_IDLE_THREADS -1 /* thread destruction is disabled
40 * by default */
41
42/* an arbitrary large value that cannot be valid */
43#define FUSE_LOOP_MT_MAX_THREADS (100U * 1000)
44
45struct fuse_worker {
46 struct fuse_worker *prev;
47 struct fuse_worker *next;
48 pthread_t thread_id;
49
50 // We need to include fuse_buf so that we can properly free
51 // it when a thread is terminated by pthread_cancel().
52 struct fuse_buf fbuf;
53 struct fuse_chan *ch;
54 struct fuse_mt *mt;
55};
56
57/* synchronization via se->mt_lock */
58struct fuse_mt {
59 int numworker;
60 int numavail;
61 struct fuse_session *se;
62 struct fuse_worker main;
63 int error;
64 int clone_fd;
65 int max_idle;
66 int max_threads;
67};
68
69static struct fuse_chan *fuse_chan_new(int fd)
70{
71 struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
72 if (ch == NULL) {
73 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate channel\n");
74 return NULL;
75 }
76
77 memset(ch, 0, sizeof(*ch));
78 ch->fd = fd;
79 ch->ctr = 1;
80 pthread_mutex_init(&ch->lock, NULL);
81
82 return ch;
83}
84
85struct fuse_chan *fuse_chan_get(struct fuse_chan *ch)
86{
87 assert(ch->ctr > 0);
88 pthread_mutex_lock(&ch->lock);
89 ch->ctr++;
90 pthread_mutex_unlock(&ch->lock);
91
92 return ch;
93}
94
95void fuse_chan_put(struct fuse_chan *ch)
96{
97 if (ch == NULL)
98 return;
99 pthread_mutex_lock(&ch->lock);
100 ch->ctr--;
101 if (!ch->ctr) {
102 pthread_mutex_unlock(&ch->lock);
103 close(ch->fd);
104 pthread_mutex_destroy(&ch->lock);
105 free(ch);
106 } else
107 pthread_mutex_unlock(&ch->lock);
108}
109
110static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
111{
112 struct fuse_worker *prev = next->prev;
113 w->next = next;
114 w->prev = prev;
115 prev->next = w;
116 next->prev = w;
117}
118
119static void list_del_worker(struct fuse_worker *w)
120{
121 struct fuse_worker *prev = w->prev;
122 struct fuse_worker *next = w->next;
123 prev->next = next;
124 next->prev = prev;
125}
126
127static int fuse_loop_start_thread(struct fuse_mt *mt);
128
129static void *fuse_do_work(void *data)
130{
131 struct fuse_worker *w = (struct fuse_worker *) data;
132 struct fuse_mt *mt = w->mt;
133 struct fuse_session *se = mt->se;
134
135 fuse_set_thread_name("fuse_worker");
136
137 while (!fuse_session_exited(se)) {
138 int isforget = 0;
139 int res;
140
141 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
142 res = fuse_session_receive_buf_internal(se, &w->fbuf, w->ch);
143 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
144 if (res == -EINTR)
145 continue;
146 if (res <= 0) {
147 if (res < 0) {
149 mt->error = res;
150 }
151 break;
152 }
153
154 pthread_mutex_lock(&se->mt_lock);
155 if (fuse_session_exited(se)) {
156 pthread_mutex_unlock(&se->mt_lock);
157 return NULL;
158 }
159
160 /*
161 * This disgusting hack is needed so that zillions of threads
162 * are not created on a burst of FORGET messages
163 */
164 if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) {
165 struct fuse_in_header *in = w->fbuf.mem;
166
167 if (in->opcode == FUSE_FORGET ||
168 in->opcode == FUSE_BATCH_FORGET)
169 isforget = 1;
170 }
171
172 if (!isforget)
173 mt->numavail--;
174 if (mt->numavail == 0 && mt->numworker < mt->max_threads &&
175 likely(se->got_init))
176 fuse_loop_start_thread(mt);
177 pthread_mutex_unlock(&se->mt_lock);
178
179 fuse_session_process_buf_internal(se, &w->fbuf, w->ch);
180
181 pthread_mutex_lock(&se->mt_lock);
182 if (!isforget)
183 mt->numavail++;
184
185 /* creating and destroying threads is rather expensive - and there is
186 * not much gain from destroying existing threads. It is therefore
187 * discouraged to set max_idle to anything else than -1. If there
188 * is indeed a good reason to destruct threads it should be done
189 * delayed, a moving average might be useful for that.
190 */
191 if (mt->max_idle != -1 && mt->numavail > mt->max_idle && mt->numworker > 1) {
192 if (fuse_session_exited(se)) {
193 pthread_mutex_unlock(&se->mt_lock);
194 return NULL;
195 }
196 list_del_worker(w);
197 mt->numavail--;
198 mt->numworker--;
199 pthread_mutex_unlock(&se->mt_lock);
200
201 pthread_detach(w->thread_id);
202 fuse_buf_free(&w->fbuf);
203 fuse_chan_put(w->ch);
204 free(w);
205 return NULL;
206 }
207 pthread_mutex_unlock(&se->mt_lock);
208 }
209
210 sem_post(&se->mt_finish);
211 return NULL;
212}
213
214int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
215{
216 sigset_t oldset;
217 sigset_t newset;
218 int res;
219 pthread_attr_t attr;
220 char *stack_size;
221
222 /* Override default stack size
223 * XXX: This should ideally be a parameter option. It is rather
224 * well hidden here.
225 */
226 pthread_attr_init(&attr);
227 stack_size = getenv(ENVNAME_THREAD_STACK);
228 if (stack_size) {
229 long size;
230
231 res = libfuse_strtol(stack_size, &size);
232 if (res)
233 fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n",
234 stack_size);
235 else if (pthread_attr_setstacksize(&attr, size))
236 fuse_log(FUSE_LOG_ERR, "fuse: could not set stack size: %ld\n",
237 size);
238 }
239
240 /* Disallow signal reception in worker threads */
241 sigemptyset(&newset);
242 sigaddset(&newset, SIGTERM);
243 sigaddset(&newset, SIGINT);
244 sigaddset(&newset, SIGHUP);
245 sigaddset(&newset, SIGQUIT);
246 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
247 res = pthread_create(thread_id, &attr, func, arg);
248 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
249 pthread_attr_destroy(&attr);
250 if (res != 0) {
251 fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n",
252 strerror(res));
253 return -1;
254 }
255
256 return 0;
257}
258
259static int fuse_clone_chan_fd_default(struct fuse_session *se)
260{
261 int res;
262 int clonefd;
263 uint32_t masterfd;
264 const char *devname = "/dev/fuse";
265
266#ifndef O_CLOEXEC
267#define O_CLOEXEC 0
268#endif
269 clonefd = open(devname, O_RDWR | O_CLOEXEC);
270 if (clonefd == -1) {
271 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname,
272 strerror(errno));
273 return -1;
274 }
275 if (!O_CLOEXEC) {
276 res = fcntl(clonefd, F_SETFD, FD_CLOEXEC);
277 if (res == -1) {
278 fuse_log(FUSE_LOG_ERR, "fuse: failed to set CLOEXEC: %s\n",
279 strerror(errno));
280 close(clonefd);
281 return -1;
282 }
283 }
284
285 masterfd = se->fd;
286 res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
287 if (res == -1) {
288 fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
289 strerror(errno));
290 close(clonefd);
291 return -1;
292 }
293 return clonefd;
294}
295
296static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
297{
298 int clonefd;
299 struct fuse_session *se = mt->se;
300 struct fuse_chan *newch;
301
302 if (se->io != NULL) {
303 if (se->io->clone_fd != NULL)
304 clonefd = se->io->clone_fd(se->fd);
305 else
306 return NULL;
307 } else {
308 clonefd = fuse_clone_chan_fd_default(se);
309 }
310 if (clonefd < 0)
311 return NULL;
312
313 newch = fuse_chan_new(clonefd);
314 if (newch == NULL)
315 close(clonefd);
316
317 return newch;
318}
319
320static int fuse_loop_start_thread(struct fuse_mt *mt)
321{
322 int res;
323
324 struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
325 if (!w) {
326 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
327 return -1;
328 }
329 memset(w, 0, sizeof(struct fuse_worker));
330 w->fbuf.mem = NULL;
331 w->mt = mt;
332
333 w->ch = NULL;
334 if (mt->clone_fd) {
335 w->ch = fuse_clone_chan(mt);
336 if(!w->ch) {
337 /* Don't attempt this again */
338 fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
339 "without -o clone_fd.\n");
340 mt->clone_fd = 0;
341 }
342 }
343
344 res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
345 if (res == -1) {
346 fuse_chan_put(w->ch);
347 free(w);
348 return -1;
349 }
350 list_add_worker(w, &mt->main);
351 mt->numavail ++;
352 mt->numworker ++;
353
354 return 0;
355}
356
357static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
358{
359 pthread_join(w->thread_id, NULL);
360 pthread_mutex_lock(&mt->se->mt_lock);
361 list_del_worker(w);
362 pthread_mutex_unlock(&mt->se->mt_lock);
363 fuse_buf_free(&w->fbuf);
364 fuse_chan_put(w->ch);
365 free(w);
366}
367
368int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
369FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
370int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
371{
372int err;
373 struct fuse_mt mt;
374 struct fuse_worker *w;
375 int created_config = 0;
376
377 if (config) {
378 err = fuse_loop_cfg_verify(config);
379 if (err)
380 return err;
381 } else {
382 /* The caller does not care about parameters - use the default */
383 config = fuse_loop_cfg_create();
384 created_config = 1;
385 }
386
387
388 memset(&mt, 0, sizeof(struct fuse_mt));
389 mt.se = se;
390 mt.clone_fd = config->clone_fd;
391 mt.error = 0;
392 mt.numworker = 0;
393 mt.numavail = 0;
394 mt.max_idle = config->max_idle_threads;
395 mt.max_threads = config->max_threads;
396 mt.main.thread_id = pthread_self();
397 mt.main.prev = mt.main.next = &mt.main;
398
399 pthread_mutex_lock(&se->mt_lock);
400 err = fuse_loop_start_thread(&mt);
401 pthread_mutex_unlock(&se->mt_lock);
402 if (!err) {
403 while (!fuse_session_exited(se))
404 sem_wait(&se->mt_finish);
405 if (se->debug)
406 fuse_log(FUSE_LOG_DEBUG,
407 "fuse: session exited, terminating workers\n");
408
409 pthread_mutex_lock(&se->mt_lock);
410 for (w = mt.main.next; w != &mt.main; w = w->next)
411 pthread_cancel(w->thread_id);
412 pthread_mutex_unlock(&se->mt_lock);
413
414 while (mt.main.next != &mt.main)
415 fuse_join_worker(&mt, mt.main.next);
416
417 err = mt.error;
418
419 if (se->uring.pool)
420 fuse_uring_stop(se);
421 }
422
423 pthread_mutex_destroy(&se->mt_lock);
424 if(se->error != 0)
425 err = se->error;
426
427
428 if (created_config) {
429 fuse_loop_cfg_destroy(config);
430 config = NULL;
431 }
432
433 return err;
434}
435
436int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
437FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
438int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
439{
440 int err;
441 struct fuse_loop_config *config = NULL;
442
443 if (config_v1 != NULL) {
444 /* convert the given v1 config */
445 config = fuse_loop_cfg_create();
446 if (config == NULL)
447 return ENOMEM;
448
449 fuse_loop_cfg_convert(config, config_v1);
450 }
451
452 err = fuse_session_loop_mt_312(se, config);
453
454 fuse_loop_cfg_destroy(config);
455
456 return err;
457}
458
459
460int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
461FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
462int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
463{
464 int err;
465 struct fuse_loop_config *config = fuse_loop_cfg_create();
466 if (clone_fd > 0)
467 fuse_loop_cfg_set_clone_fd(config, clone_fd);
468 err = fuse_session_loop_mt_312(se, config);
469
470 fuse_loop_cfg_destroy(config);
471
472 return err;
473}
474
475struct fuse_loop_config *fuse_loop_cfg_create(void)
476{
477 struct fuse_loop_config *config = calloc(1, sizeof(*config));
478 if (config == NULL)
479 return NULL;
480
481 config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
482 config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
483 config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
484 config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
485
486 return config;
487}
488
489void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
490{
491 free(config);
492}
493
494int fuse_loop_cfg_verify(struct fuse_loop_config *config)
495{
496 if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
497 return -EINVAL;
498
499 return 0;
500}
501
502void fuse_loop_cfg_convert(struct fuse_loop_config *config,
503 struct fuse_loop_config_v1 *v1_conf)
504{
505 fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
506
507 fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
508}
509
510void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
511 unsigned int value)
512{
513 if (value > FUSE_LOOP_MT_MAX_THREADS) {
514 if (value != UINT_MAX)
515 fuse_log(FUSE_LOG_ERR,
516 "Ignoring invalid max threads value "
517 "%u > max (%u).\n", value,
518 FUSE_LOOP_MT_MAX_THREADS);
519 return;
520 }
521 config->max_idle_threads = value;
522}
523
524void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
525 unsigned int value)
526{
527 config->max_threads = value;
528}
529
530void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
531 unsigned int value)
532{
533 config->clone_fd = value;
534}
535
@ FUSE_BUF_IS_FD
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
int fuse_session_exited(struct fuse_session *se)
unsigned int max_threads
Definition fuse_i.h:166
unsigned int max_idle_threads
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2fuse__lowlevel_8c_source.html0000644000175000017500000244137215156613443024213 0ustar berndbernd libfuse: fuse-3.17.4/lib/fuse_lowlevel.c Source File
libfuse
fuse_lowlevel.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of (most of) the low-level FUSE API. The session loop
6 functions are implemented in separate files.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file COPYING.LIB
10*/
11
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_kernel.h"
17#include "fuse_opt.h"
18#include "fuse_misc.h"
19#include "mount_util.h"
20#include "util.h"
21
22#include <stdint.h>
23#include <stdbool.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <stddef.h>
27#include <stdalign.h>
28#include <string.h>
29#include <unistd.h>
30#include <limits.h>
31#include <errno.h>
32#include <assert.h>
33#include <sys/file.h>
34#include <sys/ioctl.h>
35
36#ifndef F_LINUX_SPECIFIC_BASE
37#define F_LINUX_SPECIFIC_BASE 1024
38#endif
39#ifndef F_SETPIPE_SZ
40#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
41#endif
42
43
44#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
45#define OFFSET_MAX 0x7fffffffffffffffLL
46
47struct fuse_pollhandle {
48 uint64_t kh;
49 struct fuse_session *se;
50};
51
52static size_t pagesize;
53
54static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
55{
56 pagesize = getpagesize();
57}
58
59static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
60{
61 attr->ino = stbuf->st_ino;
62 attr->mode = stbuf->st_mode;
63 attr->nlink = stbuf->st_nlink;
64 attr->uid = stbuf->st_uid;
65 attr->gid = stbuf->st_gid;
66 attr->rdev = stbuf->st_rdev;
67 attr->size = stbuf->st_size;
68 attr->blksize = stbuf->st_blksize;
69 attr->blocks = stbuf->st_blocks;
70 attr->atime = stbuf->st_atime;
71 attr->mtime = stbuf->st_mtime;
72 attr->ctime = stbuf->st_ctime;
73 attr->atimensec = ST_ATIM_NSEC(stbuf);
74 attr->mtimensec = ST_MTIM_NSEC(stbuf);
75 attr->ctimensec = ST_CTIM_NSEC(stbuf);
76}
77
78static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
79{
80 stbuf->st_mode = attr->mode;
81 stbuf->st_uid = attr->uid;
82 stbuf->st_gid = attr->gid;
83 stbuf->st_size = attr->size;
84 stbuf->st_atime = attr->atime;
85 stbuf->st_mtime = attr->mtime;
86 stbuf->st_ctime = attr->ctime;
87 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
88 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
89 ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
90}
91
92static size_t iov_length(const struct iovec *iov, size_t count)
93{
94 size_t seg;
95 size_t ret = 0;
96
97 for (seg = 0; seg < count; seg++)
98 ret += iov[seg].iov_len;
99 return ret;
100}
101
102static void list_init_req(struct fuse_req *req)
103{
104 req->next = req;
105 req->prev = req;
106}
107
108static void list_del_req(struct fuse_req *req)
109{
110 struct fuse_req *prev = req->prev;
111 struct fuse_req *next = req->next;
112 prev->next = next;
113 next->prev = prev;
114}
115
116static void list_add_req(struct fuse_req *req, struct fuse_req *next)
117{
118 struct fuse_req *prev = next->prev;
119 req->next = next;
120 req->prev = prev;
121 prev->next = req;
122 next->prev = req;
123}
124
125static void destroy_req(fuse_req_t req)
126{
127 assert(req->ch == NULL);
128 pthread_mutex_destroy(&req->lock);
129 free(req);
130}
131
132void fuse_free_req(fuse_req_t req)
133{
134 int ctr;
135 struct fuse_session *se = req->se;
136
137 if (se->conn.no_interrupt) {
138 ctr = --req->ref_cnt;
139 fuse_chan_put(req->ch);
140 req->ch = NULL;
141 } else {
142 pthread_mutex_lock(&se->lock);
143 req->u.ni.func = NULL;
144 req->u.ni.data = NULL;
145 list_del_req(req);
146 ctr = --req->ref_cnt;
147 fuse_chan_put(req->ch);
148 req->ch = NULL;
149 pthread_mutex_unlock(&se->lock);
150 }
151 if (!ctr)
152 destroy_req(req);
153}
154
155static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
156{
157 struct fuse_req *req;
158
159 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
160 if (req == NULL) {
161 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
162 } else {
163 req->se = se;
164 req->ref_cnt = 1;
165 list_init_req(req);
166 pthread_mutex_init(&req->lock, NULL);
167 }
168
169 return req;
170}
171
172/* Send data. If *ch* is NULL, send via session master fd */
173static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
174 struct iovec *iov, int count)
175{
176 struct fuse_out_header *out = iov[0].iov_base;
177
178 assert(se != NULL);
179 out->len = iov_length(iov, count);
180 if (se->debug) {
181 if (out->unique == 0) {
182 fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
183 out->error, out->len);
184 } else if (out->error) {
185 fuse_log(FUSE_LOG_DEBUG,
186 " unique: %llu, error: %i (%s), outsize: %i\n",
187 (unsigned long long) out->unique, out->error,
188 strerror(-out->error), out->len);
189 } else {
190 fuse_log(FUSE_LOG_DEBUG,
191 " unique: %llu, success, outsize: %i\n",
192 (unsigned long long) out->unique, out->len);
193 }
194 }
195
196 ssize_t res;
197 if (se->io != NULL)
198 /* se->io->writev is never NULL if se->io is not NULL as
199 specified by fuse_session_custom_io()*/
200 res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
201 se->userdata);
202 else
203 res = writev(ch ? ch->fd : se->fd, iov, count);
204
205 int err = errno;
206
207 if (res == -1) {
208 /* ENOENT means the operation was interrupted */
209 if (!fuse_session_exited(se) && err != ENOENT)
210 perror("fuse: writing device");
211 return -err;
212 }
213
214 return 0;
215}
216
217
218int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
219 int count)
220{
221 struct fuse_out_header out;
222
223#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
224 const char *str = strerrordesc_np(error * -1);
225 if ((str == NULL && error != 0) || error > 0) {
226#else
227 if (error <= -1000 || error > 0) {
228#endif
229 fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
230 error = -ERANGE;
231 }
232
233 out.unique = req->unique;
234 out.error = error;
235
236 iov[0].iov_base = &out;
237 iov[0].iov_len = sizeof(struct fuse_out_header);
238
239 return fuse_send_msg(req->se, req->ch, iov, count);
240}
241
242static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
243 int count)
244{
245 int res;
246
247 res = fuse_send_reply_iov_nofree(req, error, iov, count);
248 fuse_free_req(req);
249 return res;
250}
251
252static int send_reply(fuse_req_t req, int error, const void *arg,
253 size_t argsize)
254{
255 struct iovec iov[2];
256 int count = 1;
257 if (argsize) {
258 iov[1].iov_base = (void *) arg;
259 iov[1].iov_len = argsize;
260 count++;
261 }
262 return send_reply_iov(req, error, iov, count);
263}
264
265int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
266{
267 int res;
268 struct iovec *padded_iov;
269
270 padded_iov = malloc((count + 1) * sizeof(struct iovec));
271 if (padded_iov == NULL)
272 return fuse_reply_err(req, ENOMEM);
273
274 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
275 count++;
276
277 res = send_reply_iov(req, 0, padded_iov, count);
278 free(padded_iov);
279
280 return res;
281}
282
283
284/* `buf` is allowed to be empty so that the proper size may be
285 allocated by the caller */
286size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
287 const char *name, const struct stat *stbuf, off_t off)
288{
289 (void)req;
290 size_t namelen;
291 size_t entlen;
292 size_t entlen_padded;
293 struct fuse_dirent *dirent;
294
295 namelen = strlen(name);
296 entlen = FUSE_NAME_OFFSET + namelen;
297 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
298
299 if ((buf == NULL) || (entlen_padded > bufsize))
300 return entlen_padded;
301
302 dirent = (struct fuse_dirent*) buf;
303 dirent->ino = stbuf->st_ino;
304 dirent->off = off;
305 dirent->namelen = namelen;
306 dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
307 memcpy(dirent->name, name, namelen);
308 memset(dirent->name + namelen, 0, entlen_padded - entlen);
309
310 return entlen_padded;
311}
312
313static void convert_statfs(const struct statvfs *stbuf,
314 struct fuse_kstatfs *kstatfs)
315{
316 kstatfs->bsize = stbuf->f_bsize;
317 kstatfs->frsize = stbuf->f_frsize;
318 kstatfs->blocks = stbuf->f_blocks;
319 kstatfs->bfree = stbuf->f_bfree;
320 kstatfs->bavail = stbuf->f_bavail;
321 kstatfs->files = stbuf->f_files;
322 kstatfs->ffree = stbuf->f_ffree;
323 kstatfs->namelen = stbuf->f_namemax;
324}
325
326static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
327{
328 return send_reply(req, 0, arg, argsize);
329}
330
331int fuse_reply_err(fuse_req_t req, int err)
332{
333 return send_reply(req, -err, NULL, 0);
334}
335
337{
338 fuse_free_req(req);
339}
340
341static unsigned long calc_timeout_sec(double t)
342{
343 if (t > (double) ULONG_MAX)
344 return ULONG_MAX;
345 else if (t < 0.0)
346 return 0;
347 else
348 return (unsigned long) t;
349}
350
351static unsigned int calc_timeout_nsec(double t)
352{
353 double f = t - (double) calc_timeout_sec(t);
354 if (f < 0.0)
355 return 0;
356 else if (f >= 0.999999999)
357 return 999999999;
358 else
359 return (unsigned int) (f * 1.0e9);
360}
361
362static void fill_entry(struct fuse_entry_out *arg,
363 const struct fuse_entry_param *e)
364{
365 arg->nodeid = e->ino;
366 arg->generation = e->generation;
367 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
368 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
369 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
370 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
371 convert_stat(&e->attr, &arg->attr);
372}
373
374/* `buf` is allowed to be empty so that the proper size may be
375 allocated by the caller */
376size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
377 const char *name,
378 const struct fuse_entry_param *e, off_t off)
379{
380 (void)req;
381 size_t namelen;
382 size_t entlen;
383 size_t entlen_padded;
384
385 namelen = strlen(name);
386 entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
387 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
388 if ((buf == NULL) || (entlen_padded > bufsize))
389 return entlen_padded;
390
391 struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
392 memset(&dp->entry_out, 0, sizeof(dp->entry_out));
393 fill_entry(&dp->entry_out, e);
394
395 struct fuse_dirent *dirent = &dp->dirent;
396 dirent->ino = e->attr.st_ino;
397 dirent->off = off;
398 dirent->namelen = namelen;
399 dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
400 memcpy(dirent->name, name, namelen);
401 memset(dirent->name + namelen, 0, entlen_padded - entlen);
402
403 return entlen_padded;
404}
405
406static void fill_open(struct fuse_open_out *arg,
407 const struct fuse_file_info *f)
408{
409 arg->fh = f->fh;
410 if (f->backing_id > 0) {
411 arg->backing_id = f->backing_id;
412 arg->open_flags |= FOPEN_PASSTHROUGH;
413 }
414 if (f->direct_io)
415 arg->open_flags |= FOPEN_DIRECT_IO;
416 if (f->keep_cache)
417 arg->open_flags |= FOPEN_KEEP_CACHE;
418 if (f->cache_readdir)
419 arg->open_flags |= FOPEN_CACHE_DIR;
420 if (f->nonseekable)
421 arg->open_flags |= FOPEN_NONSEEKABLE;
422 if (f->noflush)
423 arg->open_flags |= FOPEN_NOFLUSH;
425 arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
426}
427
429{
430 struct fuse_entry_out arg;
431 size_t size = req->se->conn.proto_minor < 9 ?
432 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
433
434 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
435 negative entry */
436 if (!e->ino && req->se->conn.proto_minor < 4)
437 return fuse_reply_err(req, ENOENT);
438
439 memset(&arg, 0, sizeof(arg));
440 fill_entry(&arg, e);
441 return send_reply_ok(req, &arg, size);
442}
443
445 const struct fuse_file_info *f)
446{
447 alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
448 size_t entrysize = req->se->conn.proto_minor < 9 ?
449 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
450 struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
451 struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
452
453 memset(buf, 0, sizeof(buf));
454 fill_entry(earg, e);
455 fill_open(oarg, f);
456 return send_reply_ok(req, buf,
457 entrysize + sizeof(struct fuse_open_out));
458}
459
460int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
461 double attr_timeout)
462{
463 struct fuse_attr_out arg;
464 size_t size = req->se->conn.proto_minor < 9 ?
465 FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
466
467 memset(&arg, 0, sizeof(arg));
468 arg.attr_valid = calc_timeout_sec(attr_timeout);
469 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
470 convert_stat(attr, &arg.attr);
471
472 return send_reply_ok(req, &arg, size);
473}
474
475int fuse_reply_readlink(fuse_req_t req, const char *linkname)
476{
477 return send_reply_ok(req, linkname, strlen(linkname));
478}
479
481{
482 struct fuse_backing_map map = { .fd = fd };
483 int ret;
484
485 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
486 if (ret <= 0) {
487 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno));
488 return 0;
489 }
490
491 return ret;
492}
493
494int fuse_passthrough_close(fuse_req_t req, int backing_id)
495{
496 int ret;
497
498 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id);
499 if (ret < 0)
500 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno));
501
502 return ret;
503}
504
506{
507 struct fuse_open_out arg;
508
509 memset(&arg, 0, sizeof(arg));
510 fill_open(&arg, f);
511 return send_reply_ok(req, &arg, sizeof(arg));
512}
513
514int fuse_reply_write(fuse_req_t req, size_t count)
515{
516 struct fuse_write_out arg;
517
518 memset(&arg, 0, sizeof(arg));
519 arg.size = count;
520
521 return send_reply_ok(req, &arg, sizeof(arg));
522}
523
524int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
525{
526 return send_reply_ok(req, buf, size);
527}
528
529static int fuse_send_data_iov_fallback(struct fuse_session *se,
530 struct fuse_chan *ch,
531 struct iovec *iov, int iov_count,
532 struct fuse_bufvec *buf,
533 size_t len)
534{
535 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
536 void *mbuf;
537 int res;
538
539 /* Optimize common case */
540 if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
541 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
542 /* FIXME: also avoid memory copy if there are multiple buffers
543 but none of them contain an fd */
544
545 iov[iov_count].iov_base = buf->buf[0].mem;
546 iov[iov_count].iov_len = len;
547 iov_count++;
548 return fuse_send_msg(se, ch, iov, iov_count);
549 }
550
551 res = posix_memalign(&mbuf, pagesize, len);
552 if (res != 0)
553 return res;
554
555 mem_buf.buf[0].mem = mbuf;
556 res = fuse_buf_copy(&mem_buf, buf, 0);
557 if (res < 0) {
558 free(mbuf);
559 return -res;
560 }
561 len = res;
562
563 iov[iov_count].iov_base = mbuf;
564 iov[iov_count].iov_len = len;
565 iov_count++;
566 res = fuse_send_msg(se, ch, iov, iov_count);
567 free(mbuf);
568
569 return res;
570}
571
572struct fuse_ll_pipe {
573 size_t size;
574 int can_grow;
575 int pipe[2];
576};
577
578static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
579{
580 close(llp->pipe[0]);
581 close(llp->pipe[1]);
582 free(llp);
583}
584
585#ifdef HAVE_SPLICE
586#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
587static int fuse_pipe(int fds[2])
588{
589 int rv = pipe(fds);
590
591 if (rv == -1)
592 return rv;
593
594 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
595 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
596 fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
597 fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
598 close(fds[0]);
599 close(fds[1]);
600 rv = -1;
601 }
602 return rv;
603}
604#else
605static int fuse_pipe(int fds[2])
606{
607 return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
608}
609#endif
610
611static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
612{
613 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
614 if (llp == NULL) {
615 int res;
616
617 llp = malloc(sizeof(struct fuse_ll_pipe));
618 if (llp == NULL)
619 return NULL;
620
621 res = fuse_pipe(llp->pipe);
622 if (res == -1) {
623 free(llp);
624 return NULL;
625 }
626
627 /*
628 *the default size is 16 pages on linux
629 */
630 llp->size = pagesize * 16;
631 llp->can_grow = 1;
632
633 pthread_setspecific(se->pipe_key, llp);
634 }
635
636 return llp;
637}
638#endif
639
640static void fuse_ll_clear_pipe(struct fuse_session *se)
641{
642 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
643 if (llp) {
644 pthread_setspecific(se->pipe_key, NULL);
645 fuse_ll_pipe_free(llp);
646 }
647}
648
649#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
650static int read_back(int fd, char *buf, size_t len)
651{
652 int res;
653
654 res = read(fd, buf, len);
655 if (res == -1) {
656 fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
657 return -EIO;
658 }
659 if (res != len) {
660 fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
661 return -EIO;
662 }
663 return 0;
664}
665
666static int grow_pipe_to_max(int pipefd)
667{
668 int res;
669 long max;
670 long maxfd;
671 char buf[32];
672
673 maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
674 if (maxfd < 0)
675 return -errno;
676
677 res = read(maxfd, buf, sizeof(buf) - 1);
678 if (res < 0) {
679 int saved_errno;
680
681 saved_errno = errno;
682 close(maxfd);
683 return -saved_errno;
684 }
685 close(maxfd);
686 buf[res] = '\0';
687
688 res = libfuse_strtol(buf, &max);
689 if (res)
690 return res;
691 res = fcntl(pipefd, F_SETPIPE_SZ, max);
692 if (res < 0)
693 return -errno;
694 return max;
695}
696
697static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
698 struct iovec *iov, int iov_count,
699 struct fuse_bufvec *buf, unsigned int flags)
700{
701 int res;
702 size_t len = fuse_buf_size(buf);
703 struct fuse_out_header *out = iov[0].iov_base;
704 struct fuse_ll_pipe *llp;
705 int splice_flags;
706 size_t pipesize;
707 size_t total_buf_size;
708 size_t idx;
709 size_t headerlen;
710 struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
711
712 if (se->broken_splice_nonblock)
713 goto fallback;
714
715 if (flags & FUSE_BUF_NO_SPLICE)
716 goto fallback;
717
718 total_buf_size = 0;
719 for (idx = buf->idx; idx < buf->count; idx++) {
720 total_buf_size += buf->buf[idx].size;
721 if (idx == buf->idx)
722 total_buf_size -= buf->off;
723 }
724 if (total_buf_size < 2 * pagesize)
725 goto fallback;
726
727 if (se->conn.proto_minor < 14 ||
728 !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
729 goto fallback;
730
731 llp = fuse_ll_get_pipe(se);
732 if (llp == NULL)
733 goto fallback;
734
735
736 headerlen = iov_length(iov, iov_count);
737
738 out->len = headerlen + len;
739
740 /*
741 * Heuristic for the required pipe size, does not work if the
742 * source contains less than page size fragments
743 */
744 pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
745
746 if (llp->size < pipesize) {
747 if (llp->can_grow) {
748 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
749 if (res == -1) {
750 res = grow_pipe_to_max(llp->pipe[0]);
751 if (res > 0)
752 llp->size = res;
753 llp->can_grow = 0;
754 goto fallback;
755 }
756 llp->size = res;
757 }
758 if (llp->size < pipesize)
759 goto fallback;
760 }
761
762
763 res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
764 if (res == -1)
765 goto fallback;
766
767 if (res != headerlen) {
768 res = -EIO;
769 fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
770 headerlen);
771 goto clear_pipe;
772 }
773
774 pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
775 pipe_buf.buf[0].fd = llp->pipe[1];
776
777 res = fuse_buf_copy(&pipe_buf, buf,
779 if (res < 0) {
780 if (res == -EAGAIN || res == -EINVAL) {
781 /*
782 * Should only get EAGAIN on kernels with
783 * broken SPLICE_F_NONBLOCK support (<=
784 * 2.6.35) where this error or a short read is
785 * returned even if the pipe itself is not
786 * full
787 *
788 * EINVAL might mean that splice can't handle
789 * this combination of input and output.
790 */
791 if (res == -EAGAIN)
792 se->broken_splice_nonblock = 1;
793
794 pthread_setspecific(se->pipe_key, NULL);
795 fuse_ll_pipe_free(llp);
796 goto fallback;
797 }
798 res = -res;
799 goto clear_pipe;
800 }
801
802 if (res != 0 && res < len) {
803 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
804 void *mbuf;
805 size_t now_len = res;
806 /*
807 * For regular files a short count is either
808 * 1) due to EOF, or
809 * 2) because of broken SPLICE_F_NONBLOCK (see above)
810 *
811 * For other inputs it's possible that we overflowed
812 * the pipe because of small buffer fragments.
813 */
814
815 res = posix_memalign(&mbuf, pagesize, len);
816 if (res != 0)
817 goto clear_pipe;
818
819 mem_buf.buf[0].mem = mbuf;
820 mem_buf.off = now_len;
821 res = fuse_buf_copy(&mem_buf, buf, 0);
822 if (res > 0) {
823 char *tmpbuf;
824 size_t extra_len = res;
825 /*
826 * Trickiest case: got more data. Need to get
827 * back the data from the pipe and then fall
828 * back to regular write.
829 */
830 tmpbuf = malloc(headerlen);
831 if (tmpbuf == NULL) {
832 free(mbuf);
833 res = ENOMEM;
834 goto clear_pipe;
835 }
836 res = read_back(llp->pipe[0], tmpbuf, headerlen);
837 free(tmpbuf);
838 if (res != 0) {
839 free(mbuf);
840 goto clear_pipe;
841 }
842 res = read_back(llp->pipe[0], mbuf, now_len);
843 if (res != 0) {
844 free(mbuf);
845 goto clear_pipe;
846 }
847 len = now_len + extra_len;
848 iov[iov_count].iov_base = mbuf;
849 iov[iov_count].iov_len = len;
850 iov_count++;
851 res = fuse_send_msg(se, ch, iov, iov_count);
852 free(mbuf);
853 return res;
854 }
855 free(mbuf);
856 res = now_len;
857 }
858 len = res;
859 out->len = headerlen + len;
860
861 if (se->debug) {
862 fuse_log(FUSE_LOG_DEBUG,
863 " unique: %llu, success, outsize: %i (splice)\n",
864 (unsigned long long) out->unique, out->len);
865 }
866
867 splice_flags = 0;
868 if ((flags & FUSE_BUF_SPLICE_MOVE) &&
869 (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
870 splice_flags |= SPLICE_F_MOVE;
871
872 if (se->io != NULL && se->io->splice_send != NULL) {
873 res = se->io->splice_send(llp->pipe[0], NULL,
874 ch ? ch->fd : se->fd, NULL, out->len,
875 splice_flags, se->userdata);
876 } else {
877 res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
878 out->len, splice_flags);
879 }
880 if (res == -1) {
881 res = -errno;
882 perror("fuse: splice from pipe");
883 goto clear_pipe;
884 }
885 if (res != out->len) {
886 res = -EIO;
887 fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
888 res, out->len);
889 goto clear_pipe;
890 }
891 return 0;
892
893clear_pipe:
894 fuse_ll_clear_pipe(se);
895 return res;
896
897fallback:
898 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
899}
900#else
901static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
902 struct iovec *iov, int iov_count,
903 struct fuse_bufvec *buf, unsigned int flags)
904{
905 size_t len = fuse_buf_size(buf);
906 (void) flags;
907
908 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
909}
910#endif
911
913 enum fuse_buf_copy_flags flags)
914{
915 struct iovec iov[2];
916 struct fuse_out_header out;
917 int res;
918
919 iov[0].iov_base = &out;
920 iov[0].iov_len = sizeof(struct fuse_out_header);
921
922 out.unique = req->unique;
923 out.error = 0;
924
925 res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags);
926 if (res <= 0) {
927 fuse_free_req(req);
928 return res;
929 } else {
930 return fuse_reply_err(req, res);
931 }
932}
933
934int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
935{
936 struct fuse_statfs_out arg;
937 size_t size = req->se->conn.proto_minor < 4 ?
938 FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
939
940 memset(&arg, 0, sizeof(arg));
941 convert_statfs(stbuf, &arg.st);
942
943 return send_reply_ok(req, &arg, size);
944}
945
946int fuse_reply_xattr(fuse_req_t req, size_t count)
947{
948 struct fuse_getxattr_out arg;
949
950 memset(&arg, 0, sizeof(arg));
951 arg.size = count;
952
953 return send_reply_ok(req, &arg, sizeof(arg));
954}
955
956int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
957{
958 struct fuse_lk_out arg;
959
960 memset(&arg, 0, sizeof(arg));
961 arg.lk.type = lock->l_type;
962 if (lock->l_type != F_UNLCK) {
963 arg.lk.start = lock->l_start;
964 if (lock->l_len == 0)
965 arg.lk.end = OFFSET_MAX;
966 else
967 arg.lk.end = lock->l_start + lock->l_len - 1;
968 }
969 arg.lk.pid = lock->l_pid;
970 return send_reply_ok(req, &arg, sizeof(arg));
971}
972
973int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
974{
975 struct fuse_bmap_out arg;
976
977 memset(&arg, 0, sizeof(arg));
978 arg.block = idx;
979
980 return send_reply_ok(req, &arg, sizeof(arg));
981}
982
983static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
984 size_t count)
985{
986 struct fuse_ioctl_iovec *fiov;
987 size_t i;
988
989 fiov = malloc(sizeof(fiov[0]) * count);
990 if (!fiov)
991 return NULL;
992
993 for (i = 0; i < count; i++) {
994 fiov[i].base = (uintptr_t) iov[i].iov_base;
995 fiov[i].len = iov[i].iov_len;
996 }
997
998 return fiov;
999}
1000
1002 const struct iovec *in_iov, size_t in_count,
1003 const struct iovec *out_iov, size_t out_count)
1004{
1005 struct fuse_ioctl_out arg;
1006 struct fuse_ioctl_iovec *in_fiov = NULL;
1007 struct fuse_ioctl_iovec *out_fiov = NULL;
1008 struct iovec iov[4];
1009 size_t count = 1;
1010 int res;
1011
1012 memset(&arg, 0, sizeof(arg));
1013 arg.flags |= FUSE_IOCTL_RETRY;
1014 arg.in_iovs = in_count;
1015 arg.out_iovs = out_count;
1016 iov[count].iov_base = &arg;
1017 iov[count].iov_len = sizeof(arg);
1018 count++;
1019
1020 if (req->se->conn.proto_minor < 16) {
1021 if (in_count) {
1022 iov[count].iov_base = (void *)in_iov;
1023 iov[count].iov_len = sizeof(in_iov[0]) * in_count;
1024 count++;
1025 }
1026
1027 if (out_count) {
1028 iov[count].iov_base = (void *)out_iov;
1029 iov[count].iov_len = sizeof(out_iov[0]) * out_count;
1030 count++;
1031 }
1032 } else {
1033 /* Can't handle non-compat 64bit ioctls on 32bit */
1034 if (sizeof(void *) == 4 && req->ioctl_64bit) {
1035 res = fuse_reply_err(req, EINVAL);
1036 goto out;
1037 }
1038
1039 if (in_count) {
1040 in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
1041 if (!in_fiov)
1042 goto enomem;
1043
1044 iov[count].iov_base = (void *)in_fiov;
1045 iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
1046 count++;
1047 }
1048 if (out_count) {
1049 out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
1050 if (!out_fiov)
1051 goto enomem;
1052
1053 iov[count].iov_base = (void *)out_fiov;
1054 iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
1055 count++;
1056 }
1057 }
1058
1059 res = send_reply_iov(req, 0, iov, count);
1060out:
1061 free(in_fiov);
1062 free(out_fiov);
1063
1064 return res;
1065
1066enomem:
1067 res = fuse_reply_err(req, ENOMEM);
1068 goto out;
1069}
1070
1071int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
1072{
1073 struct fuse_ioctl_out arg;
1074 struct iovec iov[3];
1075 size_t count = 1;
1076
1077 memset(&arg, 0, sizeof(arg));
1078 arg.result = result;
1079 iov[count].iov_base = &arg;
1080 iov[count].iov_len = sizeof(arg);
1081 count++;
1082
1083 if (size) {
1084 iov[count].iov_base = (char *) buf;
1085 iov[count].iov_len = size;
1086 count++;
1087 }
1088
1089 return send_reply_iov(req, 0, iov, count);
1090}
1091
1092int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
1093 int count)
1094{
1095 struct iovec *padded_iov;
1096 struct fuse_ioctl_out arg;
1097 int res;
1098
1099 padded_iov = malloc((count + 2) * sizeof(struct iovec));
1100 if (padded_iov == NULL)
1101 return fuse_reply_err(req, ENOMEM);
1102
1103 memset(&arg, 0, sizeof(arg));
1104 arg.result = result;
1105 padded_iov[1].iov_base = &arg;
1106 padded_iov[1].iov_len = sizeof(arg);
1107
1108 memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
1109
1110 res = send_reply_iov(req, 0, padded_iov, count + 2);
1111 free(padded_iov);
1112
1113 return res;
1114}
1115
1116int fuse_reply_poll(fuse_req_t req, unsigned revents)
1117{
1118 struct fuse_poll_out arg;
1119
1120 memset(&arg, 0, sizeof(arg));
1121 arg.revents = revents;
1122
1123 return send_reply_ok(req, &arg, sizeof(arg));
1124}
1125
1126int fuse_reply_lseek(fuse_req_t req, off_t off)
1127{
1128 struct fuse_lseek_out arg;
1129
1130 memset(&arg, 0, sizeof(arg));
1131 arg.offset = off;
1132
1133 return send_reply_ok(req, &arg, sizeof(arg));
1134}
1135
1136static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1137{
1138 char *name = (char *) inarg;
1139
1140 if (req->se->op.lookup)
1141 req->se->op.lookup(req, nodeid, name);
1142 else
1143 fuse_reply_err(req, ENOSYS);
1144}
1145
1146static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1147{
1148 struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
1149
1150 if (req->se->op.forget)
1151 req->se->op.forget(req, nodeid, arg->nlookup);
1152 else
1153 fuse_reply_none(req);
1154}
1155
1156static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
1157 const void *inarg)
1158{
1159 struct fuse_batch_forget_in *arg = (void *) inarg;
1160 struct fuse_forget_one *param = (void *) PARAM(arg);
1161 unsigned int i;
1162
1163 (void) nodeid;
1164
1165 if (req->se->op.forget_multi) {
1166 req->se->op.forget_multi(req, arg->count,
1167 (struct fuse_forget_data *) param);
1168 } else if (req->se->op.forget) {
1169 for (i = 0; i < arg->count; i++) {
1170 struct fuse_forget_one *forget = &param[i];
1171 struct fuse_req *dummy_req;
1172
1173 dummy_req = fuse_ll_alloc_req(req->se);
1174 if (dummy_req == NULL)
1175 break;
1176
1177 dummy_req->unique = req->unique;
1178 dummy_req->ctx = req->ctx;
1179 dummy_req->ch = NULL;
1180
1181 req->se->op.forget(dummy_req, forget->nodeid,
1182 forget->nlookup);
1183 }
1184 fuse_reply_none(req);
1185 } else {
1186 fuse_reply_none(req);
1187 }
1188}
1189
1190static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1191{
1192 struct fuse_file_info *fip = NULL;
1193 struct fuse_file_info fi;
1194
1195 if (req->se->conn.proto_minor >= 9) {
1196 struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
1197
1198 if (arg->getattr_flags & FUSE_GETATTR_FH) {
1199 memset(&fi, 0, sizeof(fi));
1200 fi.fh = arg->fh;
1201 fip = &fi;
1202 }
1203 }
1204
1205 if (req->se->op.getattr)
1206 req->se->op.getattr(req, nodeid, fip);
1207 else
1208 fuse_reply_err(req, ENOSYS);
1209}
1210
1211static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1212{
1213 struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
1214
1215 if (req->se->op.setattr) {
1216 struct fuse_file_info *fi = NULL;
1217 struct fuse_file_info fi_store;
1218 struct stat stbuf;
1219 memset(&stbuf, 0, sizeof(stbuf));
1220 convert_attr(arg, &stbuf);
1221 if (arg->valid & FATTR_FH) {
1222 arg->valid &= ~FATTR_FH;
1223 memset(&fi_store, 0, sizeof(fi_store));
1224 fi = &fi_store;
1225 fi->fh = arg->fh;
1226 }
1227 arg->valid &=
1228 FUSE_SET_ATTR_MODE |
1229 FUSE_SET_ATTR_UID |
1230 FUSE_SET_ATTR_GID |
1231 FUSE_SET_ATTR_SIZE |
1232 FUSE_SET_ATTR_ATIME |
1233 FUSE_SET_ATTR_MTIME |
1234 FUSE_SET_ATTR_KILL_SUID |
1235 FUSE_SET_ATTR_KILL_SGID |
1236 FUSE_SET_ATTR_ATIME_NOW |
1237 FUSE_SET_ATTR_MTIME_NOW |
1238 FUSE_SET_ATTR_CTIME;
1239
1240 req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
1241 } else
1242 fuse_reply_err(req, ENOSYS);
1243}
1244
1245static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1246{
1247 struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
1248
1249 if (req->se->op.access)
1250 req->se->op.access(req, nodeid, arg->mask);
1251 else
1252 fuse_reply_err(req, ENOSYS);
1253}
1254
1255static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1256{
1257 (void) inarg;
1258
1259 if (req->se->op.readlink)
1260 req->se->op.readlink(req, nodeid);
1261 else
1262 fuse_reply_err(req, ENOSYS);
1263}
1264
1265static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1266{
1267 struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
1268 char *name = PARAM(arg);
1269
1270 if (req->se->conn.proto_minor >= 12)
1271 req->ctx.umask = arg->umask;
1272 else
1273 name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
1274
1275 if (req->se->op.mknod)
1276 req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
1277 else
1278 fuse_reply_err(req, ENOSYS);
1279}
1280
1281static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1282{
1283 struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
1284
1285 if (req->se->conn.proto_minor >= 12)
1286 req->ctx.umask = arg->umask;
1287
1288 if (req->se->op.mkdir)
1289 req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
1290 else
1291 fuse_reply_err(req, ENOSYS);
1292}
1293
1294static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1295{
1296 char *name = (char *) inarg;
1297
1298 if (req->se->op.unlink)
1299 req->se->op.unlink(req, nodeid, name);
1300 else
1301 fuse_reply_err(req, ENOSYS);
1302}
1303
1304static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1305{
1306 char *name = (char *) inarg;
1307
1308 if (req->se->op.rmdir)
1309 req->se->op.rmdir(req, nodeid, name);
1310 else
1311 fuse_reply_err(req, ENOSYS);
1312}
1313
1314static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1315{
1316 char *name = (char *) inarg;
1317 char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
1318
1319 if (req->se->op.symlink)
1320 req->se->op.symlink(req, linkname, nodeid, name);
1321 else
1322 fuse_reply_err(req, ENOSYS);
1323}
1324
1325static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1326{
1327 struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
1328 char *oldname = PARAM(arg);
1329 char *newname = oldname + strlen(oldname) + 1;
1330
1331 if (req->se->op.rename)
1332 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1333 0);
1334 else
1335 fuse_reply_err(req, ENOSYS);
1336}
1337
1338static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1339{
1340 struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
1341 char *oldname = PARAM(arg);
1342 char *newname = oldname + strlen(oldname) + 1;
1343
1344 if (req->se->op.rename)
1345 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1346 arg->flags);
1347 else
1348 fuse_reply_err(req, ENOSYS);
1349}
1350
1351static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1352{
1353 struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
1354
1355 if (req->se->op.link)
1356 req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
1357 else
1358 fuse_reply_err(req, ENOSYS);
1359}
1360
1361static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1362{
1363 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
1364
1365 if (req->se->op.tmpfile) {
1366 struct fuse_file_info fi;
1367
1368 memset(&fi, 0, sizeof(fi));
1369 fi.flags = arg->flags;
1370
1371 if (req->se->conn.proto_minor >= 12)
1372 req->ctx.umask = arg->umask;
1373
1374 req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
1375 } else
1376 fuse_reply_err(req, ENOSYS);
1377}
1378
1379static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1380{
1381 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
1382
1383 if (req->se->op.create) {
1384 struct fuse_file_info fi;
1385 char *name = PARAM(arg);
1386
1387 memset(&fi, 0, sizeof(fi));
1388 fi.flags = arg->flags;
1389
1390 if (req->se->conn.proto_minor >= 12)
1391 req->ctx.umask = arg->umask;
1392 else
1393 name = (char *) inarg + sizeof(struct fuse_open_in);
1394
1395 req->se->op.create(req, nodeid, name, arg->mode, &fi);
1396 } else
1397 fuse_reply_err(req, ENOSYS);
1398}
1399
1400static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1401{
1402 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
1403 struct fuse_file_info fi;
1404
1405 memset(&fi, 0, sizeof(fi));
1406 fi.flags = arg->flags;
1407
1408 if (req->se->op.open)
1409 req->se->op.open(req, nodeid, &fi);
1410 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
1411 fuse_reply_err(req, ENOSYS);
1412 else
1413 fuse_reply_open(req, &fi);
1414}
1415
1416static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1417{
1418 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
1419
1420 if (req->se->op.read) {
1421 struct fuse_file_info fi;
1422
1423 memset(&fi, 0, sizeof(fi));
1424 fi.fh = arg->fh;
1425 if (req->se->conn.proto_minor >= 9) {
1426 fi.lock_owner = arg->lock_owner;
1427 fi.flags = arg->flags;
1428 }
1429 req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
1430 } else
1431 fuse_reply_err(req, ENOSYS);
1432}
1433
1434static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1435{
1436 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
1437 struct fuse_file_info fi;
1438 char *param;
1439
1440 memset(&fi, 0, sizeof(fi));
1441 fi.fh = arg->fh;
1442 fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
1443
1444 if (req->se->conn.proto_minor < 9) {
1445 param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1446 } else {
1447 fi.lock_owner = arg->lock_owner;
1448 fi.flags = arg->flags;
1449 param = PARAM(arg);
1450 }
1451
1452 if (req->se->op.write)
1453 req->se->op.write(req, nodeid, param, arg->size,
1454 arg->offset, &fi);
1455 else
1456 fuse_reply_err(req, ENOSYS);
1457}
1458
1459static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
1460 const struct fuse_buf *ibuf)
1461{
1462 struct fuse_session *se = req->se;
1463 struct fuse_bufvec bufv = {
1464 .buf[0] = *ibuf,
1465 .count = 1,
1466 };
1467 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
1468 struct fuse_file_info fi;
1469
1470 memset(&fi, 0, sizeof(fi));
1471 fi.fh = arg->fh;
1472 fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
1473
1474 if (se->conn.proto_minor < 9) {
1475 bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1476 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1477 FUSE_COMPAT_WRITE_IN_SIZE;
1478 assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
1479 } else {
1480 fi.lock_owner = arg->lock_owner;
1481 fi.flags = arg->flags;
1482 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
1483 bufv.buf[0].mem = PARAM(arg);
1484
1485 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1486 sizeof(struct fuse_write_in);
1487 }
1488 if (bufv.buf[0].size < arg->size) {
1489 fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
1490 fuse_reply_err(req, EIO);
1491 goto out;
1492 }
1493 bufv.buf[0].size = arg->size;
1494
1495 se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
1496
1497out:
1498 /* Need to reset the pipe if ->write_buf() didn't consume all data */
1499 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
1500 fuse_ll_clear_pipe(se);
1501}
1502
1503static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1504{
1505 struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
1506 struct fuse_file_info fi;
1507
1508 memset(&fi, 0, sizeof(fi));
1509 fi.fh = arg->fh;
1510 fi.flush = 1;
1511 if (req->se->conn.proto_minor >= 7)
1512 fi.lock_owner = arg->lock_owner;
1513
1514 if (req->se->op.flush)
1515 req->se->op.flush(req, nodeid, &fi);
1516 else
1517 fuse_reply_err(req, ENOSYS);
1518}
1519
1520static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1521{
1522 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
1523 struct fuse_file_info fi;
1524
1525 memset(&fi, 0, sizeof(fi));
1526 fi.flags = arg->flags;
1527 fi.fh = arg->fh;
1528 if (req->se->conn.proto_minor >= 8) {
1529 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
1530 fi.lock_owner = arg->lock_owner;
1531 }
1532 if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
1533 fi.flock_release = 1;
1534 fi.lock_owner = arg->lock_owner;
1535 }
1536
1537 if (req->se->op.release)
1538 req->se->op.release(req, nodeid, &fi);
1539 else
1540 fuse_reply_err(req, 0);
1541}
1542
1543static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1544{
1545 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
1546 struct fuse_file_info fi;
1547 int datasync = arg->fsync_flags & 1;
1548
1549 memset(&fi, 0, sizeof(fi));
1550 fi.fh = arg->fh;
1551
1552 if (req->se->op.fsync)
1553 req->se->op.fsync(req, nodeid, datasync, &fi);
1554 else
1555 fuse_reply_err(req, ENOSYS);
1556}
1557
1558static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1559{
1560 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
1561 struct fuse_file_info fi;
1562
1563 memset(&fi, 0, sizeof(fi));
1564 fi.flags = arg->flags;
1565
1566 if (req->se->op.opendir)
1567 req->se->op.opendir(req, nodeid, &fi);
1568 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
1569 fuse_reply_err(req, ENOSYS);
1570 else
1571 fuse_reply_open(req, &fi);
1572}
1573
1574static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1575{
1576 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
1577 struct fuse_file_info fi;
1578
1579 memset(&fi, 0, sizeof(fi));
1580 fi.fh = arg->fh;
1581
1582 if (req->se->op.readdir)
1583 req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
1584 else
1585 fuse_reply_err(req, ENOSYS);
1586}
1587
1588static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1589{
1590 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
1591 struct fuse_file_info fi;
1592
1593 memset(&fi, 0, sizeof(fi));
1594 fi.fh = arg->fh;
1595
1596 if (req->se->op.readdirplus)
1597 req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
1598 else
1599 fuse_reply_err(req, ENOSYS);
1600}
1601
1602static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1603{
1604 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
1605 struct fuse_file_info fi;
1606
1607 memset(&fi, 0, sizeof(fi));
1608 fi.flags = arg->flags;
1609 fi.fh = arg->fh;
1610
1611 if (req->se->op.releasedir)
1612 req->se->op.releasedir(req, nodeid, &fi);
1613 else
1614 fuse_reply_err(req, 0);
1615}
1616
1617static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1618{
1619 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
1620 struct fuse_file_info fi;
1621 int datasync = arg->fsync_flags & 1;
1622
1623 memset(&fi, 0, sizeof(fi));
1624 fi.fh = arg->fh;
1625
1626 if (req->se->op.fsyncdir)
1627 req->se->op.fsyncdir(req, nodeid, datasync, &fi);
1628 else
1629 fuse_reply_err(req, ENOSYS);
1630}
1631
1632static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1633{
1634 (void) nodeid;
1635 (void) inarg;
1636
1637 if (req->se->op.statfs)
1638 req->se->op.statfs(req, nodeid);
1639 else {
1640 struct statvfs buf = {
1641 .f_namemax = 255,
1642 .f_bsize = 512,
1643 };
1644 fuse_reply_statfs(req, &buf);
1645 }
1646}
1647
1648static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1649{
1650 struct fuse_session *se = req->se;
1651 unsigned int xattr_ext = !!(se->conn.want_ext & FUSE_CAP_SETXATTR_EXT);
1652 struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
1653 char *name = xattr_ext ? PARAM(arg) :
1654 (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
1655 char *value = name + strlen(name) + 1;
1656
1657 /* XXX:The API should be extended to support extra_flags/setxattr_flags */
1658 if (req->se->op.setxattr)
1659 req->se->op.setxattr(req, nodeid, name, value, arg->size,
1660 arg->flags);
1661 else
1662 fuse_reply_err(req, ENOSYS);
1663}
1664
1665static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1666{
1667 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
1668
1669 if (req->se->op.getxattr)
1670 req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
1671 else
1672 fuse_reply_err(req, ENOSYS);
1673}
1674
1675static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1676{
1677 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
1678
1679 if (req->se->op.listxattr)
1680 req->se->op.listxattr(req, nodeid, arg->size);
1681 else
1682 fuse_reply_err(req, ENOSYS);
1683}
1684
1685static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1686{
1687 char *name = (char *) inarg;
1688
1689 if (req->se->op.removexattr)
1690 req->se->op.removexattr(req, nodeid, name);
1691 else
1692 fuse_reply_err(req, ENOSYS);
1693}
1694
1695static void convert_fuse_file_lock(struct fuse_file_lock *fl,
1696 struct flock *flock)
1697{
1698 memset(flock, 0, sizeof(struct flock));
1699 flock->l_type = fl->type;
1700 flock->l_whence = SEEK_SET;
1701 flock->l_start = fl->start;
1702 if (fl->end == OFFSET_MAX)
1703 flock->l_len = 0;
1704 else
1705 flock->l_len = fl->end - fl->start + 1;
1706 flock->l_pid = fl->pid;
1707}
1708
1709static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1710{
1711 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
1712 struct fuse_file_info fi;
1713 struct flock flock;
1714
1715 memset(&fi, 0, sizeof(fi));
1716 fi.fh = arg->fh;
1717 fi.lock_owner = arg->owner;
1718
1719 convert_fuse_file_lock(&arg->lk, &flock);
1720 if (req->se->op.getlk)
1721 req->se->op.getlk(req, nodeid, &fi, &flock);
1722 else
1723 fuse_reply_err(req, ENOSYS);
1724}
1725
1726static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
1727 const void *inarg, int sleep)
1728{
1729 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
1730 struct fuse_file_info fi;
1731 struct flock flock;
1732
1733 memset(&fi, 0, sizeof(fi));
1734 fi.fh = arg->fh;
1735 fi.lock_owner = arg->owner;
1736
1737 if (arg->lk_flags & FUSE_LK_FLOCK) {
1738 int op = 0;
1739
1740 switch (arg->lk.type) {
1741 case F_RDLCK:
1742 op = LOCK_SH;
1743 break;
1744 case F_WRLCK:
1745 op = LOCK_EX;
1746 break;
1747 case F_UNLCK:
1748 op = LOCK_UN;
1749 break;
1750 }
1751 if (!sleep)
1752 op |= LOCK_NB;
1753
1754 if (req->se->op.flock)
1755 req->se->op.flock(req, nodeid, &fi, op);
1756 else
1757 fuse_reply_err(req, ENOSYS);
1758 } else {
1759 convert_fuse_file_lock(&arg->lk, &flock);
1760 if (req->se->op.setlk)
1761 req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
1762 else
1763 fuse_reply_err(req, ENOSYS);
1764 }
1765}
1766
1767static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1768{
1769 do_setlk_common(req, nodeid, inarg, 0);
1770}
1771
1772static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1773{
1774 do_setlk_common(req, nodeid, inarg, 1);
1775}
1776
1777static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
1778{
1779 struct fuse_req *curr;
1780
1781 for (curr = se->list.next; curr != &se->list; curr = curr->next) {
1782 if (curr->unique == req->u.i.unique) {
1784 void *data;
1785
1786 curr->ref_cnt++;
1787 pthread_mutex_unlock(&se->lock);
1788
1789 /* Ugh, ugly locking */
1790 pthread_mutex_lock(&curr->lock);
1791 pthread_mutex_lock(&se->lock);
1792 curr->interrupted = 1;
1793 func = curr->u.ni.func;
1794 data = curr->u.ni.data;
1795 pthread_mutex_unlock(&se->lock);
1796 if (func)
1797 func(curr, data);
1798 pthread_mutex_unlock(&curr->lock);
1799
1800 pthread_mutex_lock(&se->lock);
1801 curr->ref_cnt--;
1802 if (!curr->ref_cnt) {
1803 destroy_req(curr);
1804 }
1805
1806 return 1;
1807 }
1808 }
1809 for (curr = se->interrupts.next; curr != &se->interrupts;
1810 curr = curr->next) {
1811 if (curr->u.i.unique == req->u.i.unique)
1812 return 1;
1813 }
1814 return 0;
1815}
1816
1817static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1818{
1819 struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
1820 struct fuse_session *se = req->se;
1821
1822 (void) nodeid;
1823 if (se->debug)
1824 fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
1825 (unsigned long long) arg->unique);
1826
1827 req->u.i.unique = arg->unique;
1828
1829 pthread_mutex_lock(&se->lock);
1830 if (find_interrupted(se, req)) {
1831 fuse_chan_put(req->ch);
1832 req->ch = NULL;
1833 destroy_req(req);
1834 } else
1835 list_add_req(req, &se->interrupts);
1836 pthread_mutex_unlock(&se->lock);
1837}
1838
1839static struct fuse_req *check_interrupt(struct fuse_session *se,
1840 struct fuse_req *req)
1841{
1842 struct fuse_req *curr;
1843
1844 for (curr = se->interrupts.next; curr != &se->interrupts;
1845 curr = curr->next) {
1846 if (curr->u.i.unique == req->unique) {
1847 req->interrupted = 1;
1848 list_del_req(curr);
1849 fuse_chan_put(curr->ch);
1850 curr->ch = NULL;
1851 destroy_req(curr);
1852 return NULL;
1853 }
1854 }
1855 curr = se->interrupts.next;
1856 if (curr != &se->interrupts) {
1857 list_del_req(curr);
1858 list_init_req(curr);
1859 return curr;
1860 } else
1861 return NULL;
1862}
1863
1864static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1865{
1866 struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
1867
1868 if (req->se->op.bmap)
1869 req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
1870 else
1871 fuse_reply_err(req, ENOSYS);
1872}
1873
1874static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1875{
1876 struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
1877 unsigned int flags = arg->flags;
1878 void *in_buf = arg->in_size ? PARAM(arg) : NULL;
1879 struct fuse_file_info fi;
1880
1881 if (flags & FUSE_IOCTL_DIR &&
1882 !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
1883 fuse_reply_err(req, ENOTTY);
1884 return;
1885 }
1886
1887 memset(&fi, 0, sizeof(fi));
1888 fi.fh = arg->fh;
1889
1890 if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
1891 !(flags & FUSE_IOCTL_32BIT)) {
1892 req->ioctl_64bit = 1;
1893 }
1894
1895 if (req->se->op.ioctl)
1896 req->se->op.ioctl(req, nodeid, arg->cmd,
1897 (void *)(uintptr_t)arg->arg, &fi, flags,
1898 in_buf, arg->in_size, arg->out_size);
1899 else
1900 fuse_reply_err(req, ENOSYS);
1901}
1902
1903void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
1904{
1905 free(ph);
1906}
1907
1908static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1909{
1910 struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
1911 struct fuse_file_info fi;
1912
1913 memset(&fi, 0, sizeof(fi));
1914 fi.fh = arg->fh;
1915 fi.poll_events = arg->events;
1916
1917 if (req->se->op.poll) {
1918 struct fuse_pollhandle *ph = NULL;
1919
1920 if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
1921 ph = malloc(sizeof(struct fuse_pollhandle));
1922 if (ph == NULL) {
1923 fuse_reply_err(req, ENOMEM);
1924 return;
1925 }
1926 ph->kh = arg->kh;
1927 ph->se = req->se;
1928 }
1929
1930 req->se->op.poll(req, nodeid, &fi, ph);
1931 } else {
1932 fuse_reply_err(req, ENOSYS);
1933 }
1934}
1935
1936static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1937{
1938 struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
1939 struct fuse_file_info fi;
1940
1941 memset(&fi, 0, sizeof(fi));
1942 fi.fh = arg->fh;
1943
1944 if (req->se->op.fallocate)
1945 req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
1946 else
1947 fuse_reply_err(req, ENOSYS);
1948}
1949
1950static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
1951{
1952 struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
1953 struct fuse_file_info fi_in, fi_out;
1954
1955 memset(&fi_in, 0, sizeof(fi_in));
1956 fi_in.fh = arg->fh_in;
1957
1958 memset(&fi_out, 0, sizeof(fi_out));
1959 fi_out.fh = arg->fh_out;
1960
1961
1962 if (req->se->op.copy_file_range)
1963 req->se->op.copy_file_range(req, nodeid_in, arg->off_in,
1964 &fi_in, arg->nodeid_out,
1965 arg->off_out, &fi_out, arg->len,
1966 arg->flags);
1967 else
1968 fuse_reply_err(req, ENOSYS);
1969}
1970
1971static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1972{
1973 struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg;
1974 struct fuse_file_info fi;
1975
1976 memset(&fi, 0, sizeof(fi));
1977 fi.fh = arg->fh;
1978
1979 if (req->se->op.lseek)
1980 req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
1981 else
1982 fuse_reply_err(req, ENOSYS);
1983}
1984
1985static bool want_flags_valid(uint64_t capable, uint64_t want)
1986{
1987 uint64_t unknown_flags = want & (~capable);
1988 if (unknown_flags != 0) {
1989 fuse_log(FUSE_LOG_ERR,
1990 "fuse: unknown connection 'want' flags: 0x%08lx\n",
1991 unknown_flags);
1992 return false;
1993 }
1994 return true;
1995}
1996
2001{
2002 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2003
2004 /*
2005 * Convert want to want_ext if necessary.
2006 * For the high level interface this function might be called
2007 * twice, once from the high level interface and once from the
2008 * low level interface. Both, with different want_ext_default and
2009 * want_default values. In order to suppress a failure for the
2010 * second call, we check if the lower 32 bits of want_ext are
2011 * already set to the value of want.
2012 */
2013 if (conn->want != se->conn_want &&
2014 fuse_lower_32_bits(conn->want_ext) != conn->want) {
2015 if (conn->want_ext != se->conn_want_ext) {
2016 fuse_log(FUSE_LOG_ERR,
2017 "%s: Both conn->want_ext and conn->want are set.\n"
2018 "want=%x, want_ext=%lx, se->want=%lx se->want_ext=%lx\n",
2019 __func__, conn->want, conn->want_ext,
2020 se->conn_want, se->conn_want_ext);
2021 return -EINVAL;
2022 }
2023
2024 /* high bits from want_ext, low bits from want */
2025 conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
2026 conn->want;
2027 }
2028
2029 /* ensure there won't be a second conversion */
2030 conn->want = fuse_lower_32_bits(conn->want_ext);
2031
2032 return 0;
2033}
2034
2036 uint64_t flag)
2037{
2038 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2039
2040 if (conn->capable_ext & flag) {
2041 conn->want_ext |= flag;
2042 se->conn_want_ext |= flag;
2043 conn->want |= flag;
2044 se->conn_want |= flag;
2045 return true;
2046 }
2047 return false;
2048}
2049
2051 uint64_t flag)
2052{
2053 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2054
2055 conn->want_ext &= ~flag;
2056 se->conn_want_ext &= ~flag;
2057 conn->want &= ~flag;
2058 se->conn_want &= ~flag;
2059}
2060
2062 uint64_t flag)
2063{
2064 return conn->capable_ext & flag ? true : false;
2065}
2066
2067
2068/* Prevent bogus data races (bogus since "init" is called before
2069 * multi-threading becomes relevant */
2070static __attribute__((no_sanitize("thread")))
2071void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2072{
2073 struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
2074 struct fuse_init_out outarg;
2075 struct fuse_session *se = req->se;
2076 size_t bufsize = se->bufsize;
2077 size_t outargsize = sizeof(outarg);
2078 uint64_t inargflags = 0;
2079 uint64_t outargflags = 0;
2080 bool buf_reallocable = se->buf_reallocable;
2081 (void) nodeid;
2082 if (se->debug) {
2083 fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
2084 if (arg->major == 7 && arg->minor >= 6) {
2085 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
2086 fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
2087 arg->max_readahead);
2088 }
2089 }
2090 se->conn.proto_major = arg->major;
2091 se->conn.proto_minor = arg->minor;
2092 se->conn.capable_ext = 0;
2093 se->conn.want_ext = 0;
2094
2095 memset(&outarg, 0, sizeof(outarg));
2096 outarg.major = FUSE_KERNEL_VERSION;
2097 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
2098
2099 if (arg->major < 7) {
2100 fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
2101 arg->major, arg->minor);
2102 fuse_reply_err(req, EPROTO);
2103 return;
2104 }
2105
2106 if (arg->major > 7) {
2107 /* Wait for a second INIT request with a 7.X version */
2108 send_reply_ok(req, &outarg, sizeof(outarg));
2109 return;
2110 }
2111
2112 if (arg->minor >= 6) {
2113 if (arg->max_readahead < se->conn.max_readahead)
2114 se->conn.max_readahead = arg->max_readahead;
2115 inargflags = arg->flags;
2116 if (inargflags & FUSE_INIT_EXT)
2117 inargflags = inargflags | (uint64_t) arg->flags2 << 32;
2118 if (inargflags & FUSE_ASYNC_READ)
2119 se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
2120 if (inargflags & FUSE_POSIX_LOCKS)
2121 se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
2122 if (inargflags & FUSE_ATOMIC_O_TRUNC)
2123 se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
2124 if (inargflags & FUSE_EXPORT_SUPPORT)
2125 se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
2126 if (inargflags & FUSE_DONT_MASK)
2127 se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
2128 if (inargflags & FUSE_FLOCK_LOCKS)
2129 se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
2130 if (inargflags & FUSE_AUTO_INVAL_DATA)
2131 se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
2132 if (inargflags & FUSE_DO_READDIRPLUS)
2133 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
2134 if (inargflags & FUSE_READDIRPLUS_AUTO)
2135 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
2136 if (inargflags & FUSE_ASYNC_DIO)
2137 se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
2138 if (inargflags & FUSE_WRITEBACK_CACHE)
2139 se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
2140 if (inargflags & FUSE_NO_OPEN_SUPPORT)
2141 se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
2142 if (inargflags & FUSE_PARALLEL_DIROPS)
2143 se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
2144 if (inargflags & FUSE_POSIX_ACL)
2145 se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
2146 if (inargflags & FUSE_HANDLE_KILLPRIV)
2147 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
2148 if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
2149 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
2150 if (inargflags & FUSE_CACHE_SYMLINKS)
2151 se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
2152 if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
2153 se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
2154 if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
2155 se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
2156 if (inargflags & FUSE_SETXATTR_EXT)
2157 se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
2158 if (!(inargflags & FUSE_MAX_PAGES)) {
2159 size_t max_bufsize =
2160 FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
2161 + FUSE_BUFFER_HEADER_SIZE;
2162 if (bufsize > max_bufsize) {
2163 bufsize = max_bufsize;
2164 }
2165 buf_reallocable = false;
2166 }
2167 if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
2168 se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
2169 if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
2170 se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
2171 if (inargflags & FUSE_PASSTHROUGH)
2172 se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
2173 if (inargflags & FUSE_NO_EXPORT_SUPPORT)
2174 se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
2175 } else {
2176 se->conn.max_readahead = 0;
2177 }
2178
2179 if (se->conn.proto_minor >= 14) {
2180#ifdef HAVE_SPLICE
2181#ifdef HAVE_VMSPLICE
2182 if ((se->io == NULL) || (se->io->splice_send != NULL)) {
2183 se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
2185 }
2186#endif
2187 if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
2188 se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
2189 }
2190#endif
2191 }
2192 if (se->conn.proto_minor >= 18)
2193 se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
2194
2195 /* Default settings for modern filesystems.
2196 *
2197 * Most of these capabilities were disabled by default in
2198 * libfuse2 for backwards compatibility reasons. In libfuse3,
2199 * we can finally enable them by default (as long as they're
2200 * supported by the kernel).
2201 */
2202#define LL_SET_DEFAULT(cond, cap) \
2203 if ((cond)) \
2204 fuse_set_feature_flag(&se->conn, cap)
2205
2206 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
2207 LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
2208 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
2209 LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
2210 LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
2211 LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
2212 LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
2214 LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
2215 LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
2216 LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
2218
2219 /* This could safely become default, but libfuse needs an API extension
2220 * to support it
2221 * LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
2222 */
2223
2224 se->conn.time_gran = 1;
2225
2226 se->got_init = 1;
2227 if (se->op.init) {
2228 // Apply the first 32 bits of capable_ext to capable
2229 se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
2230
2231 se->op.init(se->userdata, &se->conn);
2232
2233 /*
2234 * se->conn.want is 32-bit value and deprecated in favour of
2235 * se->conn.want_ext
2236 * Userspace might still use conn.want - we need to convert it
2237 */
2239 }
2240
2241 if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
2242 fuse_reply_err(req, EPROTO);
2243 se->error = -EPROTO;
2245 return;
2246 }
2247
2248 unsigned max_read_mo = get_max_read(se->mo);
2249 if (se->conn.max_read != max_read_mo) {
2250 fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
2251 "requested different maximum read size (%u vs %u)\n",
2252 se->conn.max_read, max_read_mo);
2253 fuse_reply_err(req, EPROTO);
2254 se->error = -EPROTO;
2256 return;
2257 }
2258
2259 if (bufsize < FUSE_MIN_READ_BUFFER) {
2260 fuse_log(FUSE_LOG_ERR,
2261 "fuse: warning: buffer size too small: %zu\n",
2262 bufsize);
2263 bufsize = FUSE_MIN_READ_BUFFER;
2264 }
2265
2266 if (buf_reallocable)
2267 bufsize = UINT_MAX;
2268 se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE);
2269 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
2270
2271 if (arg->flags & FUSE_MAX_PAGES) {
2272 outarg.flags |= FUSE_MAX_PAGES;
2273 outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
2274 }
2275 outargflags = outarg.flags;
2276 /* Always enable big writes, this is superseded
2277 by the max_write option */
2278 outargflags |= FUSE_BIG_WRITES;
2279
2280 if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
2281 outargflags |= FUSE_ASYNC_READ;
2282 if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
2283 outargflags |= FUSE_POSIX_LOCKS;
2284 if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
2285 outargflags |= FUSE_ATOMIC_O_TRUNC;
2286 if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
2287 outargflags |= FUSE_EXPORT_SUPPORT;
2288 if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
2289 outargflags |= FUSE_DONT_MASK;
2290 if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
2291 outargflags |= FUSE_FLOCK_LOCKS;
2292 if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
2293 outargflags |= FUSE_AUTO_INVAL_DATA;
2294 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
2295 outargflags |= FUSE_DO_READDIRPLUS;
2296 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
2297 outargflags |= FUSE_READDIRPLUS_AUTO;
2298 if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
2299 outargflags |= FUSE_ASYNC_DIO;
2300 if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
2301 outargflags |= FUSE_WRITEBACK_CACHE;
2302 if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
2303 outargflags |= FUSE_PARALLEL_DIROPS;
2304 if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
2305 outargflags |= FUSE_POSIX_ACL;
2306 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
2307 outargflags |= FUSE_HANDLE_KILLPRIV;
2308 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
2309 outargflags |= FUSE_HANDLE_KILLPRIV_V2;
2310 if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
2311 outargflags |= FUSE_CACHE_SYMLINKS;
2312 if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
2313 outargflags |= FUSE_EXPLICIT_INVAL_DATA;
2314 if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
2315 outargflags |= FUSE_SETXATTR_EXT;
2316 if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
2317 outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
2318 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
2319 outargflags |= FUSE_PASSTHROUGH;
2320 /*
2321 * outarg.max_stack_depth includes the fuse stack layer,
2322 * so it is one more than max_backing_stack_depth.
2323 */
2324 outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
2325 }
2326 if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
2327 outargflags |= FUSE_NO_EXPORT_SUPPORT;
2328
2329 if (inargflags & FUSE_INIT_EXT) {
2330 outargflags |= FUSE_INIT_EXT;
2331 outarg.flags2 = outargflags >> 32;
2332 }
2333
2334 outarg.flags = outargflags;
2335
2336 outarg.max_readahead = se->conn.max_readahead;
2337 outarg.max_write = se->conn.max_write;
2338 if (se->conn.proto_minor >= 13) {
2339 if (se->conn.max_background >= (1 << 16))
2340 se->conn.max_background = (1 << 16) - 1;
2341 if (se->conn.congestion_threshold > se->conn.max_background)
2342 se->conn.congestion_threshold = se->conn.max_background;
2343 if (!se->conn.congestion_threshold) {
2344 se->conn.congestion_threshold =
2345 se->conn.max_background * 3 / 4;
2346 }
2347
2348 outarg.max_background = se->conn.max_background;
2349 outarg.congestion_threshold = se->conn.congestion_threshold;
2350 }
2351 if (se->conn.proto_minor >= 23)
2352 outarg.time_gran = se->conn.time_gran;
2353
2354 if (se->debug) {
2355 fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
2356 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
2357 fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
2358 outarg.max_readahead);
2359 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
2360 fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
2361 outarg.max_background);
2362 fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
2363 outarg.congestion_threshold);
2364 fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
2365 outarg.time_gran);
2366 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
2367 fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
2368 outarg.max_stack_depth);
2369 }
2370 if (arg->minor < 5)
2371 outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
2372 else if (arg->minor < 23)
2373 outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
2374
2375 send_reply_ok(req, &outarg, outargsize);
2376}
2377
2378static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2379{
2380 struct fuse_session *se = req->se;
2381 char *mountpoint;
2382
2383 (void) nodeid;
2384 (void) inarg;
2385
2386 mountpoint = atomic_exchange(&se->mountpoint, NULL);
2387 free(mountpoint);
2388
2389 se->got_destroy = 1;
2390 se->got_init = 0;
2391 if (se->op.destroy)
2392 se->op.destroy(se->userdata);
2393
2394 send_reply_ok(req, NULL, 0);
2395}
2396
2397static void list_del_nreq(struct fuse_notify_req *nreq)
2398{
2399 struct fuse_notify_req *prev = nreq->prev;
2400 struct fuse_notify_req *next = nreq->next;
2401 prev->next = next;
2402 next->prev = prev;
2403}
2404
2405static void list_add_nreq(struct fuse_notify_req *nreq,
2406 struct fuse_notify_req *next)
2407{
2408 struct fuse_notify_req *prev = next->prev;
2409 nreq->next = next;
2410 nreq->prev = prev;
2411 prev->next = nreq;
2412 next->prev = nreq;
2413}
2414
2415static void list_init_nreq(struct fuse_notify_req *nreq)
2416{
2417 nreq->next = nreq;
2418 nreq->prev = nreq;
2419}
2420
2421static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
2422 const void *inarg, const struct fuse_buf *buf)
2423{
2424 struct fuse_session *se = req->se;
2425 struct fuse_notify_req *nreq;
2426 struct fuse_notify_req *head;
2427
2428 pthread_mutex_lock(&se->lock);
2429 head = &se->notify_list;
2430 for (nreq = head->next; nreq != head; nreq = nreq->next) {
2431 if (nreq->unique == req->unique) {
2432 list_del_nreq(nreq);
2433 break;
2434 }
2435 }
2436 pthread_mutex_unlock(&se->lock);
2437
2438 if (nreq != head)
2439 nreq->reply(nreq, req, nodeid, inarg, buf);
2440}
2441
2442static int send_notify_iov(struct fuse_session *se, int notify_code,
2443 struct iovec *iov, int count)
2444{
2445 struct fuse_out_header out;
2446
2447 if (!se->got_init)
2448 return -ENOTCONN;
2449
2450 out.unique = 0;
2451 out.error = notify_code;
2452 iov[0].iov_base = &out;
2453 iov[0].iov_len = sizeof(struct fuse_out_header);
2454
2455 return fuse_send_msg(se, NULL, iov, count);
2456}
2457
2458int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
2459{
2460 if (ph != NULL) {
2461 struct fuse_notify_poll_wakeup_out outarg;
2462 struct iovec iov[2];
2463
2464 outarg.kh = ph->kh;
2465
2466 iov[1].iov_base = &outarg;
2467 iov[1].iov_len = sizeof(outarg);
2468
2469 return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
2470 } else {
2471 return 0;
2472 }
2473}
2474
2475int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
2476 off_t off, off_t len)
2477{
2478 struct fuse_notify_inval_inode_out outarg;
2479 struct iovec iov[2];
2480
2481 if (!se)
2482 return -EINVAL;
2483
2484 if (se->conn.proto_minor < 12)
2485 return -ENOSYS;
2486
2487 outarg.ino = ino;
2488 outarg.off = off;
2489 outarg.len = len;
2490
2491 iov[1].iov_base = &outarg;
2492 iov[1].iov_len = sizeof(outarg);
2493
2494 return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
2495}
2496
2516static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
2517 const char *name, size_t namelen,
2518 enum fuse_notify_entry_flags flags)
2519{
2520 struct fuse_notify_inval_entry_out outarg;
2521 struct iovec iov[3];
2522
2523 if (!se)
2524 return -EINVAL;
2525
2526 if (se->conn.proto_minor < 12)
2527 return -ENOSYS;
2528
2529 outarg.parent = parent;
2530 outarg.namelen = namelen;
2531 outarg.flags = 0;
2532 if (flags & FUSE_LL_EXPIRE_ONLY)
2533 outarg.flags |= FUSE_EXPIRE_ONLY;
2534
2535 iov[1].iov_base = &outarg;
2536 iov[1].iov_len = sizeof(outarg);
2537 iov[2].iov_base = (void *)name;
2538 iov[2].iov_len = namelen + 1;
2539
2540 return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
2541}
2542
2543int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
2544 const char *name, size_t namelen)
2545{
2546 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
2547}
2548
2549int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
2550 const char *name, size_t namelen)
2551{
2552 if (!se)
2553 return -EINVAL;
2554
2555 if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
2556 return -ENOSYS;
2557
2558 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
2559}
2560
2561
2562int fuse_lowlevel_notify_delete(struct fuse_session *se,
2563 fuse_ino_t parent, fuse_ino_t child,
2564 const char *name, size_t namelen)
2565{
2566 struct fuse_notify_delete_out outarg;
2567 struct iovec iov[3];
2568
2569 if (!se)
2570 return -EINVAL;
2571
2572 if (se->conn.proto_minor < 18)
2573 return -ENOSYS;
2574
2575 outarg.parent = parent;
2576 outarg.child = child;
2577 outarg.namelen = namelen;
2578 outarg.padding = 0;
2579
2580 iov[1].iov_base = &outarg;
2581 iov[1].iov_len = sizeof(outarg);
2582 iov[2].iov_base = (void *)name;
2583 iov[2].iov_len = namelen + 1;
2584
2585 return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
2586}
2587
2588int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
2589 off_t offset, struct fuse_bufvec *bufv,
2590 enum fuse_buf_copy_flags flags)
2591{
2592 struct fuse_out_header out;
2593 struct fuse_notify_store_out outarg;
2594 struct iovec iov[3];
2595 size_t size = fuse_buf_size(bufv);
2596 int res;
2597
2598 if (!se)
2599 return -EINVAL;
2600
2601 if (se->conn.proto_minor < 15)
2602 return -ENOSYS;
2603
2604 out.unique = 0;
2605 out.error = FUSE_NOTIFY_STORE;
2606
2607 outarg.nodeid = ino;
2608 outarg.offset = offset;
2609 outarg.size = size;
2610 outarg.padding = 0;
2611
2612 iov[0].iov_base = &out;
2613 iov[0].iov_len = sizeof(out);
2614 iov[1].iov_base = &outarg;
2615 iov[1].iov_len = sizeof(outarg);
2616
2617 res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags);
2618 if (res > 0)
2619 res = -res;
2620
2621 return res;
2622}
2623
2624struct fuse_retrieve_req {
2625 struct fuse_notify_req nreq;
2626 void *cookie;
2627};
2628
2629static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
2630 fuse_req_t req, fuse_ino_t ino,
2631 const void *inarg,
2632 const struct fuse_buf *ibuf)
2633{
2634 struct fuse_session *se = req->se;
2635 struct fuse_retrieve_req *rreq =
2636 container_of(nreq, struct fuse_retrieve_req, nreq);
2637 const struct fuse_notify_retrieve_in *arg = inarg;
2638 struct fuse_bufvec bufv = {
2639 .buf[0] = *ibuf,
2640 .count = 1,
2641 };
2642
2643 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
2644 bufv.buf[0].mem = PARAM(arg);
2645
2646 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
2647 sizeof(struct fuse_notify_retrieve_in);
2648
2649 if (bufv.buf[0].size < arg->size) {
2650 fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
2651 fuse_reply_none(req);
2652 goto out;
2653 }
2654 bufv.buf[0].size = arg->size;
2655
2656 if (se->op.retrieve_reply) {
2657 se->op.retrieve_reply(req, rreq->cookie, ino,
2658 arg->offset, &bufv);
2659 } else {
2660 fuse_reply_none(req);
2661 }
2662out:
2663 free(rreq);
2664 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
2665 fuse_ll_clear_pipe(se);
2666}
2667
2668int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
2669 size_t size, off_t offset, void *cookie)
2670{
2671 struct fuse_notify_retrieve_out outarg;
2672 struct iovec iov[2];
2673 struct fuse_retrieve_req *rreq;
2674 int err;
2675
2676 if (!se)
2677 return -EINVAL;
2678
2679 if (se->conn.proto_minor < 15)
2680 return -ENOSYS;
2681
2682 rreq = malloc(sizeof(*rreq));
2683 if (rreq == NULL)
2684 return -ENOMEM;
2685
2686 pthread_mutex_lock(&se->lock);
2687 rreq->cookie = cookie;
2688 rreq->nreq.unique = se->notify_ctr++;
2689 rreq->nreq.reply = fuse_ll_retrieve_reply;
2690 list_add_nreq(&rreq->nreq, &se->notify_list);
2691 pthread_mutex_unlock(&se->lock);
2692
2693 outarg.notify_unique = rreq->nreq.unique;
2694 outarg.nodeid = ino;
2695 outarg.offset = offset;
2696 outarg.size = size;
2697 outarg.padding = 0;
2698
2699 iov[1].iov_base = &outarg;
2700 iov[1].iov_len = sizeof(outarg);
2701
2702 err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
2703 if (err) {
2704 pthread_mutex_lock(&se->lock);
2705 list_del_nreq(&rreq->nreq);
2706 pthread_mutex_unlock(&se->lock);
2707 free(rreq);
2708 }
2709
2710 return err;
2711}
2712
2714{
2715 return req->se->userdata;
2716}
2717
2719{
2720 return &req->ctx;
2721}
2722
2724 void *data)
2725{
2726 pthread_mutex_lock(&req->lock);
2727 pthread_mutex_lock(&req->se->lock);
2728 req->u.ni.func = func;
2729 req->u.ni.data = data;
2730 pthread_mutex_unlock(&req->se->lock);
2731 if (req->interrupted && func)
2732 func(req, data);
2733 pthread_mutex_unlock(&req->lock);
2734}
2735
2737{
2738 int interrupted;
2739
2740 pthread_mutex_lock(&req->se->lock);
2741 interrupted = req->interrupted;
2742 pthread_mutex_unlock(&req->se->lock);
2743
2744 return interrupted;
2745}
2746
2747static struct {
2748 void (*func)(fuse_req_t, fuse_ino_t, const void *);
2749 const char *name;
2750} fuse_ll_ops[] = {
2751 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
2752 [FUSE_FORGET] = { do_forget, "FORGET" },
2753 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
2754 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
2755 [FUSE_READLINK] = { do_readlink, "READLINK" },
2756 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
2757 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
2758 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
2759 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
2760 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
2761 [FUSE_RENAME] = { do_rename, "RENAME" },
2762 [FUSE_LINK] = { do_link, "LINK" },
2763 [FUSE_OPEN] = { do_open, "OPEN" },
2764 [FUSE_READ] = { do_read, "READ" },
2765 [FUSE_WRITE] = { do_write, "WRITE" },
2766 [FUSE_STATFS] = { do_statfs, "STATFS" },
2767 [FUSE_RELEASE] = { do_release, "RELEASE" },
2768 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
2769 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
2770 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
2771 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
2772 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
2773 [FUSE_FLUSH] = { do_flush, "FLUSH" },
2774 [FUSE_INIT] = { do_init, "INIT" },
2775 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
2776 [FUSE_READDIR] = { do_readdir, "READDIR" },
2777 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
2778 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
2779 [FUSE_GETLK] = { do_getlk, "GETLK" },
2780 [FUSE_SETLK] = { do_setlk, "SETLK" },
2781 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
2782 [FUSE_ACCESS] = { do_access, "ACCESS" },
2783 [FUSE_CREATE] = { do_create, "CREATE" },
2784 [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
2785 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
2786 [FUSE_BMAP] = { do_bmap, "BMAP" },
2787 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
2788 [FUSE_POLL] = { do_poll, "POLL" },
2789 [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
2790 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
2791 [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
2792 [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
2793 [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
2794 [FUSE_RENAME2] = { do_rename2, "RENAME2" },
2795 [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
2796 [FUSE_LSEEK] = { do_lseek, "LSEEK" },
2797 [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
2798};
2799
2800/*
2801 * For ABI compatibility we cannot allow higher values than CUSE_INIT.
2802 * Without ABI compatibility we could use the size of the array.
2803 * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
2804 */
2805#define FUSE_MAXOP (CUSE_INIT + 1)
2806
2807static const char *opname(enum fuse_opcode opcode)
2808{
2809 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
2810 return "???";
2811 else
2812 return fuse_ll_ops[opcode].name;
2813}
2814
2815static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
2816 struct fuse_bufvec *src)
2817{
2818 ssize_t res = fuse_buf_copy(dst, src, 0);
2819 if (res < 0) {
2820 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
2821 return res;
2822 }
2823 if ((size_t)res < fuse_buf_size(dst)) {
2824 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
2825 return -1;
2826 }
2827 return 0;
2828}
2829
2830void fuse_session_process_buf(struct fuse_session *se,
2831 const struct fuse_buf *buf)
2832{
2833 fuse_session_process_buf_internal(se, buf, NULL);
2834}
2835
2836/* libfuse internal handler */
2837void fuse_session_process_buf_internal(struct fuse_session *se,
2838 const struct fuse_buf *buf, struct fuse_chan *ch)
2839{
2840 const size_t write_header_size = sizeof(struct fuse_in_header) +
2841 sizeof(struct fuse_write_in);
2842 struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
2843 struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
2844 struct fuse_in_header *in;
2845 const void *inarg;
2846 struct fuse_req *req;
2847 void *mbuf = NULL;
2848 int err;
2849 int res;
2850
2851 if (buf->flags & FUSE_BUF_IS_FD) {
2852 if (buf->size < tmpbuf.buf[0].size)
2853 tmpbuf.buf[0].size = buf->size;
2854
2855 mbuf = malloc(tmpbuf.buf[0].size);
2856 if (mbuf == NULL) {
2857 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
2858 goto clear_pipe;
2859 }
2860 tmpbuf.buf[0].mem = mbuf;
2861
2862 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
2863 if (res < 0)
2864 goto clear_pipe;
2865
2866 in = mbuf;
2867 } else {
2868 in = buf->mem;
2869 }
2870
2871 if (se->debug) {
2872 fuse_log(FUSE_LOG_DEBUG,
2873 "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
2874 (unsigned long long) in->unique,
2875 opname((enum fuse_opcode) in->opcode), in->opcode,
2876 (unsigned long long) in->nodeid, buf->size, in->pid);
2877 }
2878
2879 req = fuse_ll_alloc_req(se);
2880 if (req == NULL) {
2881 struct fuse_out_header out = {
2882 .unique = in->unique,
2883 .error = -ENOMEM,
2884 };
2885 struct iovec iov = {
2886 .iov_base = &out,
2887 .iov_len = sizeof(struct fuse_out_header),
2888 };
2889
2890 fuse_send_msg(se, ch, &iov, 1);
2891 goto clear_pipe;
2892 }
2893
2894 req->unique = in->unique;
2895 req->ctx.uid = in->uid;
2896 req->ctx.gid = in->gid;
2897 req->ctx.pid = in->pid;
2898 req->ch = ch ? fuse_chan_get(ch) : NULL;
2899
2900 err = EIO;
2901 if (!se->got_init) {
2902 enum fuse_opcode expected;
2903
2904 expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
2905 if (in->opcode != expected)
2906 goto reply_err;
2907 } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
2908 goto reply_err;
2909
2910 err = EACCES;
2911 /* Implement -o allow_root */
2912 if (se->deny_others && in->uid != se->owner && in->uid != 0 &&
2913 in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
2914 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
2915 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
2916 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
2917 in->opcode != FUSE_NOTIFY_REPLY &&
2918 in->opcode != FUSE_READDIRPLUS)
2919 goto reply_err;
2920
2921 err = ENOSYS;
2922 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
2923 goto reply_err;
2924 /* Do not process interrupt request */
2925 if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) {
2926 if (se->debug)
2927 fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
2928 goto reply_err;
2929 }
2930 if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) {
2931 struct fuse_req *intr;
2932 pthread_mutex_lock(&se->lock);
2933 intr = check_interrupt(se, req);
2934 list_add_req(req, &se->list);
2935 pthread_mutex_unlock(&se->lock);
2936 if (intr)
2937 fuse_reply_err(intr, EAGAIN);
2938 }
2939
2940 if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
2941 (in->opcode != FUSE_WRITE || !se->op.write_buf) &&
2942 in->opcode != FUSE_NOTIFY_REPLY) {
2943 void *newmbuf;
2944
2945 err = ENOMEM;
2946 newmbuf = realloc(mbuf, buf->size);
2947 if (newmbuf == NULL)
2948 goto reply_err;
2949 mbuf = newmbuf;
2950
2951 tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
2952 tmpbuf.buf[0].mem = (char *)mbuf + write_header_size;
2953
2954 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
2955 err = -res;
2956 if (res < 0)
2957 goto reply_err;
2958
2959 in = mbuf;
2960 }
2961
2962 inarg = (void *) &in[1];
2963 if (in->opcode == FUSE_WRITE && se->op.write_buf)
2964 do_write_buf(req, in->nodeid, inarg, buf);
2965 else if (in->opcode == FUSE_NOTIFY_REPLY)
2966 do_notify_reply(req, in->nodeid, inarg, buf);
2967 else
2968 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
2969
2970out_free:
2971 free(mbuf);
2972 return;
2973
2974reply_err:
2975 fuse_reply_err(req, err);
2976clear_pipe:
2977 if (buf->flags & FUSE_BUF_IS_FD)
2978 fuse_ll_clear_pipe(se);
2979 goto out_free;
2980}
2981
2982#define LL_OPTION(n,o,v) \
2983 { n, offsetof(struct fuse_session, o), v }
2984
2985static const struct fuse_opt fuse_ll_opts[] = {
2986 LL_OPTION("debug", debug, 1),
2987 LL_OPTION("-d", debug, 1),
2988 LL_OPTION("--debug", debug, 1),
2989 LL_OPTION("allow_root", deny_others, 1),
2991};
2992
2994{
2995 printf("using FUSE kernel interface version %i.%i\n",
2996 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
2997 fuse_mount_version();
2998}
2999
3001{
3002 /* These are not all options, but the ones that are
3003 potentially of interest to an end-user */
3004 printf(
3005" -o allow_other allow access by all users\n"
3006" -o allow_root allow access by root\n"
3007" -o auto_unmount auto unmount on process termination\n");
3008}
3009
3010void fuse_session_destroy(struct fuse_session *se)
3011{
3012 struct fuse_ll_pipe *llp;
3013
3014 if (se->got_init && !se->got_destroy) {
3015 if (se->op.destroy)
3016 se->op.destroy(se->userdata);
3017 }
3018 llp = pthread_getspecific(se->pipe_key);
3019 if (llp != NULL)
3020 fuse_ll_pipe_free(llp);
3021 pthread_key_delete(se->pipe_key);
3022 pthread_mutex_destroy(&se->lock);
3023 free(se->cuse_data);
3024 if (se->fd != -1)
3025 close(se->fd);
3026 if (se->io != NULL)
3027 free(se->io);
3028 destroy_mount_opts(se->mo);
3029 free(se);
3030}
3031
3032
3033static void fuse_ll_pipe_destructor(void *data)
3034{
3035 struct fuse_ll_pipe *llp = data;
3036 fuse_ll_pipe_free(llp);
3037}
3038
3039void fuse_buf_free(struct fuse_buf *buf)
3040{
3041 if (buf->mem == NULL)
3042 return;
3043
3044 size_t write_header_sz =
3045 sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
3046
3047 char *ptr = (char *)buf->mem - pagesize + write_header_sz;
3048 free(ptr);
3049 buf->mem = NULL;
3050}
3051
3052/*
3053 * This is used to allocate buffers that hold fuse requests
3054 */
3055static void *buf_alloc(size_t size, bool internal)
3056{
3057 /*
3058 * For libfuse internal caller add in alignment. That cannot be done
3059 * for an external caller, as it is not guaranteed that the external
3060 * caller frees the raw pointer.
3061 */
3062 if (internal) {
3063 size_t write_header_sz = sizeof(struct fuse_in_header) +
3064 sizeof(struct fuse_write_in);
3065 size_t new_size = ROUND_UP(size + write_header_sz, pagesize);
3066
3067 char *buf = aligned_alloc(pagesize, new_size);
3068 if (buf == NULL)
3069 return NULL;
3070
3071 buf += pagesize - write_header_sz;
3072
3073 return buf;
3074 } else {
3075 return malloc(size);
3076 }
3077}
3078
3079/*
3080 *@param internal true if called from libfuse internal code
3081 */
3082static int _fuse_session_receive_buf(struct fuse_session *se,
3083 struct fuse_buf *buf, struct fuse_chan *ch,
3084 bool internal)
3085{
3086 int err;
3087 ssize_t res;
3088 size_t bufsize;
3089#ifdef HAVE_SPLICE
3090 struct fuse_ll_pipe *llp;
3091 struct fuse_buf tmpbuf;
3092
3093pipe_retry:
3094 bufsize = se->bufsize;
3095
3096 if (se->conn.proto_minor < 14 ||
3097 !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
3098 goto fallback;
3099
3100 llp = fuse_ll_get_pipe(se);
3101 if (llp == NULL)
3102 goto fallback;
3103
3104 if (llp->size < bufsize) {
3105 if (llp->can_grow) {
3106 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
3107 if (res == -1) {
3108 llp->can_grow = 0;
3109 res = grow_pipe_to_max(llp->pipe[0]);
3110 if (res > 0)
3111 llp->size = res;
3112 goto fallback;
3113 }
3114 llp->size = res;
3115 }
3116 if (llp->size < bufsize)
3117 goto fallback;
3118 }
3119
3120 if (se->io != NULL && se->io->splice_receive != NULL) {
3121 res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
3122 llp->pipe[1], NULL, bufsize, 0,
3123 se->userdata);
3124 } else {
3125 res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
3126 bufsize, 0);
3127 }
3128 err = errno;
3129
3130 if (fuse_session_exited(se))
3131 return 0;
3132
3133 if (res == -1) {
3134 if (err == ENODEV) {
3135 /* Filesystem was unmounted, or connection was aborted
3136 via /sys/fs/fuse/connections */
3138 return 0;
3139 }
3140
3141 /* FUSE_INIT might have increased the required bufsize */
3142 if (err == EINVAL && bufsize < se->bufsize) {
3143 fuse_ll_clear_pipe(se);
3144 goto pipe_retry;
3145 }
3146
3147 if (err != EINTR && err != EAGAIN)
3148 perror("fuse: splice from device");
3149 return -err;
3150 }
3151
3152 if (res < sizeof(struct fuse_in_header)) {
3153 fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
3154 return -EIO;
3155 }
3156
3157 tmpbuf = (struct fuse_buf){
3158 .size = res,
3159 .flags = FUSE_BUF_IS_FD,
3160 .fd = llp->pipe[0],
3161 };
3162
3163 /*
3164 * Don't bother with zero copy for small requests.
3165 * fuse_loop_mt() needs to check for FORGET so this more than
3166 * just an optimization.
3167 */
3168 if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) +
3169 pagesize) {
3170 struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
3171 struct fuse_bufvec dst = { .count = 1 };
3172
3173 if (!buf->mem) {
3174 buf->mem = buf_alloc(bufsize, internal);
3175 if (!buf->mem) {
3176 fuse_log(
3177 FUSE_LOG_ERR,
3178 "fuse: failed to allocate read buffer\n");
3179 return -ENOMEM;
3180 }
3181 buf->mem_size = bufsize;
3182 }
3183 buf->size = bufsize;
3184 buf->flags = 0;
3185 dst.buf[0] = *buf;
3186
3187 res = fuse_buf_copy(&dst, &src, 0);
3188 if (res < 0) {
3189 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
3190 strerror(-res));
3191 fuse_ll_clear_pipe(se);
3192 return res;
3193 }
3194 if (res < tmpbuf.size) {
3195 fuse_log(FUSE_LOG_ERR,
3196 "fuse: copy from pipe: short read\n");
3197 fuse_ll_clear_pipe(se);
3198 return -EIO;
3199 }
3200 assert(res == tmpbuf.size);
3201
3202 } else {
3203 /* Don't overwrite buf->mem, as that would cause a leak */
3204 buf->fd = tmpbuf.fd;
3205 buf->flags = tmpbuf.flags;
3206 }
3207 buf->size = tmpbuf.size;
3208
3209 return res;
3210
3211fallback:
3212#endif
3213 bufsize = internal ? buf->mem_size : se->bufsize;
3214 if (!buf->mem) {
3215 bufsize = se->bufsize; /* might have changed */
3216 buf->mem = buf_alloc(bufsize, internal);
3217 if (!buf->mem) {
3218 fuse_log(FUSE_LOG_ERR,
3219 "fuse: failed to allocate read buffer\n");
3220 return -ENOMEM;
3221 }
3222
3223 if (internal)
3224 buf->mem_size = bufsize;
3225 }
3226
3227restart:
3228 if (se->io != NULL) {
3229 /* se->io->read is never NULL if se->io is not NULL as
3230 specified by fuse_session_custom_io()*/
3231 res = se->io->read(ch ? ch->fd : se->fd, buf->mem, bufsize,
3232 se->userdata);
3233 } else {
3234 res = read(ch ? ch->fd : se->fd, buf->mem, bufsize);
3235 }
3236 err = errno;
3237
3238 if (fuse_session_exited(se))
3239 return 0;
3240 if (res == -1) {
3241 if (err == EINVAL && internal && se->bufsize > bufsize) {
3242 /* FUSE_INIT might have increased the required bufsize */
3243 bufsize = se->bufsize;
3244 void *newbuf = buf_alloc(bufsize, internal);
3245 if (!newbuf) {
3246 fuse_log(
3247 FUSE_LOG_ERR,
3248 "fuse: failed to (re)allocate read buffer\n");
3249 return -ENOMEM;
3250 }
3251 fuse_buf_free(buf);
3252 buf->mem = newbuf;
3253 buf->mem_size = bufsize;
3254 goto restart;
3255 }
3256
3257 /* ENOENT means the operation was interrupted, it's safe
3258 to restart */
3259 if (err == ENOENT)
3260 goto restart;
3261
3262 if (err == ENODEV) {
3263 /* Filesystem was unmounted, or connection was aborted
3264 via /sys/fs/fuse/connections */
3266 return 0;
3267 }
3268 /* Errors occurring during normal operation: EINTR (read
3269 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
3270 umounted) */
3271 if (err != EINTR && err != EAGAIN)
3272 perror("fuse: reading device");
3273 return -err;
3274 }
3275 if ((size_t)res < sizeof(struct fuse_in_header)) {
3276 fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
3277 return -EIO;
3278 }
3279
3280 buf->size = res;
3281
3282 return res;
3283}
3284
3285int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
3286{
3287 return _fuse_session_receive_buf(se, buf, NULL, false);
3288}
3289
3290/* libfuse internal handler */
3291int fuse_session_receive_buf_internal(struct fuse_session *se,
3292 struct fuse_buf *buf,
3293 struct fuse_chan *ch)
3294{
3295 /*
3296 * if run internally thread buffers are from libfuse - we can
3297 * reallocate them
3298 */
3299 if (unlikely(!se->got_init) && !se->buf_reallocable)
3300 se->buf_reallocable = true;
3301
3302 return _fuse_session_receive_buf(se, buf, ch, true);
3303}
3304
3305struct fuse_session *
3306fuse_session_new_versioned(struct fuse_args *args,
3307 const struct fuse_lowlevel_ops *op, size_t op_size,
3308 struct libfuse_version *version, void *userdata)
3309{
3310 int err;
3311 struct fuse_session *se;
3312 struct mount_opts *mo;
3313
3314 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
3315 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
3316 op_size = sizeof(struct fuse_lowlevel_ops);
3317 }
3318
3319 if (args->argc == 0) {
3320 fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
3321 return NULL;
3322 }
3323
3324 se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
3325 if (se == NULL) {
3326 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
3327 goto out1;
3328 }
3329 se->fd = -1;
3330 se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize();
3331 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
3332 se->conn.max_readahead = UINT_MAX;
3333
3334 /* Parse options */
3335 if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
3336 goto out2;
3337 if(se->deny_others) {
3338 /* Allowing access only by root is done by instructing
3339 * kernel to allow access by everyone, and then restricting
3340 * access to root and mountpoint owner in libfuse.
3341 */
3342 // We may be adding the option a second time, but
3343 // that doesn't hurt.
3344 if(fuse_opt_add_arg(args, "-oallow_other") == -1)
3345 goto out2;
3346 }
3347 mo = parse_mount_opts(args);
3348 if (mo == NULL)
3349 goto out3;
3350
3351 if(args->argc == 1 &&
3352 args->argv[0][0] == '-') {
3353 fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
3354 "will be ignored\n");
3355 } else if (args->argc != 1) {
3356 int i;
3357 fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
3358 for(i = 1; i < args->argc-1; i++)
3359 fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
3360 fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
3361 goto out4;
3362 }
3363
3364 if (se->debug)
3365 fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
3366
3367 list_init_req(&se->list);
3368 list_init_req(&se->interrupts);
3369 list_init_nreq(&se->notify_list);
3370 se->notify_ctr = 1;
3371 pthread_mutex_init(&se->lock, NULL);
3372
3373 err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
3374 if (err) {
3375 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
3376 strerror(err));
3377 goto out5;
3378 }
3379
3380 memcpy(&se->op, op, op_size);
3381 se->owner = getuid();
3382 se->userdata = userdata;
3383
3384 se->mo = mo;
3385
3386 /* Fuse server application should pass the version it was compiled
3387 * against and pass it. If a libfuse version accidentally introduces an
3388 * ABI incompatibility, it might be possible to 'fix' that at run time,
3389 * by checking the version numbers.
3390 */
3391 se->version = *version;
3392
3393 return se;
3394
3395out5:
3396 pthread_mutex_destroy(&se->lock);
3397out4:
3398 fuse_opt_free_args(args);
3399out3:
3400 if (mo != NULL)
3401 destroy_mount_opts(mo);
3402out2:
3403 free(se);
3404out1:
3405 return NULL;
3406}
3407
3408struct fuse_session *fuse_session_new_30(struct fuse_args *args,
3409 const struct fuse_lowlevel_ops *op,
3410 size_t op_size, void *userdata);
3411struct fuse_session *fuse_session_new_30(struct fuse_args *args,
3412 const struct fuse_lowlevel_ops *op,
3413 size_t op_size,
3414 void *userdata)
3415{
3416 /* unknown version */
3417 struct libfuse_version version = { 0 };
3418
3419 return fuse_session_new_versioned(args, op, op_size, &version,
3420 userdata);
3421}
3422
3423FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17")
3424int fuse_session_custom_io_317(struct fuse_session *se,
3425 const struct fuse_custom_io *io, size_t op_size, int fd)
3426{
3427 if (sizeof(struct fuse_custom_io) < op_size) {
3428 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
3429 op_size = sizeof(struct fuse_custom_io);
3430 }
3431
3432 if (fd < 0) {
3433 fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
3434 "fuse_session_custom_io()\n", fd);
3435 return -EBADF;
3436 }
3437 if (io == NULL) {
3438 fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
3439 "fuse_session_custom_io()\n");
3440 return -EINVAL;
3441 } else if (io->read == NULL || io->writev == NULL) {
3442 /* If the user provides their own file descriptor, we can't
3443 guarantee that the default behavior of the io operations made
3444 in libfuse will function properly. Therefore, we enforce the
3445 user to implement these io operations when using custom io. */
3446 fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
3447 "implement both io->read() and io->writev\n");
3448 return -EINVAL;
3449 }
3450
3451 se->io = calloc(1, sizeof(struct fuse_custom_io));
3452 if (se->io == NULL) {
3453 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
3454 "Error: %s\n", strerror(errno));
3455 return -errno;
3456 }
3457
3458 se->fd = fd;
3459 memcpy(se->io, io, op_size);
3460 return 0;
3461}
3462
3463int fuse_session_custom_io_30(struct fuse_session *se,
3464 const struct fuse_custom_io *io, int fd);
3465FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0")
3466int fuse_session_custom_io_30(struct fuse_session *se,
3467 const struct fuse_custom_io *io, int fd)
3468{
3469 return fuse_session_custom_io_317(se, io,
3470 offsetof(struct fuse_custom_io, clone_fd), fd);
3471}
3472
3473int fuse_session_mount(struct fuse_session *se, const char *_mountpoint)
3474{
3475 int fd;
3476 char *mountpoint;
3477
3478 if (_mountpoint == NULL) {
3479 fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
3480 return -1;
3481 }
3482
3483 mountpoint = strdup(_mountpoint);
3484 if (mountpoint == NULL) {
3485 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for mountpoint. Error: %s\n",
3486 strerror(errno));
3487 return -1;
3488 }
3489
3490 /*
3491 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
3492 * would ensue.
3493 */
3494 do {
3495 fd = open("/dev/null", O_RDWR);
3496 if (fd > 2)
3497 close(fd);
3498 } while (fd >= 0 && fd <= 2);
3499
3500 /*
3501 * To allow FUSE daemons to run without privileges, the caller may open
3502 * /dev/fuse before launching the file system and pass on the file
3503 * descriptor by specifying /dev/fd/N as the mount point. Note that the
3504 * parent process takes care of performing the mount in this case.
3505 */
3506 fd = fuse_mnt_parse_fuse_fd(mountpoint);
3507 if (fd != -1) {
3508 if (fcntl(fd, F_GETFD) == -1) {
3509 fuse_log(FUSE_LOG_ERR,
3510 "fuse: Invalid file descriptor /dev/fd/%u\n",
3511 fd);
3512 goto error_out;
3513 }
3514 se->fd = fd;
3515 return 0;
3516 }
3517
3518 /* Open channel */
3519 fd = fuse_kern_mount(mountpoint, se->mo);
3520 if (fd == -1)
3521 goto error_out;
3522 se->fd = fd;
3523
3524 /* Save mountpoint */
3525 se->mountpoint = mountpoint;
3526
3527 return 0;
3528
3529error_out:
3530 free(mountpoint);
3531 return -1;
3532}
3533
3534int fuse_session_fd(struct fuse_session *se)
3535{
3536 return se->fd;
3537}
3538
3539void fuse_session_unmount(struct fuse_session *se)
3540{
3541 if (se->mountpoint != NULL) {
3542 char *mountpoint = atomic_exchange(&se->mountpoint, NULL);
3543
3544 fuse_kern_unmount(mountpoint, se->fd);
3545 se->fd = -1;
3546 free(mountpoint);
3547 }
3548}
3549
3550#ifdef linux
3551int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
3552{
3553 char *buf;
3554 size_t bufsize = 1024;
3555 char path[128];
3556 int ret;
3557 int fd;
3558 unsigned long pid = req->ctx.pid;
3559 char *s;
3560
3561 sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
3562
3563retry:
3564 buf = malloc(bufsize);
3565 if (buf == NULL)
3566 return -ENOMEM;
3567
3568 ret = -EIO;
3569 fd = open(path, O_RDONLY);
3570 if (fd == -1)
3571 goto out_free;
3572
3573 ret = read(fd, buf, bufsize);
3574 close(fd);
3575 if (ret < 0) {
3576 ret = -EIO;
3577 goto out_free;
3578 }
3579
3580 if ((size_t)ret == bufsize) {
3581 free(buf);
3582 bufsize *= 4;
3583 goto retry;
3584 }
3585
3586 buf[ret] = '\0';
3587 ret = -EIO;
3588 s = strstr(buf, "\nGroups:");
3589 if (s == NULL)
3590 goto out_free;
3591
3592 s += 8;
3593 ret = 0;
3594 while (1) {
3595 char *end;
3596 unsigned long val = strtoul(s, &end, 0);
3597 if (end == s)
3598 break;
3599
3600 s = end;
3601 if (ret < size)
3602 list[ret] = val;
3603 ret++;
3604 }
3605
3606out_free:
3607 free(buf);
3608 return ret;
3609}
3610#else /* linux */
3611/*
3612 * This is currently not implemented on other than Linux...
3613 */
3614int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
3615{
3616 (void) req; (void) size; (void) list;
3617 return -ENOSYS;
3618}
3619#endif
3620
3621/* Prevent spurious data race warning - we don't care
3622 * about races for this flag */
3623__attribute__((no_sanitize_thread))
3624void fuse_session_exit(struct fuse_session *se)
3625{
3626 se->exited = 1;
3627}
3628
3629__attribute__((no_sanitize_thread))
3630void fuse_session_reset(struct fuse_session *se)
3631{
3632 se->exited = 0;
3633 se->error = 0;
3634}
3635
3636__attribute__((no_sanitize_thread))
3637int fuse_session_exited(struct fuse_session *se)
3638{
3639 return se->exited;
3640}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
@ FUSE_BUF_IS_FD
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
fuse_notify_entry_flags
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_session_reset(struct fuse_session *se)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_passthrough_open(fuse_req_t req, int fd)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_xattr(fuse_req_t req, size_t count)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
@ FUSE_BUF_IS_FD
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
struct fuse_req * fuse_req_t
int fuse_session_fd(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
uint64_t fuse_ino_t
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
size_t mem_size
void * mem
size_t size
struct fuse_buf buf[1]
uint64_t capable_ext
uint64_t want_ext
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t nonseekable
Definition fuse_common.h:78
int32_t backing_id
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t noflush
Definition fuse_common.h:93
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__lowlevel_8c_source.html0000644000175000017500000306027515156613443024211 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_lowlevel.c Source File
libfuse
fuse_lowlevel.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of (most of) the low-level FUSE API. The session loop
6 functions are implemented in separate files.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_kernel.h"
17#include "fuse_opt.h"
18#include "fuse_misc.h"
19#include "mount_util.h"
20#include "util.h"
21#include "fuse_uring_i.h"
22
23#include <pthread.h>
24#include <stdatomic.h>
25#include <stdint.h>
26#include <inttypes.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <stddef.h>
31#include <stdalign.h>
32#include <string.h>
33#include <unistd.h>
34#include <limits.h>
35#include <errno.h>
36#include <assert.h>
37#include <sys/file.h>
38#include <sys/ioctl.h>
39#include <stdalign.h>
40
41#ifdef USDT_ENABLED
42#include "usdt.h"
43#endif
44
45#ifndef F_LINUX_SPECIFIC_BASE
46#define F_LINUX_SPECIFIC_BASE 1024
47#endif
48#ifndef F_SETPIPE_SZ
49#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
50#endif
51
52#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
53#define OFFSET_MAX 0x7fffffffffffffffLL
54
55struct fuse_pollhandle {
56 uint64_t kh;
57 struct fuse_session *se;
58};
59
60static size_t pagesize;
61
62static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
63{
64 pagesize = getpagesize();
65}
66
67#ifdef USDT_ENABLED
68/* tracepoints */
69static void trace_request_receive(int err)
70{
71 USDT(libfuse, request_receive, err);
72}
73
74static void trace_request_process(unsigned int opcode, unsigned int unique)
75{
76 USDT(libfuse, request_process, opcode, unique);
77}
78
79static void trace_request_reply(uint64_t unique, unsigned int len,
80 int error, int reply_err)
81{
82 USDT(libfuse, request_reply, unique, len, error, reply_err);
83}
84#else
85static void trace_request_receive(int err)
86{
87 (void)err;
88}
89
90static void trace_request_process(unsigned int opcode, unsigned int unique)
91{
92 (void)opcode;
93 (void)unique;
94}
95
96static void trace_request_reply(uint64_t unique, unsigned int len,
97 int error, int reply_err)
98{
99 (void)unique;
100 (void)len;
101 (void)error;
102 (void)reply_err;
103}
104#endif
105
106static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
107{
108 attr->ino = stbuf->st_ino;
109 attr->mode = stbuf->st_mode;
110 attr->nlink = stbuf->st_nlink;
111 attr->uid = stbuf->st_uid;
112 attr->gid = stbuf->st_gid;
113 attr->rdev = stbuf->st_rdev;
114 attr->size = stbuf->st_size;
115 attr->blksize = stbuf->st_blksize;
116 attr->blocks = stbuf->st_blocks;
117 attr->atime = stbuf->st_atime;
118 attr->mtime = stbuf->st_mtime;
119 attr->ctime = stbuf->st_ctime;
120 attr->atimensec = ST_ATIM_NSEC(stbuf);
121 attr->mtimensec = ST_MTIM_NSEC(stbuf);
122 attr->ctimensec = ST_CTIM_NSEC(stbuf);
123}
124
125static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
126{
127 stbuf->st_mode = attr->mode;
128 stbuf->st_uid = attr->uid;
129 stbuf->st_gid = attr->gid;
130 stbuf->st_size = attr->size;
131 stbuf->st_atime = attr->atime;
132 stbuf->st_mtime = attr->mtime;
133 stbuf->st_ctime = attr->ctime;
134 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
135 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
136 ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
137}
138
139static size_t iov_length(const struct iovec *iov, size_t count)
140{
141 size_t seg;
142 size_t ret = 0;
143
144 for (seg = 0; seg < count; seg++)
145 ret += iov[seg].iov_len;
146 return ret;
147}
148
149void list_init_req(struct fuse_req *req)
150{
151 req->next = req;
152 req->prev = req;
153}
154
155static void list_del_req(struct fuse_req *req)
156{
157 struct fuse_req *prev = req->prev;
158 struct fuse_req *next = req->next;
159 prev->next = next;
160 next->prev = prev;
161}
162
163static void list_add_req(struct fuse_req *req, struct fuse_req *next)
164{
165 struct fuse_req *prev = next->prev;
166 req->next = next;
167 req->prev = prev;
168 prev->next = req;
169 next->prev = req;
170}
171
172static void destroy_req(fuse_req_t req)
173{
174 if (req->flags.is_uring) {
175 fuse_log(FUSE_LOG_ERR, "Refusing to destruct uring req\n");
176 return;
177 }
178 assert(req->ch == NULL);
179 pthread_mutex_destroy(&req->lock);
180 free(req);
181}
182
183void fuse_free_req(fuse_req_t req)
184{
185 int ctr;
186 struct fuse_session *se = req->se;
187
188 /* XXX: for now no support for interrupts with io-uring
189 * It actually might work already, though. But then would add
190 * a lock across ring queues.
191 */
192 if (se->conn.no_interrupt || req->flags.is_uring) {
193 ctr = --req->ref_cnt;
194 fuse_chan_put(req->ch);
195 req->ch = NULL;
196 } else {
197 pthread_mutex_lock(&se->lock);
198 req->u.ni.func = NULL;
199 req->u.ni.data = NULL;
200 list_del_req(req);
201 ctr = --req->ref_cnt;
202 fuse_chan_put(req->ch);
203 req->ch = NULL;
204 pthread_mutex_unlock(&se->lock);
205 }
206 if (!ctr)
207 destroy_req(req);
208}
209
210static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
211{
212 struct fuse_req *req;
213
214 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
215 if (req == NULL) {
216 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
217 } else {
218 req->se = se;
219 req->ref_cnt = 1;
220 list_init_req(req);
221 pthread_mutex_init(&req->lock, NULL);
222 }
223
224 return req;
225}
226
227/*
228 * Send data to fuse-kernel using an fd of the fuse device.
229 */
230static int fuse_write_msg_dev(struct fuse_session *se, struct fuse_chan *ch,
231 struct iovec *iov, int count)
232{
233 ssize_t res;
234 int err;
235
236 if (se->io != NULL)
237
238 /* se->io->writev is never NULL if se->io is not NULL as
239 * specified by fuse_session_custom_io()
240 */
241 res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
242 se->userdata);
243 else
244 res = writev(ch ? ch->fd : se->fd, iov, count);
245
246 if (res == -1) {
247 /* ENOENT means the operation was interrupted */
248 err = errno;
249 if (!fuse_session_exited(se) && err != ENOENT)
250 perror("fuse: writing device");
251 return -err;
252 }
253
254 return 0;
255}
256
257static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
258 struct iovec *iov, int count, fuse_req_t req)
259{
260 struct fuse_out_header *out = iov[0].iov_base;
261 int err;
262 bool is_uring = req && req->flags.is_uring ? true : false;
263
264 if (!is_uring)
265 assert(se != NULL);
266 out->len = iov_length(iov, count);
267
268 if (se->debug) {
269 if (out->unique == 0) {
270 fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
271 out->error, out->len);
272 } else if (out->error) {
273 fuse_log(FUSE_LOG_DEBUG,
274 " unique: %llu, error: %i (%s), outsize: %i\n",
275 (unsigned long long) out->unique, out->error,
276 strerror(-out->error), out->len);
277 } else {
278 fuse_log(FUSE_LOG_DEBUG,
279 " unique: %llu, success, outsize: %i\n",
280 (unsigned long long) out->unique, out->len);
281 }
282 }
283
284 if (is_uring)
285 err = fuse_send_msg_uring(req, iov, count);
286 else
287 err = fuse_write_msg_dev(se, ch, iov, count);
288
289 trace_request_reply(out->unique, out->len, out->error, err);
290 return err;
291}
292
293int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
294 int count)
295{
296 struct fuse_out_header out;
297
298#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
299 const char *str = strerrordesc_np(error * -1);
300 if ((str == NULL && error != 0) || error > 0) {
301#else
302 if (error <= -1000 || error > 0) {
303#endif
304 fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
305 error = -ERANGE;
306 }
307
308 out.unique = req->unique;
309 out.error = error;
310
311 iov[0].iov_base = &out;
312 iov[0].iov_len = sizeof(struct fuse_out_header);
313
314 return fuse_send_msg(req->se, req->ch, iov, count, req);
315}
316
317static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
318 int count)
319{
320 int res;
321
322 res = fuse_send_reply_iov_nofree(req, error, iov, count);
323 fuse_free_req(req);
324 return res;
325}
326
327static int send_reply(fuse_req_t req, int error, const void *arg,
328 size_t argsize)
329{
330 if (req->flags.is_uring)
331 return send_reply_uring(req, error, arg, argsize);
332
333 struct iovec iov[2];
334 int count = 1;
335 if (argsize) {
336 iov[1].iov_base = (void *) arg;
337 iov[1].iov_len = argsize;
338 count++;
339 }
340 return send_reply_iov(req, error, iov, count);
341}
342
343int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
344{
345 int res;
346 struct iovec *padded_iov;
347
348 padded_iov = malloc((count + 1) * sizeof(struct iovec));
349 if (padded_iov == NULL)
350 return fuse_reply_err(req, ENOMEM);
351
352 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
353 count++;
354
355 res = send_reply_iov(req, 0, padded_iov, count);
356 free(padded_iov);
357
358 return res;
359}
360
361
362/* `buf` is allowed to be empty so that the proper size may be
363 allocated by the caller */
364size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
365 const char *name, const struct stat *stbuf, off_t off)
366{
367 (void)req;
368 size_t namelen;
369 size_t entlen;
370 size_t entlen_padded;
371 struct fuse_dirent *dirent;
372
373 namelen = strlen(name);
374 entlen = FUSE_NAME_OFFSET + namelen;
375 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
376
377 if ((buf == NULL) || (entlen_padded > bufsize))
378 return entlen_padded;
379
380 dirent = (struct fuse_dirent*) buf;
381 dirent->ino = stbuf->st_ino;
382 dirent->off = off;
383 dirent->namelen = namelen;
384 dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
385 memcpy(dirent->name, name, namelen);
386 memset(dirent->name + namelen, 0, entlen_padded - entlen);
387
388 return entlen_padded;
389}
390
391static void convert_statfs(const struct statvfs *stbuf,
392 struct fuse_kstatfs *kstatfs)
393{
394 kstatfs->bsize = stbuf->f_bsize;
395 kstatfs->frsize = stbuf->f_frsize;
396 kstatfs->blocks = stbuf->f_blocks;
397 kstatfs->bfree = stbuf->f_bfree;
398 kstatfs->bavail = stbuf->f_bavail;
399 kstatfs->files = stbuf->f_files;
400 kstatfs->ffree = stbuf->f_ffree;
401 kstatfs->namelen = stbuf->f_namemax;
402}
403
404static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
405{
406 return send_reply(req, 0, arg, argsize);
407}
408
409int fuse_reply_err(fuse_req_t req, int err)
410{
411 return send_reply(req, -err, NULL, 0);
412}
413
415{
416 fuse_free_req(req);
417}
418
419static unsigned long calc_timeout_sec(double t)
420{
421 if (t > (double) ULONG_MAX)
422 return ULONG_MAX;
423 else if (t < 0.0)
424 return 0;
425 else
426 return (unsigned long) t;
427}
428
429static unsigned int calc_timeout_nsec(double t)
430{
431 double f = t - (double) calc_timeout_sec(t);
432 if (f < 0.0)
433 return 0;
434 else if (f >= 0.999999999)
435 return 999999999;
436 else
437 return (unsigned int) (f * 1.0e9);
438}
439
440static void fill_entry(struct fuse_entry_out *arg,
441 const struct fuse_entry_param *e)
442{
443 arg->nodeid = e->ino;
444 arg->generation = e->generation;
445 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
446 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
447 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
448 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
449 convert_stat(&e->attr, &arg->attr);
450}
451
452/* `buf` is allowed to be empty so that the proper size may be
453 allocated by the caller */
454size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
455 const char *name,
456 const struct fuse_entry_param *e, off_t off)
457{
458 (void)req;
459 size_t namelen;
460 size_t entlen;
461 size_t entlen_padded;
462
463 namelen = strlen(name);
464 entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
465 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
466 if ((buf == NULL) || (entlen_padded > bufsize))
467 return entlen_padded;
468
469 struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
470 memset(&dp->entry_out, 0, sizeof(dp->entry_out));
471 fill_entry(&dp->entry_out, e);
472
473 struct fuse_dirent *dirent = &dp->dirent;
474 dirent->ino = e->attr.st_ino;
475 dirent->off = off;
476 dirent->namelen = namelen;
477 dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
478 memcpy(dirent->name, name, namelen);
479 memset(dirent->name + namelen, 0, entlen_padded - entlen);
480
481 return entlen_padded;
482}
483
484static void fill_open(struct fuse_open_out *arg,
485 const struct fuse_file_info *f)
486{
487 arg->fh = f->fh;
488 if (f->backing_id > 0) {
489 arg->backing_id = f->backing_id;
490 arg->open_flags |= FOPEN_PASSTHROUGH;
491 }
492 if (f->direct_io)
493 arg->open_flags |= FOPEN_DIRECT_IO;
494 if (f->keep_cache)
495 arg->open_flags |= FOPEN_KEEP_CACHE;
496 if (f->cache_readdir)
497 arg->open_flags |= FOPEN_CACHE_DIR;
498 if (f->nonseekable)
499 arg->open_flags |= FOPEN_NONSEEKABLE;
500 if (f->noflush)
501 arg->open_flags |= FOPEN_NOFLUSH;
503 arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
504}
505
506int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
507{
508 struct fuse_entry_out arg;
509 size_t size = req->se->conn.proto_minor < 9 ?
510 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
511
512 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
513 negative entry */
514 if (!e->ino && req->se->conn.proto_minor < 4)
515 return fuse_reply_err(req, ENOENT);
516
517 memset(&arg, 0, sizeof(arg));
518 fill_entry(&arg, e);
519 return send_reply_ok(req, &arg, size);
520}
521
522int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
523 const struct fuse_file_info *f)
524{
525 alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
526 size_t entrysize = req->se->conn.proto_minor < 9 ?
527 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
528 struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
529 struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
530
531 memset(buf, 0, sizeof(buf));
532 fill_entry(earg, e);
533 fill_open(oarg, f);
534 return send_reply_ok(req, buf,
535 entrysize + sizeof(struct fuse_open_out));
536}
537
538int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
539 double attr_timeout)
540{
541 struct fuse_attr_out arg;
542 size_t size = req->se->conn.proto_minor < 9 ?
543 FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
544
545 memset(&arg, 0, sizeof(arg));
546 arg.attr_valid = calc_timeout_sec(attr_timeout);
547 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
548 convert_stat(attr, &arg.attr);
549
550 return send_reply_ok(req, &arg, size);
551}
552
553int fuse_reply_readlink(fuse_req_t req, const char *linkname)
554{
555 return send_reply_ok(req, linkname, strlen(linkname));
556}
557
558int fuse_passthrough_open(fuse_req_t req, int fd)
559{
560 struct fuse_backing_map map = { .fd = fd };
561 int ret;
562
563 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
564 if (ret <= 0) {
565 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno));
566 return 0;
567 }
568
569 return ret;
570}
571
572int fuse_passthrough_close(fuse_req_t req, int backing_id)
573{
574 int ret;
575
576 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id);
577 if (ret < 0)
578 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno));
579
580 return ret;
581}
582
583int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
584{
585 struct fuse_open_out arg;
586
587 memset(&arg, 0, sizeof(arg));
588 fill_open(&arg, f);
589 return send_reply_ok(req, &arg, sizeof(arg));
590}
591
592static int do_fuse_reply_write(fuse_req_t req, size_t count)
593{
594 struct fuse_write_out arg;
595
596 memset(&arg, 0, sizeof(arg));
597 arg.size = count;
598
599 return send_reply_ok(req, &arg, sizeof(arg));
600}
601
602static int do_fuse_reply_copy(fuse_req_t req, size_t count)
603{
604 struct fuse_copy_file_range_out arg;
605
606 memset(&arg, 0, sizeof(arg));
607 arg.bytes_copied = count;
608
609 return send_reply_ok(req, &arg, sizeof(arg));
610}
611
612int fuse_reply_write(fuse_req_t req, size_t count)
613{
614 /*
615 * This function is also used by FUSE_COPY_FILE_RANGE and its 64-bit
616 * variant.
617 */
618 if (req->flags.is_copy_file_range_64)
619 return do_fuse_reply_copy(req, count);
620 else
621 return do_fuse_reply_write(req, count);
622}
623
624int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
625{
626 return send_reply_ok(req, buf, size);
627}
628
629static int fuse_send_data_iov_fallback(struct fuse_session *se,
630 struct fuse_chan *ch,
631 struct iovec *iov, int iov_count,
632 struct fuse_bufvec *buf,
633 size_t len, fuse_req_t req)
634{
635 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
636 void *mbuf;
637 int res;
638
639 /* Optimize common case */
640 if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
641 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
642 /* FIXME: also avoid memory copy if there are multiple buffers
643 but none of them contain an fd */
644
645 iov[iov_count].iov_base = buf->buf[0].mem;
646 iov[iov_count].iov_len = len;
647 iov_count++;
648 return fuse_send_msg(se, ch, iov, iov_count, req);
649 }
650
651 res = posix_memalign(&mbuf, pagesize, len);
652 if (res != 0)
653 return res;
654
655 mem_buf.buf[0].mem = mbuf;
656 res = fuse_buf_copy(&mem_buf, buf, 0);
657 if (res < 0) {
658 free(mbuf);
659 return -res;
660 }
661 len = res;
662
663 iov[iov_count].iov_base = mbuf;
664 iov[iov_count].iov_len = len;
665 iov_count++;
666 res = fuse_send_msg(se, ch, iov, iov_count, req);
667 free(mbuf);
668
669 return res;
670}
671
672struct fuse_ll_pipe {
673 size_t size;
674 int can_grow;
675 int pipe[2];
676};
677
678static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
679{
680 close(llp->pipe[0]);
681 close(llp->pipe[1]);
682 free(llp);
683}
684
685#ifdef HAVE_SPLICE
686#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
687static int fuse_pipe(int fds[2])
688{
689 int rv = pipe(fds);
690
691 if (rv == -1)
692 return rv;
693
694 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
695 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
696 fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
697 fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
698 close(fds[0]);
699 close(fds[1]);
700 rv = -1;
701 }
702 return rv;
703}
704#else
705static int fuse_pipe(int fds[2])
706{
707 return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
708}
709#endif
710
711static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
712{
713 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
714 if (llp == NULL) {
715 int res;
716
717 llp = malloc(sizeof(struct fuse_ll_pipe));
718 if (llp == NULL)
719 return NULL;
720
721 res = fuse_pipe(llp->pipe);
722 if (res == -1) {
723 free(llp);
724 return NULL;
725 }
726
727 /*
728 *the default size is 16 pages on linux
729 */
730 llp->size = pagesize * 16;
731 llp->can_grow = 1;
732
733 pthread_setspecific(se->pipe_key, llp);
734 }
735
736 return llp;
737}
738#endif
739
740static void fuse_ll_clear_pipe(struct fuse_session *se)
741{
742 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
743 if (llp) {
744 pthread_setspecific(se->pipe_key, NULL);
745 fuse_ll_pipe_free(llp);
746 }
747}
748
749#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
750static int read_back(int fd, char *buf, size_t len)
751{
752 int res;
753
754 res = read(fd, buf, len);
755 if (res == -1) {
756 fuse_log(FUSE_LOG_ERR,
757 "fuse: internal error: failed to read back from pipe: %s\n",
758 strerror(errno));
759 return -EIO;
760 }
761 if (res != len) {
762 fuse_log(FUSE_LOG_ERR,
763 "fuse: internal error: short read back from pipe: %i from %zd\n",
764 res, len);
765 return -EIO;
766 }
767 return 0;
768}
769
770static int grow_pipe_to_max(int pipefd)
771{
772 int res;
773 long max;
774 long maxfd;
775 char buf[32];
776
777 maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
778 if (maxfd < 0)
779 return -errno;
780
781 res = read(maxfd, buf, sizeof(buf) - 1);
782 if (res < 0) {
783 int saved_errno;
784
785 saved_errno = errno;
786 close(maxfd);
787 return -saved_errno;
788 }
789 close(maxfd);
790 buf[res] = '\0';
791
792 res = libfuse_strtol(buf, &max);
793 if (res)
794 return res;
795 res = fcntl(pipefd, F_SETPIPE_SZ, max);
796 if (res < 0)
797 return -errno;
798 return max;
799}
800
801static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
802 struct iovec *iov, int iov_count,
803 struct fuse_bufvec *buf, unsigned int flags,
804 fuse_req_t req)
805{
806 int res;
807 size_t len = fuse_buf_size(buf);
808 struct fuse_out_header *out = iov[0].iov_base;
809 struct fuse_ll_pipe *llp;
810 int splice_flags;
811 size_t pipesize;
812 size_t total_buf_size;
813 size_t idx;
814 size_t headerlen;
815 struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
816
817 if (se->broken_splice_nonblock)
818 goto fallback;
819
820 if (flags & FUSE_BUF_NO_SPLICE)
821 goto fallback;
822
823 total_buf_size = 0;
824 for (idx = buf->idx; idx < buf->count; idx++) {
825 total_buf_size += buf->buf[idx].size;
826 if (idx == buf->idx)
827 total_buf_size -= buf->off;
828 }
829 if (total_buf_size < 2 * pagesize)
830 goto fallback;
831
832 if (se->conn.proto_minor < 14 ||
833 !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
834 goto fallback;
835
836 llp = fuse_ll_get_pipe(se);
837 if (llp == NULL)
838 goto fallback;
839
840
841 headerlen = iov_length(iov, iov_count);
842
843 out->len = headerlen + len;
844
845 /*
846 * Heuristic for the required pipe size, does not work if the
847 * source contains less than page size fragments
848 */
849 pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
850
851 if (llp->size < pipesize) {
852 if (llp->can_grow) {
853 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
854 if (res == -1) {
855 res = grow_pipe_to_max(llp->pipe[0]);
856 if (res > 0)
857 llp->size = res;
858 llp->can_grow = 0;
859 goto fallback;
860 }
861 llp->size = res;
862 }
863 if (llp->size < pipesize)
864 goto fallback;
865 }
866
867
868 res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
869 if (res == -1)
870 goto fallback;
871
872 if (res != headerlen) {
873 res = -EIO;
874 fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
875 headerlen);
876 goto clear_pipe;
877 }
878
879 pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
880 pipe_buf.buf[0].fd = llp->pipe[1];
881
882 res = fuse_buf_copy(&pipe_buf, buf,
884 if (res < 0) {
885 if (res == -EAGAIN || res == -EINVAL) {
886 /*
887 * Should only get EAGAIN on kernels with
888 * broken SPLICE_F_NONBLOCK support (<=
889 * 2.6.35) where this error or a short read is
890 * returned even if the pipe itself is not
891 * full
892 *
893 * EINVAL might mean that splice can't handle
894 * this combination of input and output.
895 */
896 if (res == -EAGAIN)
897 se->broken_splice_nonblock = 1;
898
899 pthread_setspecific(se->pipe_key, NULL);
900 fuse_ll_pipe_free(llp);
901 goto fallback;
902 }
903 res = -res;
904 goto clear_pipe;
905 }
906
907 if (res != 0 && res < len) {
908 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
909 void *mbuf;
910 size_t now_len = res;
911 /*
912 * For regular files a short count is either
913 * 1) due to EOF, or
914 * 2) because of broken SPLICE_F_NONBLOCK (see above)
915 *
916 * For other inputs it's possible that we overflowed
917 * the pipe because of small buffer fragments.
918 */
919
920 res = posix_memalign(&mbuf, pagesize, len);
921 if (res != 0)
922 goto clear_pipe;
923
924 mem_buf.buf[0].mem = mbuf;
925 mem_buf.off = now_len;
926 res = fuse_buf_copy(&mem_buf, buf, 0);
927 if (res > 0) {
928 char *tmpbuf;
929 size_t extra_len = res;
930 /*
931 * Trickiest case: got more data. Need to get
932 * back the data from the pipe and then fall
933 * back to regular write.
934 */
935 tmpbuf = malloc(headerlen);
936 if (tmpbuf == NULL) {
937 free(mbuf);
938 res = ENOMEM;
939 goto clear_pipe;
940 }
941 res = read_back(llp->pipe[0], tmpbuf, headerlen);
942 free(tmpbuf);
943 if (res != 0) {
944 free(mbuf);
945 goto clear_pipe;
946 }
947 res = read_back(llp->pipe[0], mbuf, now_len);
948 if (res != 0) {
949 free(mbuf);
950 goto clear_pipe;
951 }
952 len = now_len + extra_len;
953 iov[iov_count].iov_base = mbuf;
954 iov[iov_count].iov_len = len;
955 iov_count++;
956 res = fuse_send_msg(se, ch, iov, iov_count, req);
957 free(mbuf);
958 return res;
959 }
960 free(mbuf);
961 res = now_len;
962 }
963 len = res;
964 out->len = headerlen + len;
965
966 if (se->debug) {
967 fuse_log(FUSE_LOG_DEBUG,
968 " unique: %llu, success, outsize: %i (splice)\n",
969 (unsigned long long) out->unique, out->len);
970 }
971
972 splice_flags = 0;
973 if ((flags & FUSE_BUF_SPLICE_MOVE) &&
974 (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
975 splice_flags |= SPLICE_F_MOVE;
976
977 if (se->io != NULL && se->io->splice_send != NULL) {
978 res = se->io->splice_send(llp->pipe[0], NULL,
979 ch ? ch->fd : se->fd, NULL, out->len,
980 splice_flags, se->userdata);
981 } else {
982 res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
983 out->len, splice_flags);
984 }
985 if (res == -1) {
986 res = -errno;
987 perror("fuse: splice from pipe");
988 goto clear_pipe;
989 }
990 if (res != out->len) {
991 res = -EIO;
992 fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
993 res, out->len);
994 goto clear_pipe;
995 }
996 return 0;
997
998clear_pipe:
999 fuse_ll_clear_pipe(se);
1000 return res;
1001
1002fallback:
1003 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len, req);
1004}
1005#else
1006static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
1007 struct iovec *iov, int iov_count,
1008 struct fuse_bufvec *req_data, unsigned int flags,
1009 fuse_req_t req)
1010{
1011 size_t len = fuse_buf_size(req_data);
1012 (void) flags;
1013
1014 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, req_data, len, req);
1015}
1016#endif
1017
1018int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
1019 enum fuse_buf_copy_flags flags)
1020{
1021 struct iovec iov[2];
1022 struct fuse_out_header out;
1023 int res;
1024
1025 if (req->flags.is_uring)
1026 return fuse_reply_data_uring(req, bufv, flags);
1027
1028 iov[0].iov_base = &out;
1029 iov[0].iov_len = sizeof(struct fuse_out_header);
1030
1031 out.unique = req->unique;
1032 out.error = 0;
1033
1034 res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags, req);
1035 if (res <= 0) {
1036 fuse_free_req(req);
1037 return res;
1038 } else {
1039 return fuse_reply_err(req, res);
1040 }
1041}
1042
1043int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
1044{
1045 struct fuse_statfs_out arg;
1046 size_t size = req->se->conn.proto_minor < 4 ?
1047 FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
1048
1049 memset(&arg, 0, sizeof(arg));
1050 convert_statfs(stbuf, &arg.st);
1051
1052 return send_reply_ok(req, &arg, size);
1053}
1054
1055int fuse_reply_xattr(fuse_req_t req, size_t count)
1056{
1057 struct fuse_getxattr_out arg;
1058
1059 memset(&arg, 0, sizeof(arg));
1060 arg.size = count;
1061
1062 return send_reply_ok(req, &arg, sizeof(arg));
1063}
1064
1065int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
1066{
1067 struct fuse_lk_out arg;
1068
1069 memset(&arg, 0, sizeof(arg));
1070 arg.lk.type = lock->l_type;
1071 if (lock->l_type != F_UNLCK) {
1072 arg.lk.start = lock->l_start;
1073 if (lock->l_len == 0)
1074 arg.lk.end = OFFSET_MAX;
1075 else
1076 arg.lk.end = lock->l_start + lock->l_len - 1;
1077 }
1078 arg.lk.pid = lock->l_pid;
1079 return send_reply_ok(req, &arg, sizeof(arg));
1080}
1081
1082int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
1083{
1084 struct fuse_bmap_out arg;
1085
1086 memset(&arg, 0, sizeof(arg));
1087 arg.block = idx;
1088
1089 return send_reply_ok(req, &arg, sizeof(arg));
1090}
1091
1092static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
1093 size_t count)
1094{
1095 struct fuse_ioctl_iovec *fiov;
1096 size_t i;
1097
1098 fiov = malloc(sizeof(fiov[0]) * count);
1099 if (!fiov)
1100 return NULL;
1101
1102 for (i = 0; i < count; i++) {
1103 fiov[i].base = (uintptr_t) iov[i].iov_base;
1104 fiov[i].len = iov[i].iov_len;
1105 }
1106
1107 return fiov;
1108}
1109
1111 const struct iovec *in_iov, size_t in_count,
1112 const struct iovec *out_iov, size_t out_count)
1113{
1114 struct fuse_ioctl_out arg;
1115 struct fuse_ioctl_iovec *in_fiov = NULL;
1116 struct fuse_ioctl_iovec *out_fiov = NULL;
1117 struct iovec iov[4];
1118 size_t count = 1;
1119 int res;
1120
1121 memset(&arg, 0, sizeof(arg));
1122 arg.flags |= FUSE_IOCTL_RETRY;
1123 arg.in_iovs = in_count;
1124 arg.out_iovs = out_count;
1125 iov[count].iov_base = &arg;
1126 iov[count].iov_len = sizeof(arg);
1127 count++;
1128
1129 if (req->se->conn.proto_minor < 16) {
1130 if (in_count) {
1131 iov[count].iov_base = (void *)in_iov;
1132 iov[count].iov_len = sizeof(in_iov[0]) * in_count;
1133 count++;
1134 }
1135
1136 if (out_count) {
1137 iov[count].iov_base = (void *)out_iov;
1138 iov[count].iov_len = sizeof(out_iov[0]) * out_count;
1139 count++;
1140 }
1141 } else {
1142 /* Can't handle non-compat 64bit ioctls on 32bit */
1143 if (sizeof(void *) == 4 && req->flags.ioctl_64bit) {
1144 res = fuse_reply_err(req, EINVAL);
1145 goto out;
1146 }
1147
1148 if (in_count) {
1149 in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
1150 if (!in_fiov)
1151 goto enomem;
1152
1153 iov[count].iov_base = (void *)in_fiov;
1154 iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
1155 count++;
1156 }
1157 if (out_count) {
1158 out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
1159 if (!out_fiov)
1160 goto enomem;
1161
1162 iov[count].iov_base = (void *)out_fiov;
1163 iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
1164 count++;
1165 }
1166 }
1167
1168 res = send_reply_iov(req, 0, iov, count);
1169out:
1170 free(in_fiov);
1171 free(out_fiov);
1172
1173 return res;
1174
1175enomem:
1176 res = fuse_reply_err(req, ENOMEM);
1177 goto out;
1178}
1179
1180int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
1181{
1182 struct fuse_ioctl_out arg;
1183 struct iovec iov[3];
1184 size_t count = 1;
1185
1186 memset(&arg, 0, sizeof(arg));
1187 arg.result = result;
1188 iov[count].iov_base = &arg;
1189 iov[count].iov_len = sizeof(arg);
1190 count++;
1191
1192 if (size) {
1193 iov[count].iov_base = (char *) buf;
1194 iov[count].iov_len = size;
1195 count++;
1196 }
1197
1198 return send_reply_iov(req, 0, iov, count);
1199}
1200
1201int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
1202 int count)
1203{
1204 struct iovec *padded_iov;
1205 struct fuse_ioctl_out arg;
1206 int res;
1207
1208 padded_iov = malloc((count + 2) * sizeof(struct iovec));
1209 if (padded_iov == NULL)
1210 return fuse_reply_err(req, ENOMEM);
1211
1212 memset(&arg, 0, sizeof(arg));
1213 arg.result = result;
1214 padded_iov[1].iov_base = &arg;
1215 padded_iov[1].iov_len = sizeof(arg);
1216
1217 memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
1218
1219 res = send_reply_iov(req, 0, padded_iov, count + 2);
1220 free(padded_iov);
1221
1222 return res;
1223}
1224
1225int fuse_reply_poll(fuse_req_t req, unsigned revents)
1226{
1227 struct fuse_poll_out arg;
1228
1229 memset(&arg, 0, sizeof(arg));
1230 arg.revents = revents;
1231
1232 return send_reply_ok(req, &arg, sizeof(arg));
1233}
1234
1235int fuse_reply_lseek(fuse_req_t req, off_t off)
1236{
1237 struct fuse_lseek_out arg;
1238
1239 memset(&arg, 0, sizeof(arg));
1240 arg.offset = off;
1241
1242 return send_reply_ok(req, &arg, sizeof(arg));
1243}
1244
1245#ifdef HAVE_STATX
1246int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx,
1247 double attr_timeout)
1248{
1249 struct fuse_statx_out arg;
1250
1251 memset(&arg, 0, sizeof(arg));
1252 arg.flags = flags;
1253 arg.attr_valid = calc_timeout_sec(attr_timeout);
1254 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
1255 memcpy(&arg.stat, statx, sizeof(arg.stat));
1256
1257 return send_reply_ok(req, &arg, sizeof(arg));
1258}
1259#else
1260int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx,
1261 double attr_timeout)
1262{
1263 (void)req;
1264 (void)flags;
1265 (void)statx;
1266 (void)attr_timeout;
1267
1268 return -ENOSYS;
1269}
1270#endif
1271
1272static void _do_lookup(fuse_req_t req, const fuse_ino_t nodeid,
1273 const void *op_in, const void *in_payload)
1274{
1275 (void)op_in;
1276
1277 char *name = (char *)in_payload;
1278
1279 if (req->se->op.lookup)
1280 req->se->op.lookup(req, nodeid, name);
1281 else
1282 fuse_reply_err(req, ENOSYS);
1283}
1284
1285static void do_lookup(fuse_req_t req, const fuse_ino_t nodeid,
1286 const void *inarg)
1287{
1288 _do_lookup(req, nodeid, NULL, inarg);
1289}
1290
1291static void _do_forget(fuse_req_t req, const fuse_ino_t nodeid,
1292 const void *op_in, const void *in_payload)
1293{
1294 (void)in_payload;
1295
1296 struct fuse_forget_in *arg = (struct fuse_forget_in *)op_in;
1297
1298 if (req->se->op.forget)
1299 req->se->op.forget(req, nodeid, arg->nlookup);
1300 else
1301 fuse_reply_none(req);
1302}
1303
1304static void do_forget(fuse_req_t req, const fuse_ino_t nodeid,
1305 const void *inarg)
1306{
1307 _do_forget(req, nodeid, inarg, NULL);
1308}
1309
1310static void _do_batch_forget(fuse_req_t req, const fuse_ino_t nodeid,
1311 const void *op_in, const void *in_payload)
1312{
1313 (void)nodeid;
1314 unsigned int i;
1315
1316 const struct fuse_batch_forget_in *arg = op_in;
1317 const struct fuse_forget_one *forgets = in_payload;
1318
1319 if (req->se->op.forget_multi) {
1320 req->se->op.forget_multi(req, arg->count,
1321 (struct fuse_forget_data *)in_payload);
1322 } else if (req->se->op.forget) {
1323 for (i = 0; i < arg->count; i++) {
1324 const struct fuse_forget_one *forget = &forgets[i];
1325 struct fuse_req *dummy_req;
1326
1327 dummy_req = fuse_ll_alloc_req(req->se);
1328 if (dummy_req == NULL)
1329 break;
1330
1331 dummy_req->unique = req->unique;
1332 dummy_req->ctx = req->ctx;
1333 dummy_req->ch = NULL;
1334
1335 req->se->op.forget(dummy_req, forget->nodeid,
1336 forget->nlookup);
1337 }
1338 fuse_reply_none(req);
1339 } else {
1340 fuse_reply_none(req);
1341 }
1342}
1343
1344static void do_batch_forget(fuse_req_t req, const fuse_ino_t nodeid,
1345 const void *inarg)
1346{
1347 struct fuse_batch_forget_in *arg = (void *)inarg;
1348 struct fuse_forget_one *param = (void *)PARAM(arg);
1349
1350 _do_batch_forget(req, nodeid, inarg, param);
1351}
1352
1353static void _do_getattr(fuse_req_t req, const fuse_ino_t nodeid,
1354 const void *op_in, const void *in_payload)
1355{
1356 struct fuse_getattr_in *arg = (struct fuse_getattr_in *)op_in;
1357 (void)in_payload;
1358
1359 struct fuse_file_info *fip = NULL;
1360 struct fuse_file_info fi;
1361
1362 if (req->se->conn.proto_minor >= 9) {
1363 if (arg->getattr_flags & FUSE_GETATTR_FH) {
1364 memset(&fi, 0, sizeof(fi));
1365 fi.fh = arg->fh;
1366 fip = &fi;
1367 }
1368 }
1369
1370 if (req->se->op.getattr)
1371 req->se->op.getattr(req, nodeid, fip);
1372 else
1373 fuse_reply_err(req, ENOSYS);
1374}
1375
1376static void do_getattr(fuse_req_t req, const fuse_ino_t nodeid,
1377 const void *inarg)
1378{
1379 _do_getattr(req, nodeid, inarg, NULL);
1380}
1381
1382static void _do_setattr(fuse_req_t req, const fuse_ino_t nodeid,
1383 const void *op_in, const void *in_payload)
1384{
1385 (void)in_payload;
1386 const struct fuse_setattr_in *arg = op_in;
1387 uint32_t valid = arg->valid;
1388
1389 if (req->se->op.setattr) {
1390 struct fuse_file_info *fi = NULL;
1391 struct fuse_file_info fi_store;
1392 struct stat stbuf;
1393 memset(&stbuf, 0, sizeof(stbuf));
1394 convert_attr(arg, &stbuf);
1395 if (arg->valid & FATTR_FH) {
1396 valid &= ~FATTR_FH;
1397 memset(&fi_store, 0, sizeof(fi_store));
1398 fi = &fi_store;
1399 fi->fh = arg->fh;
1400 }
1401 valid &= FUSE_SET_ATTR_MODE | FUSE_SET_ATTR_UID |
1402 FUSE_SET_ATTR_GID | FUSE_SET_ATTR_SIZE |
1403 FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME |
1404 FUSE_SET_ATTR_KILL_SUID | FUSE_SET_ATTR_KILL_SGID |
1405 FUSE_SET_ATTR_ATIME_NOW | FUSE_SET_ATTR_MTIME_NOW |
1406 FUSE_SET_ATTR_CTIME;
1407
1408 req->se->op.setattr(req, nodeid, &stbuf, valid, fi);
1409 } else
1410 fuse_reply_err(req, ENOSYS);
1411}
1412
1413static void do_setattr(fuse_req_t req, const fuse_ino_t nodeid,
1414 const void *inarg)
1415{
1416 _do_setattr(req, nodeid, inarg, NULL);
1417}
1418
1419static void _do_access(fuse_req_t req, const fuse_ino_t nodeid,
1420 const void *op_in, const void *in_payload)
1421{
1422 (void)in_payload;
1423 const struct fuse_access_in *arg = op_in;
1424
1425 if (req->se->op.access)
1426 req->se->op.access(req, nodeid, arg->mask);
1427 else
1428 fuse_reply_err(req, ENOSYS);
1429}
1430
1431static void do_access(fuse_req_t req, const fuse_ino_t nodeid,
1432 const void *inarg)
1433{
1434 _do_access(req, nodeid, inarg, NULL);
1435}
1436
1437static void _do_readlink(fuse_req_t req, const fuse_ino_t nodeid,
1438 const void *op_in, const void *in_payload)
1439{
1440 (void)op_in;
1441 (void)in_payload;
1442
1443 if (req->se->op.readlink)
1444 req->se->op.readlink(req, nodeid);
1445 else
1446 fuse_reply_err(req, ENOSYS);
1447}
1448
1449static void do_readlink(fuse_req_t req, const fuse_ino_t nodeid,
1450 const void *inarg)
1451{
1452 _do_readlink(req, nodeid, inarg, NULL);
1453}
1454
1455static void _do_mknod(fuse_req_t req, const fuse_ino_t nodeid,
1456 const void *op_in, const void *in_payload)
1457{
1458 const struct fuse_mknod_in *arg = (struct fuse_mknod_in *)op_in;
1459 const char *name = in_payload;
1460
1461 if (req->se->conn.proto_minor >= 12)
1462 req->ctx.umask = arg->umask;
1463
1464 if (req->se->op.mknod)
1465 req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
1466 else
1467 fuse_reply_err(req, ENOSYS);
1468}
1469
1470static void do_mknod(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1471{
1472 struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg;
1473 char *name = PARAM(arg);
1474
1475 if (req->se->conn.proto_minor < 12)
1476 name = (char *)inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
1477
1478 _do_mknod(req, nodeid, inarg, name);
1479}
1480
1481static void _do_mkdir(fuse_req_t req, const fuse_ino_t nodeid,
1482 const void *op_in, const void *in_payload)
1483{
1484 const char *name = in_payload;
1485 const struct fuse_mkdir_in *arg = op_in;
1486
1487 if (req->se->conn.proto_minor >= 12)
1488 req->ctx.umask = arg->umask;
1489
1490 if (req->se->op.mkdir)
1491 req->se->op.mkdir(req, nodeid, name, arg->mode);
1492 else
1493 fuse_reply_err(req, ENOSYS);
1494}
1495
1496static void do_mkdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1497{
1498 const struct fuse_mkdir_in *arg = inarg;
1499 const char *name = PARAM(arg);
1500
1501 _do_mkdir(req, nodeid, inarg, name);
1502}
1503
1504static void _do_unlink(fuse_req_t req, const fuse_ino_t nodeid,
1505 const void *op_in, const void *in_payload)
1506{
1507 (void)op_in;
1508 const char *name = in_payload;
1509
1510 if (req->se->op.unlink)
1511 req->se->op.unlink(req, nodeid, name);
1512 else
1513 fuse_reply_err(req, ENOSYS);
1514}
1515
1516static void do_unlink(fuse_req_t req, const fuse_ino_t nodeid,
1517 const void *inarg)
1518{
1519 _do_unlink(req, nodeid, NULL, inarg);
1520}
1521
1522static void _do_rmdir(fuse_req_t req, const fuse_ino_t nodeid,
1523 const void *op_in, const void *in_payload)
1524{
1525 (void)op_in;
1526 const char *name = in_payload;
1527
1528 if (req->se->op.rmdir)
1529 req->se->op.rmdir(req, nodeid, name);
1530 else
1531 fuse_reply_err(req, ENOSYS);
1532}
1533
1534static void do_rmdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1535{
1536 _do_rmdir(req, nodeid, NULL, inarg);
1537}
1538
1539static void _do_symlink(fuse_req_t req, const fuse_ino_t nodeid,
1540 const void *op_in, const void *in_payload)
1541{
1542 (void)op_in;
1543 const char *name = (char *)in_payload;
1544 const char *linkname = name + strlen(name) + 1;
1545
1546 if (req->se->op.symlink)
1547 req->se->op.symlink(req, linkname, nodeid, name);
1548 else
1549 fuse_reply_err(req, ENOSYS);
1550}
1551
1552static void do_symlink(fuse_req_t req, const fuse_ino_t nodeid,
1553 const void *inarg)
1554{
1555 _do_symlink(req, nodeid, NULL, inarg);
1556}
1557
1558static void _do_rename(fuse_req_t req, const fuse_ino_t nodeid,
1559 const void *op_in, const void *in_payload)
1560{
1561 const struct fuse_rename_in *arg = (struct fuse_rename_in *)op_in;
1562 const char *oldname = in_payload;
1563 const char *newname = oldname + strlen(oldname) + 1;
1564
1565 if (req->se->op.rename)
1566 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1567 0);
1568 else
1569 fuse_reply_err(req, ENOSYS);
1570}
1571
1572static void do_rename(fuse_req_t req, const fuse_ino_t nodeid,
1573 const void *inarg)
1574{
1575 const struct fuse_rename_in *arg = inarg;
1576 const void *payload = PARAM(arg);
1577
1578 _do_rename(req, nodeid, arg, payload);
1579}
1580
1581static void _do_rename2(fuse_req_t req, const fuse_ino_t nodeid,
1582 const void *op_in, const void *in_payload)
1583{
1584 const struct fuse_rename2_in *arg = op_in;
1585 const char *oldname = in_payload;
1586 const char *newname = oldname + strlen(oldname) + 1;
1587
1588 if (req->se->op.rename)
1589 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1590 arg->flags);
1591 else
1592 fuse_reply_err(req, ENOSYS);
1593}
1594
1595static void do_rename2(fuse_req_t req, const fuse_ino_t nodeid,
1596 const void *inarg)
1597{
1598 const struct fuse_rename2_in *arg = inarg;
1599 const void *payload = PARAM(arg);
1600
1601 _do_rename2(req, nodeid, arg, payload);
1602}
1603
1604static void _do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *op_in,
1605 const void *in_payload)
1606{
1607 (void)in_payload;
1608 const struct fuse_create_in *arg = op_in;
1609
1610 if (req->se->op.tmpfile) {
1611 struct fuse_file_info fi;
1612
1613 memset(&fi, 0, sizeof(fi));
1614 fi.flags = arg->flags;
1615
1616 if (req->se->conn.proto_minor >= 12)
1617 req->ctx.umask = arg->umask;
1618
1619 req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
1620 } else
1621 fuse_reply_err(req, ENOSYS);
1622}
1623
1624static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1625{
1626 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
1627
1628 _do_tmpfile(req, nodeid, arg, NULL);
1629}
1630
1631static void _do_link(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
1632 const void *in_payload)
1633{
1634 struct fuse_link_in *arg = (struct fuse_link_in *)op_in;
1635
1636 if (req->se->op.link)
1637 req->se->op.link(req, arg->oldnodeid, nodeid, in_payload);
1638 else
1639 fuse_reply_err(req, ENOSYS);
1640}
1641
1642static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1643{
1644 const struct fuse_link_in *arg = inarg;
1645 const void *name = PARAM(arg);
1646
1647 _do_link(req, nodeid, inarg, name);
1648}
1649
1650static void _do_create(fuse_req_t req, const fuse_ino_t nodeid,
1651 const void *op_in, const void *in_payload)
1652{
1653 const struct fuse_create_in *arg = op_in;
1654 const char *name = in_payload;
1655
1656 if (req->se->op.create) {
1657 struct fuse_file_info fi;
1658
1659 memset(&fi, 0, sizeof(fi));
1660 fi.flags = arg->flags;
1661
1662 if (req->se->conn.proto_minor >= 12)
1663 req->ctx.umask = arg->umask;
1664
1665 /* XXX: fuse_create_in::open_flags */
1666
1667 req->se->op.create(req, nodeid, name, arg->mode, &fi);
1668 } else {
1669 fuse_reply_err(req, ENOSYS);
1670 }
1671}
1672
1673static void do_create(fuse_req_t req, const fuse_ino_t nodeid,
1674 const void *inarg)
1675{
1676 const struct fuse_create_in *arg = (struct fuse_create_in *)inarg;
1677 void *payload = PARAM(arg);
1678
1679 if (req->se->conn.proto_minor < 12)
1680 payload = (char *)inarg + sizeof(struct fuse_open_in);
1681
1682 _do_create(req, nodeid, arg, payload);
1683}
1684
1685static void _do_open(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
1686 const void *in_payload)
1687{
1688 (void)in_payload;
1689 struct fuse_open_in *arg = (struct fuse_open_in *)op_in;
1690 struct fuse_file_info fi;
1691
1692 memset(&fi, 0, sizeof(fi));
1693 fi.flags = arg->flags;
1694
1695 /* XXX: fuse_open_in::open_flags */
1696
1697 if (req->se->op.open)
1698 req->se->op.open(req, nodeid, &fi);
1699 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
1700 fuse_reply_err(req, ENOSYS);
1701 else
1702 fuse_reply_open(req, &fi);
1703}
1704
1705static void do_open(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1706{
1707 _do_open(req, nodeid, inarg, NULL);
1708}
1709
1710static void _do_read(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
1711 const void *in_payload)
1712{
1713 (void)in_payload;
1714 struct fuse_read_in *arg = (struct fuse_read_in *)op_in;
1715
1716 if (req->se->op.read) {
1717 struct fuse_file_info fi;
1718
1719 memset(&fi, 0, sizeof(fi));
1720 fi.fh = arg->fh;
1721 if (req->se->conn.proto_minor >= 9) {
1722 fi.lock_owner = arg->lock_owner;
1723 fi.flags = arg->flags;
1724 }
1725 req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
1726 } else
1727 fuse_reply_err(req, ENOSYS);
1728}
1729
1730static void do_read(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1731{
1732 _do_read(req, nodeid, inarg, NULL);
1733}
1734
1735static void _do_write(fuse_req_t req, const fuse_ino_t nodeid,
1736 const void *op_in, const void *in_payload)
1737{
1738 struct fuse_write_in *arg = (struct fuse_write_in *)op_in;
1739 const char *buf = in_payload;
1740 struct fuse_file_info fi;
1741
1742 memset(&fi, 0, sizeof(fi));
1743 fi.fh = arg->fh;
1744 fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
1745
1746 if (req->se->conn.proto_minor >= 9) {
1747 fi.lock_owner = arg->lock_owner;
1748 fi.flags = arg->flags;
1749 }
1750
1751 if (req->se->op.write)
1752 req->se->op.write(req, nodeid, buf, arg->size, arg->offset,
1753 &fi);
1754 else
1755 fuse_reply_err(req, ENOSYS);
1756}
1757
1758static void do_write(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1759{
1760 struct fuse_write_in *arg = (struct fuse_write_in *)inarg;
1761 const void *payload;
1762
1763 if (req->se->conn.proto_minor < 9)
1764 payload = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1765 else
1766 payload = PARAM(arg);
1767
1768 _do_write(req, nodeid, arg, payload);
1769}
1770
1771static void _do_write_buf(fuse_req_t req, const fuse_ino_t nodeid,
1772 const void *op_in, struct fuse_bufvec *bufv)
1773{
1774 struct fuse_session *se = req->se;
1775 struct fuse_write_in *arg = (struct fuse_write_in *)op_in;
1776 struct fuse_file_info fi;
1777
1778 memset(&fi, 0, sizeof(fi));
1779 fi.fh = arg->fh;
1780 fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
1781
1782 if (se->conn.proto_minor >= 9) {
1783 fi.lock_owner = arg->lock_owner;
1784 fi.flags = arg->flags;
1785 }
1786
1787 se->op.write_buf(req, nodeid, bufv, arg->offset, &fi);
1788}
1789
1790static void do_write_buf(fuse_req_t req, const fuse_ino_t nodeid,
1791 const void *inarg, const struct fuse_buf *ibuf)
1792{
1793 struct fuse_session *se = req->se;
1794 struct fuse_bufvec bufv = {
1795 .buf[0] = *ibuf,
1796 .count = 1,
1797 };
1798 struct fuse_write_in *arg = (struct fuse_write_in *)inarg;
1799
1800 if (se->conn.proto_minor < 9) {
1801 bufv.buf[0].mem = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1802 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1803 FUSE_COMPAT_WRITE_IN_SIZE;
1804 assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
1805 } else {
1806 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
1807 bufv.buf[0].mem = PARAM(arg);
1808
1809 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1810 sizeof(struct fuse_write_in);
1811 }
1812 if (bufv.buf[0].size < arg->size) {
1813 fuse_log(FUSE_LOG_ERR,
1814 "fuse: %s: buffer size too small\n", __func__);
1815 fuse_reply_err(req, EIO);
1816 goto out;
1817 }
1818 bufv.buf[0].size = arg->size;
1819
1820 _do_write_buf(req, nodeid, inarg, &bufv);
1821
1822out:
1823 /* Need to reset the pipe if ->write_buf() didn't consume all data */
1824 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
1825 fuse_ll_clear_pipe(se);
1826}
1827
1828static void _do_flush(fuse_req_t req, const fuse_ino_t nodeid,
1829 const void *op_in, const void *in_payload)
1830{
1831 (void)in_payload;
1832 struct fuse_flush_in *arg = (struct fuse_flush_in *)op_in;
1833 struct fuse_file_info fi;
1834
1835 memset(&fi, 0, sizeof(fi));
1836 fi.fh = arg->fh;
1837 fi.flush = 1;
1838 if (req->se->conn.proto_minor >= 7)
1839 fi.lock_owner = arg->lock_owner;
1840
1841 if (req->se->op.flush)
1842 req->se->op.flush(req, nodeid, &fi);
1843 else
1844 fuse_reply_err(req, ENOSYS);
1845}
1846
1847static void do_flush(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1848{
1849 _do_flush(req, nodeid, inarg, NULL);
1850}
1851
1852static void _do_release(fuse_req_t req, const fuse_ino_t nodeid,
1853 const void *op_in, const void *in_payload)
1854{
1855 (void)in_payload;
1856 const struct fuse_release_in *arg = op_in;
1857 struct fuse_file_info fi;
1858
1859 memset(&fi, 0, sizeof(fi));
1860 fi.flags = arg->flags;
1861 fi.fh = arg->fh;
1862 if (req->se->conn.proto_minor >= 8) {
1863 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
1864 fi.lock_owner = arg->lock_owner;
1865 }
1866 if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
1867 fi.flock_release = 1;
1868 fi.lock_owner = arg->lock_owner;
1869 }
1870
1871 if (req->se->op.release)
1872 req->se->op.release(req, nodeid, &fi);
1873 else
1874 fuse_reply_err(req, 0);
1875}
1876
1877static void do_release(fuse_req_t req, const fuse_ino_t nodeid,
1878 const void *inarg)
1879{
1880 _do_release(req, nodeid, inarg, NULL);
1881}
1882
1883static void _do_fsync(fuse_req_t req, const fuse_ino_t nodeid,
1884 const void *op_in, const void *in_payload)
1885{
1886 (void)in_payload;
1887 const struct fuse_fsync_in *arg = op_in;
1888 struct fuse_file_info fi;
1889 int datasync = arg->fsync_flags & 1;
1890
1891 memset(&fi, 0, sizeof(fi));
1892 fi.fh = arg->fh;
1893
1894 if (req->se->op.fsync)
1895 req->se->op.fsync(req, nodeid, datasync, &fi);
1896 else
1897 fuse_reply_err(req, ENOSYS);
1898}
1899
1900static void do_fsync(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1901{
1902 _do_fsync(req, nodeid, inarg, NULL);
1903}
1904
1905static void _do_opendir(fuse_req_t req, const fuse_ino_t nodeid,
1906 const void *op_in, const void *in_payload)
1907{
1908 (void)in_payload;
1909 const struct fuse_open_in *arg = op_in;
1910 struct fuse_file_info fi;
1911
1912 memset(&fi, 0, sizeof(fi));
1913 fi.flags = arg->flags;
1914 /* XXX: fuse_open_in::open_flags */
1915
1916 if (req->se->op.opendir)
1917 req->se->op.opendir(req, nodeid, &fi);
1918 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
1919 fuse_reply_err(req, ENOSYS);
1920 else
1921 fuse_reply_open(req, &fi);
1922}
1923
1924static void do_opendir(fuse_req_t req, const fuse_ino_t nodeid,
1925 const void *inarg)
1926{
1927 _do_opendir(req, nodeid, inarg, NULL);
1928}
1929
1930static void _do_readdir(fuse_req_t req, const fuse_ino_t nodeid,
1931 const void *op_in, const void *in_payload)
1932{
1933 (void)in_payload;
1934 struct fuse_read_in *arg = (struct fuse_read_in *)op_in;
1935 struct fuse_file_info fi;
1936
1937 memset(&fi, 0, sizeof(fi));
1938 fi.fh = arg->fh;
1939
1940 if (req->se->op.readdir)
1941 req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
1942 else
1943 fuse_reply_err(req, ENOSYS);
1944}
1945
1946static void do_readdir(fuse_req_t req, const fuse_ino_t nodeid,
1947 const void *inarg)
1948{
1949 _do_readdir(req, nodeid, inarg, NULL);
1950}
1951
1952static void _do_readdirplus(fuse_req_t req, const fuse_ino_t nodeid,
1953 const void *op_in, const void *in_payload)
1954{
1955 (void)in_payload;
1956 struct fuse_read_in *arg = (struct fuse_read_in *)op_in;
1957 struct fuse_file_info fi;
1958
1959 memset(&fi, 0, sizeof(fi));
1960 fi.fh = arg->fh;
1961
1962 if (req->se->op.readdirplus)
1963 req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
1964 else
1965 fuse_reply_err(req, ENOSYS);
1966}
1967
1968static void do_readdirplus(fuse_req_t req, const fuse_ino_t nodeid,
1969 const void *inarg)
1970{
1971 _do_readdirplus(req, nodeid, inarg, NULL);
1972}
1973
1974static void _do_releasedir(fuse_req_t req, const fuse_ino_t nodeid,
1975 const void *op_in, const void *in_payload)
1976{
1977 (void)in_payload;
1978 struct fuse_release_in *arg = (struct fuse_release_in *)op_in;
1979 struct fuse_file_info fi;
1980
1981 memset(&fi, 0, sizeof(fi));
1982 fi.flags = arg->flags;
1983 fi.fh = arg->fh;
1984
1985 if (req->se->op.releasedir)
1986 req->se->op.releasedir(req, nodeid, &fi);
1987 else
1988 fuse_reply_err(req, 0);
1989}
1990
1991static void do_releasedir(fuse_req_t req, const fuse_ino_t nodeid,
1992 const void *inarg)
1993{
1994 _do_releasedir(req, nodeid, inarg, NULL);
1995}
1996
1997static void _do_fsyncdir(fuse_req_t req, const fuse_ino_t nodeid,
1998 const void *op_in, const void *in_payload)
1999{
2000 (void)in_payload;
2001 struct fuse_fsync_in *arg = (struct fuse_fsync_in *)op_in;
2002 struct fuse_file_info fi;
2003 int datasync = arg->fsync_flags & 1;
2004
2005 memset(&fi, 0, sizeof(fi));
2006 fi.fh = arg->fh;
2007
2008 if (req->se->op.fsyncdir)
2009 req->se->op.fsyncdir(req, nodeid, datasync, &fi);
2010 else
2011 fuse_reply_err(req, ENOSYS);
2012}
2013static void do_fsyncdir(fuse_req_t req, const fuse_ino_t nodeid,
2014 const void *inarg)
2015{
2016 _do_fsyncdir(req, nodeid, inarg, NULL);
2017}
2018
2019static void _do_statfs(fuse_req_t req, const fuse_ino_t nodeid,
2020 const void *op_in, const void *in_payload)
2021{
2022 (void) nodeid;
2023 (void)op_in;
2024 (void)in_payload;
2025
2026 if (req->se->op.statfs)
2027 req->se->op.statfs(req, nodeid);
2028 else {
2029 struct statvfs buf = {
2030 .f_namemax = 255,
2031 .f_bsize = 512,
2032 };
2033 fuse_reply_statfs(req, &buf);
2034 }
2035}
2036static void do_statfs(fuse_req_t req, const fuse_ino_t nodeid,
2037 const void *inarg)
2038{
2039 _do_statfs(req, nodeid, inarg, NULL);
2040}
2041
2042static void _do_setxattr(fuse_req_t req, const fuse_ino_t nodeid,
2043 const void *op_in, const void *in_payload)
2044{
2045 struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *)op_in;
2046 const char *name = in_payload;
2047 const char *value = name + strlen(name) + 1;
2048
2049 /* XXX:The API should be extended to support extra_flags/setxattr_flags */
2050
2051 if (req->se->op.setxattr)
2052 req->se->op.setxattr(req, nodeid, name, value, arg->size,
2053 arg->flags);
2054 else
2055 fuse_reply_err(req, ENOSYS);
2056}
2057static void do_setxattr(fuse_req_t req, const fuse_ino_t nodeid,
2058 const void *inarg)
2059{
2060 struct fuse_session *se = req->se;
2061 unsigned int xattr_ext = !!(se->conn.want & FUSE_CAP_SETXATTR_EXT);
2062 const struct fuse_setxattr_in *arg = inarg;
2063 char *payload = xattr_ext ? PARAM(arg) :
2064 (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
2065
2066 _do_setxattr(req, nodeid, arg, payload);
2067}
2068
2069static void _do_getxattr(fuse_req_t req, const fuse_ino_t nodeid,
2070 const void *op_in, const void *in_payload)
2071{
2072 const struct fuse_getxattr_in *arg = op_in;
2073
2074 if (req->se->op.getxattr)
2075 req->se->op.getxattr(req, nodeid, in_payload, arg->size);
2076 else
2077 fuse_reply_err(req, ENOSYS);
2078}
2079
2080static void do_getxattr(fuse_req_t req, const fuse_ino_t nodeid,
2081 const void *inarg)
2082{
2083 const struct fuse_getxattr_in *arg = inarg;
2084 const void *payload = PARAM(arg);
2085
2086 _do_getxattr(req, nodeid, arg, payload);
2087}
2088
2089static void _do_listxattr(fuse_req_t req, const fuse_ino_t nodeid,
2090 const void *inarg, const void *in_payload)
2091{
2092 (void)in_payload;
2093 const struct fuse_getxattr_in *arg = inarg;
2094
2095 if (req->se->op.listxattr)
2096 req->se->op.listxattr(req, nodeid, arg->size);
2097 else
2098 fuse_reply_err(req, ENOSYS);
2099}
2100static void do_listxattr(fuse_req_t req, const fuse_ino_t nodeid,
2101 const void *inarg)
2102{
2103 _do_listxattr(req, nodeid, inarg, NULL);
2104}
2105
2106static void _do_removexattr(fuse_req_t req, const fuse_ino_t nodeid,
2107 const void *inarg, const void *in_payload)
2108{
2109 (void)inarg;
2110 const char *name = in_payload;
2111
2112 if (req->se->op.removexattr)
2113 req->se->op.removexattr(req, nodeid, name);
2114 else
2115 fuse_reply_err(req, ENOSYS);
2116}
2117static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2118{
2119 _do_removexattr(req, nodeid, NULL, inarg);
2120}
2121
2122static void convert_fuse_file_lock(const struct fuse_file_lock *fl,
2123 struct flock *flock)
2124{
2125 memset(flock, 0, sizeof(struct flock));
2126 flock->l_type = fl->type;
2127 flock->l_whence = SEEK_SET;
2128 flock->l_start = fl->start;
2129 if (fl->end == OFFSET_MAX)
2130 flock->l_len = 0;
2131 else
2132 flock->l_len = fl->end - fl->start + 1;
2133 flock->l_pid = fl->pid;
2134}
2135
2136static void _do_getlk(fuse_req_t req, const fuse_ino_t nodeid,
2137 const void *op_in, const void *in_payload)
2138{
2139 (void)in_payload;
2140 const struct fuse_lk_in *arg = op_in;
2141 struct fuse_file_info fi;
2142 struct flock flock;
2143
2144 memset(&fi, 0, sizeof(fi));
2145 fi.fh = arg->fh;
2146 fi.lock_owner = arg->owner;
2147
2148 convert_fuse_file_lock(&arg->lk, &flock);
2149 if (req->se->op.getlk)
2150 req->se->op.getlk(req, nodeid, &fi, &flock);
2151 else
2152 fuse_reply_err(req, ENOSYS);
2153}
2154static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2155{
2156 _do_getlk(req, nodeid, inarg, NULL);
2157}
2158
2159static void do_setlk_common(fuse_req_t req, const fuse_ino_t nodeid,
2160 const void *op_in, int sleep)
2161{
2162 const struct fuse_lk_in *arg = op_in;
2163 struct fuse_file_info fi;
2164 struct flock flock;
2165
2166 memset(&fi, 0, sizeof(fi));
2167 fi.fh = arg->fh;
2168 fi.lock_owner = arg->owner;
2169
2170 if (arg->lk_flags & FUSE_LK_FLOCK) {
2171 int op = 0;
2172
2173 switch (arg->lk.type) {
2174 case F_RDLCK:
2175 op = LOCK_SH;
2176 break;
2177 case F_WRLCK:
2178 op = LOCK_EX;
2179 break;
2180 case F_UNLCK:
2181 op = LOCK_UN;
2182 break;
2183 }
2184 if (!sleep)
2185 op |= LOCK_NB;
2186
2187 if (req->se->op.flock)
2188 req->se->op.flock(req, nodeid, &fi, op);
2189 else
2190 fuse_reply_err(req, ENOSYS);
2191 } else {
2192 convert_fuse_file_lock(&arg->lk, &flock);
2193 if (req->se->op.setlk)
2194 req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
2195 else
2196 fuse_reply_err(req, ENOSYS);
2197 }
2198}
2199
2200static void _do_setlk(fuse_req_t req, const fuse_ino_t nodeid,
2201 const void *op_in, const void *in_payload)
2202{
2203 (void)in_payload;
2204 do_setlk_common(req, nodeid, op_in, 0);
2205}
2206
2207static void do_setlk(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
2208{
2209 _do_setlk(req, nodeid, inarg, NULL);
2210}
2211
2212static void _do_setlkw(fuse_req_t req, const fuse_ino_t nodeid,
2213 const void *op_in, const void *in_payload)
2214{
2215 (void)in_payload;
2216 do_setlk_common(req, nodeid, op_in, 1);
2217}
2218static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2219{
2220 _do_setlkw(req, nodeid, inarg, NULL);
2221}
2222
2223static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
2224{
2225 struct fuse_req *curr;
2226
2227 for (curr = se->list.next; curr != &se->list; curr = curr->next) {
2228 if (curr->unique == req->u.i.unique) {
2230 void *data;
2231
2232 curr->ref_cnt++;
2233 pthread_mutex_unlock(&se->lock);
2234
2235 /* Ugh, ugly locking */
2236 pthread_mutex_lock(&curr->lock);
2237 pthread_mutex_lock(&se->lock);
2238 curr->interrupted = 1;
2239 func = curr->u.ni.func;
2240 data = curr->u.ni.data;
2241 pthread_mutex_unlock(&se->lock);
2242 if (func)
2243 func(curr, data);
2244 pthread_mutex_unlock(&curr->lock);
2245
2246 pthread_mutex_lock(&se->lock);
2247 curr->ref_cnt--;
2248 if (!curr->ref_cnt) {
2249 destroy_req(curr);
2250 }
2251
2252 return 1;
2253 }
2254 }
2255 for (curr = se->interrupts.next; curr != &se->interrupts;
2256 curr = curr->next) {
2257 if (curr->u.i.unique == req->u.i.unique)
2258 return 1;
2259 }
2260 return 0;
2261}
2262
2263static void _do_interrupt(fuse_req_t req, const fuse_ino_t nodeid,
2264 const void *op_in, const void *in_payload)
2265{
2266 (void)in_payload;
2267 const struct fuse_interrupt_in *arg = op_in;
2268 struct fuse_session *se = req->se;
2269
2270 (void) nodeid;
2271 if (se->debug)
2272 fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
2273 (unsigned long long) arg->unique);
2274
2275 req->u.i.unique = arg->unique;
2276
2277 pthread_mutex_lock(&se->lock);
2278 if (find_interrupted(se, req)) {
2279 fuse_chan_put(req->ch);
2280 req->ch = NULL;
2281 destroy_req(req);
2282 } else
2283 list_add_req(req, &se->interrupts);
2284 pthread_mutex_unlock(&se->lock);
2285}
2286static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2287{
2288 _do_interrupt(req, nodeid, inarg, NULL);
2289}
2290
2291static struct fuse_req *check_interrupt(struct fuse_session *se,
2292 struct fuse_req *req)
2293{
2294 struct fuse_req *curr;
2295
2296 for (curr = se->interrupts.next; curr != &se->interrupts;
2297 curr = curr->next) {
2298 if (curr->u.i.unique == req->unique) {
2299 req->interrupted = 1;
2300 list_del_req(curr);
2301 fuse_chan_put(curr->ch);
2302 curr->ch = NULL;
2303 destroy_req(curr);
2304 return NULL;
2305 }
2306 }
2307 curr = se->interrupts.next;
2308 if (curr != &se->interrupts) {
2309 list_del_req(curr);
2310 list_init_req(curr);
2311 return curr;
2312 } else
2313 return NULL;
2314}
2315
2316static void _do_bmap(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
2317 const void *in_payload)
2318{
2319 (void)in_payload;
2320 const struct fuse_bmap_in *arg = op_in;
2321
2322 if (req->se->op.bmap)
2323 req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
2324 else
2325 fuse_reply_err(req, ENOSYS);
2326}
2327static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2328{
2329 _do_bmap(req, nodeid, inarg, NULL);
2330}
2331
2332static void _do_ioctl(fuse_req_t req, const fuse_ino_t nodeid,
2333 const void *op_in, const void *in_payload)
2334{
2335 struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *)op_in;
2336 unsigned int flags = arg->flags;
2337 const void *in_buf = in_payload;
2338 struct fuse_file_info fi;
2339
2340 if (flags & FUSE_IOCTL_DIR &&
2341 !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
2342 fuse_reply_err(req, ENOTTY);
2343 return;
2344 }
2345
2346 memset(&fi, 0, sizeof(fi));
2347 fi.fh = arg->fh;
2348
2349 if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
2350 !(flags & FUSE_IOCTL_32BIT)) {
2351 req->flags.ioctl_64bit = 1;
2352 }
2353
2354 if (req->se->op.ioctl)
2355 req->se->op.ioctl(req, nodeid, arg->cmd,
2356 (void *)(uintptr_t)arg->arg, &fi, flags,
2357 in_buf, arg->in_size, arg->out_size);
2358 else
2359 fuse_reply_err(req, ENOSYS);
2360}
2361static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2362{
2363 const struct fuse_ioctl_in *arg = inarg;
2364 void *in_buf = arg->in_size ? PARAM(arg) : NULL;
2365
2366 _do_ioctl(req, nodeid, arg, in_buf);
2367}
2368
2369void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
2370{
2371 free(ph);
2372}
2373
2374static void _do_poll(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
2375 const void *in_payload)
2376{
2377 (void)in_payload;
2378 struct fuse_poll_in *arg = (struct fuse_poll_in *)op_in;
2379 struct fuse_file_info fi;
2380
2381 memset(&fi, 0, sizeof(fi));
2382 fi.fh = arg->fh;
2383 fi.poll_events = arg->events;
2384
2385 if (req->se->op.poll) {
2386 struct fuse_pollhandle *ph = NULL;
2387
2388 if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
2389 ph = malloc(sizeof(struct fuse_pollhandle));
2390 if (ph == NULL) {
2391 fuse_reply_err(req, ENOMEM);
2392 return;
2393 }
2394 ph->kh = arg->kh;
2395 ph->se = req->se;
2396 }
2397
2398 req->se->op.poll(req, nodeid, &fi, ph);
2399 } else {
2400 fuse_reply_err(req, ENOSYS);
2401 }
2402}
2403
2404static void do_poll(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
2405{
2406 _do_poll(req, nodeid, inarg, NULL);
2407}
2408
2409static void _do_fallocate(fuse_req_t req, const fuse_ino_t nodeid,
2410 const void *op_in, const void *in_payload)
2411{
2412 (void)in_payload;
2413 const struct fuse_fallocate_in *arg = op_in;
2414 struct fuse_file_info fi;
2415
2416 memset(&fi, 0, sizeof(fi));
2417 fi.fh = arg->fh;
2418
2419 if (req->se->op.fallocate)
2420 req->se->op.fallocate(req, nodeid, arg->mode, arg->offset,
2421 arg->length, &fi);
2422 else
2423 fuse_reply_err(req, ENOSYS);
2424}
2425
2426static void do_fallocate(fuse_req_t req, const fuse_ino_t nodeid,
2427 const void *inarg)
2428{
2429 _do_fallocate(req, nodeid, inarg, NULL);
2430}
2431
2432static void copy_file_range_common(fuse_req_t req, const fuse_ino_t nodeid_in,
2433 const struct fuse_copy_file_range_in *arg)
2434{
2435 struct fuse_file_info fi_in, fi_out;
2436
2437 memset(&fi_in, 0, sizeof(fi_in));
2438 fi_in.fh = arg->fh_in;
2439
2440 memset(&fi_out, 0, sizeof(fi_out));
2441 fi_out.fh = arg->fh_out;
2442
2443 if (req->se->op.copy_file_range)
2444 req->se->op.copy_file_range(req, nodeid_in, arg->off_in, &fi_in,
2445 arg->nodeid_out, arg->off_out,
2446 &fi_out, arg->len, arg->flags);
2447 else
2448 fuse_reply_err(req, ENOSYS);
2449}
2450
2451static void _do_copy_file_range(fuse_req_t req, const fuse_ino_t nodeid_in,
2452 const void *op_in, const void *in_payload)
2453{
2454 const struct fuse_copy_file_range_in *arg = op_in;
2455 struct fuse_copy_file_range_in arg_tmp;
2456
2457 (void) in_payload;
2458 /* fuse_write_out can only handle 32bit copy size */
2459 if (arg->len > 0xfffff000) {
2460 arg_tmp = *arg;
2461 arg_tmp.len = 0xfffff000;
2462 arg = &arg_tmp;
2463 }
2464 copy_file_range_common(req, nodeid_in, arg);
2465}
2466
2467static void do_copy_file_range(fuse_req_t req, const fuse_ino_t nodeid_in,
2468 const void *inarg)
2469{
2470 _do_copy_file_range(req, nodeid_in, inarg, NULL);
2471}
2472
2473static void _do_copy_file_range_64(fuse_req_t req, const fuse_ino_t nodeid_in,
2474 const void *op_in, const void *in_payload)
2475{
2476 (void) in_payload;
2477 req->flags.is_copy_file_range_64 = 1;
2478 /* Limit size on 32bit userspace to avoid conversion overflow */
2479 if (sizeof(size_t) == 4)
2480 _do_copy_file_range(req, nodeid_in, op_in, NULL);
2481 else
2482 copy_file_range_common(req, nodeid_in, op_in);
2483}
2484
2485static void do_copy_file_range_64(fuse_req_t req, const fuse_ino_t nodeid_in,
2486 const void *inarg)
2487{
2488 _do_copy_file_range_64(req, nodeid_in, inarg, NULL);
2489}
2490
2491/*
2492 * Note that the uint64_t offset in struct fuse_lseek_in is derived from
2493 * linux kernel loff_t and is therefore signed.
2494 */
2495static void _do_lseek(fuse_req_t req, const fuse_ino_t nodeid,
2496 const void *op_in, const void *in_payload)
2497{
2498 (void)in_payload;
2499 const struct fuse_lseek_in *arg = op_in;
2500 struct fuse_file_info fi;
2501
2502 memset(&fi, 0, sizeof(fi));
2503 fi.fh = arg->fh;
2504
2505 if (req->se->op.lseek)
2506 req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
2507 else
2508 fuse_reply_err(req, ENOSYS);
2509}
2510
2511static void do_lseek(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
2512{
2513 _do_lseek(req, nodeid, inarg, NULL);
2514}
2515
2516#ifdef HAVE_STATX
2517static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid,
2518 const void *op_in, const void *in_payload)
2519{
2520 (void)in_payload;
2521 const struct fuse_statx_in *arg = op_in;
2522 struct fuse_file_info *fip = NULL;
2523 struct fuse_file_info fi;
2524
2525 if (arg->getattr_flags & FUSE_GETATTR_FH) {
2526 memset(&fi, 0, sizeof(fi));
2527 fi.fh = arg->fh;
2528 fip = &fi;
2529 }
2530
2531 if (req->se->op.statx)
2532 req->se->op.statx(req, nodeid, arg->sx_flags, arg->sx_mask, fip);
2533 else
2534 fuse_reply_err(req, ENOSYS);
2535}
2536#else
2537static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid,
2538 const void *op_in, const void *in_payload)
2539{
2540 (void)in_payload;
2541 (void)req;
2542 (void)nodeid;
2543 (void)op_in;
2544}
2545#endif
2546
2547static void do_statx(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2548{
2549 _do_statx(req, nodeid, inarg, NULL);
2550}
2551
2552static bool want_flags_valid(uint64_t capable, uint64_t want)
2553{
2554 uint64_t unknown_flags = want & (~capable);
2555 if (unknown_flags != 0) {
2556 fuse_log(FUSE_LOG_ERR,
2557 "fuse: unknown connection 'want' flags: 0x%08llx\n",
2558 (unsigned long long)unknown_flags);
2559 return false;
2560 }
2561 return true;
2562}
2563
2568{
2569 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2570
2571 /*
2572 * Convert want to want_ext if necessary.
2573 * For the high level interface this function might be called
2574 * twice, once from the high level interface and once from the
2575 * low level interface. Both, with different want_ext_default and
2576 * want_default values. In order to suppress a failure for the
2577 * second call, we check if the lower 32 bits of want_ext are
2578 * already set to the value of want.
2579 */
2580 if (conn->want != se->conn_want &&
2581 fuse_lower_32_bits(conn->want_ext) != conn->want) {
2582 if (conn->want_ext != se->conn_want_ext) {
2583 fuse_log(FUSE_LOG_ERR,
2584 "%s: Both conn->want_ext and conn->want are set.\n"
2585 "want=%x want_ext=%llx, se->want=%x se->want_ext=%llx\n",
2586 __func__, conn->want,
2587 (unsigned long long)conn->want_ext,
2588 se->conn_want,
2589 (unsigned long long)se->conn_want_ext);
2590 return -EINVAL;
2591 }
2592
2593 /* high bits from want_ext, low bits from want */
2594 conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
2595 conn->want;
2596 }
2597
2598 /* ensure there won't be a second conversion */
2599 conn->want = fuse_lower_32_bits(conn->want_ext);
2600
2601 return 0;
2602}
2603
2604bool fuse_set_feature_flag(struct fuse_conn_info *conn,
2605 uint64_t flag)
2606{
2607 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2608
2609 if (conn->capable_ext & flag) {
2610 conn->want_ext |= flag;
2611 se->conn_want_ext |= flag;
2612 conn->want |= flag;
2613 se->conn_want |= flag;
2614 return true;
2615 }
2616 return false;
2617}
2618
2619void fuse_unset_feature_flag(struct fuse_conn_info *conn,
2620 uint64_t flag)
2621{
2622 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2623
2624 conn->want_ext &= ~flag;
2625 se->conn_want_ext &= ~flag;
2626 conn->want &= ~flag;
2627 se->conn_want &= ~flag;
2628}
2629
2630bool fuse_get_feature_flag(struct fuse_conn_info *conn,
2631 uint64_t flag)
2632{
2633 return conn->capable_ext & flag ? true : false;
2634}
2635
2636/* Prevent bogus data races (bogus since "init" is called before
2637 * multi-threading becomes relevant */
2638static __attribute__((no_sanitize("thread"))) void
2639_do_init(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
2640 const void *in_payload)
2641{
2642 (void)in_payload;
2643 const struct fuse_init_in *arg = op_in;
2644 struct fuse_init_out outarg;
2645 struct fuse_session *se = req->se;
2646 size_t bufsize = se->bufsize;
2647 size_t outargsize = sizeof(outarg);
2648 uint64_t inargflags = 0;
2649 uint64_t outargflags = 0;
2650 bool buf_reallocable = se->buf_reallocable;
2651 (void) nodeid;
2652 bool enable_io_uring = false;
2653
2654 if (se->debug) {
2655 fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
2656 if (arg->major == 7 && arg->minor >= 6) {
2657 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
2658 fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
2659 arg->max_readahead);
2660 }
2661 }
2662 se->conn.proto_major = arg->major;
2663 se->conn.proto_minor = arg->minor;
2664 se->conn.capable_ext = 0;
2665 se->conn.want_ext = 0;
2666
2667 memset(&outarg, 0, sizeof(outarg));
2668 outarg.major = FUSE_KERNEL_VERSION;
2669 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
2670
2671 if (arg->major < 7) {
2672 fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
2673 arg->major, arg->minor);
2674 fuse_reply_err(req, EPROTO);
2675 return;
2676 }
2677
2678 if (arg->major > 7) {
2679 /* Wait for a second INIT request with a 7.X version */
2680 send_reply_ok(req, &outarg, sizeof(outarg));
2681 return;
2682 }
2683
2684 if (arg->minor >= 6) {
2685 if (arg->max_readahead < se->conn.max_readahead)
2686 se->conn.max_readahead = arg->max_readahead;
2687 inargflags = arg->flags;
2688 if (inargflags & FUSE_INIT_EXT)
2689 inargflags = inargflags | (uint64_t) arg->flags2 << 32;
2690 if (inargflags & FUSE_ASYNC_READ)
2691 se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
2692 if (inargflags & FUSE_POSIX_LOCKS)
2693 se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
2694 if (inargflags & FUSE_ATOMIC_O_TRUNC)
2695 se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
2696 if (inargflags & FUSE_EXPORT_SUPPORT)
2697 se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
2698 if (inargflags & FUSE_DONT_MASK)
2699 se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
2700 if (inargflags & FUSE_FLOCK_LOCKS)
2701 se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
2702 if (inargflags & FUSE_AUTO_INVAL_DATA)
2703 se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
2704 if (inargflags & FUSE_DO_READDIRPLUS)
2705 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
2706 if (inargflags & FUSE_READDIRPLUS_AUTO)
2707 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
2708 if (inargflags & FUSE_ASYNC_DIO)
2709 se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
2710 if (inargflags & FUSE_WRITEBACK_CACHE)
2711 se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
2712 if (inargflags & FUSE_NO_OPEN_SUPPORT)
2713 se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
2714 if (inargflags & FUSE_PARALLEL_DIROPS)
2715 se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
2716 if (inargflags & FUSE_POSIX_ACL)
2717 se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
2718 if (inargflags & FUSE_HANDLE_KILLPRIV)
2719 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
2720 if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
2721 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
2722 if (inargflags & FUSE_CACHE_SYMLINKS)
2723 se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
2724 if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
2725 se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
2726 if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
2727 se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
2728 if (inargflags & FUSE_SETXATTR_EXT)
2729 se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
2730 if (!(inargflags & FUSE_MAX_PAGES)) {
2731 size_t max_bufsize =
2732 FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
2733 + FUSE_BUFFER_HEADER_SIZE;
2734 if (bufsize > max_bufsize) {
2735 bufsize = max_bufsize;
2736 }
2737 buf_reallocable = false;
2738 }
2739 if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
2740 se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
2741 if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
2742 se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
2743 if (inargflags & FUSE_PASSTHROUGH)
2744 se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
2745 if (inargflags & FUSE_NO_EXPORT_SUPPORT)
2746 se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
2747 if (inargflags & FUSE_OVER_IO_URING)
2748 se->conn.capable_ext |= FUSE_CAP_OVER_IO_URING;
2749
2750 } else {
2751 se->conn.max_readahead = 0;
2752 }
2753
2754 if (se->conn.proto_minor >= 14) {
2755#ifdef HAVE_SPLICE
2756#ifdef HAVE_VMSPLICE
2757 if ((se->io == NULL) || (se->io->splice_send != NULL)) {
2758 se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
2760 }
2761#endif
2762 if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
2763 se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
2764 }
2765#endif
2766 }
2767 if (se->conn.proto_minor >= 18)
2768 se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
2769
2770 /* Default settings for modern filesystems.
2771 *
2772 * Most of these capabilities were disabled by default in
2773 * libfuse2 for backwards compatibility reasons. In libfuse3,
2774 * we can finally enable them by default (as long as they're
2775 * supported by the kernel).
2776 */
2777#define LL_SET_DEFAULT(cond, cap) \
2778 if ((cond)) \
2779 fuse_set_feature_flag(&se->conn, cap)
2780
2781 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
2782 LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
2783 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
2784 LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
2785 LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
2786 LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
2787 LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
2789 LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
2790 LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
2791 LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
2793 LL_SET_DEFAULT(1, FUSE_CAP_OVER_IO_URING);
2794
2795 /* This could safely become default, but libfuse needs an API extension
2796 * to support it
2797 * LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
2798 */
2799
2800 se->conn.time_gran = 1;
2801
2802 if (se->op.init) {
2803 // Apply the first 32 bits of capable_ext to capable
2804 se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
2805
2806 se->op.init(se->userdata, &se->conn);
2807
2808 /*
2809 * se->conn.want is 32-bit value and deprecated in favour of
2810 * se->conn.want_ext
2811 * Userspace might still use conn.want - we need to convert it
2812 */
2814 }
2815
2816 if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
2817 fuse_reply_err(req, EPROTO);
2818 se->error = -EPROTO;
2820 return;
2821 }
2822
2823 unsigned max_read_mo = get_max_read(se->mo);
2824 if (se->conn.max_read != max_read_mo) {
2825 fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
2826 "requested different maximum read size (%u vs %u)\n",
2827 se->conn.max_read, max_read_mo);
2828 fuse_reply_err(req, EPROTO);
2829 se->error = -EPROTO;
2831 return;
2832 }
2833
2834 if (bufsize < FUSE_MIN_READ_BUFFER) {
2835 fuse_log(FUSE_LOG_ERR,
2836 "fuse: warning: buffer size too small: %zu\n",
2837 bufsize);
2838 bufsize = FUSE_MIN_READ_BUFFER;
2839 }
2840
2841 if (buf_reallocable)
2842 bufsize = UINT_MAX;
2843 se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE);
2844 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
2845
2846 if (arg->flags & FUSE_MAX_PAGES) {
2847 outarg.flags |= FUSE_MAX_PAGES;
2848 outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
2849 }
2850 outargflags = outarg.flags;
2851 /* Always enable big writes, this is superseded
2852 by the max_write option */
2853 outargflags |= FUSE_BIG_WRITES;
2854
2855 if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
2856 outargflags |= FUSE_ASYNC_READ;
2857 if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
2858 outargflags |= FUSE_POSIX_LOCKS;
2859 if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
2860 outargflags |= FUSE_ATOMIC_O_TRUNC;
2861 if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
2862 outargflags |= FUSE_EXPORT_SUPPORT;
2863 if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
2864 outargflags |= FUSE_DONT_MASK;
2865 if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
2866 outargflags |= FUSE_FLOCK_LOCKS;
2867 if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
2868 outargflags |= FUSE_AUTO_INVAL_DATA;
2869 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
2870 outargflags |= FUSE_DO_READDIRPLUS;
2871 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
2872 outargflags |= FUSE_READDIRPLUS_AUTO;
2873 if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
2874 outargflags |= FUSE_ASYNC_DIO;
2875 if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
2876 outargflags |= FUSE_WRITEBACK_CACHE;
2877 if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
2878 outargflags |= FUSE_PARALLEL_DIROPS;
2879 if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
2880 outargflags |= FUSE_POSIX_ACL;
2881 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
2882 outargflags |= FUSE_HANDLE_KILLPRIV;
2883 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
2884 outargflags |= FUSE_HANDLE_KILLPRIV_V2;
2885 if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
2886 outargflags |= FUSE_CACHE_SYMLINKS;
2887 if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
2888 outargflags |= FUSE_EXPLICIT_INVAL_DATA;
2889 if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
2890 outargflags |= FUSE_SETXATTR_EXT;
2891 if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
2892 outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
2893 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
2894 outargflags |= FUSE_PASSTHROUGH;
2895 /*
2896 * outarg.max_stack_depth includes the fuse stack layer,
2897 * so it is one more than max_backing_stack_depth.
2898 */
2899 outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
2900 }
2901 if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
2902 outargflags |= FUSE_NO_EXPORT_SUPPORT;
2903 if (se->uring.enable && se->conn.want_ext & FUSE_CAP_OVER_IO_URING) {
2904 outargflags |= FUSE_OVER_IO_URING;
2905 enable_io_uring = true;
2906 }
2907
2908 if ((inargflags & FUSE_REQUEST_TIMEOUT) && se->conn.request_timeout) {
2909 outargflags |= FUSE_REQUEST_TIMEOUT;
2910 outarg.request_timeout = se->conn.request_timeout;
2911 }
2912
2913 outarg.max_readahead = se->conn.max_readahead;
2914 outarg.max_write = se->conn.max_write;
2915 if (se->conn.proto_minor >= 13) {
2916 if (se->conn.max_background >= (1 << 16))
2917 se->conn.max_background = (1 << 16) - 1;
2918 if (se->conn.congestion_threshold > se->conn.max_background)
2919 se->conn.congestion_threshold = se->conn.max_background;
2920 if (!se->conn.congestion_threshold) {
2921 se->conn.congestion_threshold =
2922 se->conn.max_background * 3 / 4;
2923 }
2924
2925 outarg.max_background = se->conn.max_background;
2926 outarg.congestion_threshold = se->conn.congestion_threshold;
2927 }
2928 if (se->conn.proto_minor >= 23)
2929 outarg.time_gran = se->conn.time_gran;
2930
2931 if (se->debug) {
2932 fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
2933 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
2934 fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
2935 outarg.max_readahead);
2936 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
2937 fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
2938 outarg.max_background);
2939 fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
2940 outarg.congestion_threshold);
2941 fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
2942 outarg.time_gran);
2943 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
2944 fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
2945 outarg.max_stack_depth);
2946 }
2947 if (arg->minor < 5)
2948 outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
2949 else if (arg->minor < 23)
2950 outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
2951
2952 /* XXX: Add an option to make non-available io-uring fatal */
2953 if (enable_io_uring) {
2954 int ring_rc = fuse_uring_start(se);
2955
2956 if (ring_rc != 0) {
2957 fuse_log(FUSE_LOG_INFO,
2958 "fuse: failed to start io-uring: %s\n",
2959 strerror(ring_rc));
2960 outargflags &= ~FUSE_OVER_IO_URING;
2961 enable_io_uring = false;
2962 }
2963 }
2964
2965 if (inargflags & FUSE_INIT_EXT) {
2966 outargflags |= FUSE_INIT_EXT;
2967 outarg.flags2 = outargflags >> 32;
2968 }
2969 outarg.flags = outargflags;
2970
2971 /*
2972 * Has to be set before replying, as new kernel requests might
2973 * immediately arrive and got_init is used for op-code sanity.
2974 * Especially with external handlers, where we have no control
2975 * over the thread scheduling.
2976 */
2977 se->got_init = 1;
2978 send_reply_ok(req, &outarg, outargsize);
2979 if (enable_io_uring)
2980 fuse_uring_wake_ring_threads(se);
2981}
2982
2983static __attribute__((no_sanitize("thread"))) void
2984do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2985{
2986 _do_init(req, nodeid, inarg, NULL);
2987}
2988
2989static void _do_destroy(fuse_req_t req, const fuse_ino_t nodeid,
2990 const void *op_in, const void *in_payload)
2991{
2992 struct fuse_session *se = req->se;
2993 char *mountpoint;
2994
2995 (void) nodeid;
2996 (void)op_in;
2997 (void)in_payload;
2998
2999 mountpoint = atomic_exchange(&se->mountpoint, NULL);
3000 free(mountpoint);
3001
3002 se->got_destroy = 1;
3003 se->got_init = 0;
3004 if (se->op.destroy)
3005 se->op.destroy(se->userdata);
3006
3007 send_reply_ok(req, NULL, 0);
3008}
3009
3010static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
3011{
3012 _do_destroy(req, nodeid, inarg, NULL);
3013}
3014
3015static void list_del_nreq(struct fuse_notify_req *nreq)
3016{
3017 struct fuse_notify_req *prev = nreq->prev;
3018 struct fuse_notify_req *next = nreq->next;
3019 prev->next = next;
3020 next->prev = prev;
3021}
3022
3023static void list_add_nreq(struct fuse_notify_req *nreq,
3024 struct fuse_notify_req *next)
3025{
3026 struct fuse_notify_req *prev = next->prev;
3027 nreq->next = next;
3028 nreq->prev = prev;
3029 prev->next = nreq;
3030 next->prev = nreq;
3031}
3032
3033static void list_init_nreq(struct fuse_notify_req *nreq)
3034{
3035 nreq->next = nreq;
3036 nreq->prev = nreq;
3037}
3038
3039static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
3040 const void *inarg, const struct fuse_buf *buf)
3041{
3042 struct fuse_session *se = req->se;
3043 struct fuse_notify_req *nreq;
3044 struct fuse_notify_req *head;
3045
3046 pthread_mutex_lock(&se->lock);
3047 head = &se->notify_list;
3048 for (nreq = head->next; nreq != head; nreq = nreq->next) {
3049 if (nreq->unique == req->unique) {
3050 list_del_nreq(nreq);
3051 break;
3052 }
3053 }
3054 pthread_mutex_unlock(&se->lock);
3055
3056 if (nreq != head)
3057 nreq->reply(nreq, req, nodeid, inarg, buf);
3058}
3059
3060static int send_notify_iov(struct fuse_session *se, int notify_code,
3061 struct iovec *iov, int count)
3062{
3063 struct fuse_out_header out;
3064 struct fuse_req *req = NULL;
3065
3066 if (!se->got_init)
3067 return -ENOTCONN;
3068
3069 out.unique = 0;
3070 out.error = notify_code;
3071 iov[0].iov_base = &out;
3072 iov[0].iov_len = sizeof(struct fuse_out_header);
3073
3074 return fuse_send_msg(se, NULL, iov, count, req);
3075}
3076
3077int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
3078{
3079 if (ph != NULL) {
3080 struct fuse_notify_poll_wakeup_out outarg;
3081 struct iovec iov[2];
3082
3083 outarg.kh = ph->kh;
3084
3085 iov[1].iov_base = &outarg;
3086 iov[1].iov_len = sizeof(outarg);
3087
3088 return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
3089 } else {
3090 return 0;
3091 }
3092}
3093
3094int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
3095 off_t off, off_t len)
3096{
3097 struct fuse_notify_inval_inode_out outarg;
3098 struct iovec iov[2];
3099
3100 if (!se)
3101 return -EINVAL;
3102
3103 if (se->conn.proto_minor < 12)
3104 return -ENOSYS;
3105
3106 outarg.ino = ino;
3107 outarg.off = off;
3108 outarg.len = len;
3109
3110 iov[1].iov_base = &outarg;
3111 iov[1].iov_len = sizeof(outarg);
3112
3113 return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
3114}
3115
3116int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
3117{
3118 struct iovec iov[1];
3119
3120 if (!se)
3121 return -EINVAL;
3122
3123 if (se->conn.proto_minor < 44)
3124 return -ENOSYS;
3125
3126 return send_notify_iov(se, FUSE_NOTIFY_INC_EPOCH, iov, 1);
3127}
3128
3148static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
3149 const char *name, size_t namelen,
3150 enum fuse_notify_entry_flags flags)
3151{
3152 struct fuse_notify_inval_entry_out outarg;
3153 struct iovec iov[3];
3154
3155 if (!se)
3156 return -EINVAL;
3157
3158 if (se->conn.proto_minor < 12)
3159 return -ENOSYS;
3160
3161 outarg.parent = parent;
3162 outarg.namelen = namelen;
3163 outarg.flags = 0;
3164 if (flags & FUSE_LL_EXPIRE_ONLY)
3165 outarg.flags |= FUSE_EXPIRE_ONLY;
3166
3167 iov[1].iov_base = &outarg;
3168 iov[1].iov_len = sizeof(outarg);
3169 iov[2].iov_base = (void *)name;
3170 iov[2].iov_len = namelen + 1;
3171
3172 return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
3173}
3174
3175int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
3176 const char *name, size_t namelen)
3177{
3178 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
3179}
3180
3181int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
3182 const char *name, size_t namelen)
3183{
3184 if (!se)
3185 return -EINVAL;
3186
3187 if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
3188 return -ENOSYS;
3189
3190 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
3191}
3192
3193
3194int fuse_lowlevel_notify_delete(struct fuse_session *se,
3195 fuse_ino_t parent, fuse_ino_t child,
3196 const char *name, size_t namelen)
3197{
3198 struct fuse_notify_delete_out outarg;
3199 struct iovec iov[3];
3200
3201 if (!se)
3202 return -EINVAL;
3203
3204 if (se->conn.proto_minor < 18)
3205 return -ENOSYS;
3206
3207 outarg.parent = parent;
3208 outarg.child = child;
3209 outarg.namelen = namelen;
3210 outarg.padding = 0;
3211
3212 iov[1].iov_base = &outarg;
3213 iov[1].iov_len = sizeof(outarg);
3214 iov[2].iov_base = (void *)name;
3215 iov[2].iov_len = namelen + 1;
3216
3217 return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
3218}
3219
3220int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
3221 off_t offset, struct fuse_bufvec *bufv,
3222 enum fuse_buf_copy_flags flags)
3223{
3224 struct fuse_out_header out;
3225 struct fuse_notify_store_out outarg;
3226 struct iovec iov[3];
3227 size_t size = fuse_buf_size(bufv);
3228 int res;
3229 struct fuse_req *req = NULL;
3230
3231 if (!se)
3232 return -EINVAL;
3233
3234 if (se->conn.proto_minor < 15)
3235 return -ENOSYS;
3236
3237 out.unique = 0;
3238 out.error = FUSE_NOTIFY_STORE;
3239
3240 outarg.nodeid = ino;
3241 outarg.offset = offset;
3242 outarg.size = size;
3243 outarg.padding = 0;
3244
3245 iov[0].iov_base = &out;
3246 iov[0].iov_len = sizeof(out);
3247 iov[1].iov_base = &outarg;
3248 iov[1].iov_len = sizeof(outarg);
3249
3250 res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags, req);
3251 if (res > 0)
3252 res = -res;
3253
3254 return res;
3255}
3256
3257struct fuse_retrieve_req {
3258 struct fuse_notify_req nreq;
3259 void *cookie;
3260};
3261
3262static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
3263 fuse_req_t req, fuse_ino_t ino,
3264 const void *inarg,
3265 const struct fuse_buf *ibuf)
3266{
3267 struct fuse_session *se = req->se;
3268 struct fuse_retrieve_req *rreq =
3269 container_of(nreq, struct fuse_retrieve_req, nreq);
3270 const struct fuse_notify_retrieve_in *arg = inarg;
3271 struct fuse_bufvec bufv = {
3272 .buf[0] = *ibuf,
3273 .count = 1,
3274 };
3275
3276 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
3277 bufv.buf[0].mem = PARAM(arg);
3278
3279 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
3280 sizeof(struct fuse_notify_retrieve_in);
3281
3282 if (bufv.buf[0].size < arg->size) {
3283 fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
3284 fuse_reply_none(req);
3285 goto out;
3286 }
3287 bufv.buf[0].size = arg->size;
3288
3289 if (se->op.retrieve_reply) {
3290 se->op.retrieve_reply(req, rreq->cookie, ino,
3291 arg->offset, &bufv);
3292 } else {
3293 fuse_reply_none(req);
3294 }
3295out:
3296 free(rreq);
3297 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
3298 fuse_ll_clear_pipe(se);
3299}
3300
3301int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
3302 size_t size, off_t offset, void *cookie)
3303{
3304 struct fuse_notify_retrieve_out outarg;
3305 struct iovec iov[2];
3306 struct fuse_retrieve_req *rreq;
3307 int err;
3308
3309 if (!se)
3310 return -EINVAL;
3311
3312 if (se->conn.proto_minor < 15)
3313 return -ENOSYS;
3314
3315 rreq = malloc(sizeof(*rreq));
3316 if (rreq == NULL)
3317 return -ENOMEM;
3318
3319 pthread_mutex_lock(&se->lock);
3320 rreq->cookie = cookie;
3321 rreq->nreq.unique = se->notify_ctr++;
3322 rreq->nreq.reply = fuse_ll_retrieve_reply;
3323 list_add_nreq(&rreq->nreq, &se->notify_list);
3324 pthread_mutex_unlock(&se->lock);
3325
3326 outarg.notify_unique = rreq->nreq.unique;
3327 outarg.nodeid = ino;
3328 outarg.offset = offset;
3329 outarg.size = size;
3330 outarg.padding = 0;
3331
3332 iov[1].iov_base = &outarg;
3333 iov[1].iov_len = sizeof(outarg);
3334
3335 err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
3336 if (err) {
3337 pthread_mutex_lock(&se->lock);
3338 list_del_nreq(&rreq->nreq);
3339 pthread_mutex_unlock(&se->lock);
3340 free(rreq);
3341 }
3342
3343 return err;
3344}
3345
3347{
3348 return req->se->userdata;
3349}
3350
3351const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
3352{
3353 return &req->ctx;
3354}
3355
3357 void *data)
3358{
3359 pthread_mutex_lock(&req->lock);
3360 pthread_mutex_lock(&req->se->lock);
3361 req->u.ni.func = func;
3362 req->u.ni.data = data;
3363 pthread_mutex_unlock(&req->se->lock);
3364 if (req->interrupted && func)
3365 func(req, data);
3366 pthread_mutex_unlock(&req->lock);
3367}
3368
3370{
3371 int interrupted;
3372
3373 pthread_mutex_lock(&req->se->lock);
3374 interrupted = req->interrupted;
3375 pthread_mutex_unlock(&req->se->lock);
3376
3377 return interrupted;
3378}
3379
3381{
3382 return req->flags.is_uring;
3383}
3384
3385#ifndef HAVE_URING
3386int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
3387 void **mr)
3388{
3389 (void)req;
3390 (void)payload;
3391 (void)payload_sz;
3392 (void)mr;
3393 return -ENOTSUP;
3394}
3395#endif
3396
3397static struct {
3398 void (*func)(fuse_req_t req, const fuse_ino_t node, const void *arg);
3399 const char *name;
3400} fuse_ll_ops[] = {
3401 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
3402 [FUSE_FORGET] = { do_forget, "FORGET" },
3403 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
3404 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
3405 [FUSE_READLINK] = { do_readlink, "READLINK" },
3406 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
3407 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
3408 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
3409 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
3410 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
3411 [FUSE_RENAME] = { do_rename, "RENAME" },
3412 [FUSE_LINK] = { do_link, "LINK" },
3413 [FUSE_OPEN] = { do_open, "OPEN" },
3414 [FUSE_READ] = { do_read, "READ" },
3415 [FUSE_WRITE] = { do_write, "WRITE" },
3416 [FUSE_STATFS] = { do_statfs, "STATFS" },
3417 [FUSE_RELEASE] = { do_release, "RELEASE" },
3418 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
3419 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
3420 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
3421 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
3422 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
3423 [FUSE_FLUSH] = { do_flush, "FLUSH" },
3424 [FUSE_INIT] = { do_init, "INIT" },
3425 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
3426 [FUSE_READDIR] = { do_readdir, "READDIR" },
3427 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
3428 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
3429 [FUSE_GETLK] = { do_getlk, "GETLK" },
3430 [FUSE_SETLK] = { do_setlk, "SETLK" },
3431 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
3432 [FUSE_ACCESS] = { do_access, "ACCESS" },
3433 [FUSE_CREATE] = { do_create, "CREATE" },
3434 [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
3435 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
3436 [FUSE_BMAP] = { do_bmap, "BMAP" },
3437 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
3438 [FUSE_POLL] = { do_poll, "POLL" },
3439 [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
3440 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
3441 [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
3442 [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
3443 [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
3444 [FUSE_RENAME2] = { do_rename2, "RENAME2" },
3445 [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
3446 [FUSE_COPY_FILE_RANGE_64] = { do_copy_file_range_64, "COPY_FILE_RANGE_64" },
3447 [FUSE_LSEEK] = { do_lseek, "LSEEK" },
3448 [FUSE_STATX] = { do_statx, "STATX" },
3449 [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
3450};
3451
3452static struct {
3453 void (*func)(fuse_req_t req, const fuse_ino_t ino, const void *op_in,
3454 const void *op_payload);
3455 const char *name;
3456} fuse_ll_ops2[] __attribute__((unused)) = {
3457 [FUSE_LOOKUP] = { _do_lookup, "LOOKUP" },
3458 [FUSE_FORGET] = { _do_forget, "FORGET" },
3459 [FUSE_GETATTR] = { _do_getattr, "GETATTR" },
3460 [FUSE_SETATTR] = { _do_setattr, "SETATTR" },
3461 [FUSE_READLINK] = { _do_readlink, "READLINK" },
3462 [FUSE_SYMLINK] = { _do_symlink, "SYMLINK" },
3463 [FUSE_MKNOD] = { _do_mknod, "MKNOD" },
3464 [FUSE_MKDIR] = { _do_mkdir, "MKDIR" },
3465 [FUSE_UNLINK] = { _do_unlink, "UNLINK" },
3466 [FUSE_RMDIR] = { _do_rmdir, "RMDIR" },
3467 [FUSE_RENAME] = { _do_rename, "RENAME" },
3468 [FUSE_LINK] = { _do_link, "LINK" },
3469 [FUSE_OPEN] = { _do_open, "OPEN" },
3470 [FUSE_READ] = { _do_read, "READ" },
3471 [FUSE_WRITE] = { _do_write, "WRITE" },
3472 [FUSE_STATFS] = { _do_statfs, "STATFS" },
3473 [FUSE_RELEASE] = { _do_release, "RELEASE" },
3474 [FUSE_FSYNC] = { _do_fsync, "FSYNC" },
3475 [FUSE_SETXATTR] = { _do_setxattr, "SETXATTR" },
3476 [FUSE_GETXATTR] = { _do_getxattr, "GETXATTR" },
3477 [FUSE_LISTXATTR] = { _do_listxattr, "LISTXATTR" },
3478 [FUSE_REMOVEXATTR] = { _do_removexattr, "REMOVEXATTR" },
3479 [FUSE_FLUSH] = { _do_flush, "FLUSH" },
3480 [FUSE_INIT] = { _do_init, "INIT" },
3481 [FUSE_OPENDIR] = { _do_opendir, "OPENDIR" },
3482 [FUSE_READDIR] = { _do_readdir, "READDIR" },
3483 [FUSE_RELEASEDIR] = { _do_releasedir, "RELEASEDIR" },
3484 [FUSE_FSYNCDIR] = { _do_fsyncdir, "FSYNCDIR" },
3485 [FUSE_GETLK] = { _do_getlk, "GETLK" },
3486 [FUSE_SETLK] = { _do_setlk, "SETLK" },
3487 [FUSE_SETLKW] = { _do_setlkw, "SETLKW" },
3488 [FUSE_ACCESS] = { _do_access, "ACCESS" },
3489 [FUSE_CREATE] = { _do_create, "CREATE" },
3490 [FUSE_TMPFILE] = { _do_tmpfile, "TMPFILE" },
3491 [FUSE_INTERRUPT] = { _do_interrupt, "INTERRUPT" },
3492 [FUSE_BMAP] = { _do_bmap, "BMAP" },
3493 [FUSE_IOCTL] = { _do_ioctl, "IOCTL" },
3494 [FUSE_POLL] = { _do_poll, "POLL" },
3495 [FUSE_FALLOCATE] = { _do_fallocate, "FALLOCATE" },
3496 [FUSE_DESTROY] = { _do_destroy, "DESTROY" },
3497 [FUSE_NOTIFY_REPLY] = { (void *)1, "NOTIFY_REPLY" },
3498 [FUSE_BATCH_FORGET] = { _do_batch_forget, "BATCH_FORGET" },
3499 [FUSE_READDIRPLUS] = { _do_readdirplus, "READDIRPLUS" },
3500 [FUSE_RENAME2] = { _do_rename2, "RENAME2" },
3501 [FUSE_COPY_FILE_RANGE] = { _do_copy_file_range, "COPY_FILE_RANGE" },
3502 [FUSE_COPY_FILE_RANGE_64] = { _do_copy_file_range_64, "COPY_FILE_RANGE_64" },
3503 [FUSE_LSEEK] = { _do_lseek, "LSEEK" },
3504 [FUSE_STATX] = { _do_statx, "STATX" },
3505 [CUSE_INIT] = { _cuse_lowlevel_init, "CUSE_INIT" },
3506};
3507
3508/*
3509 * For ABI compatibility we cannot allow higher values than CUSE_INIT.
3510 * Without ABI compatibility we could use the size of the array.
3511 * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
3512 */
3513#define FUSE_MAXOP (CUSE_INIT + 1)
3514
3515
3520static inline int
3521fuse_req_opcode_sanity_ok(struct fuse_session *se, enum fuse_opcode in_op)
3522{
3523 int err = EIO;
3524
3525 if (!se->got_init) {
3526 enum fuse_opcode expected;
3527
3528 expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
3529 if (in_op != expected)
3530 return err;
3531 } else if (in_op == FUSE_INIT || in_op == CUSE_INIT)
3532 return err;
3533
3534 return 0;
3535}
3536
3537static inline void
3538fuse_session_in2req(struct fuse_req *req, struct fuse_in_header *in)
3539{
3540 req->unique = in->unique;
3541 req->ctx.uid = in->uid;
3542 req->ctx.gid = in->gid;
3543 req->ctx.pid = in->pid;
3544}
3545
3549static inline int
3550fuse_req_check_allow_root(struct fuse_session *se, enum fuse_opcode in_op,
3551 uid_t in_uid)
3552{
3553 int err = EACCES;
3554
3555 if (se->deny_others && in_uid != se->owner && in_uid != 0 &&
3556 in_op != FUSE_INIT && in_op != FUSE_READ &&
3557 in_op != FUSE_WRITE && in_op != FUSE_FSYNC &&
3558 in_op != FUSE_RELEASE && in_op != FUSE_READDIR &&
3559 in_op != FUSE_FSYNCDIR && in_op != FUSE_RELEASEDIR &&
3560 in_op != FUSE_NOTIFY_REPLY &&
3561 in_op != FUSE_READDIRPLUS)
3562 return err;
3563
3564 return 0;
3565}
3566
3567static const char *opname(enum fuse_opcode opcode)
3568{
3569 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
3570 return "???";
3571 else
3572 return fuse_ll_ops[opcode].name;
3573}
3574
3575static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
3576 struct fuse_bufvec *src)
3577{
3578 ssize_t res = fuse_buf_copy(dst, src, 0);
3579 if (res < 0) {
3580 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
3581 return res;
3582 }
3583 if ((size_t)res < fuse_buf_size(dst)) {
3584 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
3585 return -1;
3586 }
3587 return 0;
3588}
3589
3590void fuse_session_process_buf(struct fuse_session *se,
3591 const struct fuse_buf *buf)
3592{
3593 fuse_session_process_buf_internal(se, buf, NULL);
3594}
3595
3596/* libfuse internal handler */
3597void fuse_session_process_buf_internal(struct fuse_session *se,
3598 const struct fuse_buf *buf, struct fuse_chan *ch)
3599{
3600 const size_t write_header_size = sizeof(struct fuse_in_header) +
3601 sizeof(struct fuse_write_in);
3602 struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
3603 struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
3604 struct fuse_in_header *in;
3605 const void *inarg;
3606 struct fuse_req *req;
3607 void *mbuf = NULL;
3608 int err;
3609 int res;
3610
3611 if (buf->flags & FUSE_BUF_IS_FD) {
3612 if (buf->size < tmpbuf.buf[0].size)
3613 tmpbuf.buf[0].size = buf->size;
3614
3615 mbuf = malloc(tmpbuf.buf[0].size);
3616 if (mbuf == NULL) {
3617 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
3618 goto clear_pipe;
3619 }
3620 tmpbuf.buf[0].mem = mbuf;
3621
3622 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
3623 if (res < 0)
3624 goto clear_pipe;
3625
3626 in = mbuf;
3627 } else {
3628 in = buf->mem;
3629 }
3630
3631 trace_request_process(in->opcode, in->unique);
3632
3633 if (se->debug) {
3634 fuse_log(FUSE_LOG_DEBUG,
3635 "dev unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
3636 (unsigned long long) in->unique,
3637 opname((enum fuse_opcode) in->opcode), in->opcode,
3638 (unsigned long long) in->nodeid, buf->size, in->pid);
3639 }
3640
3641 req = fuse_ll_alloc_req(se);
3642 if (req == NULL) {
3643 struct fuse_out_header out = {
3644 .unique = in->unique,
3645 .error = -ENOMEM,
3646 };
3647 struct iovec iov = {
3648 .iov_base = &out,
3649 .iov_len = sizeof(struct fuse_out_header),
3650 };
3651
3652 fuse_send_msg(se, ch, &iov, 1, NULL);
3653 goto clear_pipe;
3654 }
3655
3656 fuse_session_in2req(req, in);
3657 req->ch = ch ? fuse_chan_get(ch) : NULL;
3658
3659 err = fuse_req_opcode_sanity_ok(se, in->opcode);
3660 if (err)
3661 goto reply_err;
3662
3663 err = fuse_req_check_allow_root(se, in->opcode, in->uid);
3664 if (err)
3665 goto reply_err;
3666
3667 err = ENOSYS;
3668 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
3669 goto reply_err;
3670 /* Do not process interrupt request */
3671 if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) {
3672 if (se->debug)
3673 fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
3674 goto reply_err;
3675 }
3676 if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) {
3677 struct fuse_req *intr;
3678 pthread_mutex_lock(&se->lock);
3679 intr = check_interrupt(se, req);
3680 list_add_req(req, &se->list);
3681 pthread_mutex_unlock(&se->lock);
3682 if (intr)
3683 fuse_reply_err(intr, EAGAIN);
3684 }
3685
3686 if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
3687 (in->opcode != FUSE_WRITE || !se->op.write_buf) &&
3688 in->opcode != FUSE_NOTIFY_REPLY) {
3689 void *newmbuf;
3690
3691 err = ENOMEM;
3692 newmbuf = realloc(mbuf, buf->size);
3693 if (newmbuf == NULL)
3694 goto reply_err;
3695 mbuf = newmbuf;
3696
3697 tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
3698 tmpbuf.buf[0].mem = (char *)mbuf + write_header_size;
3699
3700 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
3701 err = -res;
3702 if (res < 0)
3703 goto reply_err;
3704
3705 in = mbuf;
3706 }
3707
3708 inarg = (void *) &in[1];
3709 if (in->opcode == FUSE_WRITE && se->op.write_buf)
3710 do_write_buf(req, in->nodeid, inarg, buf);
3711 else if (in->opcode == FUSE_NOTIFY_REPLY)
3712 do_notify_reply(req, in->nodeid, inarg, buf);
3713 else
3714 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
3715
3716out_free:
3717 free(mbuf);
3718 return;
3719
3720reply_err:
3721 fuse_reply_err(req, err);
3722clear_pipe:
3723 if (buf->flags & FUSE_BUF_IS_FD)
3724 fuse_ll_clear_pipe(se);
3725 goto out_free;
3726}
3727
3728void fuse_session_process_uring_cqe(struct fuse_session *se,
3729 struct fuse_req *req,
3730 struct fuse_in_header *in, void *op_in,
3731 void *op_payload, size_t payload_len)
3732{
3733 int err;
3734
3735 fuse_session_in2req(req, in);
3736
3737 err = fuse_req_opcode_sanity_ok(se, in->opcode);
3738 if (err)
3739 goto reply_err;
3740
3741 err = fuse_req_check_allow_root(se, in->opcode, in->uid);
3742 if (err)
3743 goto reply_err;
3744
3745 err = ENOSYS;
3746 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
3747 goto reply_err;
3748
3749 if (se->debug) {
3750 fuse_log(
3751 FUSE_LOG_DEBUG,
3752 "cqe unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
3753 (unsigned long long)in->unique,
3754 opname((enum fuse_opcode)in->opcode), in->opcode,
3755 (unsigned long long)in->nodeid, payload_len, in->pid);
3756 }
3757
3758 if (in->opcode == FUSE_WRITE && se->op.write_buf) {
3759 struct fuse_bufvec bufv = {
3760 .buf[0] = { .size = payload_len,
3761 .flags = 0,
3762 .mem = op_payload },
3763 .count = 1,
3764 };
3765 _do_write_buf(req, in->nodeid, op_in, &bufv);
3766 } else if (in->opcode == FUSE_NOTIFY_REPLY) {
3767 struct fuse_buf buf = { .size = payload_len,
3768 .mem = op_payload };
3769 do_notify_reply(req, in->nodeid, op_in, &buf);
3770 } else {
3771 fuse_ll_ops2[in->opcode].func(req, in->nodeid, op_in,
3772 op_payload);
3773 }
3774
3775 return;
3776
3777reply_err:
3778 fuse_reply_err(req, err);
3779}
3780
3781#define LL_OPTION(n,o,v) \
3782 { n, offsetof(struct fuse_session, o), v }
3783
3784static const struct fuse_opt fuse_ll_opts[] = {
3785 LL_OPTION("debug", debug, 1),
3786 LL_OPTION("-d", debug, 1),
3787 LL_OPTION("--debug", debug, 1),
3788 LL_OPTION("allow_root", deny_others, 1),
3789 LL_OPTION("io_uring", uring.enable, 1),
3790 LL_OPTION("io_uring_q_depth=%u", uring.q_depth, -1),
3792};
3793
3794void fuse_lowlevel_version(void)
3795{
3796 printf("using FUSE kernel interface version %i.%i\n",
3797 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
3798 fuse_mount_version();
3799}
3800
3801void fuse_lowlevel_help(void)
3802{
3803 /* These are not all options, but the ones that are
3804 potentially of interest to an end-user */
3805 printf(
3806" -o allow_other allow access by all users\n"
3807" -o allow_root allow access by root\n"
3808" -o auto_unmount auto unmount on process termination\n"
3809" -o io_uring enable io-uring\n"
3810" -o io_uring_q_depth=<n> io-uring queue depth\n"
3811);
3812}
3813
3814void fuse_session_destroy(struct fuse_session *se)
3815{
3816 struct fuse_ll_pipe *llp;
3817
3818 if (se->got_init && !se->got_destroy) {
3819 if (se->op.destroy)
3820 se->op.destroy(se->userdata);
3821 }
3822 llp = pthread_getspecific(se->pipe_key);
3823 if (llp != NULL)
3824 fuse_ll_pipe_free(llp);
3825 pthread_key_delete(se->pipe_key);
3826 sem_destroy(&se->mt_finish);
3827 pthread_mutex_destroy(&se->mt_lock);
3828 pthread_mutex_destroy(&se->lock);
3829 free(se->cuse_data);
3830 if (se->fd != -1)
3831 close(se->fd);
3832 if (se->io != NULL)
3833 free(se->io);
3834 destroy_mount_opts(se->mo);
3835 free(se);
3836}
3837
3838
3839static void fuse_ll_pipe_destructor(void *data)
3840{
3841 struct fuse_ll_pipe *llp = data;
3842 fuse_ll_pipe_free(llp);
3843}
3844
3845void fuse_buf_free(struct fuse_buf *buf)
3846{
3847 if (buf->mem == NULL)
3848 return;
3849
3850 size_t write_header_sz =
3851 sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
3852
3853 char *ptr = (char *)buf->mem - pagesize + write_header_sz;
3854 free(ptr);
3855 buf->mem = NULL;
3856}
3857
3858/*
3859 * This is used to allocate buffers that hold fuse requests
3860 */
3861static void *buf_alloc(size_t size, bool internal)
3862{
3863 /*
3864 * For libfuse internal caller add in alignment. That cannot be done
3865 * for an external caller, as it is not guaranteed that the external
3866 * caller frees the raw pointer.
3867 */
3868 if (internal) {
3869 size_t write_header_sz = sizeof(struct fuse_in_header) +
3870 sizeof(struct fuse_write_in);
3871 size_t new_size = ROUND_UP(size + write_header_sz, pagesize);
3872
3873 char *buf = aligned_alloc(pagesize, new_size);
3874 if (buf == NULL)
3875 return NULL;
3876
3877 buf += pagesize - write_header_sz;
3878
3879 return buf;
3880 } else {
3881 return malloc(size);
3882 }
3883}
3884
3885/*
3886 *@param internal true if called from libfuse internal code
3887 */
3888static int _fuse_session_receive_buf(struct fuse_session *se,
3889 struct fuse_buf *buf, struct fuse_chan *ch,
3890 bool internal)
3891{
3892 int err;
3893 ssize_t res;
3894 size_t bufsize;
3895#ifdef HAVE_SPLICE
3896 struct fuse_ll_pipe *llp;
3897 struct fuse_buf tmpbuf;
3898
3899pipe_retry:
3900 bufsize = se->bufsize;
3901
3902 if (se->conn.proto_minor < 14 ||
3903 !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
3904 goto fallback;
3905
3906 llp = fuse_ll_get_pipe(se);
3907 if (llp == NULL)
3908 goto fallback;
3909
3910 if (llp->size < bufsize) {
3911 if (llp->can_grow) {
3912 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
3913 if (res == -1) {
3914 llp->can_grow = 0;
3915 res = grow_pipe_to_max(llp->pipe[0]);
3916 if (res > 0)
3917 llp->size = res;
3918 goto fallback;
3919 }
3920 llp->size = res;
3921 }
3922 if (llp->size < bufsize)
3923 goto fallback;
3924 }
3925
3926 if (se->io != NULL && se->io->splice_receive != NULL) {
3927 res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
3928 llp->pipe[1], NULL, bufsize, 0,
3929 se->userdata);
3930 } else {
3931 res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
3932 bufsize, 0);
3933 }
3934 err = errno;
3935 trace_request_receive(err);
3936
3937 if (fuse_session_exited(se))
3938 return 0;
3939
3940 if (res == -1) {
3941 if (err == ENODEV) {
3942 /* Filesystem was unmounted, or connection was aborted
3943 via /sys/fs/fuse/connections */
3945 return 0;
3946 }
3947
3948 /* FUSE_INIT might have increased the required bufsize */
3949 if (err == EINVAL && bufsize < se->bufsize) {
3950 fuse_ll_clear_pipe(se);
3951 goto pipe_retry;
3952 }
3953
3954 if (err != EINTR && err != EAGAIN)
3955 perror("fuse: splice from device");
3956 return -err;
3957 }
3958
3959 if (res < sizeof(struct fuse_in_header)) {
3960 fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
3961 return -EIO;
3962 }
3963
3964 tmpbuf = (struct fuse_buf){
3965 .size = res,
3966 .flags = FUSE_BUF_IS_FD,
3967 .fd = llp->pipe[0],
3968 };
3969
3970 /*
3971 * Don't bother with zero copy for small requests.
3972 * fuse_loop_mt() needs to check for FORGET so this more than
3973 * just an optimization.
3974 */
3975 if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) +
3976 pagesize) {
3977 struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
3978 struct fuse_bufvec dst = { .count = 1 };
3979
3980 if (!buf->mem) {
3981 buf->mem = buf_alloc(bufsize, internal);
3982 if (!buf->mem) {
3983 fuse_log(
3984 FUSE_LOG_ERR,
3985 "fuse: failed to allocate read buffer\n");
3986 return -ENOMEM;
3987 }
3988 buf->mem_size = bufsize;
3989 }
3990 buf->size = bufsize;
3991 buf->flags = 0;
3992 dst.buf[0] = *buf;
3993
3994 res = fuse_buf_copy(&dst, &src, 0);
3995 if (res < 0) {
3996 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
3997 strerror(-res));
3998 fuse_ll_clear_pipe(se);
3999 return res;
4000 }
4001 if (res < tmpbuf.size) {
4002 fuse_log(FUSE_LOG_ERR,
4003 "fuse: copy from pipe: short read\n");
4004 fuse_ll_clear_pipe(se);
4005 return -EIO;
4006 }
4007 assert(res == tmpbuf.size);
4008
4009 } else {
4010 /* Don't overwrite buf->mem, as that would cause a leak */
4011 buf->fd = tmpbuf.fd;
4012 buf->flags = tmpbuf.flags;
4013 }
4014 buf->size = tmpbuf.size;
4015
4016 return res;
4017
4018fallback:
4019#endif
4020 bufsize = internal ? buf->mem_size : se->bufsize;
4021 if (!buf->mem) {
4022 bufsize = se->bufsize; /* might have changed */
4023 buf->mem = buf_alloc(bufsize, internal);
4024 if (!buf->mem) {
4025 fuse_log(FUSE_LOG_ERR,
4026 "fuse: failed to allocate read buffer\n");
4027 return -ENOMEM;
4028 }
4029
4030 if (internal)
4031 buf->mem_size = bufsize;
4032 }
4033
4034restart:
4035 if (se->io != NULL) {
4036 /* se->io->read is never NULL if se->io is not NULL as
4037 specified by fuse_session_custom_io()*/
4038 res = se->io->read(ch ? ch->fd : se->fd, buf->mem, bufsize,
4039 se->userdata);
4040 } else {
4041 res = read(ch ? ch->fd : se->fd, buf->mem, bufsize);
4042 }
4043 err = errno;
4044 trace_request_receive(err);
4045
4046 if (fuse_session_exited(se))
4047 return 0;
4048 if (res == -1) {
4049 if (err == EINVAL && internal && se->bufsize > bufsize) {
4050 /* FUSE_INIT might have increased the required bufsize */
4051 bufsize = se->bufsize;
4052 void *newbuf = buf_alloc(bufsize, internal);
4053 if (!newbuf) {
4054 fuse_log(
4055 FUSE_LOG_ERR,
4056 "fuse: failed to (re)allocate read buffer\n");
4057 return -ENOMEM;
4058 }
4059 fuse_buf_free(buf);
4060 buf->mem = newbuf;
4061 buf->mem_size = bufsize;
4062 goto restart;
4063 }
4064
4065 /* ENOENT means the operation was interrupted, it's safe
4066 to restart */
4067 if (err == ENOENT)
4068 goto restart;
4069
4070 if (err == ENODEV) {
4071 /* Filesystem was unmounted, or connection was aborted
4072 via /sys/fs/fuse/connections */
4074 return 0;
4075 }
4076 /* Errors occurring during normal operation: EINTR (read
4077 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
4078 umounted) */
4079 if (err != EINTR && err != EAGAIN)
4080 perror("fuse: reading device");
4081 return -err;
4082 }
4083 if ((size_t)res < sizeof(struct fuse_in_header)) {
4084 fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
4085 return -EIO;
4086 }
4087
4088 buf->size = res;
4089
4090 return res;
4091}
4092
4093int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
4094{
4095 return _fuse_session_receive_buf(se, buf, NULL, false);
4096}
4097
4098/* libfuse internal handler */
4099int fuse_session_receive_buf_internal(struct fuse_session *se,
4100 struct fuse_buf *buf,
4101 struct fuse_chan *ch)
4102{
4103 /*
4104 * if run internally thread buffers are from libfuse - we can
4105 * reallocate them
4106 */
4107 if (unlikely(!se->got_init) && !se->buf_reallocable)
4108 se->buf_reallocable = true;
4109
4110 return _fuse_session_receive_buf(se, buf, ch, true);
4111}
4112
4113struct fuse_session *
4114fuse_session_new_versioned(struct fuse_args *args,
4115 const struct fuse_lowlevel_ops *op, size_t op_size,
4116 struct libfuse_version *version, void *userdata)
4117{
4118 int err;
4119 struct fuse_session *se;
4120 struct mount_opts *mo;
4121
4122 if (op == NULL || op_size == 0) {
4123 fuse_log(FUSE_LOG_ERR,
4124 "fuse: warning: empty op list passed to fuse_session_new()\n");
4125 return NULL;
4126 }
4127
4128 if (version == NULL) {
4129 fuse_log(FUSE_LOG_ERR, "fuse: warning: version not passed to fuse_session_new()\n");
4130 return NULL;
4131 }
4132
4133 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
4134 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
4135 op_size = sizeof(struct fuse_lowlevel_ops);
4136 }
4137
4138 if (args == NULL || args->argc == 0) {
4139 fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
4140 return NULL;
4141 }
4142
4143 se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
4144 if (se == NULL) {
4145 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
4146 goto out1;
4147 }
4148 se->fd = -1;
4149 se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize();
4150 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
4151 se->conn.max_readahead = UINT_MAX;
4152
4153 /*
4154 * Allow overriding with env, mostly to avoid the need to modify
4155 * all tests. I.e. to test with and without io-uring being enabled.
4156 */
4157 se->uring.enable = getenv("FUSE_URING_ENABLE") ?
4158 atoi(getenv("FUSE_URING_ENABLE")) :
4159 SESSION_DEF_URING_ENABLE;
4160 se->uring.q_depth = getenv("FUSE_URING_QUEUE_DEPTH") ?
4161 atoi(getenv("FUSE_URING_QUEUE_DEPTH")) :
4162 SESSION_DEF_URING_Q_DEPTH;
4163
4164 /* Parse options */
4165 if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
4166 goto out2;
4167 if(se->deny_others) {
4168 /* Allowing access only by root is done by instructing
4169 * kernel to allow access by everyone, and then restricting
4170 * access to root and mountpoint owner in libfuse.
4171 */
4172 // We may be adding the option a second time, but
4173 // that doesn't hurt.
4174 if(fuse_opt_add_arg(args, "-oallow_other") == -1)
4175 goto out2;
4176 }
4177 mo = parse_mount_opts(args);
4178 if (mo == NULL)
4179 goto out3;
4180
4181 if(args->argc == 1 &&
4182 args->argv[0][0] == '-') {
4183 fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
4184 "will be ignored\n");
4185 } else if (args->argc != 1) {
4186 int i;
4187 fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
4188 for(i = 1; i < args->argc-1; i++)
4189 fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
4190 fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
4191 goto out4;
4192 }
4193
4194 if (se->debug)
4195 fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
4196
4197 list_init_req(&se->list);
4198 list_init_req(&se->interrupts);
4199 list_init_nreq(&se->notify_list);
4200 se->notify_ctr = 1;
4201 pthread_mutex_init(&se->lock, NULL);
4202 sem_init(&se->mt_finish, 0, 0);
4203 pthread_mutex_init(&se->mt_lock, NULL);
4204
4205 err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
4206 if (err) {
4207 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
4208 strerror(err));
4209 goto out5;
4210 }
4211
4212 memcpy(&se->op, op, op_size);
4213 se->owner = getuid();
4214 se->userdata = userdata;
4215
4216 se->mo = mo;
4217
4218 /* Fuse server application should pass the version it was compiled
4219 * against and pass it. If a libfuse version accidentally introduces an
4220 * ABI incompatibility, it might be possible to 'fix' that at run time,
4221 * by checking the version numbers.
4222 */
4223 se->version = *version;
4224
4225 return se;
4226
4227out5:
4228 sem_destroy(&se->mt_finish);
4229 pthread_mutex_destroy(&se->mt_lock);
4230 pthread_mutex_destroy(&se->lock);
4231out4:
4232 fuse_opt_free_args(args);
4233out3:
4234 if (mo != NULL)
4235 destroy_mount_opts(mo);
4236out2:
4237 free(se);
4238out1:
4239 return NULL;
4240}
4241
4242struct fuse_session *fuse_session_new_30(struct fuse_args *args,
4243 const struct fuse_lowlevel_ops *op,
4244 size_t op_size, void *userdata);
4245struct fuse_session *fuse_session_new_30(struct fuse_args *args,
4246 const struct fuse_lowlevel_ops *op,
4247 size_t op_size,
4248 void *userdata)
4249{
4250 struct fuse_lowlevel_ops null_ops = { 0 };
4251
4252 /* unknown version */
4253 struct libfuse_version version = { 0 };
4254
4255 /*
4256 * This function is the ABI interface function from fuse_session_new in
4257 * compat.c. External libraries like "fuser" might call fuse_session_new()
4258 * with NULL ops and then pass that session to fuse_session_mount().
4259 * The actual FUSE operations are handled in their own library.
4260 */
4261 if (op == NULL) {
4262 op = &null_ops;
4263 op_size = sizeof(null_ops);
4264 }
4265
4266 return fuse_session_new_versioned(args, op, op_size, &version,
4267 userdata);
4268}
4269
4270FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17")
4271int fuse_session_custom_io_317(struct fuse_session *se,
4272 const struct fuse_custom_io *io, size_t op_size, int fd)
4273{
4274 if (sizeof(struct fuse_custom_io) < op_size) {
4275 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
4276 op_size = sizeof(struct fuse_custom_io);
4277 }
4278
4279 if (fd < 0) {
4280 fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
4281 "fuse_session_custom_io()\n", fd);
4282 return -EBADF;
4283 }
4284 if (io == NULL) {
4285 fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
4286 "fuse_session_custom_io()\n");
4287 return -EINVAL;
4288 } else if (io->read == NULL || io->writev == NULL) {
4289 /* If the user provides their own file descriptor, we can't
4290 guarantee that the default behavior of the io operations made
4291 in libfuse will function properly. Therefore, we enforce the
4292 user to implement these io operations when using custom io. */
4293 fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
4294 "implement both io->read() and io->writev\n");
4295 return -EINVAL;
4296 }
4297
4298 se->io = calloc(1, sizeof(struct fuse_custom_io));
4299 if (se->io == NULL) {
4300 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
4301 "Error: %s\n", strerror(errno));
4302 return -errno;
4303 }
4304
4305 se->fd = fd;
4306 memcpy(se->io, io, op_size);
4307 return 0;
4308}
4309
4310int fuse_session_custom_io_30(struct fuse_session *se,
4311 const struct fuse_custom_io *io, int fd);
4312FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0")
4313int fuse_session_custom_io_30(struct fuse_session *se,
4314 const struct fuse_custom_io *io, int fd)
4315{
4316 return fuse_session_custom_io_317(se, io,
4317 offsetof(struct fuse_custom_io, clone_fd), fd);
4318}
4319
4320int fuse_session_mount(struct fuse_session *se, const char *_mountpoint)
4321{
4322 int fd;
4323 char *mountpoint;
4324
4325 if (_mountpoint == NULL) {
4326 fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
4327 return -1;
4328 }
4329
4330 mountpoint = strdup(_mountpoint);
4331 if (mountpoint == NULL) {
4332 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for mountpoint. Error: %s\n",
4333 strerror(errno));
4334 return -1;
4335 }
4336
4337 /*
4338 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
4339 * would ensue.
4340 */
4341 do {
4342 fd = open("/dev/null", O_RDWR);
4343 if (fd > 2)
4344 close(fd);
4345 } while (fd >= 0 && fd <= 2);
4346
4347 /*
4348 * To allow FUSE daemons to run without privileges, the caller may open
4349 * /dev/fuse before launching the file system and pass on the file
4350 * descriptor by specifying /dev/fd/N as the mount point. Note that the
4351 * parent process takes care of performing the mount in this case.
4352 */
4353 fd = fuse_mnt_parse_fuse_fd(mountpoint);
4354 if (fd != -1) {
4355 if (fcntl(fd, F_GETFD) == -1) {
4356 fuse_log(FUSE_LOG_ERR,
4357 "fuse: Invalid file descriptor /dev/fd/%u\n",
4358 fd);
4359 goto error_out;
4360 }
4361 se->fd = fd;
4362 return 0;
4363 }
4364
4365 /* Open channel */
4366 fd = fuse_kern_mount(mountpoint, se->mo);
4367 if (fd == -1)
4368 goto error_out;
4369 se->fd = fd;
4370
4371 /* Save mountpoint */
4372 se->mountpoint = mountpoint;
4373
4374 return 0;
4375
4376error_out:
4377 free(mountpoint);
4378 return -1;
4379}
4380
4381int fuse_session_fd(struct fuse_session *se)
4382{
4383 return se->fd;
4384}
4385
4386void fuse_session_unmount(struct fuse_session *se)
4387{
4388 if (se->mountpoint != NULL) {
4389 char *mountpoint = atomic_exchange(&se->mountpoint, NULL);
4390
4391 fuse_kern_unmount(mountpoint, se->fd);
4392 se->fd = -1;
4393 free(mountpoint);
4394 }
4395}
4396
4397#ifdef linux
4398int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
4399{
4400 char *buf;
4401 size_t bufsize = 1024;
4402 char path[128];
4403 int ret;
4404 int fd;
4405 unsigned long pid = req->ctx.pid;
4406 char *s;
4407
4408 sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
4409
4410retry:
4411 buf = malloc(bufsize);
4412 if (buf == NULL)
4413 return -ENOMEM;
4414
4415 ret = -EIO;
4416 fd = open(path, O_RDONLY);
4417 if (fd == -1)
4418 goto out_free;
4419
4420 ret = read(fd, buf, bufsize);
4421 close(fd);
4422 if (ret < 0) {
4423 ret = -EIO;
4424 goto out_free;
4425 }
4426
4427 if ((size_t)ret == bufsize) {
4428 free(buf);
4429 bufsize *= 4;
4430 goto retry;
4431 }
4432
4433 buf[ret] = '\0';
4434 ret = -EIO;
4435 s = strstr(buf, "\nGroups:");
4436 if (s == NULL)
4437 goto out_free;
4438
4439 s += 8;
4440 ret = 0;
4441 while (1) {
4442 char *end;
4443 unsigned long val = strtoul(s, &end, 0);
4444 if (end == s)
4445 break;
4446
4447 s = end;
4448 if (ret < size)
4449 list[ret] = val;
4450 ret++;
4451 }
4452
4453out_free:
4454 free(buf);
4455 return ret;
4456}
4457#else /* linux */
4458/*
4459 * This is currently not implemented on other than Linux...
4460 */
4461int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
4462{
4463 (void) req; (void) size; (void) list;
4464 return -ENOSYS;
4465}
4466#endif
4467
4468/* Prevent spurious data race warning - we don't care
4469 * about races for this flag */
4470__attribute__((no_sanitize_thread))
4471void fuse_session_exit(struct fuse_session *se)
4472{
4473 atomic_store_explicit(&se->mt_exited, 1, memory_order_relaxed);
4474 sem_post(&se->mt_finish);
4475}
4476
4477__attribute__((no_sanitize_thread))
4478void fuse_session_reset(struct fuse_session *se)
4479{
4480 se->mt_exited = false;
4481 se->error = 0;
4482}
4483
4484__attribute__((no_sanitize_thread))
4485int fuse_session_exited(struct fuse_session *se)
4486{
4487 bool exited =
4488 atomic_load_explicit(&se->mt_exited, memory_order_relaxed);
4489
4490 return exited ? 1 : 0;
4491}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
@ FUSE_BUF_IS_FD
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
fuse_notify_entry_flags
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_session_reset(struct fuse_session *se)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_passthrough_open(fuse_req_t req, int fd)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_xattr(fuse_req_t req, size_t count)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_CAP_OVER_IO_URING
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
@ FUSE_BUF_IS_FD
bool fuse_req_is_uring(fuse_req_t req)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
int fuse_session_fd(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
void fuse_session_unmount(struct fuse_session *se)
int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
size_t mem_size
void * mem
size_t size
struct fuse_buf buf[1]
uint64_t capable_ext
uint64_t want_ext
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t nonseekable
Definition fuse_common.h:78
int32_t backing_id
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t noflush
Definition fuse_common.h:93
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
fuse-3.18.2/doc/html/lib_2fuse__lowlevel_8c_source.html0000644000175000017500000305654615156613443022043 0ustar berndbernd libfuse: lib/fuse_lowlevel.c Source File
libfuse
fuse_lowlevel.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of (most of) the low-level FUSE API. The session loop
6 functions are implemented in separate files.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_kernel.h"
17#include "fuse_opt.h"
18#include "fuse_misc.h"
19#include "mount_util.h"
20#include "util.h"
21#include "fuse_uring_i.h"
22
23#include <pthread.h>
24#include <stdatomic.h>
25#include <stdint.h>
26#include <inttypes.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <stddef.h>
31#include <stdalign.h>
32#include <string.h>
33#include <unistd.h>
34#include <limits.h>
35#include <errno.h>
36#include <assert.h>
37#include <sys/file.h>
38#include <sys/ioctl.h>
39#include <stdalign.h>
40
41#ifdef USDT_ENABLED
42#include "usdt.h"
43#endif
44
45#ifndef F_LINUX_SPECIFIC_BASE
46#define F_LINUX_SPECIFIC_BASE 1024
47#endif
48#ifndef F_SETPIPE_SZ
49#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
50#endif
51
52#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
53#define OFFSET_MAX 0x7fffffffffffffffLL
54
55struct fuse_pollhandle {
56 uint64_t kh;
57 struct fuse_session *se;
58};
59
60static size_t pagesize;
61
62static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
63{
64 pagesize = getpagesize();
65}
66
67#ifdef USDT_ENABLED
68/* tracepoints */
69static void trace_request_receive(int err)
70{
71 USDT(libfuse, request_receive, err);
72}
73
74static void trace_request_process(unsigned int opcode, unsigned int unique)
75{
76 USDT(libfuse, request_process, opcode, unique);
77}
78
79static void trace_request_reply(uint64_t unique, unsigned int len,
80 int error, int reply_err)
81{
82 USDT(libfuse, request_reply, unique, len, error, reply_err);
83}
84#else
85static void trace_request_receive(int err)
86{
87 (void)err;
88}
89
90static void trace_request_process(unsigned int opcode, unsigned int unique)
91{
92 (void)opcode;
93 (void)unique;
94}
95
96static void trace_request_reply(uint64_t unique, unsigned int len,
97 int error, int reply_err)
98{
99 (void)unique;
100 (void)len;
101 (void)error;
102 (void)reply_err;
103}
104#endif
105
106static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
107{
108 attr->ino = stbuf->st_ino;
109 attr->mode = stbuf->st_mode;
110 attr->nlink = stbuf->st_nlink;
111 attr->uid = stbuf->st_uid;
112 attr->gid = stbuf->st_gid;
113 attr->rdev = stbuf->st_rdev;
114 attr->size = stbuf->st_size;
115 attr->blksize = stbuf->st_blksize;
116 attr->blocks = stbuf->st_blocks;
117 attr->atime = stbuf->st_atime;
118 attr->mtime = stbuf->st_mtime;
119 attr->ctime = stbuf->st_ctime;
120 attr->atimensec = ST_ATIM_NSEC(stbuf);
121 attr->mtimensec = ST_MTIM_NSEC(stbuf);
122 attr->ctimensec = ST_CTIM_NSEC(stbuf);
123}
124
125static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
126{
127 stbuf->st_mode = attr->mode;
128 stbuf->st_uid = attr->uid;
129 stbuf->st_gid = attr->gid;
130 stbuf->st_size = attr->size;
131 stbuf->st_atime = attr->atime;
132 stbuf->st_mtime = attr->mtime;
133 stbuf->st_ctime = attr->ctime;
134 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
135 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
136 ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
137}
138
139static size_t iov_length(const struct iovec *iov, size_t count)
140{
141 size_t seg;
142 size_t ret = 0;
143
144 for (seg = 0; seg < count; seg++)
145 ret += iov[seg].iov_len;
146 return ret;
147}
148
149void list_init_req(struct fuse_req *req)
150{
151 req->next = req;
152 req->prev = req;
153}
154
155static void list_del_req(struct fuse_req *req)
156{
157 struct fuse_req *prev = req->prev;
158 struct fuse_req *next = req->next;
159 prev->next = next;
160 next->prev = prev;
161}
162
163static void list_add_req(struct fuse_req *req, struct fuse_req *next)
164{
165 struct fuse_req *prev = next->prev;
166 req->next = next;
167 req->prev = prev;
168 prev->next = req;
169 next->prev = req;
170}
171
172static void destroy_req(fuse_req_t req)
173{
174 if (req->flags.is_uring) {
175 fuse_log(FUSE_LOG_ERR, "Refusing to destruct uring req\n");
176 return;
177 }
178 assert(req->ch == NULL);
179 pthread_mutex_destroy(&req->lock);
180 free(req);
181}
182
183void fuse_free_req(fuse_req_t req)
184{
185 int ctr;
186 struct fuse_session *se = req->se;
187
188 /* XXX: for now no support for interrupts with io-uring
189 * It actually might work already, though. But then would add
190 * a lock across ring queues.
191 */
192 if (se->conn.no_interrupt || req->flags.is_uring) {
193 ctr = --req->ref_cnt;
194 fuse_chan_put(req->ch);
195 req->ch = NULL;
196 } else {
197 pthread_mutex_lock(&se->lock);
198 req->u.ni.func = NULL;
199 req->u.ni.data = NULL;
200 list_del_req(req);
201 ctr = --req->ref_cnt;
202 fuse_chan_put(req->ch);
203 req->ch = NULL;
204 pthread_mutex_unlock(&se->lock);
205 }
206 if (!ctr)
207 destroy_req(req);
208}
209
210static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
211{
212 struct fuse_req *req;
213
214 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
215 if (req == NULL) {
216 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
217 } else {
218 req->se = se;
219 req->ref_cnt = 1;
220 list_init_req(req);
221 pthread_mutex_init(&req->lock, NULL);
222 }
223
224 return req;
225}
226
227/*
228 * Send data to fuse-kernel using an fd of the fuse device.
229 */
230static int fuse_write_msg_dev(struct fuse_session *se, struct fuse_chan *ch,
231 struct iovec *iov, int count)
232{
233 ssize_t res;
234 int err;
235
236 if (se->io != NULL)
237
238 /* se->io->writev is never NULL if se->io is not NULL as
239 * specified by fuse_session_custom_io()
240 */
241 res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
242 se->userdata);
243 else
244 res = writev(ch ? ch->fd : se->fd, iov, count);
245
246 if (res == -1) {
247 /* ENOENT means the operation was interrupted */
248 err = errno;
249 if (!fuse_session_exited(se) && err != ENOENT)
250 perror("fuse: writing device");
251 return -err;
252 }
253
254 return 0;
255}
256
257static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
258 struct iovec *iov, int count, fuse_req_t req)
259{
260 struct fuse_out_header *out = iov[0].iov_base;
261 int err;
262 bool is_uring = req && req->flags.is_uring ? true : false;
263
264 if (!is_uring)
265 assert(se != NULL);
266 out->len = iov_length(iov, count);
267
268 if (se->debug) {
269 if (out->unique == 0) {
270 fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
271 out->error, out->len);
272 } else if (out->error) {
273 fuse_log(FUSE_LOG_DEBUG,
274 " unique: %llu, error: %i (%s), outsize: %i\n",
275 (unsigned long long) out->unique, out->error,
276 strerror(-out->error), out->len);
277 } else {
278 fuse_log(FUSE_LOG_DEBUG,
279 " unique: %llu, success, outsize: %i\n",
280 (unsigned long long) out->unique, out->len);
281 }
282 }
283
284 if (is_uring)
285 err = fuse_send_msg_uring(req, iov, count);
286 else
287 err = fuse_write_msg_dev(se, ch, iov, count);
288
289 trace_request_reply(out->unique, out->len, out->error, err);
290 return err;
291}
292
293int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
294 int count)
295{
296 struct fuse_out_header out;
297
298#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
299 const char *str = strerrordesc_np(error * -1);
300 if ((str == NULL && error != 0) || error > 0) {
301#else
302 if (error <= -1000 || error > 0) {
303#endif
304 fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
305 error = -ERANGE;
306 }
307
308 out.unique = req->unique;
309 out.error = error;
310
311 iov[0].iov_base = &out;
312 iov[0].iov_len = sizeof(struct fuse_out_header);
313
314 return fuse_send_msg(req->se, req->ch, iov, count, req);
315}
316
317static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
318 int count)
319{
320 int res;
321
322 res = fuse_send_reply_iov_nofree(req, error, iov, count);
323 fuse_free_req(req);
324 return res;
325}
326
327static int send_reply(fuse_req_t req, int error, const void *arg,
328 size_t argsize)
329{
330 if (req->flags.is_uring)
331 return send_reply_uring(req, error, arg, argsize);
332
333 struct iovec iov[2];
334 int count = 1;
335 if (argsize) {
336 iov[1].iov_base = (void *) arg;
337 iov[1].iov_len = argsize;
338 count++;
339 }
340 return send_reply_iov(req, error, iov, count);
341}
342
343int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
344{
345 int res;
346 struct iovec *padded_iov;
347
348 padded_iov = malloc((count + 1) * sizeof(struct iovec));
349 if (padded_iov == NULL)
350 return fuse_reply_err(req, ENOMEM);
351
352 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
353 count++;
354
355 res = send_reply_iov(req, 0, padded_iov, count);
356 free(padded_iov);
357
358 return res;
359}
360
361
362/* `buf` is allowed to be empty so that the proper size may be
363 allocated by the caller */
364size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
365 const char *name, const struct stat *stbuf, off_t off)
366{
367 (void)req;
368 size_t namelen;
369 size_t entlen;
370 size_t entlen_padded;
371 struct fuse_dirent *dirent;
372
373 namelen = strlen(name);
374 entlen = FUSE_NAME_OFFSET + namelen;
375 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
376
377 if ((buf == NULL) || (entlen_padded > bufsize))
378 return entlen_padded;
379
380 dirent = (struct fuse_dirent*) buf;
381 dirent->ino = stbuf->st_ino;
382 dirent->off = off;
383 dirent->namelen = namelen;
384 dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
385 memcpy(dirent->name, name, namelen);
386 memset(dirent->name + namelen, 0, entlen_padded - entlen);
387
388 return entlen_padded;
389}
390
391static void convert_statfs(const struct statvfs *stbuf,
392 struct fuse_kstatfs *kstatfs)
393{
394 kstatfs->bsize = stbuf->f_bsize;
395 kstatfs->frsize = stbuf->f_frsize;
396 kstatfs->blocks = stbuf->f_blocks;
397 kstatfs->bfree = stbuf->f_bfree;
398 kstatfs->bavail = stbuf->f_bavail;
399 kstatfs->files = stbuf->f_files;
400 kstatfs->ffree = stbuf->f_ffree;
401 kstatfs->namelen = stbuf->f_namemax;
402}
403
404static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
405{
406 return send_reply(req, 0, arg, argsize);
407}
408
409int fuse_reply_err(fuse_req_t req, int err)
410{
411 return send_reply(req, -err, NULL, 0);
412}
413
415{
416 fuse_free_req(req);
417}
418
419static unsigned long calc_timeout_sec(double t)
420{
421 if (t > (double) ULONG_MAX)
422 return ULONG_MAX;
423 else if (t < 0.0)
424 return 0;
425 else
426 return (unsigned long) t;
427}
428
429static unsigned int calc_timeout_nsec(double t)
430{
431 double f = t - (double) calc_timeout_sec(t);
432 if (f < 0.0)
433 return 0;
434 else if (f >= 0.999999999)
435 return 999999999;
436 else
437 return (unsigned int) (f * 1.0e9);
438}
439
440static void fill_entry(struct fuse_entry_out *arg,
441 const struct fuse_entry_param *e)
442{
443 arg->nodeid = e->ino;
444 arg->generation = e->generation;
445 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
446 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
447 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
448 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
449 convert_stat(&e->attr, &arg->attr);
450}
451
452/* `buf` is allowed to be empty so that the proper size may be
453 allocated by the caller */
454size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
455 const char *name,
456 const struct fuse_entry_param *e, off_t off)
457{
458 (void)req;
459 size_t namelen;
460 size_t entlen;
461 size_t entlen_padded;
462
463 namelen = strlen(name);
464 entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
465 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
466 if ((buf == NULL) || (entlen_padded > bufsize))
467 return entlen_padded;
468
469 struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
470 memset(&dp->entry_out, 0, sizeof(dp->entry_out));
471 fill_entry(&dp->entry_out, e);
472
473 struct fuse_dirent *dirent = &dp->dirent;
474 dirent->ino = e->attr.st_ino;
475 dirent->off = off;
476 dirent->namelen = namelen;
477 dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
478 memcpy(dirent->name, name, namelen);
479 memset(dirent->name + namelen, 0, entlen_padded - entlen);
480
481 return entlen_padded;
482}
483
484static void fill_open(struct fuse_open_out *arg,
485 const struct fuse_file_info *f)
486{
487 arg->fh = f->fh;
488 if (f->backing_id > 0) {
489 arg->backing_id = f->backing_id;
490 arg->open_flags |= FOPEN_PASSTHROUGH;
491 }
492 if (f->direct_io)
493 arg->open_flags |= FOPEN_DIRECT_IO;
494 if (f->keep_cache)
495 arg->open_flags |= FOPEN_KEEP_CACHE;
496 if (f->cache_readdir)
497 arg->open_flags |= FOPEN_CACHE_DIR;
498 if (f->nonseekable)
499 arg->open_flags |= FOPEN_NONSEEKABLE;
500 if (f->noflush)
501 arg->open_flags |= FOPEN_NOFLUSH;
503 arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
504}
505
506int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
507{
508 struct fuse_entry_out arg;
509 size_t size = req->se->conn.proto_minor < 9 ?
510 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
511
512 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
513 negative entry */
514 if (!e->ino && req->se->conn.proto_minor < 4)
515 return fuse_reply_err(req, ENOENT);
516
517 memset(&arg, 0, sizeof(arg));
518 fill_entry(&arg, e);
519 return send_reply_ok(req, &arg, size);
520}
521
522int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
523 const struct fuse_file_info *f)
524{
525 alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
526 size_t entrysize = req->se->conn.proto_minor < 9 ?
527 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
528 struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
529 struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
530
531 memset(buf, 0, sizeof(buf));
532 fill_entry(earg, e);
533 fill_open(oarg, f);
534 return send_reply_ok(req, buf,
535 entrysize + sizeof(struct fuse_open_out));
536}
537
538int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
539 double attr_timeout)
540{
541 struct fuse_attr_out arg;
542 size_t size = req->se->conn.proto_minor < 9 ?
543 FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
544
545 memset(&arg, 0, sizeof(arg));
546 arg.attr_valid = calc_timeout_sec(attr_timeout);
547 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
548 convert_stat(attr, &arg.attr);
549
550 return send_reply_ok(req, &arg, size);
551}
552
553int fuse_reply_readlink(fuse_req_t req, const char *linkname)
554{
555 return send_reply_ok(req, linkname, strlen(linkname));
556}
557
558int fuse_passthrough_open(fuse_req_t req, int fd)
559{
560 struct fuse_backing_map map = { .fd = fd };
561 int ret;
562
563 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
564 if (ret <= 0) {
565 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno));
566 return 0;
567 }
568
569 return ret;
570}
571
572int fuse_passthrough_close(fuse_req_t req, int backing_id)
573{
574 int ret;
575
576 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id);
577 if (ret < 0)
578 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno));
579
580 return ret;
581}
582
583int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
584{
585 struct fuse_open_out arg;
586
587 memset(&arg, 0, sizeof(arg));
588 fill_open(&arg, f);
589 return send_reply_ok(req, &arg, sizeof(arg));
590}
591
592static int do_fuse_reply_write(fuse_req_t req, size_t count)
593{
594 struct fuse_write_out arg;
595
596 memset(&arg, 0, sizeof(arg));
597 arg.size = count;
598
599 return send_reply_ok(req, &arg, sizeof(arg));
600}
601
602static int do_fuse_reply_copy(fuse_req_t req, size_t count)
603{
604 struct fuse_copy_file_range_out arg;
605
606 memset(&arg, 0, sizeof(arg));
607 arg.bytes_copied = count;
608
609 return send_reply_ok(req, &arg, sizeof(arg));
610}
611
612int fuse_reply_write(fuse_req_t req, size_t count)
613{
614 /*
615 * This function is also used by FUSE_COPY_FILE_RANGE and its 64-bit
616 * variant.
617 */
618 if (req->flags.is_copy_file_range_64)
619 return do_fuse_reply_copy(req, count);
620 else
621 return do_fuse_reply_write(req, count);
622}
623
624int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
625{
626 return send_reply_ok(req, buf, size);
627}
628
629static int fuse_send_data_iov_fallback(struct fuse_session *se,
630 struct fuse_chan *ch,
631 struct iovec *iov, int iov_count,
632 struct fuse_bufvec *buf,
633 size_t len, fuse_req_t req)
634{
635 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
636 void *mbuf;
637 int res;
638
639 /* Optimize common case */
640 if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
641 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
642 /* FIXME: also avoid memory copy if there are multiple buffers
643 but none of them contain an fd */
644
645 iov[iov_count].iov_base = buf->buf[0].mem;
646 iov[iov_count].iov_len = len;
647 iov_count++;
648 return fuse_send_msg(se, ch, iov, iov_count, req);
649 }
650
651 res = posix_memalign(&mbuf, pagesize, len);
652 if (res != 0)
653 return res;
654
655 mem_buf.buf[0].mem = mbuf;
656 res = fuse_buf_copy(&mem_buf, buf, 0);
657 if (res < 0) {
658 free(mbuf);
659 return -res;
660 }
661 len = res;
662
663 iov[iov_count].iov_base = mbuf;
664 iov[iov_count].iov_len = len;
665 iov_count++;
666 res = fuse_send_msg(se, ch, iov, iov_count, req);
667 free(mbuf);
668
669 return res;
670}
671
672struct fuse_ll_pipe {
673 size_t size;
674 int can_grow;
675 int pipe[2];
676};
677
678static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
679{
680 close(llp->pipe[0]);
681 close(llp->pipe[1]);
682 free(llp);
683}
684
685#ifdef HAVE_SPLICE
686#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
687static int fuse_pipe(int fds[2])
688{
689 int rv = pipe(fds);
690
691 if (rv == -1)
692 return rv;
693
694 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
695 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
696 fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
697 fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
698 close(fds[0]);
699 close(fds[1]);
700 rv = -1;
701 }
702 return rv;
703}
704#else
705static int fuse_pipe(int fds[2])
706{
707 return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
708}
709#endif
710
711static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
712{
713 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
714 if (llp == NULL) {
715 int res;
716
717 llp = malloc(sizeof(struct fuse_ll_pipe));
718 if (llp == NULL)
719 return NULL;
720
721 res = fuse_pipe(llp->pipe);
722 if (res == -1) {
723 free(llp);
724 return NULL;
725 }
726
727 /*
728 *the default size is 16 pages on linux
729 */
730 llp->size = pagesize * 16;
731 llp->can_grow = 1;
732
733 pthread_setspecific(se->pipe_key, llp);
734 }
735
736 return llp;
737}
738#endif
739
740static void fuse_ll_clear_pipe(struct fuse_session *se)
741{
742 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
743 if (llp) {
744 pthread_setspecific(se->pipe_key, NULL);
745 fuse_ll_pipe_free(llp);
746 }
747}
748
749#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
750static int read_back(int fd, char *buf, size_t len)
751{
752 int res;
753
754 res = read(fd, buf, len);
755 if (res == -1) {
756 fuse_log(FUSE_LOG_ERR,
757 "fuse: internal error: failed to read back from pipe: %s\n",
758 strerror(errno));
759 return -EIO;
760 }
761 if (res != len) {
762 fuse_log(FUSE_LOG_ERR,
763 "fuse: internal error: short read back from pipe: %i from %zd\n",
764 res, len);
765 return -EIO;
766 }
767 return 0;
768}
769
770static int grow_pipe_to_max(int pipefd)
771{
772 int res;
773 long max;
774 long maxfd;
775 char buf[32];
776
777 maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
778 if (maxfd < 0)
779 return -errno;
780
781 res = read(maxfd, buf, sizeof(buf) - 1);
782 if (res < 0) {
783 int saved_errno;
784
785 saved_errno = errno;
786 close(maxfd);
787 return -saved_errno;
788 }
789 close(maxfd);
790 buf[res] = '\0';
791
792 res = libfuse_strtol(buf, &max);
793 if (res)
794 return res;
795 res = fcntl(pipefd, F_SETPIPE_SZ, max);
796 if (res < 0)
797 return -errno;
798 return max;
799}
800
801static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
802 struct iovec *iov, int iov_count,
803 struct fuse_bufvec *buf, unsigned int flags,
804 fuse_req_t req)
805{
806 int res;
807 size_t len = fuse_buf_size(buf);
808 struct fuse_out_header *out = iov[0].iov_base;
809 struct fuse_ll_pipe *llp;
810 int splice_flags;
811 size_t pipesize;
812 size_t total_buf_size;
813 size_t idx;
814 size_t headerlen;
815 struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
816
817 if (se->broken_splice_nonblock)
818 goto fallback;
819
820 if (flags & FUSE_BUF_NO_SPLICE)
821 goto fallback;
822
823 total_buf_size = 0;
824 for (idx = buf->idx; idx < buf->count; idx++) {
825 total_buf_size += buf->buf[idx].size;
826 if (idx == buf->idx)
827 total_buf_size -= buf->off;
828 }
829 if (total_buf_size < 2 * pagesize)
830 goto fallback;
831
832 if (se->conn.proto_minor < 14 ||
833 !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
834 goto fallback;
835
836 llp = fuse_ll_get_pipe(se);
837 if (llp == NULL)
838 goto fallback;
839
840
841 headerlen = iov_length(iov, iov_count);
842
843 out->len = headerlen + len;
844
845 /*
846 * Heuristic for the required pipe size, does not work if the
847 * source contains less than page size fragments
848 */
849 pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
850
851 if (llp->size < pipesize) {
852 if (llp->can_grow) {
853 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
854 if (res == -1) {
855 res = grow_pipe_to_max(llp->pipe[0]);
856 if (res > 0)
857 llp->size = res;
858 llp->can_grow = 0;
859 goto fallback;
860 }
861 llp->size = res;
862 }
863 if (llp->size < pipesize)
864 goto fallback;
865 }
866
867
868 res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
869 if (res == -1)
870 goto fallback;
871
872 if (res != headerlen) {
873 res = -EIO;
874 fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
875 headerlen);
876 goto clear_pipe;
877 }
878
879 pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
880 pipe_buf.buf[0].fd = llp->pipe[1];
881
882 res = fuse_buf_copy(&pipe_buf, buf,
884 if (res < 0) {
885 if (res == -EAGAIN || res == -EINVAL) {
886 /*
887 * Should only get EAGAIN on kernels with
888 * broken SPLICE_F_NONBLOCK support (<=
889 * 2.6.35) where this error or a short read is
890 * returned even if the pipe itself is not
891 * full
892 *
893 * EINVAL might mean that splice can't handle
894 * this combination of input and output.
895 */
896 if (res == -EAGAIN)
897 se->broken_splice_nonblock = 1;
898
899 pthread_setspecific(se->pipe_key, NULL);
900 fuse_ll_pipe_free(llp);
901 goto fallback;
902 }
903 res = -res;
904 goto clear_pipe;
905 }
906
907 if (res != 0 && res < len) {
908 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
909 void *mbuf;
910 size_t now_len = res;
911 /*
912 * For regular files a short count is either
913 * 1) due to EOF, or
914 * 2) because of broken SPLICE_F_NONBLOCK (see above)
915 *
916 * For other inputs it's possible that we overflowed
917 * the pipe because of small buffer fragments.
918 */
919
920 res = posix_memalign(&mbuf, pagesize, len);
921 if (res != 0)
922 goto clear_pipe;
923
924 mem_buf.buf[0].mem = mbuf;
925 mem_buf.off = now_len;
926 res = fuse_buf_copy(&mem_buf, buf, 0);
927 if (res > 0) {
928 char *tmpbuf;
929 size_t extra_len = res;
930 /*
931 * Trickiest case: got more data. Need to get
932 * back the data from the pipe and then fall
933 * back to regular write.
934 */
935 tmpbuf = malloc(headerlen);
936 if (tmpbuf == NULL) {
937 free(mbuf);
938 res = ENOMEM;
939 goto clear_pipe;
940 }
941 res = read_back(llp->pipe[0], tmpbuf, headerlen);
942 free(tmpbuf);
943 if (res != 0) {
944 free(mbuf);
945 goto clear_pipe;
946 }
947 res = read_back(llp->pipe[0], mbuf, now_len);
948 if (res != 0) {
949 free(mbuf);
950 goto clear_pipe;
951 }
952 len = now_len + extra_len;
953 iov[iov_count].iov_base = mbuf;
954 iov[iov_count].iov_len = len;
955 iov_count++;
956 res = fuse_send_msg(se, ch, iov, iov_count, req);
957 free(mbuf);
958 return res;
959 }
960 free(mbuf);
961 res = now_len;
962 }
963 len = res;
964 out->len = headerlen + len;
965
966 if (se->debug) {
967 fuse_log(FUSE_LOG_DEBUG,
968 " unique: %llu, success, outsize: %i (splice)\n",
969 (unsigned long long) out->unique, out->len);
970 }
971
972 splice_flags = 0;
973 if ((flags & FUSE_BUF_SPLICE_MOVE) &&
974 (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
975 splice_flags |= SPLICE_F_MOVE;
976
977 if (se->io != NULL && se->io->splice_send != NULL) {
978 res = se->io->splice_send(llp->pipe[0], NULL,
979 ch ? ch->fd : se->fd, NULL, out->len,
980 splice_flags, se->userdata);
981 } else {
982 res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
983 out->len, splice_flags);
984 }
985 if (res == -1) {
986 res = -errno;
987 perror("fuse: splice from pipe");
988 goto clear_pipe;
989 }
990 if (res != out->len) {
991 res = -EIO;
992 fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
993 res, out->len);
994 goto clear_pipe;
995 }
996 return 0;
997
998clear_pipe:
999 fuse_ll_clear_pipe(se);
1000 return res;
1001
1002fallback:
1003 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len, req);
1004}
1005#else
1006static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
1007 struct iovec *iov, int iov_count,
1008 struct fuse_bufvec *req_data, unsigned int flags,
1009 fuse_req_t req)
1010{
1011 size_t len = fuse_buf_size(req_data);
1012 (void) flags;
1013
1014 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, req_data, len, req);
1015}
1016#endif
1017
1018int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
1019 enum fuse_buf_copy_flags flags)
1020{
1021 struct iovec iov[2];
1022 struct fuse_out_header out;
1023 int res;
1024
1025 if (req->flags.is_uring)
1026 return fuse_reply_data_uring(req, bufv, flags);
1027
1028 iov[0].iov_base = &out;
1029 iov[0].iov_len = sizeof(struct fuse_out_header);
1030
1031 out.unique = req->unique;
1032 out.error = 0;
1033
1034 res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags, req);
1035 if (res <= 0) {
1036 fuse_free_req(req);
1037 return res;
1038 } else {
1039 return fuse_reply_err(req, res);
1040 }
1041}
1042
1043int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
1044{
1045 struct fuse_statfs_out arg;
1046 size_t size = req->se->conn.proto_minor < 4 ?
1047 FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
1048
1049 memset(&arg, 0, sizeof(arg));
1050 convert_statfs(stbuf, &arg.st);
1051
1052 return send_reply_ok(req, &arg, size);
1053}
1054
1055int fuse_reply_xattr(fuse_req_t req, size_t count)
1056{
1057 struct fuse_getxattr_out arg;
1058
1059 memset(&arg, 0, sizeof(arg));
1060 arg.size = count;
1061
1062 return send_reply_ok(req, &arg, sizeof(arg));
1063}
1064
1065int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
1066{
1067 struct fuse_lk_out arg;
1068
1069 memset(&arg, 0, sizeof(arg));
1070 arg.lk.type = lock->l_type;
1071 if (lock->l_type != F_UNLCK) {
1072 arg.lk.start = lock->l_start;
1073 if (lock->l_len == 0)
1074 arg.lk.end = OFFSET_MAX;
1075 else
1076 arg.lk.end = lock->l_start + lock->l_len - 1;
1077 }
1078 arg.lk.pid = lock->l_pid;
1079 return send_reply_ok(req, &arg, sizeof(arg));
1080}
1081
1082int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
1083{
1084 struct fuse_bmap_out arg;
1085
1086 memset(&arg, 0, sizeof(arg));
1087 arg.block = idx;
1088
1089 return send_reply_ok(req, &arg, sizeof(arg));
1090}
1091
1092static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
1093 size_t count)
1094{
1095 struct fuse_ioctl_iovec *fiov;
1096 size_t i;
1097
1098 fiov = malloc(sizeof(fiov[0]) * count);
1099 if (!fiov)
1100 return NULL;
1101
1102 for (i = 0; i < count; i++) {
1103 fiov[i].base = (uintptr_t) iov[i].iov_base;
1104 fiov[i].len = iov[i].iov_len;
1105 }
1106
1107 return fiov;
1108}
1109
1111 const struct iovec *in_iov, size_t in_count,
1112 const struct iovec *out_iov, size_t out_count)
1113{
1114 struct fuse_ioctl_out arg;
1115 struct fuse_ioctl_iovec *in_fiov = NULL;
1116 struct fuse_ioctl_iovec *out_fiov = NULL;
1117 struct iovec iov[4];
1118 size_t count = 1;
1119 int res;
1120
1121 memset(&arg, 0, sizeof(arg));
1122 arg.flags |= FUSE_IOCTL_RETRY;
1123 arg.in_iovs = in_count;
1124 arg.out_iovs = out_count;
1125 iov[count].iov_base = &arg;
1126 iov[count].iov_len = sizeof(arg);
1127 count++;
1128
1129 if (req->se->conn.proto_minor < 16) {
1130 if (in_count) {
1131 iov[count].iov_base = (void *)in_iov;
1132 iov[count].iov_len = sizeof(in_iov[0]) * in_count;
1133 count++;
1134 }
1135
1136 if (out_count) {
1137 iov[count].iov_base = (void *)out_iov;
1138 iov[count].iov_len = sizeof(out_iov[0]) * out_count;
1139 count++;
1140 }
1141 } else {
1142 /* Can't handle non-compat 64bit ioctls on 32bit */
1143 if (sizeof(void *) == 4 && req->flags.ioctl_64bit) {
1144 res = fuse_reply_err(req, EINVAL);
1145 goto out;
1146 }
1147
1148 if (in_count) {
1149 in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
1150 if (!in_fiov)
1151 goto enomem;
1152
1153 iov[count].iov_base = (void *)in_fiov;
1154 iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
1155 count++;
1156 }
1157 if (out_count) {
1158 out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
1159 if (!out_fiov)
1160 goto enomem;
1161
1162 iov[count].iov_base = (void *)out_fiov;
1163 iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
1164 count++;
1165 }
1166 }
1167
1168 res = send_reply_iov(req, 0, iov, count);
1169out:
1170 free(in_fiov);
1171 free(out_fiov);
1172
1173 return res;
1174
1175enomem:
1176 res = fuse_reply_err(req, ENOMEM);
1177 goto out;
1178}
1179
1180int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
1181{
1182 struct fuse_ioctl_out arg;
1183 struct iovec iov[3];
1184 size_t count = 1;
1185
1186 memset(&arg, 0, sizeof(arg));
1187 arg.result = result;
1188 iov[count].iov_base = &arg;
1189 iov[count].iov_len = sizeof(arg);
1190 count++;
1191
1192 if (size) {
1193 iov[count].iov_base = (char *) buf;
1194 iov[count].iov_len = size;
1195 count++;
1196 }
1197
1198 return send_reply_iov(req, 0, iov, count);
1199}
1200
1201int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
1202 int count)
1203{
1204 struct iovec *padded_iov;
1205 struct fuse_ioctl_out arg;
1206 int res;
1207
1208 padded_iov = malloc((count + 2) * sizeof(struct iovec));
1209 if (padded_iov == NULL)
1210 return fuse_reply_err(req, ENOMEM);
1211
1212 memset(&arg, 0, sizeof(arg));
1213 arg.result = result;
1214 padded_iov[1].iov_base = &arg;
1215 padded_iov[1].iov_len = sizeof(arg);
1216
1217 memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
1218
1219 res = send_reply_iov(req, 0, padded_iov, count + 2);
1220 free(padded_iov);
1221
1222 return res;
1223}
1224
1225int fuse_reply_poll(fuse_req_t req, unsigned revents)
1226{
1227 struct fuse_poll_out arg;
1228
1229 memset(&arg, 0, sizeof(arg));
1230 arg.revents = revents;
1231
1232 return send_reply_ok(req, &arg, sizeof(arg));
1233}
1234
1235int fuse_reply_lseek(fuse_req_t req, off_t off)
1236{
1237 struct fuse_lseek_out arg;
1238
1239 memset(&arg, 0, sizeof(arg));
1240 arg.offset = off;
1241
1242 return send_reply_ok(req, &arg, sizeof(arg));
1243}
1244
1245#ifdef HAVE_STATX
1246int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx,
1247 double attr_timeout)
1248{
1249 struct fuse_statx_out arg;
1250
1251 memset(&arg, 0, sizeof(arg));
1252 arg.flags = flags;
1253 arg.attr_valid = calc_timeout_sec(attr_timeout);
1254 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
1255 memcpy(&arg.stat, statx, sizeof(arg.stat));
1256
1257 return send_reply_ok(req, &arg, sizeof(arg));
1258}
1259#else
1260int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx,
1261 double attr_timeout)
1262{
1263 (void)req;
1264 (void)flags;
1265 (void)statx;
1266 (void)attr_timeout;
1267
1268 return -ENOSYS;
1269}
1270#endif
1271
1272static void _do_lookup(fuse_req_t req, const fuse_ino_t nodeid,
1273 const void *op_in, const void *in_payload)
1274{
1275 (void)op_in;
1276
1277 char *name = (char *)in_payload;
1278
1279 if (req->se->op.lookup)
1280 req->se->op.lookup(req, nodeid, name);
1281 else
1282 fuse_reply_err(req, ENOSYS);
1283}
1284
1285static void do_lookup(fuse_req_t req, const fuse_ino_t nodeid,
1286 const void *inarg)
1287{
1288 _do_lookup(req, nodeid, NULL, inarg);
1289}
1290
1291static void _do_forget(fuse_req_t req, const fuse_ino_t nodeid,
1292 const void *op_in, const void *in_payload)
1293{
1294 (void)in_payload;
1295
1296 struct fuse_forget_in *arg = (struct fuse_forget_in *)op_in;
1297
1298 if (req->se->op.forget)
1299 req->se->op.forget(req, nodeid, arg->nlookup);
1300 else
1301 fuse_reply_none(req);
1302}
1303
1304static void do_forget(fuse_req_t req, const fuse_ino_t nodeid,
1305 const void *inarg)
1306{
1307 _do_forget(req, nodeid, inarg, NULL);
1308}
1309
1310static void _do_batch_forget(fuse_req_t req, const fuse_ino_t nodeid,
1311 const void *op_in, const void *in_payload)
1312{
1313 (void)nodeid;
1314 unsigned int i;
1315
1316 const struct fuse_batch_forget_in *arg = op_in;
1317 const struct fuse_forget_one *forgets = in_payload;
1318
1319 if (req->se->op.forget_multi) {
1320 req->se->op.forget_multi(req, arg->count,
1321 (struct fuse_forget_data *)in_payload);
1322 } else if (req->se->op.forget) {
1323 for (i = 0; i < arg->count; i++) {
1324 const struct fuse_forget_one *forget = &forgets[i];
1325 struct fuse_req *dummy_req;
1326
1327 dummy_req = fuse_ll_alloc_req(req->se);
1328 if (dummy_req == NULL)
1329 break;
1330
1331 dummy_req->unique = req->unique;
1332 dummy_req->ctx = req->ctx;
1333 dummy_req->ch = NULL;
1334
1335 req->se->op.forget(dummy_req, forget->nodeid,
1336 forget->nlookup);
1337 }
1338 fuse_reply_none(req);
1339 } else {
1340 fuse_reply_none(req);
1341 }
1342}
1343
1344static void do_batch_forget(fuse_req_t req, const fuse_ino_t nodeid,
1345 const void *inarg)
1346{
1347 struct fuse_batch_forget_in *arg = (void *)inarg;
1348 struct fuse_forget_one *param = (void *)PARAM(arg);
1349
1350 _do_batch_forget(req, nodeid, inarg, param);
1351}
1352
1353static void _do_getattr(fuse_req_t req, const fuse_ino_t nodeid,
1354 const void *op_in, const void *in_payload)
1355{
1356 struct fuse_getattr_in *arg = (struct fuse_getattr_in *)op_in;
1357 (void)in_payload;
1358
1359 struct fuse_file_info *fip = NULL;
1360 struct fuse_file_info fi;
1361
1362 if (req->se->conn.proto_minor >= 9) {
1363 if (arg->getattr_flags & FUSE_GETATTR_FH) {
1364 memset(&fi, 0, sizeof(fi));
1365 fi.fh = arg->fh;
1366 fip = &fi;
1367 }
1368 }
1369
1370 if (req->se->op.getattr)
1371 req->se->op.getattr(req, nodeid, fip);
1372 else
1373 fuse_reply_err(req, ENOSYS);
1374}
1375
1376static void do_getattr(fuse_req_t req, const fuse_ino_t nodeid,
1377 const void *inarg)
1378{
1379 _do_getattr(req, nodeid, inarg, NULL);
1380}
1381
1382static void _do_setattr(fuse_req_t req, const fuse_ino_t nodeid,
1383 const void *op_in, const void *in_payload)
1384{
1385 (void)in_payload;
1386 const struct fuse_setattr_in *arg = op_in;
1387 uint32_t valid = arg->valid;
1388
1389 if (req->se->op.setattr) {
1390 struct fuse_file_info *fi = NULL;
1391 struct fuse_file_info fi_store;
1392 struct stat stbuf;
1393 memset(&stbuf, 0, sizeof(stbuf));
1394 convert_attr(arg, &stbuf);
1395 if (arg->valid & FATTR_FH) {
1396 valid &= ~FATTR_FH;
1397 memset(&fi_store, 0, sizeof(fi_store));
1398 fi = &fi_store;
1399 fi->fh = arg->fh;
1400 }
1401 valid &= FUSE_SET_ATTR_MODE | FUSE_SET_ATTR_UID |
1402 FUSE_SET_ATTR_GID | FUSE_SET_ATTR_SIZE |
1403 FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME |
1404 FUSE_SET_ATTR_KILL_SUID | FUSE_SET_ATTR_KILL_SGID |
1405 FUSE_SET_ATTR_ATIME_NOW | FUSE_SET_ATTR_MTIME_NOW |
1406 FUSE_SET_ATTR_CTIME;
1407
1408 req->se->op.setattr(req, nodeid, &stbuf, valid, fi);
1409 } else
1410 fuse_reply_err(req, ENOSYS);
1411}
1412
1413static void do_setattr(fuse_req_t req, const fuse_ino_t nodeid,
1414 const void *inarg)
1415{
1416 _do_setattr(req, nodeid, inarg, NULL);
1417}
1418
1419static void _do_access(fuse_req_t req, const fuse_ino_t nodeid,
1420 const void *op_in, const void *in_payload)
1421{
1422 (void)in_payload;
1423 const struct fuse_access_in *arg = op_in;
1424
1425 if (req->se->op.access)
1426 req->se->op.access(req, nodeid, arg->mask);
1427 else
1428 fuse_reply_err(req, ENOSYS);
1429}
1430
1431static void do_access(fuse_req_t req, const fuse_ino_t nodeid,
1432 const void *inarg)
1433{
1434 _do_access(req, nodeid, inarg, NULL);
1435}
1436
1437static void _do_readlink(fuse_req_t req, const fuse_ino_t nodeid,
1438 const void *op_in, const void *in_payload)
1439{
1440 (void)op_in;
1441 (void)in_payload;
1442
1443 if (req->se->op.readlink)
1444 req->se->op.readlink(req, nodeid);
1445 else
1446 fuse_reply_err(req, ENOSYS);
1447}
1448
1449static void do_readlink(fuse_req_t req, const fuse_ino_t nodeid,
1450 const void *inarg)
1451{
1452 _do_readlink(req, nodeid, inarg, NULL);
1453}
1454
1455static void _do_mknod(fuse_req_t req, const fuse_ino_t nodeid,
1456 const void *op_in, const void *in_payload)
1457{
1458 const struct fuse_mknod_in *arg = (struct fuse_mknod_in *)op_in;
1459 const char *name = in_payload;
1460
1461 if (req->se->conn.proto_minor >= 12)
1462 req->ctx.umask = arg->umask;
1463
1464 if (req->se->op.mknod)
1465 req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
1466 else
1467 fuse_reply_err(req, ENOSYS);
1468}
1469
1470static void do_mknod(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1471{
1472 struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg;
1473 char *name = PARAM(arg);
1474
1475 if (req->se->conn.proto_minor < 12)
1476 name = (char *)inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
1477
1478 _do_mknod(req, nodeid, inarg, name);
1479}
1480
1481static void _do_mkdir(fuse_req_t req, const fuse_ino_t nodeid,
1482 const void *op_in, const void *in_payload)
1483{
1484 const char *name = in_payload;
1485 const struct fuse_mkdir_in *arg = op_in;
1486
1487 if (req->se->conn.proto_minor >= 12)
1488 req->ctx.umask = arg->umask;
1489
1490 if (req->se->op.mkdir)
1491 req->se->op.mkdir(req, nodeid, name, arg->mode);
1492 else
1493 fuse_reply_err(req, ENOSYS);
1494}
1495
1496static void do_mkdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1497{
1498 const struct fuse_mkdir_in *arg = inarg;
1499 const char *name = PARAM(arg);
1500
1501 _do_mkdir(req, nodeid, inarg, name);
1502}
1503
1504static void _do_unlink(fuse_req_t req, const fuse_ino_t nodeid,
1505 const void *op_in, const void *in_payload)
1506{
1507 (void)op_in;
1508 const char *name = in_payload;
1509
1510 if (req->se->op.unlink)
1511 req->se->op.unlink(req, nodeid, name);
1512 else
1513 fuse_reply_err(req, ENOSYS);
1514}
1515
1516static void do_unlink(fuse_req_t req, const fuse_ino_t nodeid,
1517 const void *inarg)
1518{
1519 _do_unlink(req, nodeid, NULL, inarg);
1520}
1521
1522static void _do_rmdir(fuse_req_t req, const fuse_ino_t nodeid,
1523 const void *op_in, const void *in_payload)
1524{
1525 (void)op_in;
1526 const char *name = in_payload;
1527
1528 if (req->se->op.rmdir)
1529 req->se->op.rmdir(req, nodeid, name);
1530 else
1531 fuse_reply_err(req, ENOSYS);
1532}
1533
1534static void do_rmdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1535{
1536 _do_rmdir(req, nodeid, NULL, inarg);
1537}
1538
1539static void _do_symlink(fuse_req_t req, const fuse_ino_t nodeid,
1540 const void *op_in, const void *in_payload)
1541{
1542 (void)op_in;
1543 const char *name = (char *)in_payload;
1544 const char *linkname = name + strlen(name) + 1;
1545
1546 if (req->se->op.symlink)
1547 req->se->op.symlink(req, linkname, nodeid, name);
1548 else
1549 fuse_reply_err(req, ENOSYS);
1550}
1551
1552static void do_symlink(fuse_req_t req, const fuse_ino_t nodeid,
1553 const void *inarg)
1554{
1555 _do_symlink(req, nodeid, NULL, inarg);
1556}
1557
1558static void _do_rename(fuse_req_t req, const fuse_ino_t nodeid,
1559 const void *op_in, const void *in_payload)
1560{
1561 const struct fuse_rename_in *arg = (struct fuse_rename_in *)op_in;
1562 const char *oldname = in_payload;
1563 const char *newname = oldname + strlen(oldname) + 1;
1564
1565 if (req->se->op.rename)
1566 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1567 0);
1568 else
1569 fuse_reply_err(req, ENOSYS);
1570}
1571
1572static void do_rename(fuse_req_t req, const fuse_ino_t nodeid,
1573 const void *inarg)
1574{
1575 const struct fuse_rename_in *arg = inarg;
1576 const void *payload = PARAM(arg);
1577
1578 _do_rename(req, nodeid, arg, payload);
1579}
1580
1581static void _do_rename2(fuse_req_t req, const fuse_ino_t nodeid,
1582 const void *op_in, const void *in_payload)
1583{
1584 const struct fuse_rename2_in *arg = op_in;
1585 const char *oldname = in_payload;
1586 const char *newname = oldname + strlen(oldname) + 1;
1587
1588 if (req->se->op.rename)
1589 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1590 arg->flags);
1591 else
1592 fuse_reply_err(req, ENOSYS);
1593}
1594
1595static void do_rename2(fuse_req_t req, const fuse_ino_t nodeid,
1596 const void *inarg)
1597{
1598 const struct fuse_rename2_in *arg = inarg;
1599 const void *payload = PARAM(arg);
1600
1601 _do_rename2(req, nodeid, arg, payload);
1602}
1603
1604static void _do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *op_in,
1605 const void *in_payload)
1606{
1607 (void)in_payload;
1608 const struct fuse_create_in *arg = op_in;
1609
1610 if (req->se->op.tmpfile) {
1611 struct fuse_file_info fi;
1612
1613 memset(&fi, 0, sizeof(fi));
1614 fi.flags = arg->flags;
1615
1616 if (req->se->conn.proto_minor >= 12)
1617 req->ctx.umask = arg->umask;
1618
1619 req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
1620 } else
1621 fuse_reply_err(req, ENOSYS);
1622}
1623
1624static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1625{
1626 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
1627
1628 _do_tmpfile(req, nodeid, arg, NULL);
1629}
1630
1631static void _do_link(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
1632 const void *in_payload)
1633{
1634 struct fuse_link_in *arg = (struct fuse_link_in *)op_in;
1635
1636 if (req->se->op.link)
1637 req->se->op.link(req, arg->oldnodeid, nodeid, in_payload);
1638 else
1639 fuse_reply_err(req, ENOSYS);
1640}
1641
1642static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1643{
1644 const struct fuse_link_in *arg = inarg;
1645 const void *name = PARAM(arg);
1646
1647 _do_link(req, nodeid, inarg, name);
1648}
1649
1650static void _do_create(fuse_req_t req, const fuse_ino_t nodeid,
1651 const void *op_in, const void *in_payload)
1652{
1653 const struct fuse_create_in *arg = op_in;
1654 const char *name = in_payload;
1655
1656 if (req->se->op.create) {
1657 struct fuse_file_info fi;
1658
1659 memset(&fi, 0, sizeof(fi));
1660 fi.flags = arg->flags;
1661
1662 if (req->se->conn.proto_minor >= 12)
1663 req->ctx.umask = arg->umask;
1664
1665 /* XXX: fuse_create_in::open_flags */
1666
1667 req->se->op.create(req, nodeid, name, arg->mode, &fi);
1668 } else {
1669 fuse_reply_err(req, ENOSYS);
1670 }
1671}
1672
1673static void do_create(fuse_req_t req, const fuse_ino_t nodeid,
1674 const void *inarg)
1675{
1676 const struct fuse_create_in *arg = (struct fuse_create_in *)inarg;
1677 void *payload = PARAM(arg);
1678
1679 if (req->se->conn.proto_minor < 12)
1680 payload = (char *)inarg + sizeof(struct fuse_open_in);
1681
1682 _do_create(req, nodeid, arg, payload);
1683}
1684
1685static void _do_open(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
1686 const void *in_payload)
1687{
1688 (void)in_payload;
1689 struct fuse_open_in *arg = (struct fuse_open_in *)op_in;
1690 struct fuse_file_info fi;
1691
1692 memset(&fi, 0, sizeof(fi));
1693 fi.flags = arg->flags;
1694
1695 /* XXX: fuse_open_in::open_flags */
1696
1697 if (req->se->op.open)
1698 req->se->op.open(req, nodeid, &fi);
1699 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
1700 fuse_reply_err(req, ENOSYS);
1701 else
1702 fuse_reply_open(req, &fi);
1703}
1704
1705static void do_open(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1706{
1707 _do_open(req, nodeid, inarg, NULL);
1708}
1709
1710static void _do_read(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
1711 const void *in_payload)
1712{
1713 (void)in_payload;
1714 struct fuse_read_in *arg = (struct fuse_read_in *)op_in;
1715
1716 if (req->se->op.read) {
1717 struct fuse_file_info fi;
1718
1719 memset(&fi, 0, sizeof(fi));
1720 fi.fh = arg->fh;
1721 if (req->se->conn.proto_minor >= 9) {
1722 fi.lock_owner = arg->lock_owner;
1723 fi.flags = arg->flags;
1724 }
1725 req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
1726 } else
1727 fuse_reply_err(req, ENOSYS);
1728}
1729
1730static void do_read(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1731{
1732 _do_read(req, nodeid, inarg, NULL);
1733}
1734
1735static void _do_write(fuse_req_t req, const fuse_ino_t nodeid,
1736 const void *op_in, const void *in_payload)
1737{
1738 struct fuse_write_in *arg = (struct fuse_write_in *)op_in;
1739 const char *buf = in_payload;
1740 struct fuse_file_info fi;
1741
1742 memset(&fi, 0, sizeof(fi));
1743 fi.fh = arg->fh;
1744 fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
1745
1746 if (req->se->conn.proto_minor >= 9) {
1747 fi.lock_owner = arg->lock_owner;
1748 fi.flags = arg->flags;
1749 }
1750
1751 if (req->se->op.write)
1752 req->se->op.write(req, nodeid, buf, arg->size, arg->offset,
1753 &fi);
1754 else
1755 fuse_reply_err(req, ENOSYS);
1756}
1757
1758static void do_write(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1759{
1760 struct fuse_write_in *arg = (struct fuse_write_in *)inarg;
1761 const void *payload;
1762
1763 if (req->se->conn.proto_minor < 9)
1764 payload = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1765 else
1766 payload = PARAM(arg);
1767
1768 _do_write(req, nodeid, arg, payload);
1769}
1770
1771static void _do_write_buf(fuse_req_t req, const fuse_ino_t nodeid,
1772 const void *op_in, struct fuse_bufvec *bufv)
1773{
1774 struct fuse_session *se = req->se;
1775 struct fuse_write_in *arg = (struct fuse_write_in *)op_in;
1776 struct fuse_file_info fi;
1777
1778 memset(&fi, 0, sizeof(fi));
1779 fi.fh = arg->fh;
1780 fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
1781
1782 if (se->conn.proto_minor >= 9) {
1783 fi.lock_owner = arg->lock_owner;
1784 fi.flags = arg->flags;
1785 }
1786
1787 se->op.write_buf(req, nodeid, bufv, arg->offset, &fi);
1788}
1789
1790static void do_write_buf(fuse_req_t req, const fuse_ino_t nodeid,
1791 const void *inarg, const struct fuse_buf *ibuf)
1792{
1793 struct fuse_session *se = req->se;
1794 struct fuse_bufvec bufv = {
1795 .buf[0] = *ibuf,
1796 .count = 1,
1797 };
1798 struct fuse_write_in *arg = (struct fuse_write_in *)inarg;
1799
1800 if (se->conn.proto_minor < 9) {
1801 bufv.buf[0].mem = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1802 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1803 FUSE_COMPAT_WRITE_IN_SIZE;
1804 assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
1805 } else {
1806 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
1807 bufv.buf[0].mem = PARAM(arg);
1808
1809 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1810 sizeof(struct fuse_write_in);
1811 }
1812 if (bufv.buf[0].size < arg->size) {
1813 fuse_log(FUSE_LOG_ERR,
1814 "fuse: %s: buffer size too small\n", __func__);
1815 fuse_reply_err(req, EIO);
1816 goto out;
1817 }
1818 bufv.buf[0].size = arg->size;
1819
1820 _do_write_buf(req, nodeid, inarg, &bufv);
1821
1822out:
1823 /* Need to reset the pipe if ->write_buf() didn't consume all data */
1824 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
1825 fuse_ll_clear_pipe(se);
1826}
1827
1828static void _do_flush(fuse_req_t req, const fuse_ino_t nodeid,
1829 const void *op_in, const void *in_payload)
1830{
1831 (void)in_payload;
1832 struct fuse_flush_in *arg = (struct fuse_flush_in *)op_in;
1833 struct fuse_file_info fi;
1834
1835 memset(&fi, 0, sizeof(fi));
1836 fi.fh = arg->fh;
1837 fi.flush = 1;
1838 if (req->se->conn.proto_minor >= 7)
1839 fi.lock_owner = arg->lock_owner;
1840
1841 if (req->se->op.flush)
1842 req->se->op.flush(req, nodeid, &fi);
1843 else
1844 fuse_reply_err(req, ENOSYS);
1845}
1846
1847static void do_flush(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1848{
1849 _do_flush(req, nodeid, inarg, NULL);
1850}
1851
1852static void _do_release(fuse_req_t req, const fuse_ino_t nodeid,
1853 const void *op_in, const void *in_payload)
1854{
1855 (void)in_payload;
1856 const struct fuse_release_in *arg = op_in;
1857 struct fuse_file_info fi;
1858
1859 memset(&fi, 0, sizeof(fi));
1860 fi.flags = arg->flags;
1861 fi.fh = arg->fh;
1862 if (req->se->conn.proto_minor >= 8) {
1863 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
1864 fi.lock_owner = arg->lock_owner;
1865 }
1866 if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
1867 fi.flock_release = 1;
1868 fi.lock_owner = arg->lock_owner;
1869 }
1870
1871 if (req->se->op.release)
1872 req->se->op.release(req, nodeid, &fi);
1873 else
1874 fuse_reply_err(req, 0);
1875}
1876
1877static void do_release(fuse_req_t req, const fuse_ino_t nodeid,
1878 const void *inarg)
1879{
1880 _do_release(req, nodeid, inarg, NULL);
1881}
1882
1883static void _do_fsync(fuse_req_t req, const fuse_ino_t nodeid,
1884 const void *op_in, const void *in_payload)
1885{
1886 (void)in_payload;
1887 const struct fuse_fsync_in *arg = op_in;
1888 struct fuse_file_info fi;
1889 int datasync = arg->fsync_flags & 1;
1890
1891 memset(&fi, 0, sizeof(fi));
1892 fi.fh = arg->fh;
1893
1894 if (req->se->op.fsync)
1895 req->se->op.fsync(req, nodeid, datasync, &fi);
1896 else
1897 fuse_reply_err(req, ENOSYS);
1898}
1899
1900static void do_fsync(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1901{
1902 _do_fsync(req, nodeid, inarg, NULL);
1903}
1904
1905static void _do_opendir(fuse_req_t req, const fuse_ino_t nodeid,
1906 const void *op_in, const void *in_payload)
1907{
1908 (void)in_payload;
1909 const struct fuse_open_in *arg = op_in;
1910 struct fuse_file_info fi;
1911
1912 memset(&fi, 0, sizeof(fi));
1913 fi.flags = arg->flags;
1914 /* XXX: fuse_open_in::open_flags */
1915
1916 if (req->se->op.opendir)
1917 req->se->op.opendir(req, nodeid, &fi);
1918 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
1919 fuse_reply_err(req, ENOSYS);
1920 else
1921 fuse_reply_open(req, &fi);
1922}
1923
1924static void do_opendir(fuse_req_t req, const fuse_ino_t nodeid,
1925 const void *inarg)
1926{
1927 _do_opendir(req, nodeid, inarg, NULL);
1928}
1929
1930static void _do_readdir(fuse_req_t req, const fuse_ino_t nodeid,
1931 const void *op_in, const void *in_payload)
1932{
1933 (void)in_payload;
1934 struct fuse_read_in *arg = (struct fuse_read_in *)op_in;
1935 struct fuse_file_info fi;
1936
1937 memset(&fi, 0, sizeof(fi));
1938 fi.fh = arg->fh;
1939
1940 if (req->se->op.readdir)
1941 req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
1942 else
1943 fuse_reply_err(req, ENOSYS);
1944}
1945
1946static void do_readdir(fuse_req_t req, const fuse_ino_t nodeid,
1947 const void *inarg)
1948{
1949 _do_readdir(req, nodeid, inarg, NULL);
1950}
1951
1952static void _do_readdirplus(fuse_req_t req, const fuse_ino_t nodeid,
1953 const void *op_in, const void *in_payload)
1954{
1955 (void)in_payload;
1956 struct fuse_read_in *arg = (struct fuse_read_in *)op_in;
1957 struct fuse_file_info fi;
1958
1959 memset(&fi, 0, sizeof(fi));
1960 fi.fh = arg->fh;
1961
1962 if (req->se->op.readdirplus)
1963 req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
1964 else
1965 fuse_reply_err(req, ENOSYS);
1966}
1967
1968static void do_readdirplus(fuse_req_t req, const fuse_ino_t nodeid,
1969 const void *inarg)
1970{
1971 _do_readdirplus(req, nodeid, inarg, NULL);
1972}
1973
1974static void _do_releasedir(fuse_req_t req, const fuse_ino_t nodeid,
1975 const void *op_in, const void *in_payload)
1976{
1977 (void)in_payload;
1978 struct fuse_release_in *arg = (struct fuse_release_in *)op_in;
1979 struct fuse_file_info fi;
1980
1981 memset(&fi, 0, sizeof(fi));
1982 fi.flags = arg->flags;
1983 fi.fh = arg->fh;
1984
1985 if (req->se->op.releasedir)
1986 req->se->op.releasedir(req, nodeid, &fi);
1987 else
1988 fuse_reply_err(req, 0);
1989}
1990
1991static void do_releasedir(fuse_req_t req, const fuse_ino_t nodeid,
1992 const void *inarg)
1993{
1994 _do_releasedir(req, nodeid, inarg, NULL);
1995}
1996
1997static void _do_fsyncdir(fuse_req_t req, const fuse_ino_t nodeid,
1998 const void *op_in, const void *in_payload)
1999{
2000 (void)in_payload;
2001 struct fuse_fsync_in *arg = (struct fuse_fsync_in *)op_in;
2002 struct fuse_file_info fi;
2003 int datasync = arg->fsync_flags & 1;
2004
2005 memset(&fi, 0, sizeof(fi));
2006 fi.fh = arg->fh;
2007
2008 if (req->se->op.fsyncdir)
2009 req->se->op.fsyncdir(req, nodeid, datasync, &fi);
2010 else
2011 fuse_reply_err(req, ENOSYS);
2012}
2013static void do_fsyncdir(fuse_req_t req, const fuse_ino_t nodeid,
2014 const void *inarg)
2015{
2016 _do_fsyncdir(req, nodeid, inarg, NULL);
2017}
2018
2019static void _do_statfs(fuse_req_t req, const fuse_ino_t nodeid,
2020 const void *op_in, const void *in_payload)
2021{
2022 (void) nodeid;
2023 (void)op_in;
2024 (void)in_payload;
2025
2026 if (req->se->op.statfs)
2027 req->se->op.statfs(req, nodeid);
2028 else {
2029 struct statvfs buf = {
2030 .f_namemax = 255,
2031 .f_bsize = 512,
2032 };
2033 fuse_reply_statfs(req, &buf);
2034 }
2035}
2036static void do_statfs(fuse_req_t req, const fuse_ino_t nodeid,
2037 const void *inarg)
2038{
2039 _do_statfs(req, nodeid, inarg, NULL);
2040}
2041
2042static void _do_setxattr(fuse_req_t req, const fuse_ino_t nodeid,
2043 const void *op_in, const void *in_payload)
2044{
2045 struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *)op_in;
2046 const char *name = in_payload;
2047 const char *value = name + strlen(name) + 1;
2048
2049 /* XXX:The API should be extended to support extra_flags/setxattr_flags */
2050
2051 if (req->se->op.setxattr)
2052 req->se->op.setxattr(req, nodeid, name, value, arg->size,
2053 arg->flags);
2054 else
2055 fuse_reply_err(req, ENOSYS);
2056}
2057static void do_setxattr(fuse_req_t req, const fuse_ino_t nodeid,
2058 const void *inarg)
2059{
2060 struct fuse_session *se = req->se;
2061 unsigned int xattr_ext = !!(se->conn.want & FUSE_CAP_SETXATTR_EXT);
2062 const struct fuse_setxattr_in *arg = inarg;
2063 char *payload = xattr_ext ? PARAM(arg) :
2064 (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
2065
2066 _do_setxattr(req, nodeid, arg, payload);
2067}
2068
2069static void _do_getxattr(fuse_req_t req, const fuse_ino_t nodeid,
2070 const void *op_in, const void *in_payload)
2071{
2072 const struct fuse_getxattr_in *arg = op_in;
2073
2074 if (req->se->op.getxattr)
2075 req->se->op.getxattr(req, nodeid, in_payload, arg->size);
2076 else
2077 fuse_reply_err(req, ENOSYS);
2078}
2079
2080static void do_getxattr(fuse_req_t req, const fuse_ino_t nodeid,
2081 const void *inarg)
2082{
2083 const struct fuse_getxattr_in *arg = inarg;
2084 const void *payload = PARAM(arg);
2085
2086 _do_getxattr(req, nodeid, arg, payload);
2087}
2088
2089static void _do_listxattr(fuse_req_t req, const fuse_ino_t nodeid,
2090 const void *inarg, const void *in_payload)
2091{
2092 (void)in_payload;
2093 const struct fuse_getxattr_in *arg = inarg;
2094
2095 if (req->se->op.listxattr)
2096 req->se->op.listxattr(req, nodeid, arg->size);
2097 else
2098 fuse_reply_err(req, ENOSYS);
2099}
2100static void do_listxattr(fuse_req_t req, const fuse_ino_t nodeid,
2101 const void *inarg)
2102{
2103 _do_listxattr(req, nodeid, inarg, NULL);
2104}
2105
2106static void _do_removexattr(fuse_req_t req, const fuse_ino_t nodeid,
2107 const void *inarg, const void *in_payload)
2108{
2109 (void)inarg;
2110 const char *name = in_payload;
2111
2112 if (req->se->op.removexattr)
2113 req->se->op.removexattr(req, nodeid, name);
2114 else
2115 fuse_reply_err(req, ENOSYS);
2116}
2117static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2118{
2119 _do_removexattr(req, nodeid, NULL, inarg);
2120}
2121
2122static void convert_fuse_file_lock(const struct fuse_file_lock *fl,
2123 struct flock *flock)
2124{
2125 memset(flock, 0, sizeof(struct flock));
2126 flock->l_type = fl->type;
2127 flock->l_whence = SEEK_SET;
2128 flock->l_start = fl->start;
2129 if (fl->end == OFFSET_MAX)
2130 flock->l_len = 0;
2131 else
2132 flock->l_len = fl->end - fl->start + 1;
2133 flock->l_pid = fl->pid;
2134}
2135
2136static void _do_getlk(fuse_req_t req, const fuse_ino_t nodeid,
2137 const void *op_in, const void *in_payload)
2138{
2139 (void)in_payload;
2140 const struct fuse_lk_in *arg = op_in;
2141 struct fuse_file_info fi;
2142 struct flock flock;
2143
2144 memset(&fi, 0, sizeof(fi));
2145 fi.fh = arg->fh;
2146 fi.lock_owner = arg->owner;
2147
2148 convert_fuse_file_lock(&arg->lk, &flock);
2149 if (req->se->op.getlk)
2150 req->se->op.getlk(req, nodeid, &fi, &flock);
2151 else
2152 fuse_reply_err(req, ENOSYS);
2153}
2154static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2155{
2156 _do_getlk(req, nodeid, inarg, NULL);
2157}
2158
2159static void do_setlk_common(fuse_req_t req, const fuse_ino_t nodeid,
2160 const void *op_in, int sleep)
2161{
2162 const struct fuse_lk_in *arg = op_in;
2163 struct fuse_file_info fi;
2164 struct flock flock;
2165
2166 memset(&fi, 0, sizeof(fi));
2167 fi.fh = arg->fh;
2168 fi.lock_owner = arg->owner;
2169
2170 if (arg->lk_flags & FUSE_LK_FLOCK) {
2171 int op = 0;
2172
2173 switch (arg->lk.type) {
2174 case F_RDLCK:
2175 op = LOCK_SH;
2176 break;
2177 case F_WRLCK:
2178 op = LOCK_EX;
2179 break;
2180 case F_UNLCK:
2181 op = LOCK_UN;
2182 break;
2183 }
2184 if (!sleep)
2185 op |= LOCK_NB;
2186
2187 if (req->se->op.flock)
2188 req->se->op.flock(req, nodeid, &fi, op);
2189 else
2190 fuse_reply_err(req, ENOSYS);
2191 } else {
2192 convert_fuse_file_lock(&arg->lk, &flock);
2193 if (req->se->op.setlk)
2194 req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
2195 else
2196 fuse_reply_err(req, ENOSYS);
2197 }
2198}
2199
2200static void _do_setlk(fuse_req_t req, const fuse_ino_t nodeid,
2201 const void *op_in, const void *in_payload)
2202{
2203 (void)in_payload;
2204 do_setlk_common(req, nodeid, op_in, 0);
2205}
2206
2207static void do_setlk(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
2208{
2209 _do_setlk(req, nodeid, inarg, NULL);
2210}
2211
2212static void _do_setlkw(fuse_req_t req, const fuse_ino_t nodeid,
2213 const void *op_in, const void *in_payload)
2214{
2215 (void)in_payload;
2216 do_setlk_common(req, nodeid, op_in, 1);
2217}
2218static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2219{
2220 _do_setlkw(req, nodeid, inarg, NULL);
2221}
2222
2223static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
2224{
2225 struct fuse_req *curr;
2226
2227 for (curr = se->list.next; curr != &se->list; curr = curr->next) {
2228 if (curr->unique == req->u.i.unique) {
2230 void *data;
2231
2232 curr->ref_cnt++;
2233 pthread_mutex_unlock(&se->lock);
2234
2235 /* Ugh, ugly locking */
2236 pthread_mutex_lock(&curr->lock);
2237 pthread_mutex_lock(&se->lock);
2238 curr->interrupted = 1;
2239 func = curr->u.ni.func;
2240 data = curr->u.ni.data;
2241 pthread_mutex_unlock(&se->lock);
2242 if (func)
2243 func(curr, data);
2244 pthread_mutex_unlock(&curr->lock);
2245
2246 pthread_mutex_lock(&se->lock);
2247 curr->ref_cnt--;
2248 if (!curr->ref_cnt) {
2249 destroy_req(curr);
2250 }
2251
2252 return 1;
2253 }
2254 }
2255 for (curr = se->interrupts.next; curr != &se->interrupts;
2256 curr = curr->next) {
2257 if (curr->u.i.unique == req->u.i.unique)
2258 return 1;
2259 }
2260 return 0;
2261}
2262
2263static void _do_interrupt(fuse_req_t req, const fuse_ino_t nodeid,
2264 const void *op_in, const void *in_payload)
2265{
2266 (void)in_payload;
2267 const struct fuse_interrupt_in *arg = op_in;
2268 struct fuse_session *se = req->se;
2269
2270 (void) nodeid;
2271 if (se->debug)
2272 fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
2273 (unsigned long long) arg->unique);
2274
2275 req->u.i.unique = arg->unique;
2276
2277 pthread_mutex_lock(&se->lock);
2278 if (find_interrupted(se, req)) {
2279 fuse_chan_put(req->ch);
2280 req->ch = NULL;
2281 destroy_req(req);
2282 } else
2283 list_add_req(req, &se->interrupts);
2284 pthread_mutex_unlock(&se->lock);
2285}
2286static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2287{
2288 _do_interrupt(req, nodeid, inarg, NULL);
2289}
2290
2291static struct fuse_req *check_interrupt(struct fuse_session *se,
2292 struct fuse_req *req)
2293{
2294 struct fuse_req *curr;
2295
2296 for (curr = se->interrupts.next; curr != &se->interrupts;
2297 curr = curr->next) {
2298 if (curr->u.i.unique == req->unique) {
2299 req->interrupted = 1;
2300 list_del_req(curr);
2301 fuse_chan_put(curr->ch);
2302 curr->ch = NULL;
2303 destroy_req(curr);
2304 return NULL;
2305 }
2306 }
2307 curr = se->interrupts.next;
2308 if (curr != &se->interrupts) {
2309 list_del_req(curr);
2310 list_init_req(curr);
2311 return curr;
2312 } else
2313 return NULL;
2314}
2315
2316static void _do_bmap(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
2317 const void *in_payload)
2318{
2319 (void)in_payload;
2320 const struct fuse_bmap_in *arg = op_in;
2321
2322 if (req->se->op.bmap)
2323 req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
2324 else
2325 fuse_reply_err(req, ENOSYS);
2326}
2327static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2328{
2329 _do_bmap(req, nodeid, inarg, NULL);
2330}
2331
2332static void _do_ioctl(fuse_req_t req, const fuse_ino_t nodeid,
2333 const void *op_in, const void *in_payload)
2334{
2335 struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *)op_in;
2336 unsigned int flags = arg->flags;
2337 const void *in_buf = in_payload;
2338 struct fuse_file_info fi;
2339
2340 if (flags & FUSE_IOCTL_DIR &&
2341 !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
2342 fuse_reply_err(req, ENOTTY);
2343 return;
2344 }
2345
2346 memset(&fi, 0, sizeof(fi));
2347 fi.fh = arg->fh;
2348
2349 if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
2350 !(flags & FUSE_IOCTL_32BIT)) {
2351 req->flags.ioctl_64bit = 1;
2352 }
2353
2354 if (req->se->op.ioctl)
2355 req->se->op.ioctl(req, nodeid, arg->cmd,
2356 (void *)(uintptr_t)arg->arg, &fi, flags,
2357 in_buf, arg->in_size, arg->out_size);
2358 else
2359 fuse_reply_err(req, ENOSYS);
2360}
2361static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2362{
2363 const struct fuse_ioctl_in *arg = inarg;
2364 void *in_buf = arg->in_size ? PARAM(arg) : NULL;
2365
2366 _do_ioctl(req, nodeid, arg, in_buf);
2367}
2368
2369void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
2370{
2371 free(ph);
2372}
2373
2374static void _do_poll(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
2375 const void *in_payload)
2376{
2377 (void)in_payload;
2378 struct fuse_poll_in *arg = (struct fuse_poll_in *)op_in;
2379 struct fuse_file_info fi;
2380
2381 memset(&fi, 0, sizeof(fi));
2382 fi.fh = arg->fh;
2383 fi.poll_events = arg->events;
2384
2385 if (req->se->op.poll) {
2386 struct fuse_pollhandle *ph = NULL;
2387
2388 if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
2389 ph = malloc(sizeof(struct fuse_pollhandle));
2390 if (ph == NULL) {
2391 fuse_reply_err(req, ENOMEM);
2392 return;
2393 }
2394 ph->kh = arg->kh;
2395 ph->se = req->se;
2396 }
2397
2398 req->se->op.poll(req, nodeid, &fi, ph);
2399 } else {
2400 fuse_reply_err(req, ENOSYS);
2401 }
2402}
2403
2404static void do_poll(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
2405{
2406 _do_poll(req, nodeid, inarg, NULL);
2407}
2408
2409static void _do_fallocate(fuse_req_t req, const fuse_ino_t nodeid,
2410 const void *op_in, const void *in_payload)
2411{
2412 (void)in_payload;
2413 const struct fuse_fallocate_in *arg = op_in;
2414 struct fuse_file_info fi;
2415
2416 memset(&fi, 0, sizeof(fi));
2417 fi.fh = arg->fh;
2418
2419 if (req->se->op.fallocate)
2420 req->se->op.fallocate(req, nodeid, arg->mode, arg->offset,
2421 arg->length, &fi);
2422 else
2423 fuse_reply_err(req, ENOSYS);
2424}
2425
2426static void do_fallocate(fuse_req_t req, const fuse_ino_t nodeid,
2427 const void *inarg)
2428{
2429 _do_fallocate(req, nodeid, inarg, NULL);
2430}
2431
2432static void copy_file_range_common(fuse_req_t req, const fuse_ino_t nodeid_in,
2433 const struct fuse_copy_file_range_in *arg)
2434{
2435 struct fuse_file_info fi_in, fi_out;
2436
2437 memset(&fi_in, 0, sizeof(fi_in));
2438 fi_in.fh = arg->fh_in;
2439
2440 memset(&fi_out, 0, sizeof(fi_out));
2441 fi_out.fh = arg->fh_out;
2442
2443 if (req->se->op.copy_file_range)
2444 req->se->op.copy_file_range(req, nodeid_in, arg->off_in, &fi_in,
2445 arg->nodeid_out, arg->off_out,
2446 &fi_out, arg->len, arg->flags);
2447 else
2448 fuse_reply_err(req, ENOSYS);
2449}
2450
2451static void _do_copy_file_range(fuse_req_t req, const fuse_ino_t nodeid_in,
2452 const void *op_in, const void *in_payload)
2453{
2454 const struct fuse_copy_file_range_in *arg = op_in;
2455 struct fuse_copy_file_range_in arg_tmp;
2456
2457 (void) in_payload;
2458 /* fuse_write_out can only handle 32bit copy size */
2459 if (arg->len > 0xfffff000) {
2460 arg_tmp = *arg;
2461 arg_tmp.len = 0xfffff000;
2462 arg = &arg_tmp;
2463 }
2464 copy_file_range_common(req, nodeid_in, arg);
2465}
2466
2467static void do_copy_file_range(fuse_req_t req, const fuse_ino_t nodeid_in,
2468 const void *inarg)
2469{
2470 _do_copy_file_range(req, nodeid_in, inarg, NULL);
2471}
2472
2473static void _do_copy_file_range_64(fuse_req_t req, const fuse_ino_t nodeid_in,
2474 const void *op_in, const void *in_payload)
2475{
2476 (void) in_payload;
2477 req->flags.is_copy_file_range_64 = 1;
2478 /* Limit size on 32bit userspace to avoid conversion overflow */
2479 if (sizeof(size_t) == 4)
2480 _do_copy_file_range(req, nodeid_in, op_in, NULL);
2481 else
2482 copy_file_range_common(req, nodeid_in, op_in);
2483}
2484
2485static void do_copy_file_range_64(fuse_req_t req, const fuse_ino_t nodeid_in,
2486 const void *inarg)
2487{
2488 _do_copy_file_range_64(req, nodeid_in, inarg, NULL);
2489}
2490
2491/*
2492 * Note that the uint64_t offset in struct fuse_lseek_in is derived from
2493 * linux kernel loff_t and is therefore signed.
2494 */
2495static void _do_lseek(fuse_req_t req, const fuse_ino_t nodeid,
2496 const void *op_in, const void *in_payload)
2497{
2498 (void)in_payload;
2499 const struct fuse_lseek_in *arg = op_in;
2500 struct fuse_file_info fi;
2501
2502 memset(&fi, 0, sizeof(fi));
2503 fi.fh = arg->fh;
2504
2505 if (req->se->op.lseek)
2506 req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
2507 else
2508 fuse_reply_err(req, ENOSYS);
2509}
2510
2511static void do_lseek(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
2512{
2513 _do_lseek(req, nodeid, inarg, NULL);
2514}
2515
2516#ifdef HAVE_STATX
2517static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid,
2518 const void *op_in, const void *in_payload)
2519{
2520 (void)in_payload;
2521 const struct fuse_statx_in *arg = op_in;
2522 struct fuse_file_info *fip = NULL;
2523 struct fuse_file_info fi;
2524
2525 if (arg->getattr_flags & FUSE_GETATTR_FH) {
2526 memset(&fi, 0, sizeof(fi));
2527 fi.fh = arg->fh;
2528 fip = &fi;
2529 }
2530
2531 if (req->se->op.statx)
2532 req->se->op.statx(req, nodeid, arg->sx_flags, arg->sx_mask, fip);
2533 else
2534 fuse_reply_err(req, ENOSYS);
2535}
2536#else
2537static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid,
2538 const void *op_in, const void *in_payload)
2539{
2540 (void)in_payload;
2541 (void)req;
2542 (void)nodeid;
2543 (void)op_in;
2544}
2545#endif
2546
2547static void do_statx(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2548{
2549 _do_statx(req, nodeid, inarg, NULL);
2550}
2551
2552static bool want_flags_valid(uint64_t capable, uint64_t want)
2553{
2554 uint64_t unknown_flags = want & (~capable);
2555 if (unknown_flags != 0) {
2556 fuse_log(FUSE_LOG_ERR,
2557 "fuse: unknown connection 'want' flags: 0x%08llx\n",
2558 (unsigned long long)unknown_flags);
2559 return false;
2560 }
2561 return true;
2562}
2563
2568{
2569 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2570
2571 /*
2572 * Convert want to want_ext if necessary.
2573 * For the high level interface this function might be called
2574 * twice, once from the high level interface and once from the
2575 * low level interface. Both, with different want_ext_default and
2576 * want_default values. In order to suppress a failure for the
2577 * second call, we check if the lower 32 bits of want_ext are
2578 * already set to the value of want.
2579 */
2580 if (conn->want != se->conn_want &&
2581 fuse_lower_32_bits(conn->want_ext) != conn->want) {
2582 if (conn->want_ext != se->conn_want_ext) {
2583 fuse_log(FUSE_LOG_ERR,
2584 "%s: Both conn->want_ext and conn->want are set.\n"
2585 "want=%x want_ext=%llx, se->want=%x se->want_ext=%llx\n",
2586 __func__, conn->want,
2587 (unsigned long long)conn->want_ext,
2588 se->conn_want,
2589 (unsigned long long)se->conn_want_ext);
2590 return -EINVAL;
2591 }
2592
2593 /* high bits from want_ext, low bits from want */
2594 conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
2595 conn->want;
2596 }
2597
2598 /* ensure there won't be a second conversion */
2599 conn->want = fuse_lower_32_bits(conn->want_ext);
2600
2601 return 0;
2602}
2603
2604bool fuse_set_feature_flag(struct fuse_conn_info *conn,
2605 uint64_t flag)
2606{
2607 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2608
2609 if (conn->capable_ext & flag) {
2610 conn->want_ext |= flag;
2611 se->conn_want_ext |= flag;
2612 conn->want |= flag;
2613 se->conn_want |= flag;
2614 return true;
2615 }
2616 return false;
2617}
2618
2619void fuse_unset_feature_flag(struct fuse_conn_info *conn,
2620 uint64_t flag)
2621{
2622 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2623
2624 conn->want_ext &= ~flag;
2625 se->conn_want_ext &= ~flag;
2626 conn->want &= ~flag;
2627 se->conn_want &= ~flag;
2628}
2629
2630bool fuse_get_feature_flag(struct fuse_conn_info *conn,
2631 uint64_t flag)
2632{
2633 return conn->capable_ext & flag ? true : false;
2634}
2635
2636/* Prevent bogus data races (bogus since "init" is called before
2637 * multi-threading becomes relevant */
2638static __attribute__((no_sanitize("thread"))) void
2639_do_init(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
2640 const void *in_payload)
2641{
2642 (void)in_payload;
2643 const struct fuse_init_in *arg = op_in;
2644 struct fuse_init_out outarg;
2645 struct fuse_session *se = req->se;
2646 size_t bufsize = se->bufsize;
2647 size_t outargsize = sizeof(outarg);
2648 uint64_t inargflags = 0;
2649 uint64_t outargflags = 0;
2650 bool buf_reallocable = se->buf_reallocable;
2651 (void) nodeid;
2652 bool enable_io_uring = false;
2653
2654 if (se->debug) {
2655 fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
2656 if (arg->major == 7 && arg->minor >= 6) {
2657 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
2658 fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
2659 arg->max_readahead);
2660 }
2661 }
2662 se->conn.proto_major = arg->major;
2663 se->conn.proto_minor = arg->minor;
2664 se->conn.capable_ext = 0;
2665 se->conn.want_ext = 0;
2666
2667 memset(&outarg, 0, sizeof(outarg));
2668 outarg.major = FUSE_KERNEL_VERSION;
2669 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
2670
2671 if (arg->major < 7) {
2672 fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
2673 arg->major, arg->minor);
2674 fuse_reply_err(req, EPROTO);
2675 return;
2676 }
2677
2678 if (arg->major > 7) {
2679 /* Wait for a second INIT request with a 7.X version */
2680 send_reply_ok(req, &outarg, sizeof(outarg));
2681 return;
2682 }
2683
2684 if (arg->minor >= 6) {
2685 if (arg->max_readahead < se->conn.max_readahead)
2686 se->conn.max_readahead = arg->max_readahead;
2687 inargflags = arg->flags;
2688 if (inargflags & FUSE_INIT_EXT)
2689 inargflags = inargflags | (uint64_t) arg->flags2 << 32;
2690 if (inargflags & FUSE_ASYNC_READ)
2691 se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
2692 if (inargflags & FUSE_POSIX_LOCKS)
2693 se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
2694 if (inargflags & FUSE_ATOMIC_O_TRUNC)
2695 se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
2696 if (inargflags & FUSE_EXPORT_SUPPORT)
2697 se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
2698 if (inargflags & FUSE_DONT_MASK)
2699 se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
2700 if (inargflags & FUSE_FLOCK_LOCKS)
2701 se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
2702 if (inargflags & FUSE_AUTO_INVAL_DATA)
2703 se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
2704 if (inargflags & FUSE_DO_READDIRPLUS)
2705 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
2706 if (inargflags & FUSE_READDIRPLUS_AUTO)
2707 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
2708 if (inargflags & FUSE_ASYNC_DIO)
2709 se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
2710 if (inargflags & FUSE_WRITEBACK_CACHE)
2711 se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
2712 if (inargflags & FUSE_NO_OPEN_SUPPORT)
2713 se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
2714 if (inargflags & FUSE_PARALLEL_DIROPS)
2715 se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
2716 if (inargflags & FUSE_POSIX_ACL)
2717 se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
2718 if (inargflags & FUSE_HANDLE_KILLPRIV)
2719 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
2720 if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
2721 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
2722 if (inargflags & FUSE_CACHE_SYMLINKS)
2723 se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
2724 if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
2725 se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
2726 if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
2727 se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
2728 if (inargflags & FUSE_SETXATTR_EXT)
2729 se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
2730 if (!(inargflags & FUSE_MAX_PAGES)) {
2731 size_t max_bufsize =
2732 FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
2733 + FUSE_BUFFER_HEADER_SIZE;
2734 if (bufsize > max_bufsize) {
2735 bufsize = max_bufsize;
2736 }
2737 buf_reallocable = false;
2738 }
2739 if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
2740 se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
2741 if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
2742 se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
2743 if (inargflags & FUSE_PASSTHROUGH)
2744 se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
2745 if (inargflags & FUSE_NO_EXPORT_SUPPORT)
2746 se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
2747 if (inargflags & FUSE_OVER_IO_URING)
2748 se->conn.capable_ext |= FUSE_CAP_OVER_IO_URING;
2749
2750 } else {
2751 se->conn.max_readahead = 0;
2752 }
2753
2754 if (se->conn.proto_minor >= 14) {
2755#ifdef HAVE_SPLICE
2756#ifdef HAVE_VMSPLICE
2757 if ((se->io == NULL) || (se->io->splice_send != NULL)) {
2758 se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
2760 }
2761#endif
2762 if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
2763 se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
2764 }
2765#endif
2766 }
2767 if (se->conn.proto_minor >= 18)
2768 se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
2769
2770 /* Default settings for modern filesystems.
2771 *
2772 * Most of these capabilities were disabled by default in
2773 * libfuse2 for backwards compatibility reasons. In libfuse3,
2774 * we can finally enable them by default (as long as they're
2775 * supported by the kernel).
2776 */
2777#define LL_SET_DEFAULT(cond, cap) \
2778 if ((cond)) \
2779 fuse_set_feature_flag(&se->conn, cap)
2780
2781 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
2782 LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
2783 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
2784 LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
2785 LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
2786 LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
2787 LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
2789 LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
2790 LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
2791 LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
2793 LL_SET_DEFAULT(1, FUSE_CAP_OVER_IO_URING);
2794
2795 /* This could safely become default, but libfuse needs an API extension
2796 * to support it
2797 * LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
2798 */
2799
2800 se->conn.time_gran = 1;
2801
2802 if (se->op.init) {
2803 // Apply the first 32 bits of capable_ext to capable
2804 se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
2805
2806 se->op.init(se->userdata, &se->conn);
2807
2808 /*
2809 * se->conn.want is 32-bit value and deprecated in favour of
2810 * se->conn.want_ext
2811 * Userspace might still use conn.want - we need to convert it
2812 */
2814 }
2815
2816 if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
2817 fuse_reply_err(req, EPROTO);
2818 se->error = -EPROTO;
2820 return;
2821 }
2822
2823 unsigned max_read_mo = get_max_read(se->mo);
2824 if (se->conn.max_read != max_read_mo) {
2825 fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
2826 "requested different maximum read size (%u vs %u)\n",
2827 se->conn.max_read, max_read_mo);
2828 fuse_reply_err(req, EPROTO);
2829 se->error = -EPROTO;
2831 return;
2832 }
2833
2834 if (bufsize < FUSE_MIN_READ_BUFFER) {
2835 fuse_log(FUSE_LOG_ERR,
2836 "fuse: warning: buffer size too small: %zu\n",
2837 bufsize);
2838 bufsize = FUSE_MIN_READ_BUFFER;
2839 }
2840
2841 if (buf_reallocable)
2842 bufsize = UINT_MAX;
2843 se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE);
2844 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
2845
2846 if (arg->flags & FUSE_MAX_PAGES) {
2847 outarg.flags |= FUSE_MAX_PAGES;
2848 outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
2849 }
2850 outargflags = outarg.flags;
2851 /* Always enable big writes, this is superseded
2852 by the max_write option */
2853 outargflags |= FUSE_BIG_WRITES;
2854
2855 if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
2856 outargflags |= FUSE_ASYNC_READ;
2857 if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
2858 outargflags |= FUSE_POSIX_LOCKS;
2859 if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
2860 outargflags |= FUSE_ATOMIC_O_TRUNC;
2861 if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
2862 outargflags |= FUSE_EXPORT_SUPPORT;
2863 if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
2864 outargflags |= FUSE_DONT_MASK;
2865 if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
2866 outargflags |= FUSE_FLOCK_LOCKS;
2867 if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
2868 outargflags |= FUSE_AUTO_INVAL_DATA;
2869 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
2870 outargflags |= FUSE_DO_READDIRPLUS;
2871 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
2872 outargflags |= FUSE_READDIRPLUS_AUTO;
2873 if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
2874 outargflags |= FUSE_ASYNC_DIO;
2875 if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
2876 outargflags |= FUSE_WRITEBACK_CACHE;
2877 if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
2878 outargflags |= FUSE_PARALLEL_DIROPS;
2879 if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
2880 outargflags |= FUSE_POSIX_ACL;
2881 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
2882 outargflags |= FUSE_HANDLE_KILLPRIV;
2883 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
2884 outargflags |= FUSE_HANDLE_KILLPRIV_V2;
2885 if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
2886 outargflags |= FUSE_CACHE_SYMLINKS;
2887 if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
2888 outargflags |= FUSE_EXPLICIT_INVAL_DATA;
2889 if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
2890 outargflags |= FUSE_SETXATTR_EXT;
2891 if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
2892 outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
2893 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
2894 outargflags |= FUSE_PASSTHROUGH;
2895 /*
2896 * outarg.max_stack_depth includes the fuse stack layer,
2897 * so it is one more than max_backing_stack_depth.
2898 */
2899 outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
2900 }
2901 if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
2902 outargflags |= FUSE_NO_EXPORT_SUPPORT;
2903 if (se->uring.enable && se->conn.want_ext & FUSE_CAP_OVER_IO_URING) {
2904 outargflags |= FUSE_OVER_IO_URING;
2905 enable_io_uring = true;
2906 }
2907
2908 if ((inargflags & FUSE_REQUEST_TIMEOUT) && se->conn.request_timeout) {
2909 outargflags |= FUSE_REQUEST_TIMEOUT;
2910 outarg.request_timeout = se->conn.request_timeout;
2911 }
2912
2913 outarg.max_readahead = se->conn.max_readahead;
2914 outarg.max_write = se->conn.max_write;
2915 if (se->conn.proto_minor >= 13) {
2916 if (se->conn.max_background >= (1 << 16))
2917 se->conn.max_background = (1 << 16) - 1;
2918 if (se->conn.congestion_threshold > se->conn.max_background)
2919 se->conn.congestion_threshold = se->conn.max_background;
2920 if (!se->conn.congestion_threshold) {
2921 se->conn.congestion_threshold =
2922 se->conn.max_background * 3 / 4;
2923 }
2924
2925 outarg.max_background = se->conn.max_background;
2926 outarg.congestion_threshold = se->conn.congestion_threshold;
2927 }
2928 if (se->conn.proto_minor >= 23)
2929 outarg.time_gran = se->conn.time_gran;
2930
2931 if (se->debug) {
2932 fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
2933 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
2934 fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
2935 outarg.max_readahead);
2936 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
2937 fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
2938 outarg.max_background);
2939 fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
2940 outarg.congestion_threshold);
2941 fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
2942 outarg.time_gran);
2943 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
2944 fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
2945 outarg.max_stack_depth);
2946 }
2947 if (arg->minor < 5)
2948 outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
2949 else if (arg->minor < 23)
2950 outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
2951
2952 /* XXX: Add an option to make non-available io-uring fatal */
2953 if (enable_io_uring) {
2954 int ring_rc = fuse_uring_start(se);
2955
2956 if (ring_rc != 0) {
2957 fuse_log(FUSE_LOG_INFO,
2958 "fuse: failed to start io-uring: %s\n",
2959 strerror(ring_rc));
2960 outargflags &= ~FUSE_OVER_IO_URING;
2961 enable_io_uring = false;
2962 }
2963 }
2964
2965 if (inargflags & FUSE_INIT_EXT) {
2966 outargflags |= FUSE_INIT_EXT;
2967 outarg.flags2 = outargflags >> 32;
2968 }
2969 outarg.flags = outargflags;
2970
2971 /*
2972 * Has to be set before replying, as new kernel requests might
2973 * immediately arrive and got_init is used for op-code sanity.
2974 * Especially with external handlers, where we have no control
2975 * over the thread scheduling.
2976 */
2977 se->got_init = 1;
2978 send_reply_ok(req, &outarg, outargsize);
2979 if (enable_io_uring)
2980 fuse_uring_wake_ring_threads(se);
2981}
2982
2983static __attribute__((no_sanitize("thread"))) void
2984do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2985{
2986 _do_init(req, nodeid, inarg, NULL);
2987}
2988
2989static void _do_destroy(fuse_req_t req, const fuse_ino_t nodeid,
2990 const void *op_in, const void *in_payload)
2991{
2992 struct fuse_session *se = req->se;
2993 char *mountpoint;
2994
2995 (void) nodeid;
2996 (void)op_in;
2997 (void)in_payload;
2998
2999 mountpoint = atomic_exchange(&se->mountpoint, NULL);
3000 free(mountpoint);
3001
3002 se->got_destroy = 1;
3003 se->got_init = 0;
3004 if (se->op.destroy)
3005 se->op.destroy(se->userdata);
3006
3007 send_reply_ok(req, NULL, 0);
3008}
3009
3010static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
3011{
3012 _do_destroy(req, nodeid, inarg, NULL);
3013}
3014
3015static void list_del_nreq(struct fuse_notify_req *nreq)
3016{
3017 struct fuse_notify_req *prev = nreq->prev;
3018 struct fuse_notify_req *next = nreq->next;
3019 prev->next = next;
3020 next->prev = prev;
3021}
3022
3023static void list_add_nreq(struct fuse_notify_req *nreq,
3024 struct fuse_notify_req *next)
3025{
3026 struct fuse_notify_req *prev = next->prev;
3027 nreq->next = next;
3028 nreq->prev = prev;
3029 prev->next = nreq;
3030 next->prev = nreq;
3031}
3032
3033static void list_init_nreq(struct fuse_notify_req *nreq)
3034{
3035 nreq->next = nreq;
3036 nreq->prev = nreq;
3037}
3038
3039static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
3040 const void *inarg, const struct fuse_buf *buf)
3041{
3042 struct fuse_session *se = req->se;
3043 struct fuse_notify_req *nreq;
3044 struct fuse_notify_req *head;
3045
3046 pthread_mutex_lock(&se->lock);
3047 head = &se->notify_list;
3048 for (nreq = head->next; nreq != head; nreq = nreq->next) {
3049 if (nreq->unique == req->unique) {
3050 list_del_nreq(nreq);
3051 break;
3052 }
3053 }
3054 pthread_mutex_unlock(&se->lock);
3055
3056 if (nreq != head)
3057 nreq->reply(nreq, req, nodeid, inarg, buf);
3058}
3059
3060static int send_notify_iov(struct fuse_session *se, int notify_code,
3061 struct iovec *iov, int count)
3062{
3063 struct fuse_out_header out;
3064 struct fuse_req *req = NULL;
3065
3066 if (!se->got_init)
3067 return -ENOTCONN;
3068
3069 out.unique = 0;
3070 out.error = notify_code;
3071 iov[0].iov_base = &out;
3072 iov[0].iov_len = sizeof(struct fuse_out_header);
3073
3074 return fuse_send_msg(se, NULL, iov, count, req);
3075}
3076
3077int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
3078{
3079 if (ph != NULL) {
3080 struct fuse_notify_poll_wakeup_out outarg;
3081 struct iovec iov[2];
3082
3083 outarg.kh = ph->kh;
3084
3085 iov[1].iov_base = &outarg;
3086 iov[1].iov_len = sizeof(outarg);
3087
3088 return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
3089 } else {
3090 return 0;
3091 }
3092}
3093
3094int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
3095 off_t off, off_t len)
3096{
3097 struct fuse_notify_inval_inode_out outarg;
3098 struct iovec iov[2];
3099
3100 if (!se)
3101 return -EINVAL;
3102
3103 if (se->conn.proto_minor < 12)
3104 return -ENOSYS;
3105
3106 outarg.ino = ino;
3107 outarg.off = off;
3108 outarg.len = len;
3109
3110 iov[1].iov_base = &outarg;
3111 iov[1].iov_len = sizeof(outarg);
3112
3113 return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
3114}
3115
3116int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
3117{
3118 struct iovec iov[1];
3119
3120 if (!se)
3121 return -EINVAL;
3122
3123 if (se->conn.proto_minor < 44)
3124 return -ENOSYS;
3125
3126 return send_notify_iov(se, FUSE_NOTIFY_INC_EPOCH, iov, 1);
3127}
3128
3148static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
3149 const char *name, size_t namelen,
3150 enum fuse_notify_entry_flags flags)
3151{
3152 struct fuse_notify_inval_entry_out outarg;
3153 struct iovec iov[3];
3154
3155 if (!se)
3156 return -EINVAL;
3157
3158 if (se->conn.proto_minor < 12)
3159 return -ENOSYS;
3160
3161 outarg.parent = parent;
3162 outarg.namelen = namelen;
3163 outarg.flags = 0;
3164 if (flags & FUSE_LL_EXPIRE_ONLY)
3165 outarg.flags |= FUSE_EXPIRE_ONLY;
3166
3167 iov[1].iov_base = &outarg;
3168 iov[1].iov_len = sizeof(outarg);
3169 iov[2].iov_base = (void *)name;
3170 iov[2].iov_len = namelen + 1;
3171
3172 return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
3173}
3174
3175int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
3176 const char *name, size_t namelen)
3177{
3178 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
3179}
3180
3181int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
3182 const char *name, size_t namelen)
3183{
3184 if (!se)
3185 return -EINVAL;
3186
3187 if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
3188 return -ENOSYS;
3189
3190 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
3191}
3192
3193
3194int fuse_lowlevel_notify_delete(struct fuse_session *se,
3195 fuse_ino_t parent, fuse_ino_t child,
3196 const char *name, size_t namelen)
3197{
3198 struct fuse_notify_delete_out outarg;
3199 struct iovec iov[3];
3200
3201 if (!se)
3202 return -EINVAL;
3203
3204 if (se->conn.proto_minor < 18)
3205 return -ENOSYS;
3206
3207 outarg.parent = parent;
3208 outarg.child = child;
3209 outarg.namelen = namelen;
3210 outarg.padding = 0;
3211
3212 iov[1].iov_base = &outarg;
3213 iov[1].iov_len = sizeof(outarg);
3214 iov[2].iov_base = (void *)name;
3215 iov[2].iov_len = namelen + 1;
3216
3217 return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
3218}
3219
3220int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
3221 off_t offset, struct fuse_bufvec *bufv,
3222 enum fuse_buf_copy_flags flags)
3223{
3224 struct fuse_out_header out;
3225 struct fuse_notify_store_out outarg;
3226 struct iovec iov[3];
3227 size_t size = fuse_buf_size(bufv);
3228 int res;
3229 struct fuse_req *req = NULL;
3230
3231 if (!se)
3232 return -EINVAL;
3233
3234 if (se->conn.proto_minor < 15)
3235 return -ENOSYS;
3236
3237 out.unique = 0;
3238 out.error = FUSE_NOTIFY_STORE;
3239
3240 outarg.nodeid = ino;
3241 outarg.offset = offset;
3242 outarg.size = size;
3243 outarg.padding = 0;
3244
3245 iov[0].iov_base = &out;
3246 iov[0].iov_len = sizeof(out);
3247 iov[1].iov_base = &outarg;
3248 iov[1].iov_len = sizeof(outarg);
3249
3250 res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags, req);
3251 if (res > 0)
3252 res = -res;
3253
3254 return res;
3255}
3256
3257struct fuse_retrieve_req {
3258 struct fuse_notify_req nreq;
3259 void *cookie;
3260};
3261
3262static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
3263 fuse_req_t req, fuse_ino_t ino,
3264 const void *inarg,
3265 const struct fuse_buf *ibuf)
3266{
3267 struct fuse_session *se = req->se;
3268 struct fuse_retrieve_req *rreq =
3269 container_of(nreq, struct fuse_retrieve_req, nreq);
3270 const struct fuse_notify_retrieve_in *arg = inarg;
3271 struct fuse_bufvec bufv = {
3272 .buf[0] = *ibuf,
3273 .count = 1,
3274 };
3275
3276 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
3277 bufv.buf[0].mem = PARAM(arg);
3278
3279 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
3280 sizeof(struct fuse_notify_retrieve_in);
3281
3282 if (bufv.buf[0].size < arg->size) {
3283 fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
3284 fuse_reply_none(req);
3285 goto out;
3286 }
3287 bufv.buf[0].size = arg->size;
3288
3289 if (se->op.retrieve_reply) {
3290 se->op.retrieve_reply(req, rreq->cookie, ino,
3291 arg->offset, &bufv);
3292 } else {
3293 fuse_reply_none(req);
3294 }
3295out:
3296 free(rreq);
3297 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
3298 fuse_ll_clear_pipe(se);
3299}
3300
3301int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
3302 size_t size, off_t offset, void *cookie)
3303{
3304 struct fuse_notify_retrieve_out outarg;
3305 struct iovec iov[2];
3306 struct fuse_retrieve_req *rreq;
3307 int err;
3308
3309 if (!se)
3310 return -EINVAL;
3311
3312 if (se->conn.proto_minor < 15)
3313 return -ENOSYS;
3314
3315 rreq = malloc(sizeof(*rreq));
3316 if (rreq == NULL)
3317 return -ENOMEM;
3318
3319 pthread_mutex_lock(&se->lock);
3320 rreq->cookie = cookie;
3321 rreq->nreq.unique = se->notify_ctr++;
3322 rreq->nreq.reply = fuse_ll_retrieve_reply;
3323 list_add_nreq(&rreq->nreq, &se->notify_list);
3324 pthread_mutex_unlock(&se->lock);
3325
3326 outarg.notify_unique = rreq->nreq.unique;
3327 outarg.nodeid = ino;
3328 outarg.offset = offset;
3329 outarg.size = size;
3330 outarg.padding = 0;
3331
3332 iov[1].iov_base = &outarg;
3333 iov[1].iov_len = sizeof(outarg);
3334
3335 err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
3336 if (err) {
3337 pthread_mutex_lock(&se->lock);
3338 list_del_nreq(&rreq->nreq);
3339 pthread_mutex_unlock(&se->lock);
3340 free(rreq);
3341 }
3342
3343 return err;
3344}
3345
3347{
3348 return req->se->userdata;
3349}
3350
3351const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
3352{
3353 return &req->ctx;
3354}
3355
3357 void *data)
3358{
3359 pthread_mutex_lock(&req->lock);
3360 pthread_mutex_lock(&req->se->lock);
3361 req->u.ni.func = func;
3362 req->u.ni.data = data;
3363 pthread_mutex_unlock(&req->se->lock);
3364 if (req->interrupted && func)
3365 func(req, data);
3366 pthread_mutex_unlock(&req->lock);
3367}
3368
3370{
3371 int interrupted;
3372
3373 pthread_mutex_lock(&req->se->lock);
3374 interrupted = req->interrupted;
3375 pthread_mutex_unlock(&req->se->lock);
3376
3377 return interrupted;
3378}
3379
3381{
3382 return req->flags.is_uring;
3383}
3384
3385#ifndef HAVE_URING
3386int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
3387 void **mr)
3388{
3389 (void)req;
3390 (void)payload;
3391 (void)payload_sz;
3392 (void)mr;
3393 return -ENOTSUP;
3394}
3395#endif
3396
3397static struct {
3398 void (*func)(fuse_req_t req, const fuse_ino_t node, const void *arg);
3399 const char *name;
3400} fuse_ll_ops[] = {
3401 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
3402 [FUSE_FORGET] = { do_forget, "FORGET" },
3403 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
3404 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
3405 [FUSE_READLINK] = { do_readlink, "READLINK" },
3406 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
3407 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
3408 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
3409 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
3410 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
3411 [FUSE_RENAME] = { do_rename, "RENAME" },
3412 [FUSE_LINK] = { do_link, "LINK" },
3413 [FUSE_OPEN] = { do_open, "OPEN" },
3414 [FUSE_READ] = { do_read, "READ" },
3415 [FUSE_WRITE] = { do_write, "WRITE" },
3416 [FUSE_STATFS] = { do_statfs, "STATFS" },
3417 [FUSE_RELEASE] = { do_release, "RELEASE" },
3418 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
3419 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
3420 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
3421 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
3422 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
3423 [FUSE_FLUSH] = { do_flush, "FLUSH" },
3424 [FUSE_INIT] = { do_init, "INIT" },
3425 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
3426 [FUSE_READDIR] = { do_readdir, "READDIR" },
3427 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
3428 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
3429 [FUSE_GETLK] = { do_getlk, "GETLK" },
3430 [FUSE_SETLK] = { do_setlk, "SETLK" },
3431 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
3432 [FUSE_ACCESS] = { do_access, "ACCESS" },
3433 [FUSE_CREATE] = { do_create, "CREATE" },
3434 [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
3435 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
3436 [FUSE_BMAP] = { do_bmap, "BMAP" },
3437 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
3438 [FUSE_POLL] = { do_poll, "POLL" },
3439 [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
3440 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
3441 [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
3442 [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
3443 [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
3444 [FUSE_RENAME2] = { do_rename2, "RENAME2" },
3445 [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
3446 [FUSE_COPY_FILE_RANGE_64] = { do_copy_file_range_64, "COPY_FILE_RANGE_64" },
3447 [FUSE_LSEEK] = { do_lseek, "LSEEK" },
3448 [FUSE_STATX] = { do_statx, "STATX" },
3449 [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
3450};
3451
3452static struct {
3453 void (*func)(fuse_req_t req, const fuse_ino_t ino, const void *op_in,
3454 const void *op_payload);
3455 const char *name;
3456} fuse_ll_ops2[] __attribute__((unused)) = {
3457 [FUSE_LOOKUP] = { _do_lookup, "LOOKUP" },
3458 [FUSE_FORGET] = { _do_forget, "FORGET" },
3459 [FUSE_GETATTR] = { _do_getattr, "GETATTR" },
3460 [FUSE_SETATTR] = { _do_setattr, "SETATTR" },
3461 [FUSE_READLINK] = { _do_readlink, "READLINK" },
3462 [FUSE_SYMLINK] = { _do_symlink, "SYMLINK" },
3463 [FUSE_MKNOD] = { _do_mknod, "MKNOD" },
3464 [FUSE_MKDIR] = { _do_mkdir, "MKDIR" },
3465 [FUSE_UNLINK] = { _do_unlink, "UNLINK" },
3466 [FUSE_RMDIR] = { _do_rmdir, "RMDIR" },
3467 [FUSE_RENAME] = { _do_rename, "RENAME" },
3468 [FUSE_LINK] = { _do_link, "LINK" },
3469 [FUSE_OPEN] = { _do_open, "OPEN" },
3470 [FUSE_READ] = { _do_read, "READ" },
3471 [FUSE_WRITE] = { _do_write, "WRITE" },
3472 [FUSE_STATFS] = { _do_statfs, "STATFS" },
3473 [FUSE_RELEASE] = { _do_release, "RELEASE" },
3474 [FUSE_FSYNC] = { _do_fsync, "FSYNC" },
3475 [FUSE_SETXATTR] = { _do_setxattr, "SETXATTR" },
3476 [FUSE_GETXATTR] = { _do_getxattr, "GETXATTR" },
3477 [FUSE_LISTXATTR] = { _do_listxattr, "LISTXATTR" },
3478 [FUSE_REMOVEXATTR] = { _do_removexattr, "REMOVEXATTR" },
3479 [FUSE_FLUSH] = { _do_flush, "FLUSH" },
3480 [FUSE_INIT] = { _do_init, "INIT" },
3481 [FUSE_OPENDIR] = { _do_opendir, "OPENDIR" },
3482 [FUSE_READDIR] = { _do_readdir, "READDIR" },
3483 [FUSE_RELEASEDIR] = { _do_releasedir, "RELEASEDIR" },
3484 [FUSE_FSYNCDIR] = { _do_fsyncdir, "FSYNCDIR" },
3485 [FUSE_GETLK] = { _do_getlk, "GETLK" },
3486 [FUSE_SETLK] = { _do_setlk, "SETLK" },
3487 [FUSE_SETLKW] = { _do_setlkw, "SETLKW" },
3488 [FUSE_ACCESS] = { _do_access, "ACCESS" },
3489 [FUSE_CREATE] = { _do_create, "CREATE" },
3490 [FUSE_TMPFILE] = { _do_tmpfile, "TMPFILE" },
3491 [FUSE_INTERRUPT] = { _do_interrupt, "INTERRUPT" },
3492 [FUSE_BMAP] = { _do_bmap, "BMAP" },
3493 [FUSE_IOCTL] = { _do_ioctl, "IOCTL" },
3494 [FUSE_POLL] = { _do_poll, "POLL" },
3495 [FUSE_FALLOCATE] = { _do_fallocate, "FALLOCATE" },
3496 [FUSE_DESTROY] = { _do_destroy, "DESTROY" },
3497 [FUSE_NOTIFY_REPLY] = { (void *)1, "NOTIFY_REPLY" },
3498 [FUSE_BATCH_FORGET] = { _do_batch_forget, "BATCH_FORGET" },
3499 [FUSE_READDIRPLUS] = { _do_readdirplus, "READDIRPLUS" },
3500 [FUSE_RENAME2] = { _do_rename2, "RENAME2" },
3501 [FUSE_COPY_FILE_RANGE] = { _do_copy_file_range, "COPY_FILE_RANGE" },
3502 [FUSE_COPY_FILE_RANGE_64] = { _do_copy_file_range_64, "COPY_FILE_RANGE_64" },
3503 [FUSE_LSEEK] = { _do_lseek, "LSEEK" },
3504 [FUSE_STATX] = { _do_statx, "STATX" },
3505 [CUSE_INIT] = { _cuse_lowlevel_init, "CUSE_INIT" },
3506};
3507
3508/*
3509 * For ABI compatibility we cannot allow higher values than CUSE_INIT.
3510 * Without ABI compatibility we could use the size of the array.
3511 * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
3512 */
3513#define FUSE_MAXOP (CUSE_INIT + 1)
3514
3515
3520static inline int
3521fuse_req_opcode_sanity_ok(struct fuse_session *se, enum fuse_opcode in_op)
3522{
3523 int err = EIO;
3524
3525 if (!se->got_init) {
3526 enum fuse_opcode expected;
3527
3528 expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
3529 if (in_op != expected)
3530 return err;
3531 } else if (in_op == FUSE_INIT || in_op == CUSE_INIT)
3532 return err;
3533
3534 return 0;
3535}
3536
3537static inline void
3538fuse_session_in2req(struct fuse_req *req, struct fuse_in_header *in)
3539{
3540 req->unique = in->unique;
3541 req->ctx.uid = in->uid;
3542 req->ctx.gid = in->gid;
3543 req->ctx.pid = in->pid;
3544}
3545
3549static inline int
3550fuse_req_check_allow_root(struct fuse_session *se, enum fuse_opcode in_op,
3551 uid_t in_uid)
3552{
3553 int err = EACCES;
3554
3555 if (se->deny_others && in_uid != se->owner && in_uid != 0 &&
3556 in_op != FUSE_INIT && in_op != FUSE_READ &&
3557 in_op != FUSE_WRITE && in_op != FUSE_FSYNC &&
3558 in_op != FUSE_RELEASE && in_op != FUSE_READDIR &&
3559 in_op != FUSE_FSYNCDIR && in_op != FUSE_RELEASEDIR &&
3560 in_op != FUSE_NOTIFY_REPLY &&
3561 in_op != FUSE_READDIRPLUS)
3562 return err;
3563
3564 return 0;
3565}
3566
3567static const char *opname(enum fuse_opcode opcode)
3568{
3569 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
3570 return "???";
3571 else
3572 return fuse_ll_ops[opcode].name;
3573}
3574
3575static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
3576 struct fuse_bufvec *src)
3577{
3578 ssize_t res = fuse_buf_copy(dst, src, 0);
3579 if (res < 0) {
3580 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
3581 return res;
3582 }
3583 if ((size_t)res < fuse_buf_size(dst)) {
3584 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
3585 return -1;
3586 }
3587 return 0;
3588}
3589
3590void fuse_session_process_buf(struct fuse_session *se,
3591 const struct fuse_buf *buf)
3592{
3593 fuse_session_process_buf_internal(se, buf, NULL);
3594}
3595
3596/* libfuse internal handler */
3597void fuse_session_process_buf_internal(struct fuse_session *se,
3598 const struct fuse_buf *buf, struct fuse_chan *ch)
3599{
3600 const size_t write_header_size = sizeof(struct fuse_in_header) +
3601 sizeof(struct fuse_write_in);
3602 struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
3603 struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
3604 struct fuse_in_header *in;
3605 const void *inarg;
3606 struct fuse_req *req;
3607 void *mbuf = NULL;
3608 int err;
3609 int res;
3610
3611 if (buf->flags & FUSE_BUF_IS_FD) {
3612 if (buf->size < tmpbuf.buf[0].size)
3613 tmpbuf.buf[0].size = buf->size;
3614
3615 mbuf = malloc(tmpbuf.buf[0].size);
3616 if (mbuf == NULL) {
3617 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
3618 goto clear_pipe;
3619 }
3620 tmpbuf.buf[0].mem = mbuf;
3621
3622 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
3623 if (res < 0)
3624 goto clear_pipe;
3625
3626 in = mbuf;
3627 } else {
3628 in = buf->mem;
3629 }
3630
3631 trace_request_process(in->opcode, in->unique);
3632
3633 if (se->debug) {
3634 fuse_log(FUSE_LOG_DEBUG,
3635 "dev unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
3636 (unsigned long long) in->unique,
3637 opname((enum fuse_opcode) in->opcode), in->opcode,
3638 (unsigned long long) in->nodeid, buf->size, in->pid);
3639 }
3640
3641 req = fuse_ll_alloc_req(se);
3642 if (req == NULL) {
3643 struct fuse_out_header out = {
3644 .unique = in->unique,
3645 .error = -ENOMEM,
3646 };
3647 struct iovec iov = {
3648 .iov_base = &out,
3649 .iov_len = sizeof(struct fuse_out_header),
3650 };
3651
3652 fuse_send_msg(se, ch, &iov, 1, NULL);
3653 goto clear_pipe;
3654 }
3655
3656 fuse_session_in2req(req, in);
3657 req->ch = ch ? fuse_chan_get(ch) : NULL;
3658
3659 err = fuse_req_opcode_sanity_ok(se, in->opcode);
3660 if (err)
3661 goto reply_err;
3662
3663 err = fuse_req_check_allow_root(se, in->opcode, in->uid);
3664 if (err)
3665 goto reply_err;
3666
3667 err = ENOSYS;
3668 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
3669 goto reply_err;
3670 /* Do not process interrupt request */
3671 if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) {
3672 if (se->debug)
3673 fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
3674 goto reply_err;
3675 }
3676 if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) {
3677 struct fuse_req *intr;
3678 pthread_mutex_lock(&se->lock);
3679 intr = check_interrupt(se, req);
3680 list_add_req(req, &se->list);
3681 pthread_mutex_unlock(&se->lock);
3682 if (intr)
3683 fuse_reply_err(intr, EAGAIN);
3684 }
3685
3686 if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
3687 (in->opcode != FUSE_WRITE || !se->op.write_buf) &&
3688 in->opcode != FUSE_NOTIFY_REPLY) {
3689 void *newmbuf;
3690
3691 err = ENOMEM;
3692 newmbuf = realloc(mbuf, buf->size);
3693 if (newmbuf == NULL)
3694 goto reply_err;
3695 mbuf = newmbuf;
3696
3697 tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
3698 tmpbuf.buf[0].mem = (char *)mbuf + write_header_size;
3699
3700 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
3701 err = -res;
3702 if (res < 0)
3703 goto reply_err;
3704
3705 in = mbuf;
3706 }
3707
3708 inarg = (void *) &in[1];
3709 if (in->opcode == FUSE_WRITE && se->op.write_buf)
3710 do_write_buf(req, in->nodeid, inarg, buf);
3711 else if (in->opcode == FUSE_NOTIFY_REPLY)
3712 do_notify_reply(req, in->nodeid, inarg, buf);
3713 else
3714 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
3715
3716out_free:
3717 free(mbuf);
3718 return;
3719
3720reply_err:
3721 fuse_reply_err(req, err);
3722clear_pipe:
3723 if (buf->flags & FUSE_BUF_IS_FD)
3724 fuse_ll_clear_pipe(se);
3725 goto out_free;
3726}
3727
3728void fuse_session_process_uring_cqe(struct fuse_session *se,
3729 struct fuse_req *req,
3730 struct fuse_in_header *in, void *op_in,
3731 void *op_payload, size_t payload_len)
3732{
3733 int err;
3734
3735 fuse_session_in2req(req, in);
3736
3737 err = fuse_req_opcode_sanity_ok(se, in->opcode);
3738 if (err)
3739 goto reply_err;
3740
3741 err = fuse_req_check_allow_root(se, in->opcode, in->uid);
3742 if (err)
3743 goto reply_err;
3744
3745 err = ENOSYS;
3746 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
3747 goto reply_err;
3748
3749 if (se->debug) {
3750 fuse_log(
3751 FUSE_LOG_DEBUG,
3752 "cqe unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
3753 (unsigned long long)in->unique,
3754 opname((enum fuse_opcode)in->opcode), in->opcode,
3755 (unsigned long long)in->nodeid, payload_len, in->pid);
3756 }
3757
3758 if (in->opcode == FUSE_WRITE && se->op.write_buf) {
3759 struct fuse_bufvec bufv = {
3760 .buf[0] = { .size = payload_len,
3761 .flags = 0,
3762 .mem = op_payload },
3763 .count = 1,
3764 };
3765 _do_write_buf(req, in->nodeid, op_in, &bufv);
3766 } else if (in->opcode == FUSE_NOTIFY_REPLY) {
3767 struct fuse_buf buf = { .size = payload_len,
3768 .mem = op_payload };
3769 do_notify_reply(req, in->nodeid, op_in, &buf);
3770 } else {
3771 fuse_ll_ops2[in->opcode].func(req, in->nodeid, op_in,
3772 op_payload);
3773 }
3774
3775 return;
3776
3777reply_err:
3778 fuse_reply_err(req, err);
3779}
3780
3781#define LL_OPTION(n,o,v) \
3782 { n, offsetof(struct fuse_session, o), v }
3783
3784static const struct fuse_opt fuse_ll_opts[] = {
3785 LL_OPTION("debug", debug, 1),
3786 LL_OPTION("-d", debug, 1),
3787 LL_OPTION("--debug", debug, 1),
3788 LL_OPTION("allow_root", deny_others, 1),
3789 LL_OPTION("io_uring", uring.enable, 1),
3790 LL_OPTION("io_uring_q_depth=%u", uring.q_depth, -1),
3792};
3793
3794void fuse_lowlevel_version(void)
3795{
3796 printf("using FUSE kernel interface version %i.%i\n",
3797 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
3798 fuse_mount_version();
3799}
3800
3801void fuse_lowlevel_help(void)
3802{
3803 /* These are not all options, but the ones that are
3804 potentially of interest to an end-user */
3805 printf(
3806" -o allow_other allow access by all users\n"
3807" -o allow_root allow access by root\n"
3808" -o auto_unmount auto unmount on process termination\n"
3809" -o io_uring enable io-uring\n"
3810" -o io_uring_q_depth=<n> io-uring queue depth\n"
3811);
3812}
3813
3814void fuse_session_destroy(struct fuse_session *se)
3815{
3816 struct fuse_ll_pipe *llp;
3817
3818 if (se->got_init && !se->got_destroy) {
3819 if (se->op.destroy)
3820 se->op.destroy(se->userdata);
3821 }
3822 llp = pthread_getspecific(se->pipe_key);
3823 if (llp != NULL)
3824 fuse_ll_pipe_free(llp);
3825 pthread_key_delete(se->pipe_key);
3826 sem_destroy(&se->mt_finish);
3827 pthread_mutex_destroy(&se->mt_lock);
3828 pthread_mutex_destroy(&se->lock);
3829 free(se->cuse_data);
3830 if (se->fd != -1)
3831 close(se->fd);
3832 if (se->io != NULL)
3833 free(se->io);
3834 destroy_mount_opts(se->mo);
3835 free(se);
3836}
3837
3838
3839static void fuse_ll_pipe_destructor(void *data)
3840{
3841 struct fuse_ll_pipe *llp = data;
3842 fuse_ll_pipe_free(llp);
3843}
3844
3845void fuse_buf_free(struct fuse_buf *buf)
3846{
3847 if (buf->mem == NULL)
3848 return;
3849
3850 size_t write_header_sz =
3851 sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
3852
3853 char *ptr = (char *)buf->mem - pagesize + write_header_sz;
3854 free(ptr);
3855 buf->mem = NULL;
3856}
3857
3858/*
3859 * This is used to allocate buffers that hold fuse requests
3860 */
3861static void *buf_alloc(size_t size, bool internal)
3862{
3863 /*
3864 * For libfuse internal caller add in alignment. That cannot be done
3865 * for an external caller, as it is not guaranteed that the external
3866 * caller frees the raw pointer.
3867 */
3868 if (internal) {
3869 size_t write_header_sz = sizeof(struct fuse_in_header) +
3870 sizeof(struct fuse_write_in);
3871 size_t new_size = ROUND_UP(size + write_header_sz, pagesize);
3872
3873 char *buf = aligned_alloc(pagesize, new_size);
3874 if (buf == NULL)
3875 return NULL;
3876
3877 buf += pagesize - write_header_sz;
3878
3879 return buf;
3880 } else {
3881 return malloc(size);
3882 }
3883}
3884
3885/*
3886 *@param internal true if called from libfuse internal code
3887 */
3888static int _fuse_session_receive_buf(struct fuse_session *se,
3889 struct fuse_buf *buf, struct fuse_chan *ch,
3890 bool internal)
3891{
3892 int err;
3893 ssize_t res;
3894 size_t bufsize;
3895#ifdef HAVE_SPLICE
3896 struct fuse_ll_pipe *llp;
3897 struct fuse_buf tmpbuf;
3898
3899pipe_retry:
3900 bufsize = se->bufsize;
3901
3902 if (se->conn.proto_minor < 14 ||
3903 !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
3904 goto fallback;
3905
3906 llp = fuse_ll_get_pipe(se);
3907 if (llp == NULL)
3908 goto fallback;
3909
3910 if (llp->size < bufsize) {
3911 if (llp->can_grow) {
3912 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
3913 if (res == -1) {
3914 llp->can_grow = 0;
3915 res = grow_pipe_to_max(llp->pipe[0]);
3916 if (res > 0)
3917 llp->size = res;
3918 goto fallback;
3919 }
3920 llp->size = res;
3921 }
3922 if (llp->size < bufsize)
3923 goto fallback;
3924 }
3925
3926 if (se->io != NULL && se->io->splice_receive != NULL) {
3927 res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
3928 llp->pipe[1], NULL, bufsize, 0,
3929 se->userdata);
3930 } else {
3931 res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
3932 bufsize, 0);
3933 }
3934 err = errno;
3935 trace_request_receive(err);
3936
3937 if (fuse_session_exited(se))
3938 return 0;
3939
3940 if (res == -1) {
3941 if (err == ENODEV) {
3942 /* Filesystem was unmounted, or connection was aborted
3943 via /sys/fs/fuse/connections */
3945 return 0;
3946 }
3947
3948 /* FUSE_INIT might have increased the required bufsize */
3949 if (err == EINVAL && bufsize < se->bufsize) {
3950 fuse_ll_clear_pipe(se);
3951 goto pipe_retry;
3952 }
3953
3954 if (err != EINTR && err != EAGAIN)
3955 perror("fuse: splice from device");
3956 return -err;
3957 }
3958
3959 if (res < sizeof(struct fuse_in_header)) {
3960 fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
3961 return -EIO;
3962 }
3963
3964 tmpbuf = (struct fuse_buf){
3965 .size = res,
3966 .flags = FUSE_BUF_IS_FD,
3967 .fd = llp->pipe[0],
3968 };
3969
3970 /*
3971 * Don't bother with zero copy for small requests.
3972 * fuse_loop_mt() needs to check for FORGET so this more than
3973 * just an optimization.
3974 */
3975 if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) +
3976 pagesize) {
3977 struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
3978 struct fuse_bufvec dst = { .count = 1 };
3979
3980 if (!buf->mem) {
3981 buf->mem = buf_alloc(bufsize, internal);
3982 if (!buf->mem) {
3983 fuse_log(
3984 FUSE_LOG_ERR,
3985 "fuse: failed to allocate read buffer\n");
3986 return -ENOMEM;
3987 }
3988 buf->mem_size = bufsize;
3989 }
3990 buf->size = bufsize;
3991 buf->flags = 0;
3992 dst.buf[0] = *buf;
3993
3994 res = fuse_buf_copy(&dst, &src, 0);
3995 if (res < 0) {
3996 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
3997 strerror(-res));
3998 fuse_ll_clear_pipe(se);
3999 return res;
4000 }
4001 if (res < tmpbuf.size) {
4002 fuse_log(FUSE_LOG_ERR,
4003 "fuse: copy from pipe: short read\n");
4004 fuse_ll_clear_pipe(se);
4005 return -EIO;
4006 }
4007 assert(res == tmpbuf.size);
4008
4009 } else {
4010 /* Don't overwrite buf->mem, as that would cause a leak */
4011 buf->fd = tmpbuf.fd;
4012 buf->flags = tmpbuf.flags;
4013 }
4014 buf->size = tmpbuf.size;
4015
4016 return res;
4017
4018fallback:
4019#endif
4020 bufsize = internal ? buf->mem_size : se->bufsize;
4021 if (!buf->mem) {
4022 bufsize = se->bufsize; /* might have changed */
4023 buf->mem = buf_alloc(bufsize, internal);
4024 if (!buf->mem) {
4025 fuse_log(FUSE_LOG_ERR,
4026 "fuse: failed to allocate read buffer\n");
4027 return -ENOMEM;
4028 }
4029
4030 if (internal)
4031 buf->mem_size = bufsize;
4032 }
4033
4034restart:
4035 if (se->io != NULL) {
4036 /* se->io->read is never NULL if se->io is not NULL as
4037 specified by fuse_session_custom_io()*/
4038 res = se->io->read(ch ? ch->fd : se->fd, buf->mem, bufsize,
4039 se->userdata);
4040 } else {
4041 res = read(ch ? ch->fd : se->fd, buf->mem, bufsize);
4042 }
4043 err = errno;
4044 trace_request_receive(err);
4045
4046 if (fuse_session_exited(se))
4047 return 0;
4048 if (res == -1) {
4049 if (err == EINVAL && internal && se->bufsize > bufsize) {
4050 /* FUSE_INIT might have increased the required bufsize */
4051 bufsize = se->bufsize;
4052 void *newbuf = buf_alloc(bufsize, internal);
4053 if (!newbuf) {
4054 fuse_log(
4055 FUSE_LOG_ERR,
4056 "fuse: failed to (re)allocate read buffer\n");
4057 return -ENOMEM;
4058 }
4059 fuse_buf_free(buf);
4060 buf->mem = newbuf;
4061 buf->mem_size = bufsize;
4062 goto restart;
4063 }
4064
4065 /* ENOENT means the operation was interrupted, it's safe
4066 to restart */
4067 if (err == ENOENT)
4068 goto restart;
4069
4070 if (err == ENODEV) {
4071 /* Filesystem was unmounted, or connection was aborted
4072 via /sys/fs/fuse/connections */
4074 return 0;
4075 }
4076 /* Errors occurring during normal operation: EINTR (read
4077 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
4078 umounted) */
4079 if (err != EINTR && err != EAGAIN)
4080 perror("fuse: reading device");
4081 return -err;
4082 }
4083 if ((size_t)res < sizeof(struct fuse_in_header)) {
4084 fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
4085 return -EIO;
4086 }
4087
4088 buf->size = res;
4089
4090 return res;
4091}
4092
4093int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
4094{
4095 return _fuse_session_receive_buf(se, buf, NULL, false);
4096}
4097
4098/* libfuse internal handler */
4099int fuse_session_receive_buf_internal(struct fuse_session *se,
4100 struct fuse_buf *buf,
4101 struct fuse_chan *ch)
4102{
4103 /*
4104 * if run internally thread buffers are from libfuse - we can
4105 * reallocate them
4106 */
4107 if (unlikely(!se->got_init) && !se->buf_reallocable)
4108 se->buf_reallocable = true;
4109
4110 return _fuse_session_receive_buf(se, buf, ch, true);
4111}
4112
4113struct fuse_session *
4114fuse_session_new_versioned(struct fuse_args *args,
4115 const struct fuse_lowlevel_ops *op, size_t op_size,
4116 struct libfuse_version *version, void *userdata)
4117{
4118 int err;
4119 struct fuse_session *se;
4120 struct mount_opts *mo;
4121
4122 if (op == NULL || op_size == 0) {
4123 fuse_log(FUSE_LOG_ERR,
4124 "fuse: warning: empty op list passed to fuse_session_new()\n");
4125 return NULL;
4126 }
4127
4128 if (version == NULL) {
4129 fuse_log(FUSE_LOG_ERR, "fuse: warning: version not passed to fuse_session_new()\n");
4130 return NULL;
4131 }
4132
4133 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
4134 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
4135 op_size = sizeof(struct fuse_lowlevel_ops);
4136 }
4137
4138 if (args == NULL || args->argc == 0) {
4139 fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
4140 return NULL;
4141 }
4142
4143 se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
4144 if (se == NULL) {
4145 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
4146 goto out1;
4147 }
4148 se->fd = -1;
4149 se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize();
4150 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
4151 se->conn.max_readahead = UINT_MAX;
4152
4153 /*
4154 * Allow overriding with env, mostly to avoid the need to modify
4155 * all tests. I.e. to test with and without io-uring being enabled.
4156 */
4157 se->uring.enable = getenv("FUSE_URING_ENABLE") ?
4158 atoi(getenv("FUSE_URING_ENABLE")) :
4159 SESSION_DEF_URING_ENABLE;
4160 se->uring.q_depth = getenv("FUSE_URING_QUEUE_DEPTH") ?
4161 atoi(getenv("FUSE_URING_QUEUE_DEPTH")) :
4162 SESSION_DEF_URING_Q_DEPTH;
4163
4164 /* Parse options */
4165 if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
4166 goto out2;
4167 if(se->deny_others) {
4168 /* Allowing access only by root is done by instructing
4169 * kernel to allow access by everyone, and then restricting
4170 * access to root and mountpoint owner in libfuse.
4171 */
4172 // We may be adding the option a second time, but
4173 // that doesn't hurt.
4174 if(fuse_opt_add_arg(args, "-oallow_other") == -1)
4175 goto out2;
4176 }
4177 mo = parse_mount_opts(args);
4178 if (mo == NULL)
4179 goto out3;
4180
4181 if(args->argc == 1 &&
4182 args->argv[0][0] == '-') {
4183 fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
4184 "will be ignored\n");
4185 } else if (args->argc != 1) {
4186 int i;
4187 fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
4188 for(i = 1; i < args->argc-1; i++)
4189 fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
4190 fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
4191 goto out4;
4192 }
4193
4194 if (se->debug)
4195 fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
4196
4197 list_init_req(&se->list);
4198 list_init_req(&se->interrupts);
4199 list_init_nreq(&se->notify_list);
4200 se->notify_ctr = 1;
4201 pthread_mutex_init(&se->lock, NULL);
4202 sem_init(&se->mt_finish, 0, 0);
4203 pthread_mutex_init(&se->mt_lock, NULL);
4204
4205 err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
4206 if (err) {
4207 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
4208 strerror(err));
4209 goto out5;
4210 }
4211
4212 memcpy(&se->op, op, op_size);
4213 se->owner = getuid();
4214 se->userdata = userdata;
4215
4216 se->mo = mo;
4217
4218 /* Fuse server application should pass the version it was compiled
4219 * against and pass it. If a libfuse version accidentally introduces an
4220 * ABI incompatibility, it might be possible to 'fix' that at run time,
4221 * by checking the version numbers.
4222 */
4223 se->version = *version;
4224
4225 return se;
4226
4227out5:
4228 sem_destroy(&se->mt_finish);
4229 pthread_mutex_destroy(&se->mt_lock);
4230 pthread_mutex_destroy(&se->lock);
4231out4:
4232 fuse_opt_free_args(args);
4233out3:
4234 if (mo != NULL)
4235 destroy_mount_opts(mo);
4236out2:
4237 free(se);
4238out1:
4239 return NULL;
4240}
4241
4242struct fuse_session *fuse_session_new_30(struct fuse_args *args,
4243 const struct fuse_lowlevel_ops *op,
4244 size_t op_size, void *userdata);
4245struct fuse_session *fuse_session_new_30(struct fuse_args *args,
4246 const struct fuse_lowlevel_ops *op,
4247 size_t op_size,
4248 void *userdata)
4249{
4250 struct fuse_lowlevel_ops null_ops = { 0 };
4251
4252 /* unknown version */
4253 struct libfuse_version version = { 0 };
4254
4255 /*
4256 * This function is the ABI interface function from fuse_session_new in
4257 * compat.c. External libraries like "fuser" might call fuse_session_new()
4258 * with NULL ops and then pass that session to fuse_session_mount().
4259 * The actual FUSE operations are handled in their own library.
4260 */
4261 if (op == NULL) {
4262 op = &null_ops;
4263 op_size = sizeof(null_ops);
4264 }
4265
4266 return fuse_session_new_versioned(args, op, op_size, &version,
4267 userdata);
4268}
4269
4270FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17")
4271int fuse_session_custom_io_317(struct fuse_session *se,
4272 const struct fuse_custom_io *io, size_t op_size, int fd)
4273{
4274 if (sizeof(struct fuse_custom_io) < op_size) {
4275 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
4276 op_size = sizeof(struct fuse_custom_io);
4277 }
4278
4279 if (fd < 0) {
4280 fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
4281 "fuse_session_custom_io()\n", fd);
4282 return -EBADF;
4283 }
4284 if (io == NULL) {
4285 fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
4286 "fuse_session_custom_io()\n");
4287 return -EINVAL;
4288 } else if (io->read == NULL || io->writev == NULL) {
4289 /* If the user provides their own file descriptor, we can't
4290 guarantee that the default behavior of the io operations made
4291 in libfuse will function properly. Therefore, we enforce the
4292 user to implement these io operations when using custom io. */
4293 fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
4294 "implement both io->read() and io->writev\n");
4295 return -EINVAL;
4296 }
4297
4298 se->io = calloc(1, sizeof(struct fuse_custom_io));
4299 if (se->io == NULL) {
4300 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
4301 "Error: %s\n", strerror(errno));
4302 return -errno;
4303 }
4304
4305 se->fd = fd;
4306 memcpy(se->io, io, op_size);
4307 return 0;
4308}
4309
4310int fuse_session_custom_io_30(struct fuse_session *se,
4311 const struct fuse_custom_io *io, int fd);
4312FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0")
4313int fuse_session_custom_io_30(struct fuse_session *se,
4314 const struct fuse_custom_io *io, int fd)
4315{
4316 return fuse_session_custom_io_317(se, io,
4317 offsetof(struct fuse_custom_io, clone_fd), fd);
4318}
4319
4320int fuse_session_mount(struct fuse_session *se, const char *_mountpoint)
4321{
4322 int fd;
4323 char *mountpoint;
4324
4325 if (_mountpoint == NULL) {
4326 fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
4327 return -1;
4328 }
4329
4330 mountpoint = strdup(_mountpoint);
4331 if (mountpoint == NULL) {
4332 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for mountpoint. Error: %s\n",
4333 strerror(errno));
4334 return -1;
4335 }
4336
4337 /*
4338 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
4339 * would ensue.
4340 */
4341 do {
4342 fd = open("/dev/null", O_RDWR);
4343 if (fd > 2)
4344 close(fd);
4345 } while (fd >= 0 && fd <= 2);
4346
4347 /*
4348 * To allow FUSE daemons to run without privileges, the caller may open
4349 * /dev/fuse before launching the file system and pass on the file
4350 * descriptor by specifying /dev/fd/N as the mount point. Note that the
4351 * parent process takes care of performing the mount in this case.
4352 */
4353 fd = fuse_mnt_parse_fuse_fd(mountpoint);
4354 if (fd != -1) {
4355 if (fcntl(fd, F_GETFD) == -1) {
4356 fuse_log(FUSE_LOG_ERR,
4357 "fuse: Invalid file descriptor /dev/fd/%u\n",
4358 fd);
4359 goto error_out;
4360 }
4361 se->fd = fd;
4362 return 0;
4363 }
4364
4365 /* Open channel */
4366 fd = fuse_kern_mount(mountpoint, se->mo);
4367 if (fd == -1)
4368 goto error_out;
4369 se->fd = fd;
4370
4371 /* Save mountpoint */
4372 se->mountpoint = mountpoint;
4373
4374 return 0;
4375
4376error_out:
4377 free(mountpoint);
4378 return -1;
4379}
4380
4381int fuse_session_fd(struct fuse_session *se)
4382{
4383 return se->fd;
4384}
4385
4386void fuse_session_unmount(struct fuse_session *se)
4387{
4388 if (se->mountpoint != NULL) {
4389 char *mountpoint = atomic_exchange(&se->mountpoint, NULL);
4390
4391 fuse_kern_unmount(mountpoint, se->fd);
4392 se->fd = -1;
4393 free(mountpoint);
4394 }
4395}
4396
4397#ifdef linux
4398int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
4399{
4400 char *buf;
4401 size_t bufsize = 1024;
4402 char path[128];
4403 int ret;
4404 int fd;
4405 unsigned long pid = req->ctx.pid;
4406 char *s;
4407
4408 sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
4409
4410retry:
4411 buf = malloc(bufsize);
4412 if (buf == NULL)
4413 return -ENOMEM;
4414
4415 ret = -EIO;
4416 fd = open(path, O_RDONLY);
4417 if (fd == -1)
4418 goto out_free;
4419
4420 ret = read(fd, buf, bufsize);
4421 close(fd);
4422 if (ret < 0) {
4423 ret = -EIO;
4424 goto out_free;
4425 }
4426
4427 if ((size_t)ret == bufsize) {
4428 free(buf);
4429 bufsize *= 4;
4430 goto retry;
4431 }
4432
4433 buf[ret] = '\0';
4434 ret = -EIO;
4435 s = strstr(buf, "\nGroups:");
4436 if (s == NULL)
4437 goto out_free;
4438
4439 s += 8;
4440 ret = 0;
4441 while (1) {
4442 char *end;
4443 unsigned long val = strtoul(s, &end, 0);
4444 if (end == s)
4445 break;
4446
4447 s = end;
4448 if (ret < size)
4449 list[ret] = val;
4450 ret++;
4451 }
4452
4453out_free:
4454 free(buf);
4455 return ret;
4456}
4457#else /* linux */
4458/*
4459 * This is currently not implemented on other than Linux...
4460 */
4461int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
4462{
4463 (void) req; (void) size; (void) list;
4464 return -ENOSYS;
4465}
4466#endif
4467
4468/* Prevent spurious data race warning - we don't care
4469 * about races for this flag */
4470__attribute__((no_sanitize_thread))
4471void fuse_session_exit(struct fuse_session *se)
4472{
4473 atomic_store_explicit(&se->mt_exited, 1, memory_order_relaxed);
4474 sem_post(&se->mt_finish);
4475}
4476
4477__attribute__((no_sanitize_thread))
4478void fuse_session_reset(struct fuse_session *se)
4479{
4480 se->mt_exited = false;
4481 se->error = 0;
4482}
4483
4484__attribute__((no_sanitize_thread))
4485int fuse_session_exited(struct fuse_session *se)
4486{
4487 bool exited =
4488 atomic_load_explicit(&se->mt_exited, memory_order_relaxed);
4489
4490 return exited ? 1 : 0;
4491}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
@ FUSE_BUF_IS_FD
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
fuse_notify_entry_flags
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_session_reset(struct fuse_session *se)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_passthrough_open(fuse_req_t req, int fd)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_xattr(fuse_req_t req, size_t count)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_CAP_OVER_IO_URING
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
@ FUSE_BUF_IS_FD
bool fuse_req_is_uring(fuse_req_t req)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
int fuse_session_fd(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
void fuse_session_unmount(struct fuse_session *se)
int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
size_t mem_size
void * mem
size_t size
struct fuse_buf buf[1]
uint64_t capable_ext
uint64_t want_ext
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t nonseekable
Definition fuse_common.h:78
int32_t backing_id
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t noflush
Definition fuse_common.h:93
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2fuse__misc_8h_source.html0000644000175000017500000002434415156613443023314 0ustar berndbernd libfuse: fuse-3.17.4/lib/fuse_misc.h Source File
libfuse
fuse_misc.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
7*/
8
9#include <pthread.h>
10
11/*
12 Versioned symbols cannot be used in some cases because it
13 - not supported on MacOSX (in MachO binary format)
14
15 Note: "@@" denotes the default symbol, "@" is binary a compat version.
16
17*/
18#ifdef LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS
19# if HAVE_SYMVER_ATTRIBUTE
20# define FUSE_SYMVER(sym1, sym2) __attribute__ ((symver (sym2)))
21# else
22# define FUSE_SYMVER(sym1, sym2) __asm__("\t.symver " sym1 "," sym2);
23# endif
24#else
25#define FUSE_SYMVER(sym1, sym2)
26#endif
27
28#ifdef HAVE_STRUCT_STAT_ST_ATIM
29/* Linux */
30#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
31#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
32#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
33#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val)
34#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctim.tv_nsec = (val)
35#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val)
36#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
37/* FreeBSD */
38#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
39#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
40#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
41#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val)
42#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctimespec.tv_nsec = (val)
43#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val)
44#else
45#define ST_ATIM_NSEC(stbuf) 0
46#define ST_CTIM_NSEC(stbuf) 0
47#define ST_MTIM_NSEC(stbuf) 0
48#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0)
49#define ST_CTIM_NSEC_SET(stbuf, val) do { } while (0)
50#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0)
51#endif
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__misc_8h_source.html0000644000175000017500000002434215156613443023310 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_misc.h Source File
libfuse
fuse_misc.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#include <pthread.h>
10
11/*
12 Versioned symbols cannot be used in some cases because it
13 - not supported on MacOSX (in MachO binary format)
14
15 Note: "@@" denotes the default symbol, "@" is binary a compat version.
16
17*/
18#ifdef LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS
19# if HAVE_SYMVER_ATTRIBUTE
20# define FUSE_SYMVER(sym1, sym2) __attribute__ ((symver (sym2)))
21# else
22# define FUSE_SYMVER(sym1, sym2) __asm__("\t.symver " sym1 "," sym2);
23# endif
24#else
25#define FUSE_SYMVER(sym1, sym2)
26#endif
27
28#ifdef HAVE_STRUCT_STAT_ST_ATIM
29/* Linux */
30#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
31#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
32#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
33#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val)
34#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctim.tv_nsec = (val)
35#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val)
36#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
37/* FreeBSD */
38#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
39#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
40#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
41#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val)
42#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctimespec.tv_nsec = (val)
43#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val)
44#else
45#define ST_ATIM_NSEC(stbuf) 0
46#define ST_CTIM_NSEC(stbuf) 0
47#define ST_MTIM_NSEC(stbuf) 0
48#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0)
49#define ST_CTIM_NSEC_SET(stbuf, val) do { } while (0)
50#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0)
51#endif
fuse-3.18.2/doc/html/lib_2fuse__misc_8h_source.html0000644000175000017500000002415715156613443021140 0ustar berndbernd libfuse: lib/fuse_misc.h Source File
libfuse
fuse_misc.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#include <pthread.h>
10
11/*
12 Versioned symbols cannot be used in some cases because it
13 - not supported on MacOSX (in MachO binary format)
14
15 Note: "@@" denotes the default symbol, "@" is binary a compat version.
16
17*/
18#ifdef LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS
19# if HAVE_SYMVER_ATTRIBUTE
20# define FUSE_SYMVER(sym1, sym2) __attribute__ ((symver (sym2)))
21# else
22# define FUSE_SYMVER(sym1, sym2) __asm__("\t.symver " sym1 "," sym2);
23# endif
24#else
25#define FUSE_SYMVER(sym1, sym2)
26#endif
27
28#ifdef HAVE_STRUCT_STAT_ST_ATIM
29/* Linux */
30#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
31#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
32#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
33#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val)
34#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctim.tv_nsec = (val)
35#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val)
36#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
37/* FreeBSD */
38#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
39#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
40#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
41#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val)
42#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctimespec.tv_nsec = (val)
43#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val)
44#else
45#define ST_ATIM_NSEC(stbuf) 0
46#define ST_CTIM_NSEC(stbuf) 0
47#define ST_MTIM_NSEC(stbuf) 0
48#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0)
49#define ST_CTIM_NSEC_SET(stbuf, val) do { } while (0)
50#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0)
51#endif
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2fuse__opt_8c_source.html0000644000175000017500000023751315156613443023162 0ustar berndbernd libfuse: fuse-3.17.4/lib/fuse_opt.c Source File
libfuse
fuse_opt.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of option parsing routines (dealing with `struct
6 fuse_args`).
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file COPYING.LIB
10*/
11
12#include "fuse_config.h"
13#include "fuse_i.h"
14#include "fuse_opt.h"
15#include "fuse_misc.h"
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <assert.h>
21
22struct fuse_opt_context {
23 void *data;
24 const struct fuse_opt *opt;
25 fuse_opt_proc_t proc;
26 int argctr;
27 int argc;
28 char **argv;
29 struct fuse_args outargs;
30 char *opts;
31 int nonopt;
32};
33
34void fuse_opt_free_args(struct fuse_args *args)
35{
36 if (args) {
37 if (args->argv && args->allocated) {
38 int i;
39 for (i = 0; i < args->argc; i++)
40 free(args->argv[i]);
41 free(args->argv);
42 }
43 args->argc = 0;
44 args->argv = NULL;
45 args->allocated = 0;
46 }
47}
48
49static int alloc_failed(void)
50{
51 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
52 return -1;
53}
54
55int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
56{
57 char **newargv;
58 char *newarg;
59
60 assert(!args->argv || args->allocated);
61
62 newarg = strdup(arg);
63 if (!newarg)
64 return alloc_failed();
65
66 newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
67 if (!newargv) {
68 free(newarg);
69 return alloc_failed();
70 }
71
72 args->argv = newargv;
73 args->allocated = 1;
74 args->argv[args->argc++] = newarg;
75 args->argv[args->argc] = NULL;
76 return 0;
77}
78
79static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
80 const char *arg)
81{
82 assert(pos <= args->argc);
83 if (fuse_opt_add_arg(args, arg) == -1)
84 return -1;
85
86 if (pos != args->argc - 1) {
87 char *newarg = args->argv[args->argc - 1];
88 memmove(&args->argv[pos + 1], &args->argv[pos],
89 sizeof(char *) * (args->argc - pos - 1));
90 args->argv[pos] = newarg;
91 }
92 return 0;
93}
94
95int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
96{
97 return fuse_opt_insert_arg_common(args, pos, arg);
98}
99
100static int next_arg(struct fuse_opt_context *ctx, const char *opt)
101{
102 if (ctx->argctr + 1 >= ctx->argc) {
103 fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt);
104 return -1;
105 }
106 ctx->argctr++;
107 return 0;
108}
109
110static int add_arg(struct fuse_opt_context *ctx, const char *arg)
111{
112 return fuse_opt_add_arg(&ctx->outargs, arg);
113}
114
115static int add_opt_common(char **opts, const char *opt, int esc)
116{
117 unsigned oldlen = *opts ? strlen(*opts) : 0;
118 char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
119
120 if (!d)
121 return alloc_failed();
122
123 *opts = d;
124 if (oldlen) {
125 d += oldlen;
126 *d++ = ',';
127 }
128
129 for (; *opt; opt++) {
130 if (esc && (*opt == ',' || *opt == '\\'))
131 *d++ = '\\';
132 *d++ = *opt;
133 }
134 *d = '\0';
135
136 return 0;
137}
138
139int fuse_opt_add_opt(char **opts, const char *opt)
140{
141 return add_opt_common(opts, opt, 0);
142}
143
144int fuse_opt_add_opt_escaped(char **opts, const char *opt)
145{
146 return add_opt_common(opts, opt, 1);
147}
148
149static int add_opt(struct fuse_opt_context *ctx, const char *opt)
150{
151 return add_opt_common(&ctx->opts, opt, 1);
152}
153
154static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
155 int iso)
156{
157 if (key == FUSE_OPT_KEY_DISCARD)
158 return 0;
159
160 if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
161 int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
162 if (res == -1 || !res)
163 return res;
164 }
165 if (iso)
166 return add_opt(ctx, arg);
167 else
168 return add_arg(ctx, arg);
169}
170
171static int match_template(const char *t, const char *arg, unsigned *sepp)
172{
173 int arglen = strlen(arg);
174 const char *sep = strchr(t, '=');
175 sep = sep ? sep : strchr(t, ' ');
176 if (sep && (!sep[1] || sep[1] == '%')) {
177 int tlen = sep - t;
178 if (sep[0] == '=')
179 tlen ++;
180 if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
181 *sepp = sep - t;
182 return 1;
183 }
184 }
185 if (strcmp(t, arg) == 0) {
186 *sepp = 0;
187 return 1;
188 }
189 return 0;
190}
191
192static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
193 const char *arg, unsigned *sepp)
194{
195 for (; opt && opt->templ; opt++)
196 if (match_template(opt->templ, arg, sepp))
197 return opt;
198 return NULL;
199}
200
201int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
202{
203 unsigned dummy;
204 return find_opt(opts, opt, &dummy) ? 1 : 0;
205}
206
207static int process_opt_param(void *var, const char *format, const char *param,
208 const char *arg)
209{
210 assert(format[0] == '%');
211 if (format[1] == 's') {
212 char **s = var;
213 char *copy = strdup(param);
214 if (!copy)
215 return alloc_failed();
216
217 free(*s);
218 *s = copy;
219 } else {
220 if (sscanf(param, format, var) != 1) {
221 fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg);
222 return -1;
223 }
224 }
225 return 0;
226}
227
228static int process_opt(struct fuse_opt_context *ctx,
229 const struct fuse_opt *opt, unsigned sep,
230 const char *arg, int iso)
231{
232 if (opt->offset == -1U) {
233 if (call_proc(ctx, arg, opt->value, iso) == -1)
234 return -1;
235 } else {
236 void *var = (char *)ctx->data + opt->offset;
237 if (sep && opt->templ[sep + 1]) {
238 const char *param = arg + sep;
239 if (opt->templ[sep] == '=')
240 param ++;
241 if (process_opt_param(var, opt->templ + sep + 1,
242 param, arg) == -1)
243 return -1;
244 } else
245 *(int *)var = opt->value;
246 }
247 return 0;
248}
249
250static int process_opt_sep_arg(struct fuse_opt_context *ctx,
251 const struct fuse_opt *opt, unsigned sep,
252 const char *arg, int iso)
253{
254 int res;
255 char *newarg;
256 char *param;
257
258 if (next_arg(ctx, arg) == -1)
259 return -1;
260
261 param = ctx->argv[ctx->argctr];
262 newarg = malloc(sep + strlen(param) + 1);
263 if (!newarg)
264 return alloc_failed();
265
266 memcpy(newarg, arg, sep);
267 strcpy(newarg + sep, param);
268 res = process_opt(ctx, opt, sep, newarg, iso);
269 free(newarg);
270
271 return res;
272}
273
274static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
275{
276 unsigned sep;
277 const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
278 if (opt) {
279 for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
280 int res;
281 if (sep && opt->templ[sep] == ' ' && !arg[sep])
282 res = process_opt_sep_arg(ctx, opt, sep, arg,
283 iso);
284 else
285 res = process_opt(ctx, opt, sep, arg, iso);
286 if (res == -1)
287 return -1;
288 }
289 return 0;
290 } else
291 return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
292}
293
294static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
295{
296 char *s = opts;
297 char *d = s;
298 int end = 0;
299
300 while (!end) {
301 if (*s == '\0')
302 end = 1;
303 if (*s == ',' || end) {
304 int res;
305
306 *d = '\0';
307 res = process_gopt(ctx, opts, 1);
308 if (res == -1)
309 return -1;
310 d = opts;
311 } else {
312 if (s[0] == '\\' && s[1] != '\0') {
313 s++;
314 if (s[0] >= '0' && s[0] <= '3' &&
315 s[1] >= '0' && s[1] <= '7' &&
316 s[2] >= '0' && s[2] <= '7') {
317 *d++ = (s[0] - '0') * 0100 +
318 (s[1] - '0') * 0010 +
319 (s[2] - '0');
320 s += 2;
321 } else {
322 *d++ = *s;
323 }
324 } else {
325 *d++ = *s;
326 }
327 }
328 s++;
329 }
330
331 return 0;
332}
333
334static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
335{
336 int res;
337 char *copy = strdup(opts);
338
339 if (!copy) {
340 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
341 return -1;
342 }
343 res = process_real_option_group(ctx, copy);
344 free(copy);
345 return res;
346}
347
348static int process_one(struct fuse_opt_context *ctx, const char *arg)
349{
350 if (ctx->nonopt || arg[0] != '-')
351 return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
352 else if (arg[1] == 'o') {
353 if (arg[2])
354 return process_option_group(ctx, arg + 2);
355 else {
356 if (next_arg(ctx, arg) == -1)
357 return -1;
358
359 return process_option_group(ctx,
360 ctx->argv[ctx->argctr]);
361 }
362 } else if (arg[1] == '-' && !arg[2]) {
363 if (add_arg(ctx, arg) == -1)
364 return -1;
365 ctx->nonopt = ctx->outargs.argc;
366 return 0;
367 } else
368 return process_gopt(ctx, arg, 0);
369}
370
371static int opt_parse(struct fuse_opt_context *ctx)
372{
373 if (ctx->argc) {
374 if (add_arg(ctx, ctx->argv[0]) == -1)
375 return -1;
376 }
377
378 for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
379 if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
380 return -1;
381
382 if (ctx->opts) {
383 if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
384 fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
385 return -1;
386 }
387
388 /* If option separator ("--") is the last argument, remove it */
389 if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
390 strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
391 free(ctx->outargs.argv[ctx->outargs.argc - 1]);
392 ctx->outargs.argv[--ctx->outargs.argc] = NULL;
393 }
394
395 return 0;
396}
397
398int fuse_opt_parse(struct fuse_args *args, void *data,
399 const struct fuse_opt opts[], fuse_opt_proc_t proc)
400{
401 int res;
402 struct fuse_opt_context ctx = {
403 .data = data,
404 .opt = opts,
405 .proc = proc,
406 };
407
408 if (!args || !args->argv || !args->argc)
409 return 0;
410
411 ctx.argc = args->argc;
412 ctx.argv = args->argv;
413
414 res = opt_parse(&ctx);
415 if (res != -1) {
416 struct fuse_args tmp = *args;
417 *args = ctx.outargs;
418 ctx.outargs = tmp;
419 }
420 free(ctx.opts);
421 fuse_opt_free_args(&ctx.outargs);
422 return res;
423}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition fuse_opt.h:180
#define FUSE_OPT_KEY_OPT
Definition fuse_opt.h:129
#define FUSE_OPT_KEY_NONOPT
Definition fuse_opt.h:137
#define FUSE_OPT_KEY_DISCARD
Definition fuse_opt.h:153
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition fuse_opt.c:95
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
int allocated
Definition fuse_opt.h:117
char ** argv
Definition fuse_opt.h:114
unsigned long offset
Definition fuse_opt.h:85
const char * templ
Definition fuse_opt.h:79
int value
Definition fuse_opt.h:91
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__opt_8c_source.html0000644000175000017500000023542115156613443023154 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_opt.c Source File
libfuse
fuse_opt.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of option parsing routines (dealing with `struct
6 fuse_args`).
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#include "fuse_config.h"
13#include "fuse_i.h"
14#include "fuse_opt.h"
15#include "fuse_misc.h"
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <assert.h>
21
22struct fuse_opt_context {
23 void *data;
24 const struct fuse_opt *opt;
25 fuse_opt_proc_t proc;
26 int argctr;
27 int argc;
28 char **argv;
29 struct fuse_args outargs;
30 char *opts;
31 int nonopt;
32};
33
34void fuse_opt_free_args(struct fuse_args *args)
35{
36 if (args) {
37 if (args->argv && args->allocated) {
38 int i;
39 for (i = 0; i < args->argc; i++)
40 free(args->argv[i]);
41 free(args->argv);
42 }
43 args->argc = 0;
44 args->argv = NULL;
45 args->allocated = 0;
46 }
47}
48
49static int alloc_failed(void)
50{
51 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
52 return -1;
53}
54
55int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
56{
57 char **newargv;
58 char *newarg;
59
60 assert(!args->argv || args->allocated);
61
62 newarg = strdup(arg);
63 if (!newarg)
64 return alloc_failed();
65
66 newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
67 if (!newargv) {
68 free(newarg);
69 return alloc_failed();
70 }
71
72 args->argv = newargv;
73 args->allocated = 1;
74 args->argv[args->argc++] = newarg;
75 args->argv[args->argc] = NULL;
76 return 0;
77}
78
79static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
80 const char *arg)
81{
82 assert(pos <= args->argc);
83 if (fuse_opt_add_arg(args, arg) == -1)
84 return -1;
85
86 if (pos != args->argc - 1) {
87 char *newarg = args->argv[args->argc - 1];
88 memmove(&args->argv[pos + 1], &args->argv[pos],
89 sizeof(char *) * (args->argc - pos - 1));
90 args->argv[pos] = newarg;
91 }
92 return 0;
93}
94
95int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
96{
97 return fuse_opt_insert_arg_common(args, pos, arg);
98}
99
100static int next_arg(struct fuse_opt_context *ctx, const char *opt)
101{
102 if (ctx->argctr + 1 >= ctx->argc) {
103 fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt);
104 return -1;
105 }
106 ctx->argctr++;
107 return 0;
108}
109
110static int add_arg(struct fuse_opt_context *ctx, const char *arg)
111{
112 return fuse_opt_add_arg(&ctx->outargs, arg);
113}
114
115static int add_opt_common(char **opts, const char *opt, int esc)
116{
117 unsigned oldlen = *opts ? strlen(*opts) : 0;
118 char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
119
120 if (!d)
121 return alloc_failed();
122
123 *opts = d;
124 if (oldlen) {
125 d += oldlen;
126 *d++ = ',';
127 }
128
129 for (; *opt; opt++) {
130 if (esc && (*opt == ',' || *opt == '\\'))
131 *d++ = '\\';
132 *d++ = *opt;
133 }
134 *d = '\0';
135
136 return 0;
137}
138
139int fuse_opt_add_opt(char **opts, const char *opt)
140{
141 return add_opt_common(opts, opt, 0);
142}
143
144int fuse_opt_add_opt_escaped(char **opts, const char *opt)
145{
146 return add_opt_common(opts, opt, 1);
147}
148
149static int add_opt(struct fuse_opt_context *ctx, const char *opt)
150{
151 return add_opt_common(&ctx->opts, opt, 1);
152}
153
154static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
155 int iso)
156{
157 if (key == FUSE_OPT_KEY_DISCARD)
158 return 0;
159
160 if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
161 int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
162 if (res == -1 || !res)
163 return res;
164 }
165 if (iso)
166 return add_opt(ctx, arg);
167 else
168 return add_arg(ctx, arg);
169}
170
171static int match_template(const char *t, const char *arg, unsigned *sepp)
172{
173 int arglen = strlen(arg);
174 const char *sep = strchr(t, '=');
175 sep = sep ? sep : strchr(t, ' ');
176 if (sep && (!sep[1] || sep[1] == '%')) {
177 int tlen = sep - t;
178 if (sep[0] == '=')
179 tlen ++;
180 if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
181 *sepp = sep - t;
182 return 1;
183 }
184 }
185 if (strcmp(t, arg) == 0) {
186 *sepp = 0;
187 return 1;
188 }
189 return 0;
190}
191
192static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
193 const char *arg, unsigned *sepp)
194{
195 for (; opt && opt->templ; opt++)
196 if (match_template(opt->templ, arg, sepp))
197 return opt;
198 return NULL;
199}
200
201int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
202{
203 unsigned dummy;
204 return find_opt(opts, opt, &dummy) ? 1 : 0;
205}
206
207static int process_opt_param(void *var, const char *format, const char *param,
208 const char *arg)
209{
210 assert(format[0] == '%');
211 if (format[1] == 's') {
212 char **s = var;
213 char *copy = strdup(param);
214 if (!copy)
215 return alloc_failed();
216
217 free(*s);
218 *s = copy;
219 } else {
220 if (sscanf(param, format, var) != 1) {
221 fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg);
222 return -1;
223 }
224 }
225 return 0;
226}
227
228static int process_opt(struct fuse_opt_context *ctx,
229 const struct fuse_opt *opt, unsigned sep,
230 const char *arg, int iso)
231{
232 if (opt->offset == -1U) {
233 if (call_proc(ctx, arg, opt->value, iso) == -1)
234 return -1;
235 } else {
236 void *var = (char *)ctx->data + opt->offset;
237 if (sep && opt->templ[sep + 1]) {
238 const char *param = arg + sep;
239 if (opt->templ[sep] == '=')
240 param ++;
241 if (process_opt_param(var, opt->templ + sep + 1,
242 param, arg) == -1)
243 return -1;
244 } else
245 *(int *)var = opt->value;
246 }
247 return 0;
248}
249
250static int process_opt_sep_arg(struct fuse_opt_context *ctx,
251 const struct fuse_opt *opt, unsigned sep,
252 const char *arg, int iso)
253{
254 int res;
255 char *newarg;
256 char *param;
257
258 if (next_arg(ctx, arg) == -1)
259 return -1;
260
261 param = ctx->argv[ctx->argctr];
262 newarg = malloc(sep + strlen(param) + 1);
263 if (!newarg)
264 return alloc_failed();
265
266 memcpy(newarg, arg, sep);
267 strcpy(newarg + sep, param);
268 res = process_opt(ctx, opt, sep, newarg, iso);
269 free(newarg);
270
271 return res;
272}
273
274static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
275{
276 unsigned sep;
277 const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
278 if (opt) {
279 for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
280 int res;
281 if (sep && opt->templ[sep] == ' ' && !arg[sep])
282 res = process_opt_sep_arg(ctx, opt, sep, arg,
283 iso);
284 else
285 res = process_opt(ctx, opt, sep, arg, iso);
286 if (res == -1)
287 return -1;
288 }
289 return 0;
290 } else
291 return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
292}
293
294static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
295{
296 char *s = opts;
297 char *d = s;
298 int end = 0;
299
300 while (!end) {
301 if (*s == '\0')
302 end = 1;
303 if (*s == ',' || end) {
304 int res;
305
306 *d = '\0';
307 res = process_gopt(ctx, opts, 1);
308 if (res == -1)
309 return -1;
310 d = opts;
311 } else {
312 if (s[0] == '\\' && s[1] != '\0') {
313 s++;
314 if (s[0] >= '0' && s[0] <= '3' &&
315 s[1] >= '0' && s[1] <= '7' &&
316 s[2] >= '0' && s[2] <= '7') {
317 *d++ = (s[0] - '0') * 0100 +
318 (s[1] - '0') * 0010 +
319 (s[2] - '0');
320 s += 2;
321 } else {
322 *d++ = *s;
323 }
324 } else {
325 *d++ = *s;
326 }
327 }
328 s++;
329 }
330
331 return 0;
332}
333
334static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
335{
336 int res;
337 char *copy = strdup(opts);
338
339 if (!copy) {
340 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
341 return -1;
342 }
343 res = process_real_option_group(ctx, copy);
344 free(copy);
345 return res;
346}
347
348static int process_one(struct fuse_opt_context *ctx, const char *arg)
349{
350 if (ctx->nonopt || arg[0] != '-')
351 return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
352 else if (arg[1] == 'o') {
353 if (arg[2])
354 return process_option_group(ctx, arg + 2);
355 else {
356 if (next_arg(ctx, arg) == -1)
357 return -1;
358
359 return process_option_group(ctx,
360 ctx->argv[ctx->argctr]);
361 }
362 } else if (arg[1] == '-' && !arg[2]) {
363 if (add_arg(ctx, arg) == -1)
364 return -1;
365 ctx->nonopt = ctx->outargs.argc;
366 return 0;
367 } else
368 return process_gopt(ctx, arg, 0);
369}
370
371static int opt_parse(struct fuse_opt_context *ctx)
372{
373 if (ctx->argc) {
374 if (add_arg(ctx, ctx->argv[0]) == -1)
375 return -1;
376 }
377
378 for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
379 if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
380 return -1;
381
382 if (ctx->opts) {
383 if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
384 fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
385 return -1;
386 }
387
388 /* If option separator ("--") is the last argument, remove it */
389 if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
390 strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
391 free(ctx->outargs.argv[ctx->outargs.argc - 1]);
392 ctx->outargs.argv[--ctx->outargs.argc] = NULL;
393 }
394
395 return 0;
396}
397
398int fuse_opt_parse(struct fuse_args *args, void *data,
399 const struct fuse_opt opts[], fuse_opt_proc_t proc)
400{
401 int res;
402 struct fuse_opt_context ctx = {
403 .data = data,
404 .opt = opts,
405 .proc = proc,
406 };
407
408 if (!args || !args->argv || !args->argc)
409 return 0;
410
411 ctx.argc = args->argc;
412 ctx.argv = args->argv;
413
414 res = opt_parse(&ctx);
415 if (res != -1) {
416 struct fuse_args tmp = *args;
417 *args = ctx.outargs;
418 ctx.outargs = tmp;
419 }
420 free(ctx.opts);
421 fuse_opt_free_args(&ctx.outargs);
422 return res;
423}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition fuse_opt.h:180
#define FUSE_OPT_KEY_OPT
Definition fuse_opt.h:129
#define FUSE_OPT_KEY_NONOPT
Definition fuse_opt.h:137
#define FUSE_OPT_KEY_DISCARD
Definition fuse_opt.h:153
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition fuse_opt.c:95
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
int allocated
Definition fuse_opt.h:117
char ** argv
Definition fuse_opt.h:114
unsigned long offset
Definition fuse_opt.h:85
const char * templ
Definition fuse_opt.h:79
int value
Definition fuse_opt.h:91
fuse-3.18.2/doc/html/lib_2fuse__opt_8c_source.html0000644000175000017500000023523615156613443021004 0ustar berndbernd libfuse: lib/fuse_opt.c Source File
libfuse
fuse_opt.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of option parsing routines (dealing with `struct
6 fuse_args`).
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#include "fuse_config.h"
13#include "fuse_i.h"
14#include "fuse_opt.h"
15#include "fuse_misc.h"
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <assert.h>
21
22struct fuse_opt_context {
23 void *data;
24 const struct fuse_opt *opt;
25 fuse_opt_proc_t proc;
26 int argctr;
27 int argc;
28 char **argv;
29 struct fuse_args outargs;
30 char *opts;
31 int nonopt;
32};
33
34void fuse_opt_free_args(struct fuse_args *args)
35{
36 if (args) {
37 if (args->argv && args->allocated) {
38 int i;
39 for (i = 0; i < args->argc; i++)
40 free(args->argv[i]);
41 free(args->argv);
42 }
43 args->argc = 0;
44 args->argv = NULL;
45 args->allocated = 0;
46 }
47}
48
49static int alloc_failed(void)
50{
51 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
52 return -1;
53}
54
55int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
56{
57 char **newargv;
58 char *newarg;
59
60 assert(!args->argv || args->allocated);
61
62 newarg = strdup(arg);
63 if (!newarg)
64 return alloc_failed();
65
66 newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
67 if (!newargv) {
68 free(newarg);
69 return alloc_failed();
70 }
71
72 args->argv = newargv;
73 args->allocated = 1;
74 args->argv[args->argc++] = newarg;
75 args->argv[args->argc] = NULL;
76 return 0;
77}
78
79static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
80 const char *arg)
81{
82 assert(pos <= args->argc);
83 if (fuse_opt_add_arg(args, arg) == -1)
84 return -1;
85
86 if (pos != args->argc - 1) {
87 char *newarg = args->argv[args->argc - 1];
88 memmove(&args->argv[pos + 1], &args->argv[pos],
89 sizeof(char *) * (args->argc - pos - 1));
90 args->argv[pos] = newarg;
91 }
92 return 0;
93}
94
95int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
96{
97 return fuse_opt_insert_arg_common(args, pos, arg);
98}
99
100static int next_arg(struct fuse_opt_context *ctx, const char *opt)
101{
102 if (ctx->argctr + 1 >= ctx->argc) {
103 fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt);
104 return -1;
105 }
106 ctx->argctr++;
107 return 0;
108}
109
110static int add_arg(struct fuse_opt_context *ctx, const char *arg)
111{
112 return fuse_opt_add_arg(&ctx->outargs, arg);
113}
114
115static int add_opt_common(char **opts, const char *opt, int esc)
116{
117 unsigned oldlen = *opts ? strlen(*opts) : 0;
118 char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
119
120 if (!d)
121 return alloc_failed();
122
123 *opts = d;
124 if (oldlen) {
125 d += oldlen;
126 *d++ = ',';
127 }
128
129 for (; *opt; opt++) {
130 if (esc && (*opt == ',' || *opt == '\\'))
131 *d++ = '\\';
132 *d++ = *opt;
133 }
134 *d = '\0';
135
136 return 0;
137}
138
139int fuse_opt_add_opt(char **opts, const char *opt)
140{
141 return add_opt_common(opts, opt, 0);
142}
143
144int fuse_opt_add_opt_escaped(char **opts, const char *opt)
145{
146 return add_opt_common(opts, opt, 1);
147}
148
149static int add_opt(struct fuse_opt_context *ctx, const char *opt)
150{
151 return add_opt_common(&ctx->opts, opt, 1);
152}
153
154static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
155 int iso)
156{
157 if (key == FUSE_OPT_KEY_DISCARD)
158 return 0;
159
160 if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
161 int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
162 if (res == -1 || !res)
163 return res;
164 }
165 if (iso)
166 return add_opt(ctx, arg);
167 else
168 return add_arg(ctx, arg);
169}
170
171static int match_template(const char *t, const char *arg, unsigned *sepp)
172{
173 int arglen = strlen(arg);
174 const char *sep = strchr(t, '=');
175 sep = sep ? sep : strchr(t, ' ');
176 if (sep && (!sep[1] || sep[1] == '%')) {
177 int tlen = sep - t;
178 if (sep[0] == '=')
179 tlen ++;
180 if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
181 *sepp = sep - t;
182 return 1;
183 }
184 }
185 if (strcmp(t, arg) == 0) {
186 *sepp = 0;
187 return 1;
188 }
189 return 0;
190}
191
192static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
193 const char *arg, unsigned *sepp)
194{
195 for (; opt && opt->templ; opt++)
196 if (match_template(opt->templ, arg, sepp))
197 return opt;
198 return NULL;
199}
200
201int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
202{
203 unsigned dummy;
204 return find_opt(opts, opt, &dummy) ? 1 : 0;
205}
206
207static int process_opt_param(void *var, const char *format, const char *param,
208 const char *arg)
209{
210 assert(format[0] == '%');
211 if (format[1] == 's') {
212 char **s = var;
213 char *copy = strdup(param);
214 if (!copy)
215 return alloc_failed();
216
217 free(*s);
218 *s = copy;
219 } else {
220 if (sscanf(param, format, var) != 1) {
221 fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg);
222 return -1;
223 }
224 }
225 return 0;
226}
227
228static int process_opt(struct fuse_opt_context *ctx,
229 const struct fuse_opt *opt, unsigned sep,
230 const char *arg, int iso)
231{
232 if (opt->offset == -1U) {
233 if (call_proc(ctx, arg, opt->value, iso) == -1)
234 return -1;
235 } else {
236 void *var = (char *)ctx->data + opt->offset;
237 if (sep && opt->templ[sep + 1]) {
238 const char *param = arg + sep;
239 if (opt->templ[sep] == '=')
240 param ++;
241 if (process_opt_param(var, opt->templ + sep + 1,
242 param, arg) == -1)
243 return -1;
244 } else
245 *(int *)var = opt->value;
246 }
247 return 0;
248}
249
250static int process_opt_sep_arg(struct fuse_opt_context *ctx,
251 const struct fuse_opt *opt, unsigned sep,
252 const char *arg, int iso)
253{
254 int res;
255 char *newarg;
256 char *param;
257
258 if (next_arg(ctx, arg) == -1)
259 return -1;
260
261 param = ctx->argv[ctx->argctr];
262 newarg = malloc(sep + strlen(param) + 1);
263 if (!newarg)
264 return alloc_failed();
265
266 memcpy(newarg, arg, sep);
267 strcpy(newarg + sep, param);
268 res = process_opt(ctx, opt, sep, newarg, iso);
269 free(newarg);
270
271 return res;
272}
273
274static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
275{
276 unsigned sep;
277 const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
278 if (opt) {
279 for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
280 int res;
281 if (sep && opt->templ[sep] == ' ' && !arg[sep])
282 res = process_opt_sep_arg(ctx, opt, sep, arg,
283 iso);
284 else
285 res = process_opt(ctx, opt, sep, arg, iso);
286 if (res == -1)
287 return -1;
288 }
289 return 0;
290 } else
291 return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
292}
293
294static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
295{
296 char *s = opts;
297 char *d = s;
298 int end = 0;
299
300 while (!end) {
301 if (*s == '\0')
302 end = 1;
303 if (*s == ',' || end) {
304 int res;
305
306 *d = '\0';
307 res = process_gopt(ctx, opts, 1);
308 if (res == -1)
309 return -1;
310 d = opts;
311 } else {
312 if (s[0] == '\\' && s[1] != '\0') {
313 s++;
314 if (s[0] >= '0' && s[0] <= '3' &&
315 s[1] >= '0' && s[1] <= '7' &&
316 s[2] >= '0' && s[2] <= '7') {
317 *d++ = (s[0] - '0') * 0100 +
318 (s[1] - '0') * 0010 +
319 (s[2] - '0');
320 s += 2;
321 } else {
322 *d++ = *s;
323 }
324 } else {
325 *d++ = *s;
326 }
327 }
328 s++;
329 }
330
331 return 0;
332}
333
334static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
335{
336 int res;
337 char *copy = strdup(opts);
338
339 if (!copy) {
340 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
341 return -1;
342 }
343 res = process_real_option_group(ctx, copy);
344 free(copy);
345 return res;
346}
347
348static int process_one(struct fuse_opt_context *ctx, const char *arg)
349{
350 if (ctx->nonopt || arg[0] != '-')
351 return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
352 else if (arg[1] == 'o') {
353 if (arg[2])
354 return process_option_group(ctx, arg + 2);
355 else {
356 if (next_arg(ctx, arg) == -1)
357 return -1;
358
359 return process_option_group(ctx,
360 ctx->argv[ctx->argctr]);
361 }
362 } else if (arg[1] == '-' && !arg[2]) {
363 if (add_arg(ctx, arg) == -1)
364 return -1;
365 ctx->nonopt = ctx->outargs.argc;
366 return 0;
367 } else
368 return process_gopt(ctx, arg, 0);
369}
370
371static int opt_parse(struct fuse_opt_context *ctx)
372{
373 if (ctx->argc) {
374 if (add_arg(ctx, ctx->argv[0]) == -1)
375 return -1;
376 }
377
378 for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
379 if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
380 return -1;
381
382 if (ctx->opts) {
383 if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
384 fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
385 return -1;
386 }
387
388 /* If option separator ("--") is the last argument, remove it */
389 if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
390 strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
391 free(ctx->outargs.argv[ctx->outargs.argc - 1]);
392 ctx->outargs.argv[--ctx->outargs.argc] = NULL;
393 }
394
395 return 0;
396}
397
398int fuse_opt_parse(struct fuse_args *args, void *data,
399 const struct fuse_opt opts[], fuse_opt_proc_t proc)
400{
401 int res;
402 struct fuse_opt_context ctx = {
403 .data = data,
404 .opt = opts,
405 .proc = proc,
406 };
407
408 if (!args || !args->argv || !args->argc)
409 return 0;
410
411 ctx.argc = args->argc;
412 ctx.argv = args->argv;
413
414 res = opt_parse(&ctx);
415 if (res != -1) {
416 struct fuse_args tmp = *args;
417 *args = ctx.outargs;
418 ctx.outargs = tmp;
419 }
420 free(ctx.opts);
421 fuse_opt_free_args(&ctx.outargs);
422 return res;
423}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition fuse_opt.h:180
#define FUSE_OPT_KEY_OPT
Definition fuse_opt.h:129
#define FUSE_OPT_KEY_NONOPT
Definition fuse_opt.h:137
#define FUSE_OPT_KEY_DISCARD
Definition fuse_opt.h:153
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition fuse_opt.c:95
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
int allocated
Definition fuse_opt.h:117
char ** argv
Definition fuse_opt.h:114
unsigned long offset
Definition fuse_opt.h:85
const char * templ
Definition fuse_opt.h:79
int value
Definition fuse_opt.h:91
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2fuse__signals_8c_source.html0000644000175000017500000010570315156613443024013 0ustar berndbernd libfuse: fuse-3.17.4/lib/fuse_signals.c Source File
libfuse
fuse_signals.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Utility functions for setting signal handlers.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB
9*/
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_i.h"
14
15#include <stdio.h>
16#include <string.h>
17#include <signal.h>
18#include <stdlib.h>
19#include <errno.h>
20
21#ifdef HAVE_BACKTRACE
22#include <execinfo.h>
23#endif
24
25static int teardown_sigs[] = { SIGHUP, SIGINT, SIGTERM };
26static int ignore_sigs[] = { SIGPIPE};
27static int fail_sigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV };
28static struct fuse_session *fuse_instance;
29
30#ifdef HAVE_BACKTRACE
31#define BT_STACK_SZ (1024 * 1024)
32static void *backtrace_buffer[BT_STACK_SZ];
33#endif
34
35static void dump_stack(void)
36{
37#ifdef HAVE_BACKTRACE
38 char **strings;
39
40 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
41 strings = backtrace_symbols(backtrace_buffer, nptrs);
42
43 if (strings == NULL) {
44 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
45 strerror(errno));
46 return;
47 }
48
49 for (int idx = 0; idx < nptrs; idx++)
50 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
51
52 free(strings);
53#endif
54}
55
56static void exit_handler(int sig)
57{
58 if (fuse_instance == NULL)
59 return;
60
61 fuse_session_exit(fuse_instance);
62
63 if (sig < 0) {
64 fuse_log(FUSE_LOG_ERR,
65 "assertion error: signal value <= 0\n");
66 dump_stack();
67 abort();
68 fuse_instance->error = sig;
69 }
70
71 fuse_instance->error = sig;
72}
73
74static void exit_backtrace(int sig)
75{
76 if (fuse_instance == NULL)
77 return;
78
79 fuse_session_exit(fuse_instance);
80
81 fuse_remove_signal_handlers(fuse_instance);
82 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
83 dump_stack();
84 abort();
85}
86
87
88static void do_nothing(int sig)
89{
90 (void) sig;
91}
92
93static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
94{
95 struct sigaction sa;
96 struct sigaction old_sa;
97
98 memset(&sa, 0, sizeof(struct sigaction));
99 sa.sa_handler = remove ? SIG_DFL : handler;
100 sigemptyset(&(sa.sa_mask));
101 sa.sa_flags = 0;
102
103 if (sigaction(sig, NULL, &old_sa) == -1) {
104 perror("fuse: cannot get old signal handler");
105 return -1;
106 }
107
108 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
109 sigaction(sig, &sa, NULL) == -1) {
110 perror("fuse: cannot set signal handler");
111 return -1;
112 }
113 return 0;
114}
115
116static int _fuse_set_signal_handlers(int signals[], int nr_signals,
117 void (*handler)(int))
118{
119 for (int idx = 0; idx < nr_signals; idx++) {
120 int signal = signals[idx];
121
122 /*
123 * If we used SIG_IGN instead of the do_nothing function,
124 * then we would be unable to tell if we set SIG_IGN (and
125 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
126 * or if it was already set to SIG_IGN (and should be left
127 * untouched.
128 */
129 if (set_one_signal_handler(signal, handler, 0) == -1) {
130 fuse_log(FUSE_LOG_ERR,
131 "Failed to install signal handler for sig %d\n",
132 signal);
133 return -1;
134 }
135 }
136
137 return 0;
138}
139
140int fuse_set_signal_handlers(struct fuse_session *se)
141{
142 size_t nr_signals;
143 int rc;
144
145 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
146 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
147 if (rc < 0)
148 return rc;
149
150 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
151 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
152 if (rc < 0)
153 return rc;
154
155 /*
156 * needs to be set independently if already set, as some applications
157 * may have multiple sessions and might rely on traditional behavior
158 * that the last session is used.
159 */
160 fuse_instance = se;
161
162 return 0;
163}
164
165int fuse_set_fail_signal_handlers(struct fuse_session *se)
166{
167 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
168
169 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
170 exit_backtrace);
171 if (rc < 0)
172 return rc;
173
174 /* See fuse_set_signal_handlers, why set unconditionally */
175 fuse_instance = se;
176
177 return 0;
178}
179
180static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
181 void (*handler)(int))
182{
183 for (int idx = 0; idx < nr_signals; idx++)
184 set_one_signal_handler(signals[idx], handler, 1);
185}
186
187void fuse_remove_signal_handlers(struct fuse_session *se)
188{
189 size_t nr_signals;
190
191 if (fuse_instance != se)
192 fuse_log(FUSE_LOG_ERR,
193 "fuse: fuse_remove_signal_handlers: unknown session\n");
194 else
195 fuse_instance = NULL;
196
197 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
198 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
199
200 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
201 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
202
203 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
204 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
205}
int fuse_set_fail_signal_handlers(struct fuse_session *se)
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__signals_8c_source.html0000644000175000017500000011025315156613443024005 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_signals.c Source File
libfuse
fuse_signals.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Utility functions for setting signal handlers.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt
9*/
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_i.h"
14
15#include <stdio.h>
16#include <string.h>
17#include <signal.h>
18#include <stdlib.h>
19#include <errno.h>
20
21#ifdef HAVE_BACKTRACE
22#include <execinfo.h>
23#endif
24
25/*
26 * Must not handle SIGCANCEL, as that is used to wake up threads from
27 * syscalls reading requests from /dev/fuse
28 */
29static int teardown_sigs[] = { SIGHUP, SIGINT, SIGTERM };
30
31static int ignore_sigs[] = { SIGPIPE};
32static int fail_sigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV };
33static struct fuse_session *fuse_instance;
34
35#ifdef HAVE_BACKTRACE
36#define BT_STACK_SZ (1024 * 1024)
37static void *backtrace_buffer[BT_STACK_SZ];
38#endif
39
40static void dump_stack(void)
41{
42#ifdef HAVE_BACKTRACE
43 char **strings;
44
45 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
46 strings = backtrace_symbols(backtrace_buffer, nptrs);
47
48 if (strings == NULL) {
49 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
50 strerror(errno));
51 return;
52 }
53
54 for (int idx = 0; idx < nptrs; idx++)
55 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
56
57 free(strings);
58#endif
59}
60
61static void exit_handler(int sig)
62{
63 if (fuse_instance == NULL) {
64 fuse_log(FUSE_LOG_ERR, "fuse_instance is NULL\n");
65 return;
66 }
67
68 if (fuse_instance->debug)
69 fuse_log(FUSE_LOG_ERR, "exit_handler called with sig %d\n",
70 sig);
71
72 fuse_session_exit(fuse_instance);
73
74 if (sig < 0) {
75 fuse_log(FUSE_LOG_ERR,
76 "assertion error: signal value <= 0\n");
77 dump_stack();
78 abort();
79 fuse_instance->error = sig;
80 }
81
82 fuse_instance->error = sig;
83}
84
85static void exit_backtrace(int sig)
86{
87 if (fuse_instance == NULL)
88 return;
89
90 fuse_session_exit(fuse_instance);
91
92 fuse_remove_signal_handlers(fuse_instance);
93 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
94 dump_stack();
95 abort();
96}
97
98
99static void do_nothing(int sig)
100{
101 (void) sig;
102}
103
104static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
105{
106 struct sigaction sa;
107 struct sigaction old_sa;
108
109 memset(&sa, 0, sizeof(struct sigaction));
110 sa.sa_handler = remove ? SIG_DFL : handler;
111 sigemptyset(&(sa.sa_mask));
112 sa.sa_flags = 0;
113
114 if (sigaction(sig, NULL, &old_sa) == -1) {
115 perror("fuse: cannot get old signal handler");
116 return -1;
117 }
118
119 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
120 sigaction(sig, &sa, NULL) == -1) {
121 perror("fuse: cannot set signal handler");
122 return -1;
123 }
124 return 0;
125}
126
127static int _fuse_set_signal_handlers(int signals[], int nr_signals,
128 void (*handler)(int))
129{
130 for (int idx = 0; idx < nr_signals; idx++) {
131 int signal = signals[idx];
132
133 /*
134 * If we used SIG_IGN instead of the do_nothing function,
135 * then we would be unable to tell if we set SIG_IGN (and
136 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
137 * or if it was already set to SIG_IGN (and should be left
138 * untouched.
139 */
140 if (set_one_signal_handler(signal, handler, 0) == -1) {
141 fuse_log(FUSE_LOG_ERR,
142 "Failed to install signal handler for sig %d\n",
143 signal);
144 return -1;
145 }
146 }
147
148 return 0;
149}
150
151int fuse_set_signal_handlers(struct fuse_session *se)
152{
153 size_t nr_signals;
154 int rc;
155
156 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
157 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
158 if (rc < 0)
159 return rc;
160
161 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
162 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
163 if (rc < 0)
164 return rc;
165
166 /*
167 * needs to be set independently if already set, as some applications
168 * may have multiple sessions and might rely on traditional behavior
169 * that the last session is used.
170 */
171 fuse_instance = se;
172
173 return 0;
174}
175
176int fuse_set_fail_signal_handlers(struct fuse_session *se)
177{
178 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
179
180 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
181 exit_backtrace);
182 if (rc < 0)
183 return rc;
184
185 /* See fuse_set_signal_handlers, why set unconditionally */
186 fuse_instance = se;
187
188 return 0;
189}
190
191static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
192 void (*handler)(int))
193{
194 for (int idx = 0; idx < nr_signals; idx++)
195 set_one_signal_handler(signals[idx], handler, 1);
196}
197
198void fuse_remove_signal_handlers(struct fuse_session *se)
199{
200 size_t nr_signals;
201
202 if (fuse_instance != se)
203 fuse_log(FUSE_LOG_ERR,
204 "fuse: fuse_remove_signal_handlers: unknown session\n");
205 else
206 fuse_instance = NULL;
207
208 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
209 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
210
211 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
212 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
213
214 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
215 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
216}
int fuse_set_fail_signal_handlers(struct fuse_session *se)
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
fuse-3.18.2/doc/html/lib_2fuse__signals_8c_source.html0000644000175000017500000011007015156613443021626 0ustar berndbernd libfuse: lib/fuse_signals.c Source File
libfuse
fuse_signals.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Utility functions for setting signal handlers.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt
9*/
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_i.h"
14
15#include <stdio.h>
16#include <string.h>
17#include <signal.h>
18#include <stdlib.h>
19#include <errno.h>
20
21#ifdef HAVE_BACKTRACE
22#include <execinfo.h>
23#endif
24
25/*
26 * Must not handle SIGCANCEL, as that is used to wake up threads from
27 * syscalls reading requests from /dev/fuse
28 */
29static int teardown_sigs[] = { SIGHUP, SIGINT, SIGTERM };
30
31static int ignore_sigs[] = { SIGPIPE};
32static int fail_sigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV };
33static struct fuse_session *fuse_instance;
34
35#ifdef HAVE_BACKTRACE
36#define BT_STACK_SZ (1024 * 1024)
37static void *backtrace_buffer[BT_STACK_SZ];
38#endif
39
40static void dump_stack(void)
41{
42#ifdef HAVE_BACKTRACE
43 char **strings;
44
45 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
46 strings = backtrace_symbols(backtrace_buffer, nptrs);
47
48 if (strings == NULL) {
49 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
50 strerror(errno));
51 return;
52 }
53
54 for (int idx = 0; idx < nptrs; idx++)
55 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
56
57 free(strings);
58#endif
59}
60
61static void exit_handler(int sig)
62{
63 if (fuse_instance == NULL) {
64 fuse_log(FUSE_LOG_ERR, "fuse_instance is NULL\n");
65 return;
66 }
67
68 if (fuse_instance->debug)
69 fuse_log(FUSE_LOG_ERR, "exit_handler called with sig %d\n",
70 sig);
71
72 fuse_session_exit(fuse_instance);
73
74 if (sig < 0) {
75 fuse_log(FUSE_LOG_ERR,
76 "assertion error: signal value <= 0\n");
77 dump_stack();
78 abort();
79 fuse_instance->error = sig;
80 }
81
82 fuse_instance->error = sig;
83}
84
85static void exit_backtrace(int sig)
86{
87 if (fuse_instance == NULL)
88 return;
89
90 fuse_session_exit(fuse_instance);
91
92 fuse_remove_signal_handlers(fuse_instance);
93 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
94 dump_stack();
95 abort();
96}
97
98
99static void do_nothing(int sig)
100{
101 (void) sig;
102}
103
104static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
105{
106 struct sigaction sa;
107 struct sigaction old_sa;
108
109 memset(&sa, 0, sizeof(struct sigaction));
110 sa.sa_handler = remove ? SIG_DFL : handler;
111 sigemptyset(&(sa.sa_mask));
112 sa.sa_flags = 0;
113
114 if (sigaction(sig, NULL, &old_sa) == -1) {
115 perror("fuse: cannot get old signal handler");
116 return -1;
117 }
118
119 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
120 sigaction(sig, &sa, NULL) == -1) {
121 perror("fuse: cannot set signal handler");
122 return -1;
123 }
124 return 0;
125}
126
127static int _fuse_set_signal_handlers(int signals[], int nr_signals,
128 void (*handler)(int))
129{
130 for (int idx = 0; idx < nr_signals; idx++) {
131 int signal = signals[idx];
132
133 /*
134 * If we used SIG_IGN instead of the do_nothing function,
135 * then we would be unable to tell if we set SIG_IGN (and
136 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
137 * or if it was already set to SIG_IGN (and should be left
138 * untouched.
139 */
140 if (set_one_signal_handler(signal, handler, 0) == -1) {
141 fuse_log(FUSE_LOG_ERR,
142 "Failed to install signal handler for sig %d\n",
143 signal);
144 return -1;
145 }
146 }
147
148 return 0;
149}
150
151int fuse_set_signal_handlers(struct fuse_session *se)
152{
153 size_t nr_signals;
154 int rc;
155
156 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
157 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
158 if (rc < 0)
159 return rc;
160
161 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
162 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
163 if (rc < 0)
164 return rc;
165
166 /*
167 * needs to be set independently if already set, as some applications
168 * may have multiple sessions and might rely on traditional behavior
169 * that the last session is used.
170 */
171 fuse_instance = se;
172
173 return 0;
174}
175
176int fuse_set_fail_signal_handlers(struct fuse_session *se)
177{
178 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
179
180 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
181 exit_backtrace);
182 if (rc < 0)
183 return rc;
184
185 /* See fuse_set_signal_handlers, why set unconditionally */
186 fuse_instance = se;
187
188 return 0;
189}
190
191static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
192 void (*handler)(int))
193{
194 for (int idx = 0; idx < nr_signals; idx++)
195 set_one_signal_handler(signals[idx], handler, 1);
196}
197
198void fuse_remove_signal_handlers(struct fuse_session *se)
199{
200 size_t nr_signals;
201
202 if (fuse_instance != se)
203 fuse_log(FUSE_LOG_ERR,
204 "fuse: fuse_remove_signal_handlers: unknown session\n");
205 else
206 fuse_instance = NULL;
207
208 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
209 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
210
211 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
212 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
213
214 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
215 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
216}
int fuse_set_fail_signal_handlers(struct fuse_session *se)
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__uring_8c_source.html0000644000175000017500000043622115156613443023477 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_uring.c Source File
libfuse
fuse_uring.c
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2025 Bernd Schubert <bschubert@ddn.com>
4 *
5 * Implementation of (most of) FUSE-over-io-uring.
6 *
7 * This program can be distributed under the terms of the GNU LGPLv2.
8 * See the file LGPL2.txt
9 */
10
11#define _GNU_SOURCE
12
13#include "fuse_i.h"
14#include "fuse_kernel.h"
15#include "fuse_uring_i.h"
16
17#include <stdlib.h>
18#include <liburing.h>
19#include <sys/sysinfo.h>
20#include <stdint.h>
21#include <inttypes.h>
22#include <stdbool.h>
23#include <string.h>
24#include <unistd.h>
25#include <numa.h>
26#include <pthread.h>
27#include <stdio.h>
28#include <linux/sched.h>
29#include <poll.h>
30#include <sys/eventfd.h>
31
32/* Size of command data area in SQE when IORING_SETUP_SQE128 is used */
33#define FUSE_URING_MAX_SQE128_CMD_DATA 80
34
35struct fuse_ring_ent {
36 struct fuse_ring_queue *ring_queue; /* back pointer */
37 struct fuse_req req;
38
39 struct fuse_uring_req_header *req_header;
40 void *op_payload;
41 size_t req_payload_sz;
42
43 /* commit id of a fuse request */
44 uint64_t req_commit_id;
45
46 enum fuse_uring_cmd last_cmd;
47
48 /* header and payload */
49 struct iovec iov[2];
50};
51
52struct fuse_ring_queue {
53 /* back pointer */
54 struct fuse_ring_pool *ring_pool;
55 int qid;
56 int numa_node;
57 pthread_t tid;
58 int eventfd;
59 size_t req_header_sz;
60 struct io_uring ring;
61
62 pthread_mutex_t ring_lock;
63 bool cqe_processing;
64
65 /* size depends on queue depth */
66 struct fuse_ring_ent ent[];
67};
68
73 struct fuse_session *se;
74
75 /* number of queues */
76 size_t nr_queues;
77
78 /* number of per queue entries */
79 size_t queue_depth;
80
81 /* max payload size for fuse requests*/
82 size_t max_req_payload_sz;
83
84 /* size of a single queue */
85 size_t queue_mem_size;
86
87 unsigned int started_threads;
88 unsigned int failed_threads;
89
90 /* Avoid sending queue entries before FUSE_INIT reply*/
91 sem_t init_sem;
92
93 pthread_cond_t thread_start_cond;
94 pthread_mutex_t thread_start_mutex;
95
96 /* pointer to the first queue */
97 struct fuse_ring_queue *queues;
98};
99
100static size_t
101fuse_ring_queue_size(const size_t q_depth)
102{
103 const size_t req_size = sizeof(struct fuse_ring_ent) * q_depth;
104
105 return sizeof(struct fuse_ring_queue) + req_size;
106}
107
108static struct fuse_ring_queue *
109fuse_uring_get_queue(struct fuse_ring_pool *fuse_ring, int qid)
110{
111 void *ptr =
112 ((char *)fuse_ring->queues) + (qid * fuse_ring->queue_mem_size);
113
114 return ptr;
115}
116
120static void *fuse_uring_get_sqe_cmd(struct io_uring_sqe *sqe)
121{
122 return (void *)&sqe->cmd[0];
123}
124
125static void fuse_uring_sqe_set_req_data(struct fuse_uring_cmd_req *req,
126 const unsigned int qid,
127 const uint64_t commit_id)
128{
129 req->qid = qid;
130 req->commit_id = commit_id;
131 req->flags = 0;
132}
133
134static void
135fuse_uring_sqe_prepare(struct io_uring_sqe *sqe, struct fuse_ring_ent *req,
136 __u32 cmd_op)
137{
138 /* These fields should be written once, never change */
139 sqe->opcode = IORING_OP_URING_CMD;
140
141 /*
142 * IOSQE_FIXED_FILE: fd is the index to the fd *array*
143 * given to io_uring_register_files()
144 */
145 sqe->flags = IOSQE_FIXED_FILE;
146 sqe->fd = 0;
147
148 sqe->rw_flags = 0;
149 sqe->ioprio = 0;
150 sqe->off = 0;
151
152 io_uring_sqe_set_data(sqe, req);
153
154 sqe->cmd_op = cmd_op;
155 sqe->__pad1 = 0;
156}
157
158static int fuse_uring_commit_sqe(struct fuse_ring_pool *ring_pool,
159 struct fuse_ring_queue *queue,
160 struct fuse_ring_ent *ring_ent)
161{
162 bool locked = false;
163 struct fuse_session *se = ring_pool->se;
164 struct fuse_uring_req_header *rrh = ring_ent->req_header;
165 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
166 struct fuse_uring_ent_in_out *ent_in_out =
167 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
168 struct io_uring_sqe *sqe;
169
170 if (pthread_self() != queue->tid) {
171 pthread_mutex_lock(&queue->ring_lock);
172 locked = true;
173 }
174
175 sqe = io_uring_get_sqe(&queue->ring);
176
177 if (sqe == NULL) {
178 /* This is an impossible condition, unless there is a bug.
179 * The kernel sent back an SQEs, which is assigned to a request.
180 * There is no way to get out of SQEs, as the number of
181 * SQEs matches the number tof requests.
182 */
183
184 se->error = -EIO;
185 fuse_log(FUSE_LOG_ERR, "Failed to get a ring SQEs\n");
186
187 return -EIO;
188 }
189
190 ring_ent->last_cmd = FUSE_IO_URING_CMD_COMMIT_AND_FETCH;
191 fuse_uring_sqe_prepare(sqe, ring_ent, ring_ent->last_cmd);
192 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid,
193 ring_ent->req_commit_id);
194
195 if (se->debug) {
196 fuse_log(FUSE_LOG_DEBUG, " unique: %" PRIu64 ", result=%d\n",
197 out->unique, ent_in_out->payload_sz);
198 }
199
200 if (!queue->cqe_processing)
201 io_uring_submit(&queue->ring);
202
203 if (locked)
204 pthread_mutex_unlock(&queue->ring_lock);
205
206 return 0;
207}
208
209int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
210 void **mr)
211{
212 struct fuse_ring_ent *ring_ent;
213
214 /* Not possible without io-uring interface */
215 if (!req->flags.is_uring)
216 return -EINVAL;
217
218 ring_ent = container_of(req, struct fuse_ring_ent, req);
219
220 *payload = ring_ent->op_payload;
221 *payload_sz = ring_ent->req_payload_sz;
222
223 /*
224 * For now unused, but will be used later when the application can
225 * allocate the buffers itself and register them for rdma.
226 */
227 if (mr)
228 *mr = NULL;
229
230 return 0;
231}
232
233int send_reply_uring(fuse_req_t req, int error, const void *arg, size_t argsize)
234{
235 int res;
236 struct fuse_ring_ent *ring_ent =
237 container_of(req, struct fuse_ring_ent, req);
238 struct fuse_uring_req_header *rrh = ring_ent->req_header;
239 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
240 struct fuse_uring_ent_in_out *ent_in_out =
241 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
242
243 struct fuse_ring_queue *queue = ring_ent->ring_queue;
244 struct fuse_ring_pool *ring_pool = queue->ring_pool;
245 size_t max_payload_sz = ring_pool->max_req_payload_sz;
246
247 if (argsize > max_payload_sz) {
248 fuse_log(FUSE_LOG_ERR, "argsize %zu exceeds buffer size %zu",
249 argsize, max_payload_sz);
250 error = -EINVAL;
251 } else if (argsize) {
252 if (arg != ring_ent->op_payload)
253 memcpy(ring_ent->op_payload, arg, argsize);
254 }
255 ent_in_out->payload_sz = argsize;
256
257 out->error = error;
258 out->unique = req->unique;
259
260 res = fuse_uring_commit_sqe(ring_pool, queue, ring_ent);
261
262 fuse_free_req(req);
263
264 return res;
265}
266
267int fuse_reply_data_uring(fuse_req_t req, struct fuse_bufvec *bufv,
268 enum fuse_buf_copy_flags flags)
269{
270 struct fuse_ring_ent *ring_ent =
271 container_of(req, struct fuse_ring_ent, req);
272
273 struct fuse_ring_queue *queue = ring_ent->ring_queue;
274 struct fuse_ring_pool *ring_pool = queue->ring_pool;
275 struct fuse_uring_req_header *rrh = ring_ent->req_header;
276 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
277 struct fuse_uring_ent_in_out *ent_in_out =
278 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
279 size_t max_payload_sz = ring_ent->req_payload_sz;
280 struct fuse_bufvec dest_vec = FUSE_BUFVEC_INIT(max_payload_sz);
281 int res;
282
283 dest_vec.buf[0].mem = ring_ent->op_payload;
284 dest_vec.buf[0].size = max_payload_sz;
285
286 res = fuse_buf_copy(&dest_vec, bufv, flags);
287
288 out->error = res < 0 ? res : 0;
289 out->unique = req->unique;
290
291 ent_in_out->payload_sz = res > 0 ? res : 0;
292
293 res = fuse_uring_commit_sqe(ring_pool, queue, ring_ent);
294
295 fuse_free_req(req);
296
297 return res;
298}
299
303int fuse_send_msg_uring(fuse_req_t req, struct iovec *iov, int count)
304{
305 struct fuse_ring_ent *ring_ent =
306 container_of(req, struct fuse_ring_ent, req);
307
308 struct fuse_ring_queue *queue = ring_ent->ring_queue;
309 struct fuse_ring_pool *ring_pool = queue->ring_pool;
310 struct fuse_uring_req_header *rrh = ring_ent->req_header;
311 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
312 struct fuse_uring_ent_in_out *ent_in_out =
313 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
314 size_t max_buf = ring_pool->max_req_payload_sz;
315 size_t len = 0;
316 int res = 0;
317
318 /* copy iov into the payload, idx=0 is the header section */
319 for (int idx = 1; idx < count; idx++) {
320 struct iovec *cur = &iov[idx];
321
322 if (len + cur->iov_len > max_buf) {
323 fuse_log(FUSE_LOG_ERR,
324 "iov[%d] exceeds buffer size %zu",
325 idx, max_buf);
326 res = -EINVAL; /* Gracefully handle this? */
327 break;
328 }
329
330 memcpy(ring_ent->op_payload + len, cur->iov_base, cur->iov_len);
331 len += cur->iov_len;
332 }
333
334 ent_in_out->payload_sz = len;
335
336 out->error = res;
337 out->unique = req->unique;
338 out->len = len;
339
340 return fuse_uring_commit_sqe(ring_pool, queue, ring_ent);
341}
342
343static int fuse_queue_setup_io_uring(struct io_uring *ring, size_t qid,
344 size_t depth, int fd, int evfd)
345{
346 int rc;
347 struct io_uring_params params = {0};
348 int files[2] = { fd, evfd };
349
350 depth += 1; /* for the eventfd poll SQE */
351
352 params.flags = IORING_SETUP_SQE128;
353
354 /* Avoid cq overflow */
355 params.flags |= IORING_SETUP_CQSIZE;
356 params.cq_entries = depth * 2;
357
358 /* These flags should help to increase performance, but actually
359 * make it a bit slower - reason should get investigated.
360 */
361 if (0) {
362 /* Has the main slow down effect */
363 params.flags |= IORING_SETUP_SINGLE_ISSUER;
364
365 // params.flags |= IORING_SETUP_DEFER_TASKRUN;
366 params.flags |= IORING_SETUP_TASKRUN_FLAG;
367
368 /* Second main effect to make it slower */
369 params.flags |= IORING_SETUP_COOP_TASKRUN;
370 }
371
372 rc = io_uring_queue_init_params(depth, ring, &params);
373 if (rc != 0) {
374 fuse_log(FUSE_LOG_ERR, "Failed to setup qid %zu: %d (%s)\n",
375 qid, rc, strerror(-rc));
376 return rc;
377 }
378
379 rc = io_uring_register_files(ring, files, 1);
380 if (rc != 0) {
381 rc = -errno;
382 fuse_log(FUSE_LOG_ERR,
383 "Failed to register files for ring idx %zu: %s",
384 qid, strerror(errno));
385 return rc;
386 }
387
388 return 0;
389}
390
391static void fuse_session_destruct_uring(struct fuse_ring_pool *fuse_ring)
392{
393 for (size_t qid = 0; qid < fuse_ring->nr_queues; qid++) {
394 struct fuse_ring_queue *queue =
395 fuse_uring_get_queue(fuse_ring, qid);
396
397 if (queue->tid != 0) {
398 uint64_t value = 1ULL;
399 int rc;
400
401 rc = write(queue->eventfd, &value, sizeof(value));
402 if (rc != sizeof(value))
403 fprintf(stderr,
404 "Wrote to eventfd=%d err=%s: rc=%d\n",
405 queue->eventfd, strerror(errno), rc);
406 pthread_cancel(queue->tid);
407 pthread_join(queue->tid, NULL);
408 queue->tid = 0;
409 }
410
411 if (queue->eventfd >= 0) {
412 close(queue->eventfd);
413 queue->eventfd = -1;
414 }
415
416 if (queue->ring.ring_fd != -1)
417 io_uring_queue_exit(&queue->ring);
418
419 for (size_t idx = 0; idx < fuse_ring->queue_depth; idx++) {
420 struct fuse_ring_ent *ent = &queue->ent[idx];
421
422 numa_free(ent->op_payload, ent->req_payload_sz);
423 numa_free(ent->req_header, queue->req_header_sz);
424 }
425
426 pthread_mutex_destroy(&queue->ring_lock);
427 }
428
429 free(fuse_ring->queues);
430 pthread_cond_destroy(&fuse_ring->thread_start_cond);
431 pthread_mutex_destroy(&fuse_ring->thread_start_mutex);
432 free(fuse_ring);
433}
434
435static int fuse_uring_register_ent(struct fuse_ring_queue *queue,
436 struct fuse_ring_ent *ent)
437{
438 struct io_uring_sqe *sqe;
439
440 sqe = io_uring_get_sqe(&queue->ring);
441 if (sqe == NULL) {
442 /*
443 * All SQEs are idle here - no good reason this
444 * could fail
445 */
446 fuse_log(FUSE_LOG_ERR, "Failed to get all ring SQEs");
447 return -EIO;
448 }
449
450 ent->last_cmd = FUSE_IO_URING_CMD_REGISTER;
451 fuse_uring_sqe_prepare(sqe, ent, ent->last_cmd);
452
453 /* only needed for fetch */
454 ent->iov[0].iov_base = ent->req_header;
455 ent->iov[0].iov_len = queue->req_header_sz;
456
457 ent->iov[1].iov_base = ent->op_payload;
458 ent->iov[1].iov_len = ent->req_payload_sz;
459
460 sqe->addr = (uint64_t)(ent->iov);
461 sqe->len = 2;
462
463 /* this is a fetch, kernel does not read commit id */
464 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid, 0);
465
466 return 0;
467
468}
469
470static int fuse_uring_register_queue(struct fuse_ring_queue *queue)
471{
472 struct fuse_ring_pool *ring_pool = queue->ring_pool;
473 unsigned int sq_ready;
474 struct io_uring_sqe *sqe;
475 int res;
476
477 for (size_t idx = 0; idx < ring_pool->queue_depth; idx++) {
478 struct fuse_ring_ent *ent = &queue->ent[idx];
479
480 res = fuse_uring_register_ent(queue, ent);
481 if (res != 0)
482 return res;
483 }
484
485 sq_ready = io_uring_sq_ready(&queue->ring);
486 if (sq_ready != ring_pool->queue_depth) {
487 fuse_log(FUSE_LOG_ERR,
488 "SQE ready mismatch, expected %zu got %u\n",
489 ring_pool->queue_depth, sq_ready);
490 return -EINVAL;
491 }
492
493 /* Poll SQE for the eventfd to wake up on teardown */
494 sqe = io_uring_get_sqe(&queue->ring);
495 if (sqe == NULL) {
496 fuse_log(FUSE_LOG_ERR, "Failed to get eventfd SQE");
497 return -EIO;
498 }
499
500 io_uring_prep_poll_add(sqe, queue->eventfd, POLLIN);
501 io_uring_sqe_set_data(sqe, (void *)(uintptr_t)queue->eventfd);
502
503 /* Only preparation until here, no submission yet */
504
505 return 0;
506}
507
508static struct fuse_ring_pool *fuse_create_ring(struct fuse_session *se)
509{
510 struct fuse_ring_pool *fuse_ring = NULL;
511 const size_t nr_queues = get_nprocs_conf();
512 size_t payload_sz = se->bufsize - FUSE_BUFFER_HEADER_SIZE;
513 size_t queue_sz;
514
515 if (se->debug)
516 fuse_log(FUSE_LOG_DEBUG, "starting io-uring q-depth=%d\n",
517 se->uring.q_depth);
518
519 fuse_ring = calloc(1, sizeof(*fuse_ring));
520 if (fuse_ring == NULL) {
521 fuse_log(FUSE_LOG_ERR, "Allocating the ring failed\n");
522 goto err;
523 }
524
525 queue_sz = fuse_ring_queue_size(se->uring.q_depth);
526 fuse_ring->queues = calloc(1, queue_sz * nr_queues);
527 if (fuse_ring->queues == NULL) {
528 fuse_log(FUSE_LOG_ERR, "Allocating the queues failed\n");
529 goto err;
530 }
531
532 fuse_ring->se = se;
533 fuse_ring->nr_queues = nr_queues;
534 fuse_ring->queue_depth = se->uring.q_depth;
535 fuse_ring->max_req_payload_sz = payload_sz;
536 fuse_ring->queue_mem_size = queue_sz;
537
538 /*
539 * very basic queue initialization, that cannot fail and will
540 * allow easy cleanup if something (like mmap) fails in the middle
541 * below
542 */
543 for (size_t qid = 0; qid < nr_queues; qid++) {
544 struct fuse_ring_queue *queue =
545 fuse_uring_get_queue(fuse_ring, qid);
546
547 queue->ring.ring_fd = -1;
548 queue->numa_node = numa_node_of_cpu(qid);
549 queue->qid = qid;
550 queue->ring_pool = fuse_ring;
551 queue->eventfd = -1;
552 pthread_mutex_init(&queue->ring_lock, NULL);
553 }
554
555 pthread_cond_init(&fuse_ring->thread_start_cond, NULL);
556 pthread_mutex_init(&fuse_ring->thread_start_mutex, NULL);
557 sem_init(&fuse_ring->init_sem, 0, 0);
558
559 return fuse_ring;
560
561err:
562 if (fuse_ring)
563 fuse_session_destruct_uring(fuse_ring);
564
565 return NULL;
566}
567
568static void fuse_uring_resubmit(struct fuse_ring_queue *queue,
569 struct fuse_ring_ent *ent)
570{
571 struct io_uring_sqe *sqe;
572
573 sqe = io_uring_get_sqe(&queue->ring);
574 if (sqe == NULL) {
575 /* This is an impossible condition, unless there is a bug.
576 * The kernel sent back an SQEs, which is assigned to a request.
577 * There is no way to get out of SQEs, as the number of
578 * SQEs matches the number tof requests.
579 */
580
581 queue->ring_pool->se->error = -EIO;
582 fuse_log(FUSE_LOG_ERR, "Failed to get a ring SQEs\n");
583
584 return;
585 }
586
587 fuse_uring_sqe_prepare(sqe, ent, ent->last_cmd);
588
589 switch (ent->last_cmd) {
590 case FUSE_IO_URING_CMD_REGISTER:
591 sqe->addr = (uint64_t)(ent->iov);
592 sqe->len = 2;
593 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe),
594 queue->qid, 0);
595 break;
596 case FUSE_IO_URING_CMD_COMMIT_AND_FETCH:
597 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe),
598 queue->qid, ent->req_commit_id);
599 break;
600 default:
601 fuse_log(FUSE_LOG_ERR, "Unknown command type: %d\n",
602 ent->last_cmd);
603 queue->ring_pool->se->error = -EINVAL;
604 break;
605 }
606
607 /* caller submits */
608}
609
610static void fuse_uring_handle_cqe(struct fuse_ring_queue *queue,
611 struct io_uring_cqe *cqe)
612{
613 struct fuse_ring_ent *ent = io_uring_cqe_get_data(cqe);
614
615 if (!ent) {
616 fuse_log(FUSE_LOG_ERR,
617 "cqe=%p io_uring_cqe_get_data returned NULL\n", cqe);
618 return;
619 }
620
621 struct fuse_req *req = &ent->req;
622 struct fuse_ring_pool *fuse_ring = queue->ring_pool;
623 struct fuse_uring_req_header *rrh = ent->req_header;
624
625 struct fuse_in_header *in = (struct fuse_in_header *)&rrh->in_out;
626 struct fuse_uring_ent_in_out *ent_in_out = &rrh->ring_ent_in_out;
627
628 ent->req_commit_id = ent_in_out->commit_id;
629 if (unlikely(ent->req_commit_id == 0)) {
630 /*
631 * If this happens kernel will not find the response - it will
632 * be stuck forever - better to abort immediately.
633 */
634 fuse_log(FUSE_LOG_ERR, "Received invalid commit_id=0\n");
635 abort();
636 }
637
638 memset(&req->flags, 0, sizeof(req->flags));
639 memset(&req->u, 0, sizeof(req->u));
640 req->flags.is_uring = 1;
641 req->ref_cnt++;
642 req->ch = NULL; /* not needed for uring */
643 req->interrupted = 0;
644 list_init_req(req);
645
646 fuse_session_process_uring_cqe(fuse_ring->se, req, in, &rrh->op_in,
647 ent->op_payload, ent_in_out->payload_sz);
648}
649
650static int fuse_uring_queue_handle_cqes(struct fuse_ring_queue *queue)
651{
652 struct fuse_ring_pool *ring_pool = queue->ring_pool;
653 struct fuse_session *se = ring_pool->se;
654 size_t num_completed = 0;
655 struct io_uring_cqe *cqe;
656 unsigned int head;
657 struct fuse_ring_ent *ent;
658 int ret = 0;
659
660 io_uring_for_each_cqe(&queue->ring, head, cqe) {
661 int err = 0;
662
663 num_completed++;
664
665 err = cqe->res;
666 if (unlikely(err != 0)) {
667 if (err > 0 && ((uintptr_t)io_uring_cqe_get_data(cqe) ==
668 (unsigned int)queue->eventfd)) {
669 /* teardown from eventfd */
670 return -ENOTCONN;
671 }
672
673
674 switch (err) {
675 case -EAGAIN:
676 fallthrough;
677 case -EINTR:
678 ent = io_uring_cqe_get_data(cqe);
679 fuse_uring_resubmit(queue, ent);
680 continue;
681 default:
682 break;
683 }
684
685 /* -ENOTCONN is ok on umount */
686 if (err != -ENOTCONN) {
687 se->error = cqe->res;
688
689 /* return first error */
690 if (ret == 0)
691 ret = err;
692 }
693
694 } else {
695 fuse_uring_handle_cqe(queue, cqe);
696 }
697 }
698
699 if (num_completed)
700 io_uring_cq_advance(&queue->ring, num_completed);
701
702 return ret == 0 ? 0 : num_completed;
703}
704
709static void fuse_uring_set_thread_core(int qid)
710{
711 cpu_set_t mask;
712 int rc;
713
714 CPU_ZERO(&mask);
715 CPU_SET(qid, &mask);
716 rc = sched_setaffinity(0, sizeof(cpu_set_t), &mask);
717 if (rc != 0)
718 fuse_log(FUSE_LOG_ERR, "Failed to bind qid=%d to its core: %s\n",
719 qid, strerror(errno));
720
721 if (0) {
722 const int policy = SCHED_IDLE;
723 const struct sched_param param = {
724 .sched_priority = sched_get_priority_min(policy),
725 };
726
727 /* Set the lowest possible priority, so that the application
728 * submitting requests is not moved away from the current core.
729 */
730 rc = sched_setscheduler(0, policy, &param);
731 if (rc != 0)
732 fuse_log(FUSE_LOG_ERR, "Failed to set scheduler: %s\n",
733 strerror(errno));
734 }
735}
736
737/*
738 * @return negative error code or io-uring file descriptor
739 */
740static int fuse_uring_init_queue(struct fuse_ring_queue *queue)
741{
742 struct fuse_ring_pool *ring = queue->ring_pool;
743 struct fuse_session *se = ring->se;
744 int res;
745 size_t page_sz = sysconf(_SC_PAGESIZE);
746
747 queue->eventfd = eventfd(0, EFD_CLOEXEC);
748 if (queue->eventfd < 0) {
749 res = -errno;
750 fuse_log(FUSE_LOG_ERR,
751 "Failed to create eventfd for qid %d: %s\n",
752 queue->qid, strerror(errno));
753 return res;
754 }
755
756 res = fuse_queue_setup_io_uring(&queue->ring, queue->qid,
757 ring->queue_depth, se->fd,
758 queue->eventfd);
759 if (res != 0) {
760 fuse_log(FUSE_LOG_ERR, "qid=%d io_uring init failed\n",
761 queue->qid);
762 goto err;
763 }
764
765 queue->req_header_sz = ROUND_UP(sizeof(struct fuse_ring_ent),
766 page_sz);
767
768 for (size_t idx = 0; idx < ring->queue_depth; idx++) {
769 struct fuse_ring_ent *ring_ent = &queue->ent[idx];
770 struct fuse_req *req = &ring_ent->req;
771
772 ring_ent->ring_queue = queue;
773
774 /*
775 * Also allocate the header to have it page aligned, which
776 * is a requirement for page pinning
777 */
778 ring_ent->req_header =
779 numa_alloc_local(queue->req_header_sz);
780 ring_ent->req_payload_sz = ring->max_req_payload_sz;
781
782 ring_ent->op_payload =
783 numa_alloc_local(ring_ent->req_payload_sz);
784
785 req->se = se;
786 pthread_mutex_init(&req->lock, NULL);
787 req->flags.is_uring = 1;
788 req->ref_cnt = 1; /* extra ref to avoid destruction */
789 list_init_req(req);
790 }
791
792 res = fuse_uring_register_queue(queue);
793 if (res != 0) {
794 fuse_log(
795 FUSE_LOG_ERR,
796 "Grave fuse-uring error on preparing SQEs, aborting\n");
797 se->error = -EIO;
799 }
800
801 return queue->ring.ring_fd;
802
803err:
804 close(queue->eventfd);
805 return res;
806}
807
808static void *fuse_uring_thread(void *arg)
809{
810 struct fuse_ring_queue *queue = arg;
811 struct fuse_ring_pool *ring_pool = queue->ring_pool;
812 struct fuse_session *se = ring_pool->se;
813 int err;
814 char thread_name[16] = { 0 };
815
816 snprintf(thread_name, 16, "fuse-ring-%d", queue->qid);
817 thread_name[15] = '\0';
818 fuse_set_thread_name(thread_name);
819
820 fuse_uring_set_thread_core(queue->qid);
821
822 err = fuse_uring_init_queue(queue);
823 pthread_mutex_lock(&ring_pool->thread_start_mutex);
824 if (err < 0)
825 ring_pool->failed_threads++;
826 ring_pool->started_threads++;
827 pthread_cond_broadcast(&ring_pool->thread_start_cond);
828 pthread_mutex_unlock(&ring_pool->thread_start_mutex);
829
830 if (err < 0) {
831 fuse_log(FUSE_LOG_ERR, "qid=%d queue setup failed\n",
832 queue->qid);
833 goto err_non_fatal;
834 }
835
836 sem_wait(&ring_pool->init_sem);
837
838 /* Not using fuse_session_exited(se), as that cannot be inlined */
839 while (!atomic_load_explicit(&se->mt_exited, memory_order_relaxed)) {
840 io_uring_submit_and_wait(&queue->ring, 1);
841
842 pthread_mutex_lock(&queue->ring_lock);
843 queue->cqe_processing = true;
844 err = fuse_uring_queue_handle_cqes(queue);
845 queue->cqe_processing = false;
846 pthread_mutex_unlock(&queue->ring_lock);
847 if (err < 0)
848 goto err;
849 }
850
851 return NULL;
852
853err:
855err_non_fatal:
856 return NULL;
857}
858
859static int fuse_uring_start_ring_threads(struct fuse_ring_pool *ring)
860{
861 int rc = 0;
862
863 for (size_t qid = 0; qid < ring->nr_queues; qid++) {
864 struct fuse_ring_queue *queue = fuse_uring_get_queue(ring, qid);
865
866 rc = pthread_create(&queue->tid, NULL, fuse_uring_thread, queue);
867 if (rc != 0)
868 break;
869 }
870
871 return rc;
872}
873
874static int fuse_uring_sanity_check(struct fuse_session *se)
875{
876 if (se->uring.q_depth == 0) {
877 fuse_log(FUSE_LOG_ERR, "io-uring queue depth must be > 0\n");
878 return -EINVAL;
879 }
880
881 _Static_assert(sizeof(struct fuse_uring_cmd_req) <=
882 FUSE_URING_MAX_SQE128_CMD_DATA,
883 "SQE128_CMD_DATA has 80B cmd data");
884
885 return 0;
886}
887
888int fuse_uring_start(struct fuse_session *se)
889{
890 int err = 0;
891 struct fuse_ring_pool *fuse_ring;
892
893 fuse_uring_sanity_check(se);
894
895 fuse_ring = fuse_create_ring(se);
896 if (fuse_ring == NULL) {
897 err = -EADDRNOTAVAIL;
898 goto err;
899 }
900
901 se->uring.pool = fuse_ring;
902
903 /* Hold off threads from send fuse ring entries (SQEs) */
904 sem_init(&fuse_ring->init_sem, 0, 0);
905 pthread_cond_init(&fuse_ring->thread_start_cond, NULL);
906 pthread_mutex_init(&fuse_ring->thread_start_mutex, NULL);
907
908 err = fuse_uring_start_ring_threads(fuse_ring);
909 if (err)
910 goto err;
911
912 /*
913 * Wait for all threads to start or to fail
914 */
915 pthread_mutex_lock(&fuse_ring->thread_start_mutex);
916 while (fuse_ring->started_threads < fuse_ring->nr_queues)
917 pthread_cond_wait(&fuse_ring->thread_start_cond,
918 &fuse_ring->thread_start_mutex);
919
920 if (fuse_ring->failed_threads != 0)
921 err = -EADDRNOTAVAIL;
922 pthread_mutex_unlock(&fuse_ring->thread_start_mutex);
923
924err:
925 if (err) {
926 /* Note all threads need to have been started */
927 fuse_session_destruct_uring(fuse_ring);
928 se->uring.pool = fuse_ring;
929 }
930 return err;
931}
932
933int fuse_uring_stop(struct fuse_session *se)
934{
935 struct fuse_ring_pool *ring = se->uring.pool;
936
937 if (ring == NULL)
938 return 0;
939
940 fuse_session_destruct_uring(ring);
941
942 return 0;
943}
944
945void fuse_uring_wake_ring_threads(struct fuse_session *se)
946{
947 struct fuse_ring_pool *ring = se->uring.pool;
948
949 /* Wake up the threads to let them send SQEs */
950 for (size_t qid = 0; qid < ring->nr_queues; qid++)
951 sem_post(&ring->init_sem);
952}
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
fuse_buf_copy_flags
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
struct fuse_req * fuse_req_t
int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
void * mem
size_t size
struct fuse_buf buf[1]
fuse-3.18.2/doc/html/lib_2fuse__uring_8c_source.html0000644000175000017500000043664115156613443021331 0ustar berndbernd libfuse: lib/fuse_uring.c Source File
libfuse
fuse_uring.c
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2025 Bernd Schubert <bschubert@ddn.com>
4 *
5 * Implementation of (most of) FUSE-over-io-uring.
6 *
7 * This program can be distributed under the terms of the GNU LGPLv2.
8 * See the file LGPL2.txt
9 */
10
11#define _GNU_SOURCE
12
13#include "fuse_i.h"
14#include "fuse_kernel.h"
15#include "fuse_uring_i.h"
16
17#include <stdlib.h>
18#include <liburing.h>
19#include <sys/sysinfo.h>
20#include <stdint.h>
21#include <inttypes.h>
22#include <stdbool.h>
23#include <string.h>
24#include <unistd.h>
25#include <numa.h>
26#include <pthread.h>
27#include <stdio.h>
28#include <linux/sched.h>
29#include <poll.h>
30#include <sys/eventfd.h>
31
32/* Size of command data area in SQE when IORING_SETUP_SQE128 is used */
33#define FUSE_URING_MAX_SQE128_CMD_DATA 80
34
35struct fuse_ring_ent {
36 struct fuse_ring_queue *ring_queue; /* back pointer */
37 struct fuse_req req;
38
39 struct fuse_uring_req_header *req_header;
40 void *op_payload;
41 size_t req_payload_sz;
42
43 /* commit id of a fuse request */
44 uint64_t req_commit_id;
45
46 enum fuse_uring_cmd last_cmd;
47
48 /* header and payload */
49 struct iovec iov[2];
50};
51
52struct fuse_ring_queue {
53 /* back pointer */
54 struct fuse_ring_pool *ring_pool;
55 int qid;
56 int numa_node;
57 pthread_t tid;
58 int eventfd;
59 size_t req_header_sz;
60 struct io_uring ring;
61
62 pthread_mutex_t ring_lock;
63 bool cqe_processing;
64
65 /* size depends on queue depth */
66 struct fuse_ring_ent ent[];
67};
68
72struct fuse_ring_pool {
73 struct fuse_session *se;
74
75 /* number of queues */
76 size_t nr_queues;
77
78 /* number of per queue entries */
79 size_t queue_depth;
80
81 /* max payload size for fuse requests*/
82 size_t max_req_payload_sz;
83
84 /* size of a single queue */
85 size_t queue_mem_size;
86
87 unsigned int started_threads;
88 unsigned int failed_threads;
89
90 /* Avoid sending queue entries before FUSE_INIT reply*/
91 sem_t init_sem;
92
93 pthread_cond_t thread_start_cond;
94 pthread_mutex_t thread_start_mutex;
95
96 /* pointer to the first queue */
97 struct fuse_ring_queue *queues;
98};
99
100static size_t
101fuse_ring_queue_size(const size_t q_depth)
102{
103 const size_t req_size = sizeof(struct fuse_ring_ent) * q_depth;
104
105 return sizeof(struct fuse_ring_queue) + req_size;
106}
107
108static struct fuse_ring_queue *
109fuse_uring_get_queue(struct fuse_ring_pool *fuse_ring, int qid)
110{
111 void *ptr =
112 ((char *)fuse_ring->queues) + (qid * fuse_ring->queue_mem_size);
113
114 return ptr;
115}
116
120static void *fuse_uring_get_sqe_cmd(struct io_uring_sqe *sqe)
121{
122 return (void *)&sqe->cmd[0];
123}
124
125static void fuse_uring_sqe_set_req_data(struct fuse_uring_cmd_req *req,
126 const unsigned int qid,
127 const uint64_t commit_id)
128{
129 req->qid = qid;
130 req->commit_id = commit_id;
131 req->flags = 0;
132}
133
134static void
135fuse_uring_sqe_prepare(struct io_uring_sqe *sqe, struct fuse_ring_ent *req,
136 __u32 cmd_op)
137{
138 /* These fields should be written once, never change */
139 sqe->opcode = IORING_OP_URING_CMD;
140
141 /*
142 * IOSQE_FIXED_FILE: fd is the index to the fd *array*
143 * given to io_uring_register_files()
144 */
145 sqe->flags = IOSQE_FIXED_FILE;
146 sqe->fd = 0;
147
148 sqe->rw_flags = 0;
149 sqe->ioprio = 0;
150 sqe->off = 0;
151
152 io_uring_sqe_set_data(sqe, req);
153
154 sqe->cmd_op = cmd_op;
155 sqe->__pad1 = 0;
156}
157
158static int fuse_uring_commit_sqe(struct fuse_ring_pool *ring_pool,
159 struct fuse_ring_queue *queue,
160 struct fuse_ring_ent *ring_ent)
161{
162 bool locked = false;
163 struct fuse_session *se = ring_pool->se;
164 struct fuse_uring_req_header *rrh = ring_ent->req_header;
165 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
166 struct fuse_uring_ent_in_out *ent_in_out =
167 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
168 struct io_uring_sqe *sqe;
169
170 if (pthread_self() != queue->tid) {
171 pthread_mutex_lock(&queue->ring_lock);
172 locked = true;
173 }
174
175 sqe = io_uring_get_sqe(&queue->ring);
176
177 if (sqe == NULL) {
178 /* This is an impossible condition, unless there is a bug.
179 * The kernel sent back an SQEs, which is assigned to a request.
180 * There is no way to get out of SQEs, as the number of
181 * SQEs matches the number tof requests.
182 */
183
184 se->error = -EIO;
185 fuse_log(FUSE_LOG_ERR, "Failed to get a ring SQEs\n");
186
187 return -EIO;
188 }
189
190 ring_ent->last_cmd = FUSE_IO_URING_CMD_COMMIT_AND_FETCH;
191 fuse_uring_sqe_prepare(sqe, ring_ent, ring_ent->last_cmd);
192 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid,
193 ring_ent->req_commit_id);
194
195 if (se->debug) {
196 fuse_log(FUSE_LOG_DEBUG, " unique: %" PRIu64 ", result=%d\n",
197 out->unique, ent_in_out->payload_sz);
198 }
199
200 if (!queue->cqe_processing)
201 io_uring_submit(&queue->ring);
202
203 if (locked)
204 pthread_mutex_unlock(&queue->ring_lock);
205
206 return 0;
207}
208
209int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
210 void **mr)
211{
212 struct fuse_ring_ent *ring_ent;
213
214 /* Not possible without io-uring interface */
215 if (!req->flags.is_uring)
216 return -EINVAL;
217
218 ring_ent = container_of(req, struct fuse_ring_ent, req);
219
220 *payload = ring_ent->op_payload;
221 *payload_sz = ring_ent->req_payload_sz;
222
223 /*
224 * For now unused, but will be used later when the application can
225 * allocate the buffers itself and register them for rdma.
226 */
227 if (mr)
228 *mr = NULL;
229
230 return 0;
231}
232
233int send_reply_uring(fuse_req_t req, int error, const void *arg, size_t argsize)
234{
235 int res;
236 struct fuse_ring_ent *ring_ent =
237 container_of(req, struct fuse_ring_ent, req);
238 struct fuse_uring_req_header *rrh = ring_ent->req_header;
239 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
240 struct fuse_uring_ent_in_out *ent_in_out =
241 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
242
243 struct fuse_ring_queue *queue = ring_ent->ring_queue;
244 struct fuse_ring_pool *ring_pool = queue->ring_pool;
245 size_t max_payload_sz = ring_pool->max_req_payload_sz;
246
247 if (argsize > max_payload_sz) {
248 fuse_log(FUSE_LOG_ERR, "argsize %zu exceeds buffer size %zu",
249 argsize, max_payload_sz);
250 error = -EINVAL;
251 } else if (argsize) {
252 if (arg != ring_ent->op_payload)
253 memcpy(ring_ent->op_payload, arg, argsize);
254 }
255 ent_in_out->payload_sz = argsize;
256
257 out->error = error;
258 out->unique = req->unique;
259
260 res = fuse_uring_commit_sqe(ring_pool, queue, ring_ent);
261
262 fuse_free_req(req);
263
264 return res;
265}
266
267int fuse_reply_data_uring(fuse_req_t req, struct fuse_bufvec *bufv,
268 enum fuse_buf_copy_flags flags)
269{
270 struct fuse_ring_ent *ring_ent =
271 container_of(req, struct fuse_ring_ent, req);
272
273 struct fuse_ring_queue *queue = ring_ent->ring_queue;
274 struct fuse_ring_pool *ring_pool = queue->ring_pool;
275 struct fuse_uring_req_header *rrh = ring_ent->req_header;
276 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
277 struct fuse_uring_ent_in_out *ent_in_out =
278 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
279 size_t max_payload_sz = ring_ent->req_payload_sz;
280 struct fuse_bufvec dest_vec = FUSE_BUFVEC_INIT(max_payload_sz);
281 int res;
282
283 dest_vec.buf[0].mem = ring_ent->op_payload;
284 dest_vec.buf[0].size = max_payload_sz;
285
286 res = fuse_buf_copy(&dest_vec, bufv, flags);
287
288 out->error = res < 0 ? res : 0;
289 out->unique = req->unique;
290
291 ent_in_out->payload_sz = res > 0 ? res : 0;
292
293 res = fuse_uring_commit_sqe(ring_pool, queue, ring_ent);
294
295 fuse_free_req(req);
296
297 return res;
298}
299
303int fuse_send_msg_uring(fuse_req_t req, struct iovec *iov, int count)
304{
305 struct fuse_ring_ent *ring_ent =
306 container_of(req, struct fuse_ring_ent, req);
307
308 struct fuse_ring_queue *queue = ring_ent->ring_queue;
309 struct fuse_ring_pool *ring_pool = queue->ring_pool;
310 struct fuse_uring_req_header *rrh = ring_ent->req_header;
311 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
312 struct fuse_uring_ent_in_out *ent_in_out =
313 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
314 size_t max_buf = ring_pool->max_req_payload_sz;
315 size_t len = 0;
316 int res = 0;
317
318 /* copy iov into the payload, idx=0 is the header section */
319 for (int idx = 1; idx < count; idx++) {
320 struct iovec *cur = &iov[idx];
321
322 if (len + cur->iov_len > max_buf) {
323 fuse_log(FUSE_LOG_ERR,
324 "iov[%d] exceeds buffer size %zu",
325 idx, max_buf);
326 res = -EINVAL; /* Gracefully handle this? */
327 break;
328 }
329
330 memcpy(ring_ent->op_payload + len, cur->iov_base, cur->iov_len);
331 len += cur->iov_len;
332 }
333
334 ent_in_out->payload_sz = len;
335
336 out->error = res;
337 out->unique = req->unique;
338 out->len = len;
339
340 return fuse_uring_commit_sqe(ring_pool, queue, ring_ent);
341}
342
343static int fuse_queue_setup_io_uring(struct io_uring *ring, size_t qid,
344 size_t depth, int fd, int evfd)
345{
346 int rc;
347 struct io_uring_params params = {0};
348 int files[2] = { fd, evfd };
349
350 depth += 1; /* for the eventfd poll SQE */
351
352 params.flags = IORING_SETUP_SQE128;
353
354 /* Avoid cq overflow */
355 params.flags |= IORING_SETUP_CQSIZE;
356 params.cq_entries = depth * 2;
357
358 /* These flags should help to increase performance, but actually
359 * make it a bit slower - reason should get investigated.
360 */
361 if (0) {
362 /* Has the main slow down effect */
363 params.flags |= IORING_SETUP_SINGLE_ISSUER;
364
365 // params.flags |= IORING_SETUP_DEFER_TASKRUN;
366 params.flags |= IORING_SETUP_TASKRUN_FLAG;
367
368 /* Second main effect to make it slower */
369 params.flags |= IORING_SETUP_COOP_TASKRUN;
370 }
371
372 rc = io_uring_queue_init_params(depth, ring, &params);
373 if (rc != 0) {
374 fuse_log(FUSE_LOG_ERR, "Failed to setup qid %zu: %d (%s)\n",
375 qid, rc, strerror(-rc));
376 return rc;
377 }
378
379 rc = io_uring_register_files(ring, files, 1);
380 if (rc != 0) {
381 rc = -errno;
382 fuse_log(FUSE_LOG_ERR,
383 "Failed to register files for ring idx %zu: %s",
384 qid, strerror(errno));
385 return rc;
386 }
387
388 return 0;
389}
390
391static void fuse_session_destruct_uring(struct fuse_ring_pool *fuse_ring)
392{
393 for (size_t qid = 0; qid < fuse_ring->nr_queues; qid++) {
394 struct fuse_ring_queue *queue =
395 fuse_uring_get_queue(fuse_ring, qid);
396
397 if (queue->tid != 0) {
398 uint64_t value = 1ULL;
399 int rc;
400
401 rc = write(queue->eventfd, &value, sizeof(value));
402 if (rc != sizeof(value))
403 fprintf(stderr,
404 "Wrote to eventfd=%d err=%s: rc=%d\n",
405 queue->eventfd, strerror(errno), rc);
406 pthread_cancel(queue->tid);
407 pthread_join(queue->tid, NULL);
408 queue->tid = 0;
409 }
410
411 if (queue->eventfd >= 0) {
412 close(queue->eventfd);
413 queue->eventfd = -1;
414 }
415
416 if (queue->ring.ring_fd != -1)
417 io_uring_queue_exit(&queue->ring);
418
419 for (size_t idx = 0; idx < fuse_ring->queue_depth; idx++) {
420 struct fuse_ring_ent *ent = &queue->ent[idx];
421
422 numa_free(ent->op_payload, ent->req_payload_sz);
423 numa_free(ent->req_header, queue->req_header_sz);
424 }
425
426 pthread_mutex_destroy(&queue->ring_lock);
427 }
428
429 free(fuse_ring->queues);
430 pthread_cond_destroy(&fuse_ring->thread_start_cond);
431 pthread_mutex_destroy(&fuse_ring->thread_start_mutex);
432 free(fuse_ring);
433}
434
435static int fuse_uring_register_ent(struct fuse_ring_queue *queue,
436 struct fuse_ring_ent *ent)
437{
438 struct io_uring_sqe *sqe;
439
440 sqe = io_uring_get_sqe(&queue->ring);
441 if (sqe == NULL) {
442 /*
443 * All SQEs are idle here - no good reason this
444 * could fail
445 */
446 fuse_log(FUSE_LOG_ERR, "Failed to get all ring SQEs");
447 return -EIO;
448 }
449
450 ent->last_cmd = FUSE_IO_URING_CMD_REGISTER;
451 fuse_uring_sqe_prepare(sqe, ent, ent->last_cmd);
452
453 /* only needed for fetch */
454 ent->iov[0].iov_base = ent->req_header;
455 ent->iov[0].iov_len = queue->req_header_sz;
456
457 ent->iov[1].iov_base = ent->op_payload;
458 ent->iov[1].iov_len = ent->req_payload_sz;
459
460 sqe->addr = (uint64_t)(ent->iov);
461 sqe->len = 2;
462
463 /* this is a fetch, kernel does not read commit id */
464 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid, 0);
465
466 return 0;
467
468}
469
470static int fuse_uring_register_queue(struct fuse_ring_queue *queue)
471{
472 struct fuse_ring_pool *ring_pool = queue->ring_pool;
473 unsigned int sq_ready;
474 struct io_uring_sqe *sqe;
475 int res;
476
477 for (size_t idx = 0; idx < ring_pool->queue_depth; idx++) {
478 struct fuse_ring_ent *ent = &queue->ent[idx];
479
480 res = fuse_uring_register_ent(queue, ent);
481 if (res != 0)
482 return res;
483 }
484
485 sq_ready = io_uring_sq_ready(&queue->ring);
486 if (sq_ready != ring_pool->queue_depth) {
487 fuse_log(FUSE_LOG_ERR,
488 "SQE ready mismatch, expected %zu got %u\n",
489 ring_pool->queue_depth, sq_ready);
490 return -EINVAL;
491 }
492
493 /* Poll SQE for the eventfd to wake up on teardown */
494 sqe = io_uring_get_sqe(&queue->ring);
495 if (sqe == NULL) {
496 fuse_log(FUSE_LOG_ERR, "Failed to get eventfd SQE");
497 return -EIO;
498 }
499
500 io_uring_prep_poll_add(sqe, queue->eventfd, POLLIN);
501 io_uring_sqe_set_data(sqe, (void *)(uintptr_t)queue->eventfd);
502
503 /* Only preparation until here, no submission yet */
504
505 return 0;
506}
507
508static struct fuse_ring_pool *fuse_create_ring(struct fuse_session *se)
509{
510 struct fuse_ring_pool *fuse_ring = NULL;
511 const size_t nr_queues = get_nprocs_conf();
512 size_t payload_sz = se->bufsize - FUSE_BUFFER_HEADER_SIZE;
513 size_t queue_sz;
514
515 if (se->debug)
516 fuse_log(FUSE_LOG_DEBUG, "starting io-uring q-depth=%d\n",
517 se->uring.q_depth);
518
519 fuse_ring = calloc(1, sizeof(*fuse_ring));
520 if (fuse_ring == NULL) {
521 fuse_log(FUSE_LOG_ERR, "Allocating the ring failed\n");
522 goto err;
523 }
524
525 queue_sz = fuse_ring_queue_size(se->uring.q_depth);
526 fuse_ring->queues = calloc(1, queue_sz * nr_queues);
527 if (fuse_ring->queues == NULL) {
528 fuse_log(FUSE_LOG_ERR, "Allocating the queues failed\n");
529 goto err;
530 }
531
532 fuse_ring->se = se;
533 fuse_ring->nr_queues = nr_queues;
534 fuse_ring->queue_depth = se->uring.q_depth;
535 fuse_ring->max_req_payload_sz = payload_sz;
536 fuse_ring->queue_mem_size = queue_sz;
537
538 /*
539 * very basic queue initialization, that cannot fail and will
540 * allow easy cleanup if something (like mmap) fails in the middle
541 * below
542 */
543 for (size_t qid = 0; qid < nr_queues; qid++) {
544 struct fuse_ring_queue *queue =
545 fuse_uring_get_queue(fuse_ring, qid);
546
547 queue->ring.ring_fd = -1;
548 queue->numa_node = numa_node_of_cpu(qid);
549 queue->qid = qid;
550 queue->ring_pool = fuse_ring;
551 queue->eventfd = -1;
552 pthread_mutex_init(&queue->ring_lock, NULL);
553 }
554
555 pthread_cond_init(&fuse_ring->thread_start_cond, NULL);
556 pthread_mutex_init(&fuse_ring->thread_start_mutex, NULL);
557 sem_init(&fuse_ring->init_sem, 0, 0);
558
559 return fuse_ring;
560
561err:
562 if (fuse_ring)
563 fuse_session_destruct_uring(fuse_ring);
564
565 return NULL;
566}
567
568static void fuse_uring_resubmit(struct fuse_ring_queue *queue,
569 struct fuse_ring_ent *ent)
570{
571 struct io_uring_sqe *sqe;
572
573 sqe = io_uring_get_sqe(&queue->ring);
574 if (sqe == NULL) {
575 /* This is an impossible condition, unless there is a bug.
576 * The kernel sent back an SQEs, which is assigned to a request.
577 * There is no way to get out of SQEs, as the number of
578 * SQEs matches the number tof requests.
579 */
580
581 queue->ring_pool->se->error = -EIO;
582 fuse_log(FUSE_LOG_ERR, "Failed to get a ring SQEs\n");
583
584 return;
585 }
586
587 fuse_uring_sqe_prepare(sqe, ent, ent->last_cmd);
588
589 switch (ent->last_cmd) {
590 case FUSE_IO_URING_CMD_REGISTER:
591 sqe->addr = (uint64_t)(ent->iov);
592 sqe->len = 2;
593 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe),
594 queue->qid, 0);
595 break;
596 case FUSE_IO_URING_CMD_COMMIT_AND_FETCH:
597 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe),
598 queue->qid, ent->req_commit_id);
599 break;
600 default:
601 fuse_log(FUSE_LOG_ERR, "Unknown command type: %d\n",
602 ent->last_cmd);
603 queue->ring_pool->se->error = -EINVAL;
604 break;
605 }
606
607 /* caller submits */
608}
609
610static void fuse_uring_handle_cqe(struct fuse_ring_queue *queue,
611 struct io_uring_cqe *cqe)
612{
613 struct fuse_ring_ent *ent = io_uring_cqe_get_data(cqe);
614
615 if (!ent) {
616 fuse_log(FUSE_LOG_ERR,
617 "cqe=%p io_uring_cqe_get_data returned NULL\n", cqe);
618 return;
619 }
620
621 struct fuse_req *req = &ent->req;
622 struct fuse_ring_pool *fuse_ring = queue->ring_pool;
623 struct fuse_uring_req_header *rrh = ent->req_header;
624
625 struct fuse_in_header *in = (struct fuse_in_header *)&rrh->in_out;
626 struct fuse_uring_ent_in_out *ent_in_out = &rrh->ring_ent_in_out;
627
628 ent->req_commit_id = ent_in_out->commit_id;
629 if (unlikely(ent->req_commit_id == 0)) {
630 /*
631 * If this happens kernel will not find the response - it will
632 * be stuck forever - better to abort immediately.
633 */
634 fuse_log(FUSE_LOG_ERR, "Received invalid commit_id=0\n");
635 abort();
636 }
637
638 memset(&req->flags, 0, sizeof(req->flags));
639 memset(&req->u, 0, sizeof(req->u));
640 req->flags.is_uring = 1;
641 req->ref_cnt++;
642 req->ch = NULL; /* not needed for uring */
643 req->interrupted = 0;
644 list_init_req(req);
645
646 fuse_session_process_uring_cqe(fuse_ring->se, req, in, &rrh->op_in,
647 ent->op_payload, ent_in_out->payload_sz);
648}
649
650static int fuse_uring_queue_handle_cqes(struct fuse_ring_queue *queue)
651{
652 struct fuse_ring_pool *ring_pool = queue->ring_pool;
653 struct fuse_session *se = ring_pool->se;
654 size_t num_completed = 0;
655 struct io_uring_cqe *cqe;
656 unsigned int head;
657 struct fuse_ring_ent *ent;
658 int ret = 0;
659
660 io_uring_for_each_cqe(&queue->ring, head, cqe) {
661 int err = 0;
662
663 num_completed++;
664
665 err = cqe->res;
666 if (unlikely(err != 0)) {
667 if (err > 0 && ((uintptr_t)io_uring_cqe_get_data(cqe) ==
668 (unsigned int)queue->eventfd)) {
669 /* teardown from eventfd */
670 return -ENOTCONN;
671 }
672
673
674 switch (err) {
675 case -EAGAIN:
676 fallthrough;
677 case -EINTR:
678 ent = io_uring_cqe_get_data(cqe);
679 fuse_uring_resubmit(queue, ent);
680 continue;
681 default:
682 break;
683 }
684
685 /* -ENOTCONN is ok on umount */
686 if (err != -ENOTCONN) {
687 se->error = cqe->res;
688
689 /* return first error */
690 if (ret == 0)
691 ret = err;
692 }
693
694 } else {
695 fuse_uring_handle_cqe(queue, cqe);
696 }
697 }
698
699 if (num_completed)
700 io_uring_cq_advance(&queue->ring, num_completed);
701
702 return ret == 0 ? 0 : num_completed;
703}
704
709static void fuse_uring_set_thread_core(int qid)
710{
711 cpu_set_t mask;
712 int rc;
713
714 CPU_ZERO(&mask);
715 CPU_SET(qid, &mask);
716 rc = sched_setaffinity(0, sizeof(cpu_set_t), &mask);
717 if (rc != 0)
718 fuse_log(FUSE_LOG_ERR, "Failed to bind qid=%d to its core: %s\n",
719 qid, strerror(errno));
720
721 if (0) {
722 const int policy = SCHED_IDLE;
723 const struct sched_param param = {
724 .sched_priority = sched_get_priority_min(policy),
725 };
726
727 /* Set the lowest possible priority, so that the application
728 * submitting requests is not moved away from the current core.
729 */
730 rc = sched_setscheduler(0, policy, &param);
731 if (rc != 0)
732 fuse_log(FUSE_LOG_ERR, "Failed to set scheduler: %s\n",
733 strerror(errno));
734 }
735}
736
737/*
738 * @return negative error code or io-uring file descriptor
739 */
740static int fuse_uring_init_queue(struct fuse_ring_queue *queue)
741{
742 struct fuse_ring_pool *ring = queue->ring_pool;
743 struct fuse_session *se = ring->se;
744 int res;
745 size_t page_sz = sysconf(_SC_PAGESIZE);
746
747 queue->eventfd = eventfd(0, EFD_CLOEXEC);
748 if (queue->eventfd < 0) {
749 res = -errno;
750 fuse_log(FUSE_LOG_ERR,
751 "Failed to create eventfd for qid %d: %s\n",
752 queue->qid, strerror(errno));
753 return res;
754 }
755
756 res = fuse_queue_setup_io_uring(&queue->ring, queue->qid,
757 ring->queue_depth, se->fd,
758 queue->eventfd);
759 if (res != 0) {
760 fuse_log(FUSE_LOG_ERR, "qid=%d io_uring init failed\n",
761 queue->qid);
762 return res;
763 }
764
765 queue->req_header_sz = ROUND_UP(sizeof(struct fuse_ring_ent),
766 page_sz);
767
768 for (size_t idx = 0; idx < ring->queue_depth; idx++) {
769 struct fuse_ring_ent *ring_ent = &queue->ent[idx];
770 struct fuse_req *req = &ring_ent->req;
771
772 ring_ent->ring_queue = queue;
773
774 /*
775 * Also allocate the header to have it page aligned, which
776 * is a requirement for page pinning
777 */
778 ring_ent->req_header =
779 numa_alloc_local(queue->req_header_sz);
780 if (!ring_ent->req_header)
781 return -ENOMEM;
782 ring_ent->req_payload_sz = ring->max_req_payload_sz;
783
784 ring_ent->op_payload =
785 numa_alloc_local(ring_ent->req_payload_sz);
786 if (!ring_ent->op_payload)
787 return -ENOMEM;
788
789 req->se = se;
790 pthread_mutex_init(&req->lock, NULL);
791 req->flags.is_uring = 1;
792 req->ref_cnt = 1; /* extra ref to avoid destruction */
793 list_init_req(req);
794 }
795
796 res = fuse_uring_register_queue(queue);
797 if (res != 0) {
798 fuse_log(
799 FUSE_LOG_ERR,
800 "Grave fuse-uring error on preparing SQEs, aborting\n");
801 se->error = -EIO;
803 return res;
804 }
805
806 return queue->ring.ring_fd;
807}
808
809static void *fuse_uring_thread(void *arg)
810{
811 struct fuse_ring_queue *queue = arg;
812 struct fuse_ring_pool *ring_pool = queue->ring_pool;
813 struct fuse_session *se = ring_pool->se;
814 int err;
815 char thread_name[16] = { 0 };
816
817 snprintf(thread_name, 16, "fuse-ring-%d", queue->qid);
818 thread_name[15] = '\0';
819 fuse_set_thread_name(thread_name);
820
821 fuse_uring_set_thread_core(queue->qid);
822
823 err = fuse_uring_init_queue(queue);
824 pthread_mutex_lock(&ring_pool->thread_start_mutex);
825 if (err < 0)
826 ring_pool->failed_threads++;
827 ring_pool->started_threads++;
828 pthread_cond_broadcast(&ring_pool->thread_start_cond);
829 pthread_mutex_unlock(&ring_pool->thread_start_mutex);
830
831 if (err < 0) {
832 fuse_log(FUSE_LOG_ERR, "qid=%d queue setup failed\n",
833 queue->qid);
834 goto err_non_fatal;
835 }
836
837 sem_wait(&ring_pool->init_sem);
838
839 /* Not using fuse_session_exited(se), as that cannot be inlined */
840 while (!atomic_load_explicit(&se->mt_exited, memory_order_relaxed)) {
841 io_uring_submit_and_wait(&queue->ring, 1);
842
843 pthread_mutex_lock(&queue->ring_lock);
844 queue->cqe_processing = true;
845 err = fuse_uring_queue_handle_cqes(queue);
846 queue->cqe_processing = false;
847 pthread_mutex_unlock(&queue->ring_lock);
848 if (err < 0)
849 goto err;
850 }
851
852 return NULL;
853
854err:
856err_non_fatal:
857 return NULL;
858}
859
860static int fuse_uring_start_ring_threads(struct fuse_ring_pool *ring)
861{
862 int rc = 0;
863
864 for (size_t qid = 0; qid < ring->nr_queues; qid++) {
865 struct fuse_ring_queue *queue = fuse_uring_get_queue(ring, qid);
866
867 rc = pthread_create(&queue->tid, NULL, fuse_uring_thread, queue);
868 if (rc != 0)
869 break;
870 }
871
872 return rc;
873}
874
875static int fuse_uring_sanity_check(struct fuse_session *se)
876{
877 if (se->uring.q_depth == 0) {
878 fuse_log(FUSE_LOG_ERR, "io-uring queue depth must be > 0\n");
879 return -EINVAL;
880 }
881
882 _Static_assert(sizeof(struct fuse_uring_cmd_req) <=
883 FUSE_URING_MAX_SQE128_CMD_DATA,
884 "SQE128_CMD_DATA has 80B cmd data");
885
886 return 0;
887}
888
889int fuse_uring_start(struct fuse_session *se)
890{
891 int err = 0;
892 struct fuse_ring_pool *fuse_ring;
893
894 fuse_uring_sanity_check(se);
895
896 fuse_ring = fuse_create_ring(se);
897 if (fuse_ring == NULL) {
898 err = -EADDRNOTAVAIL;
899 goto err;
900 }
901
902 se->uring.pool = fuse_ring;
903
904 /* Hold off threads from send fuse ring entries (SQEs) */
905 sem_init(&fuse_ring->init_sem, 0, 0);
906 pthread_cond_init(&fuse_ring->thread_start_cond, NULL);
907 pthread_mutex_init(&fuse_ring->thread_start_mutex, NULL);
908
909 err = fuse_uring_start_ring_threads(fuse_ring);
910 if (err)
911 goto err;
912
913 /*
914 * Wait for all threads to start or to fail
915 */
916 pthread_mutex_lock(&fuse_ring->thread_start_mutex);
917 while (fuse_ring->started_threads < fuse_ring->nr_queues)
918 pthread_cond_wait(&fuse_ring->thread_start_cond,
919 &fuse_ring->thread_start_mutex);
920
921 if (fuse_ring->failed_threads != 0)
922 err = -EADDRNOTAVAIL;
923 pthread_mutex_unlock(&fuse_ring->thread_start_mutex);
924
925err:
926 if (err) {
927 /* Note all threads need to have been started */
928 if (fuse_ring)
929 fuse_session_destruct_uring(fuse_ring);
930 se->uring.pool = NULL;
931 }
932 return err;
933}
934
935int fuse_uring_stop(struct fuse_session *se)
936{
937 struct fuse_ring_pool *ring = se->uring.pool;
938
939 if (ring == NULL)
940 return 0;
941
942 fuse_session_destruct_uring(ring);
943
944 return 0;
945}
946
947void fuse_uring_wake_ring_threads(struct fuse_session *se)
948{
949 struct fuse_ring_pool *ring = se->uring.pool;
950
951 /* Wake up the threads to let them send SQEs */
952 for (size_t qid = 0; qid < ring->nr_queues; qid++)
953 sem_post(&ring->init_sem);
954}
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
fuse_buf_copy_flags
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
struct fuse_req * fuse_req_t
int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
void * mem
size_t size
struct fuse_buf buf[1]
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2fuse__uring__i_8h_source.html0000644000175000017500000004213215156613443024145 0ustar berndbernd libfuse: fuse-3.18.1/lib/fuse_uring_i.h Source File
libfuse
fuse_uring_i.h
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2025 Bernd Schubert <bschubert@ddn.com>
4 * This program can be distributed under the terms of the GNU LGPLv2.
5 * See the file LGPL2.txt
6 */
7
8#ifndef FUSE_URING_I_H_
9#define FUSE_URING_I_H_
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_kernel.h"
14
15#ifndef HAVE_URING
16#include "util.h"
17#endif
18
19#include <errno.h> // IWYU pragma: keep
20
21/* io-uring defaults */
22#define SESSION_DEF_URING_ENABLE (0)
23#define SESSION_DEF_URING_Q_DEPTH (8)
24
25void fuse_session_process_uring_cqe(struct fuse_session *se,
26 struct fuse_req *req,
27 struct fuse_in_header *in, void *in_header,
28 void *in_payload, size_t payload_len);
29
30#ifdef HAVE_URING
31
32struct fuse_in_header;
33
34int fuse_uring_start(struct fuse_session *se);
35void fuse_uring_wake_ring_threads(struct fuse_session *se);
36int fuse_uring_stop(struct fuse_session *se);
37int send_reply_uring(fuse_req_t req, int error, const void *arg,
38 size_t argsize);
39
40int fuse_reply_data_uring(fuse_req_t req, struct fuse_bufvec *bufv,
41 enum fuse_buf_copy_flags flags);
42int fuse_send_msg_uring(fuse_req_t req, struct iovec *iov, int count);
43
44#else // HAVE_URING
45
46static inline int fuse_uring_start(struct fuse_session *se FUSE_VAR_UNUSED)
47{
48 return -ENOTSUP;
49}
50
51static inline void
52fuse_uring_wake_ring_threads(struct fuse_session *se FUSE_VAR_UNUSED)
53{
54}
55
56static inline int fuse_uring_stop(struct fuse_session *se FUSE_VAR_UNUSED)
57{
58 return -ENOTSUP;
59}
60
61static inline int send_reply_uring(fuse_req_t req FUSE_VAR_UNUSED,
62 int error FUSE_VAR_UNUSED,
63 const void *arg FUSE_VAR_UNUSED,
64 size_t argsize FUSE_VAR_UNUSED)
65{
66 return -ENOTSUP;
67}
68
69static inline int
70fuse_reply_data_uring(fuse_req_t req FUSE_VAR_UNUSED,
71 struct fuse_bufvec *bufv FUSE_VAR_UNUSED,
72 enum fuse_buf_copy_flags flags FUSE_VAR_UNUSED)
73{
74 return -ENOTSUP;
75}
76
77static inline int fuse_send_msg_uring(fuse_req_t req FUSE_VAR_UNUSED,
78 struct iovec *iov FUSE_VAR_UNUSED,
79 int count FUSE_VAR_UNUSED)
80{
81 return -ENOTSUP;
82}
83
84#endif // HAVE_URING
85
86#endif // FUSE_URING_I_H_
fuse_buf_copy_flags
struct fuse_req * fuse_req_t
fuse-3.18.2/doc/html/lib_2fuse__uring__i_8h_source.html0000644000175000017500000004174715156613443022004 0ustar berndbernd libfuse: lib/fuse_uring_i.h Source File
libfuse
fuse_uring_i.h
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2025 Bernd Schubert <bschubert@ddn.com>
4 * This program can be distributed under the terms of the GNU LGPLv2.
5 * See the file LGPL2.txt
6 */
7
8#ifndef FUSE_URING_I_H_
9#define FUSE_URING_I_H_
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_kernel.h"
14
15#ifndef HAVE_URING
16#include "util.h"
17#endif
18
19#include <errno.h> // IWYU pragma: keep
20
21/* io-uring defaults */
22#define SESSION_DEF_URING_ENABLE (0)
23#define SESSION_DEF_URING_Q_DEPTH (8)
24
25void fuse_session_process_uring_cqe(struct fuse_session *se,
26 struct fuse_req *req,
27 struct fuse_in_header *in, void *in_header,
28 void *in_payload, size_t payload_len);
29
30#ifdef HAVE_URING
31
32struct fuse_in_header;
33
34int fuse_uring_start(struct fuse_session *se);
35void fuse_uring_wake_ring_threads(struct fuse_session *se);
36int fuse_uring_stop(struct fuse_session *se);
37int send_reply_uring(fuse_req_t req, int error, const void *arg,
38 size_t argsize);
39
40int fuse_reply_data_uring(fuse_req_t req, struct fuse_bufvec *bufv,
41 enum fuse_buf_copy_flags flags);
42int fuse_send_msg_uring(fuse_req_t req, struct iovec *iov, int count);
43
44#else // HAVE_URING
45
46static inline int fuse_uring_start(struct fuse_session *se FUSE_VAR_UNUSED)
47{
48 return -ENOTSUP;
49}
50
51static inline void
52fuse_uring_wake_ring_threads(struct fuse_session *se FUSE_VAR_UNUSED)
53{
54}
55
56static inline int fuse_uring_stop(struct fuse_session *se FUSE_VAR_UNUSED)
57{
58 return -ENOTSUP;
59}
60
61static inline int send_reply_uring(fuse_req_t req FUSE_VAR_UNUSED,
62 int error FUSE_VAR_UNUSED,
63 const void *arg FUSE_VAR_UNUSED,
64 size_t argsize FUSE_VAR_UNUSED)
65{
66 return -ENOTSUP;
67}
68
69static inline int
70fuse_reply_data_uring(fuse_req_t req FUSE_VAR_UNUSED,
71 struct fuse_bufvec *bufv FUSE_VAR_UNUSED,
72 enum fuse_buf_copy_flags flags FUSE_VAR_UNUSED)
73{
74 return -ENOTSUP;
75}
76
77static inline int fuse_send_msg_uring(fuse_req_t req FUSE_VAR_UNUSED,
78 struct iovec *iov FUSE_VAR_UNUSED,
79 int count FUSE_VAR_UNUSED)
80{
81 return -ENOTSUP;
82}
83
84#endif // HAVE_URING
85
86#endif // FUSE_URING_I_H_
fuse_buf_copy_flags
struct fuse_req * fuse_req_t
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2helper_8c_source.html0000644000175000017500000032677715156613443022471 0ustar berndbernd libfuse: fuse-3.17.4/lib/helper.c Source File
libfuse
helper.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Helper functions to create (simple) standalone programs. With the
6 aid of these functions it should be possible to create full FUSE
7 file system by implementing nothing but the request handlers.
8
9 This program can be distributed under the terms of the GNU LGPLv2.
10 See the file COPYING.LIB.
11*/
12
13#include "fuse_config.h"
14#include "fuse_i.h"
15#include "fuse_misc.h"
16#include "fuse_opt.h"
17#include "fuse_lowlevel.h"
18#include "mount_util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <stddef.h>
23#include <unistd.h>
24#include <string.h>
25#include <limits.h>
26#include <errno.h>
27#include <sys/param.h>
28
29#define FUSE_HELPER_OPT(t, p) \
30 { t, offsetof(struct fuse_cmdline_opts, p), 1 }
31
32static const struct fuse_opt fuse_helper_opts[] = {
33 FUSE_HELPER_OPT("-h", show_help),
34 FUSE_HELPER_OPT("--help", show_help),
35 FUSE_HELPER_OPT("-V", show_version),
36 FUSE_HELPER_OPT("--version", show_version),
37 FUSE_HELPER_OPT("-d", debug),
38 FUSE_HELPER_OPT("debug", debug),
39 FUSE_HELPER_OPT("-d", foreground),
40 FUSE_HELPER_OPT("debug", foreground),
43 FUSE_HELPER_OPT("-f", foreground),
44 FUSE_HELPER_OPT("-s", singlethread),
45 FUSE_HELPER_OPT("fsname=", nodefault_subtype),
47#ifndef __FreeBSD__
48 FUSE_HELPER_OPT("subtype=", nodefault_subtype),
50#endif
51 FUSE_HELPER_OPT("clone_fd", clone_fd),
52 FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads),
53 FUSE_HELPER_OPT("max_threads=%u", max_threads),
55};
56
57struct fuse_conn_info_opts {
58 int atomic_o_trunc;
59 int no_remote_posix_lock;
60 int no_remote_flock;
61 int splice_write;
62 int splice_move;
63 int splice_read;
64 int no_splice_write;
65 int no_splice_move;
66 int no_splice_read;
67 int auto_inval_data;
68 int no_auto_inval_data;
69 int no_readdirplus;
70 int no_readdirplus_auto;
71 int async_dio;
72 int no_async_dio;
73 int writeback_cache;
74 int no_writeback_cache;
75 int async_read;
76 int sync_read;
77 unsigned max_write;
78 unsigned max_readahead;
79 unsigned max_background;
80 unsigned congestion_threshold;
81 unsigned time_gran;
82 int set_max_write;
83 int set_max_readahead;
84 int set_max_background;
85 int set_congestion_threshold;
86 int set_time_gran;
87};
88
89#define CONN_OPTION(t, p, v) \
90 { t, offsetof(struct fuse_conn_info_opts, p), v }
91static const struct fuse_opt conn_info_opt_spec[] = {
92 CONN_OPTION("max_write=%u", max_write, 0),
93 CONN_OPTION("max_write=", set_max_write, 1),
94 CONN_OPTION("max_readahead=%u", max_readahead, 0),
95 CONN_OPTION("max_readahead=", set_max_readahead, 1),
96 CONN_OPTION("max_background=%u", max_background, 0),
97 CONN_OPTION("max_background=", set_max_background, 1),
98 CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0),
99 CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1),
100 CONN_OPTION("sync_read", sync_read, 1),
101 CONN_OPTION("async_read", async_read, 1),
102 CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1),
103 CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1),
104 CONN_OPTION("no_remote_lock", no_remote_flock, 1),
105 CONN_OPTION("no_remote_flock", no_remote_flock, 1),
106 CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1),
107 CONN_OPTION("splice_write", splice_write, 1),
108 CONN_OPTION("no_splice_write", no_splice_write, 1),
109 CONN_OPTION("splice_move", splice_move, 1),
110 CONN_OPTION("no_splice_move", no_splice_move, 1),
111 CONN_OPTION("splice_read", splice_read, 1),
112 CONN_OPTION("no_splice_read", no_splice_read, 1),
113 CONN_OPTION("auto_inval_data", auto_inval_data, 1),
114 CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1),
115 CONN_OPTION("readdirplus=no", no_readdirplus, 1),
116 CONN_OPTION("readdirplus=yes", no_readdirplus, 0),
117 CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1),
118 CONN_OPTION("readdirplus=auto", no_readdirplus, 0),
119 CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0),
120 CONN_OPTION("async_dio", async_dio, 1),
121 CONN_OPTION("no_async_dio", no_async_dio, 1),
122 CONN_OPTION("writeback_cache", writeback_cache, 1),
123 CONN_OPTION("no_writeback_cache", no_writeback_cache, 1),
124 CONN_OPTION("time_gran=%u", time_gran, 0),
125 CONN_OPTION("time_gran=", set_time_gran, 1),
127};
128
129
131{
132 printf(" -h --help print help\n"
133 " -V --version print version\n"
134 " -d -o debug enable debug output (implies -f)\n"
135 " -f foreground operation\n"
136 " -s disable multi-threaded operation\n"
137 " -o clone_fd use separate fuse device fd for each thread\n"
138 " (may improve performance)\n"
139 " -o max_idle_threads the maximum number of idle worker threads\n"
140 " allowed (default: -1)\n"
141 " -o max_threads the maximum number of worker threads\n"
142 " allowed (default: 10)\n");
143}
144
145static int fuse_helper_opt_proc(void *data, const char *arg, int key,
146 struct fuse_args *outargs)
147{
148 (void) outargs;
149 struct fuse_cmdline_opts *opts = data;
150
151 switch (key) {
153 if (!opts->mountpoint) {
154 if (fuse_mnt_parse_fuse_fd(arg) != -1) {
155 return fuse_opt_add_opt(&opts->mountpoint, arg);
156 }
157
158 char mountpoint[PATH_MAX] = "";
159 if (realpath(arg, mountpoint) == NULL) {
160 fuse_log(FUSE_LOG_ERR,
161 "fuse: bad mount point `%s': %s\n",
162 arg, strerror(errno));
163 return -1;
164 }
165 return fuse_opt_add_opt(&opts->mountpoint, mountpoint);
166 } else {
167 fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg);
168 return -1;
169 }
170
171 default:
172 /* Pass through unknown options */
173 return 1;
174 }
175}
176
177/* Under FreeBSD, there is no subtype option so this
178 function actually sets the fsname */
179static int add_default_subtype(const char *progname, struct fuse_args *args)
180{
181 int res;
182 char *subtype_opt;
183
184 const char *basename = strrchr(progname, '/');
185 if (basename == NULL)
186 basename = progname;
187 else if (basename[1] != '\0')
188 basename++;
189
190 subtype_opt = (char *) malloc(strlen(basename) + 64);
191 if (subtype_opt == NULL) {
192 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
193 return -1;
194 }
195#ifdef __FreeBSD__
196 sprintf(subtype_opt, "-ofsname=%s", basename);
197#else
198 sprintf(subtype_opt, "-osubtype=%s", basename);
199#endif
200 res = fuse_opt_add_arg(args, subtype_opt);
201 free(subtype_opt);
202 return res;
203}
204
205int fuse_parse_cmdline_312(struct fuse_args *args,
206 struct fuse_cmdline_opts *opts);
207FUSE_SYMVER("fuse_parse_cmdline_312", "fuse_parse_cmdline@@FUSE_3.12")
208int fuse_parse_cmdline_312(struct fuse_args *args,
209 struct fuse_cmdline_opts *opts)
210{
211 memset(opts, 0, sizeof(struct fuse_cmdline_opts));
212
213 opts->max_idle_threads = UINT_MAX; /* new default in fuse version 3.12 */
214 opts->max_threads = 10;
215
216 if (fuse_opt_parse(args, opts, fuse_helper_opts,
217 fuse_helper_opt_proc) == -1)
218 return -1;
219
220 /* *Linux*: if neither -o subtype nor -o fsname are specified,
221 set subtype to program's basename.
222 *FreeBSD*: if fsname is not specified, set to program's
223 basename. */
224 if (!opts->nodefault_subtype)
225 if (add_default_subtype(args->argv[0], args) == -1)
226 return -1;
227
228 return 0;
229}
230
234int fuse_parse_cmdline_30(struct fuse_args *args,
235 struct fuse_cmdline_opts *opts);
236FUSE_SYMVER("fuse_parse_cmdline_30", "fuse_parse_cmdline@FUSE_3.0")
238 struct fuse_cmdline_opts *out_opts)
239{
240 struct fuse_cmdline_opts opts;
241
242 int rc = fuse_parse_cmdline_312(args, &opts);
243 if (rc == 0) {
244 /* copy up to the size of the old pre 3.12 struct */
245 memcpy(out_opts, &opts,
246 offsetof(struct fuse_cmdline_opts, max_idle_threads) +
247 sizeof(opts.max_idle_threads));
248 }
249
250 return rc;
251}
252
253int fuse_daemonize(int foreground)
254{
255 if (!foreground) {
256 int nullfd;
257 int waiter[2];
258 char completed;
259
260 if (pipe(waiter)) {
261 perror("fuse_daemonize: pipe");
262 return -1;
263 }
264
265 /*
266 * demonize current process by forking it and killing the
267 * parent. This makes current process as a child of 'init'.
268 */
269 switch(fork()) {
270 case -1:
271 perror("fuse_daemonize: fork");
272 return -1;
273 case 0:
274 break;
275 default:
276 (void) read(waiter[0], &completed, sizeof(completed));
277 _exit(0);
278 }
279
280 if (setsid() == -1) {
281 perror("fuse_daemonize: setsid");
282 return -1;
283 }
284
285 (void) chdir("/");
286
287 nullfd = open("/dev/null", O_RDWR, 0);
288 if (nullfd != -1) {
289 (void) dup2(nullfd, 0);
290 (void) dup2(nullfd, 1);
291 (void) dup2(nullfd, 2);
292 if (nullfd > 2)
293 close(nullfd);
294 }
295
296 /* Propagate completion of daemon initialization */
297 completed = 1;
298 (void) write(waiter[1], &completed, sizeof(completed));
299 close(waiter[0]);
300 close(waiter[1]);
301 } else {
302 (void) chdir("/");
303 }
304 return 0;
305}
306
307int fuse_main_real_versioned(int argc, char *argv[],
308 const struct fuse_operations *op, size_t op_size,
309 struct libfuse_version *version, void *user_data)
310{
311 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
312 struct fuse *fuse;
313 struct fuse_cmdline_opts opts;
314 int res;
315 struct fuse_loop_config *loop_config = NULL;
316
317 if (fuse_parse_cmdline(&args, &opts) != 0)
318 return 1;
319
320 if (opts.show_version) {
321 printf("FUSE library version %s\n", PACKAGE_VERSION);
323 res = 0;
324 goto out1;
325 }
326
327 if (opts.show_help) {
328 if(args.argv[0][0] != '\0')
329 printf("usage: %s [options] <mountpoint>\n\n",
330 args.argv[0]);
331 printf("FUSE options:\n");
333 fuse_lib_help(&args);
334 res = 0;
335 goto out1;
336 }
337
338 if (!opts.show_help &&
339 !opts.mountpoint) {
340 fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n");
341 res = 2;
342 goto out1;
343 }
344
345 struct fuse *_fuse_new_31(struct fuse_args *args,
346 const struct fuse_operations *op, size_t op_size,
347 struct libfuse_version *version,
348 void *user_data);
349 fuse = _fuse_new_31(&args, op, op_size, version, user_data);
350 if (fuse == NULL) {
351 res = 3;
352 goto out1;
353 }
354
355 if (fuse_mount(fuse,opts.mountpoint) != 0) {
356 res = 4;
357 goto out2;
358 }
359
360 if (fuse_daemonize(opts.foreground) != 0) {
361 res = 5;
362 goto out3;
363 }
364
365 struct fuse_session *se = fuse_get_session(fuse);
366 if (fuse_set_signal_handlers(se) != 0) {
367 res = 6;
368 goto out3;
369 }
370
371 if (opts.singlethread)
372 res = fuse_loop(fuse);
373 else {
374 loop_config = fuse_loop_cfg_create();
375 if (loop_config == NULL) {
376 res = 7;
377 goto out3;
378 }
379
380 fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
381
382 fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);
383 fuse_loop_cfg_set_max_threads(loop_config, opts.max_threads);
384 res = fuse_loop_mt(fuse, loop_config);
385 }
386 if (res)
387 res = 8;
388
390out3:
391 fuse_unmount(fuse);
392out2:
393 fuse_destroy(fuse);
394out1:
395 fuse_loop_cfg_destroy(loop_config);
396 free(opts.mountpoint);
397 fuse_opt_free_args(&args);
398 return res;
399}
400
401/* Not symboled, as not part of the official API */
402int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
403 size_t op_size, void *user_data);
404int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
405 size_t op_size, void *user_data)
406{
407 struct libfuse_version version = { 0 };
408 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
409 user_data);
410}
411
412void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
413 struct fuse_conn_info *conn)
414{
415 if(opts->set_max_write)
416 conn->max_write = opts->max_write;
417 if(opts->set_max_background)
418 conn->max_background = opts->max_background;
419 if(opts->set_congestion_threshold)
420 conn->congestion_threshold = opts->congestion_threshold;
421 if(opts->set_time_gran)
422 conn->time_gran = opts->time_gran;
423 if(opts->set_max_readahead)
424 conn->max_readahead = opts->max_readahead;
425
426#define LL_ENABLE(cond, cap) \
427 do { \
428 if (cond) \
429 fuse_set_feature_flag(conn, cap); \
430 } while (0)
431
432#define LL_DISABLE(cond, cap) \
433 do { \
434 if (cond) \
435 fuse_unset_feature_flag(conn, cap); \
436 } while (0)
437
438 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
439 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
440
441 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
442 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
443
444 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
445 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
446
447 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
448 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
449
450 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
451 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
452
453 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
454 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
455
456 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
457 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
458
459 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
460 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
461
462 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
463 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
464}
465
466struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
467{
468 struct fuse_conn_info_opts *opts;
469
470 opts = calloc(1, sizeof(struct fuse_conn_info_opts));
471 if(opts == NULL) {
472 fuse_log(FUSE_LOG_ERR, "calloc failed\n");
473 return NULL;
474 }
475 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
476 free(opts);
477 return NULL;
478 }
479 return opts;
480}
481
482int fuse_open_channel(const char *mountpoint, const char* options)
483{
484 struct mount_opts *opts = NULL;
485 int fd = -1;
486 const char *argv[] = { "", "-o", options };
487 int argc = sizeof(argv) / sizeof(argv[0]);
488 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
489
490 opts = parse_mount_opts(&args);
491 if (opts == NULL)
492 return -1;
493
494 fd = fuse_kern_mount(mountpoint, opts);
495 destroy_mount_opts(opts);
496
497 return fd;
498}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
Definition helper.c:307
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_READDIRPLUS_AUTO
struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
Definition helper.c:466
#define FUSE_CAP_ASYNC_DIO
#define FUSE_CAP_READDIRPLUS
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_NONOPT
Definition fuse_opt.h:137
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
Definition helper.c:412
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
char ** argv
Definition fuse_opt.h:114
uint32_t time_gran
uint32_t congestion_threshold
uint32_t max_write
uint32_t max_readahead
uint32_t max_background
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2helper_8c_source.html0000644000175000017500000032441015156613443022445 0ustar berndbernd libfuse: fuse-3.18.1/lib/helper.c Source File
libfuse
helper.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Helper functions to create (simple) standalone programs. With the
6 aid of these functions it should be possible to create full FUSE
7 file system by implementing nothing but the request handlers.
8
9 This program can be distributed under the terms of the GNU LGPLv2.
10 See the file LGPL2.txt.
11*/
12
13#include "fuse_config.h"
14#include "fuse_i.h"
15#include "fuse_misc.h"
16#include "fuse_opt.h"
17#include "fuse_lowlevel.h"
18#include "mount_util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <stddef.h>
23#include <unistd.h>
24#include <string.h>
25#include <limits.h>
26#include <errno.h>
27#include <sys/param.h>
28
29#define FUSE_HELPER_OPT(t, p) \
30 { t, offsetof(struct fuse_cmdline_opts, p), 1 }
31
32static const struct fuse_opt fuse_helper_opts[] = {
33 FUSE_HELPER_OPT("-h", show_help),
34 FUSE_HELPER_OPT("--help", show_help),
35 FUSE_HELPER_OPT("-V", show_version),
36 FUSE_HELPER_OPT("--version", show_version),
37 FUSE_HELPER_OPT("-d", debug),
38 FUSE_HELPER_OPT("debug", debug),
39 FUSE_HELPER_OPT("-d", foreground),
40 FUSE_HELPER_OPT("debug", foreground),
43 FUSE_HELPER_OPT("-f", foreground),
44 FUSE_HELPER_OPT("-s", singlethread),
45 FUSE_HELPER_OPT("fsname=", nodefault_subtype),
47#ifndef __FreeBSD__
48 FUSE_HELPER_OPT("subtype=", nodefault_subtype),
50#endif
51 FUSE_HELPER_OPT("clone_fd", clone_fd),
52 FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads),
53 FUSE_HELPER_OPT("max_threads=%u", max_threads),
55};
56
57struct fuse_conn_info_opts {
58 int atomic_o_trunc;
59 int no_remote_posix_lock;
60 int no_remote_flock;
61 int splice_write;
62 int splice_move;
63 int splice_read;
64 int no_splice_write;
65 int no_splice_move;
66 int no_splice_read;
67 int auto_inval_data;
68 int no_auto_inval_data;
69 int no_readdirplus;
70 int no_readdirplus_auto;
71 int async_dio;
72 int no_async_dio;
73 int writeback_cache;
74 int no_writeback_cache;
75 int async_read;
76 int sync_read;
77 unsigned max_write;
78 unsigned max_readahead;
79 unsigned max_background;
80 unsigned congestion_threshold;
81 unsigned time_gran;
82 int set_max_write;
83 int set_max_readahead;
84 int set_max_background;
85 int set_congestion_threshold;
86 int set_time_gran;
87};
88
89#define CONN_OPTION(t, p, v) \
90 { t, offsetof(struct fuse_conn_info_opts, p), v }
91static const struct fuse_opt conn_info_opt_spec[] = {
92 CONN_OPTION("max_write=%u", max_write, 0),
93 CONN_OPTION("max_write=", set_max_write, 1),
94 CONN_OPTION("max_readahead=%u", max_readahead, 0),
95 CONN_OPTION("max_readahead=", set_max_readahead, 1),
96 CONN_OPTION("max_background=%u", max_background, 0),
97 CONN_OPTION("max_background=", set_max_background, 1),
98 CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0),
99 CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1),
100 CONN_OPTION("sync_read", sync_read, 1),
101 CONN_OPTION("async_read", async_read, 1),
102 CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1),
103 CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1),
104 CONN_OPTION("no_remote_lock", no_remote_flock, 1),
105 CONN_OPTION("no_remote_flock", no_remote_flock, 1),
106 CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1),
107 CONN_OPTION("splice_write", splice_write, 1),
108 CONN_OPTION("no_splice_write", no_splice_write, 1),
109 CONN_OPTION("splice_move", splice_move, 1),
110 CONN_OPTION("no_splice_move", no_splice_move, 1),
111 CONN_OPTION("splice_read", splice_read, 1),
112 CONN_OPTION("no_splice_read", no_splice_read, 1),
113 CONN_OPTION("auto_inval_data", auto_inval_data, 1),
114 CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1),
115 CONN_OPTION("readdirplus=no", no_readdirplus, 1),
116 CONN_OPTION("readdirplus=yes", no_readdirplus, 0),
117 CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1),
118 CONN_OPTION("readdirplus=auto", no_readdirplus, 0),
119 CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0),
120 CONN_OPTION("async_dio", async_dio, 1),
121 CONN_OPTION("no_async_dio", no_async_dio, 1),
122 CONN_OPTION("writeback_cache", writeback_cache, 1),
123 CONN_OPTION("no_writeback_cache", no_writeback_cache, 1),
124 CONN_OPTION("time_gran=%u", time_gran, 0),
125 CONN_OPTION("time_gran=", set_time_gran, 1),
127};
128
129
130void fuse_cmdline_help(void)
131{
132 printf(" -h --help print help\n"
133 " -V --version print version\n"
134 " -d -o debug enable debug output (implies -f)\n"
135 " -f foreground operation\n"
136 " -s disable multi-threaded operation\n"
137 " -o clone_fd use separate fuse device fd for each thread\n"
138 " (may improve performance)\n"
139 " -o max_idle_threads the maximum number of idle worker threads\n"
140 " allowed (default: -1)\n"
141 " -o max_threads the maximum number of worker threads\n"
142 " allowed (default: 10)\n");
143}
144
145static int fuse_helper_opt_proc(void *data, const char *arg, int key,
146 struct fuse_args *outargs)
147{
148 (void) outargs;
149 struct fuse_cmdline_opts *opts = data;
150
151 switch (key) {
153 if (!opts->mountpoint) {
154 if (fuse_mnt_parse_fuse_fd(arg) != -1) {
155 return fuse_opt_add_opt(&opts->mountpoint, arg);
156 }
157
158 char mountpoint[PATH_MAX] = "";
159 if (realpath(arg, mountpoint) == NULL) {
160 fuse_log(FUSE_LOG_ERR,
161 "fuse: bad mount point `%s': %s\n",
162 arg, strerror(errno));
163 return -1;
164 }
165 return fuse_opt_add_opt(&opts->mountpoint, mountpoint);
166 } else {
167 fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg);
168 return -1;
169 }
170
171 default:
172 /* Pass through unknown options */
173 return 1;
174 }
175}
176
177/* Under FreeBSD, there is no subtype option so this
178 function actually sets the fsname */
179static int add_default_subtype(const char *progname, struct fuse_args *args)
180{
181 int res;
182 char *subtype_opt;
183
184 const char *basename = strrchr(progname, '/');
185 if (basename == NULL)
186 basename = progname;
187 else if (basename[1] != '\0')
188 basename++;
189
190 subtype_opt = (char *) malloc(strlen(basename) + 64);
191 if (subtype_opt == NULL) {
192 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
193 return -1;
194 }
195#ifdef __FreeBSD__
196 sprintf(subtype_opt, "-ofsname=%s", basename);
197#else
198 sprintf(subtype_opt, "-osubtype=%s", basename);
199#endif
200 res = fuse_opt_add_arg(args, subtype_opt);
201 free(subtype_opt);
202 return res;
203}
204
205int fuse_parse_cmdline_312(struct fuse_args *args,
206 struct fuse_cmdline_opts *opts);
207FUSE_SYMVER("fuse_parse_cmdline_312", "fuse_parse_cmdline@@FUSE_3.12")
208int fuse_parse_cmdline_312(struct fuse_args *args,
209 struct fuse_cmdline_opts *opts)
210{
211 memset(opts, 0, sizeof(struct fuse_cmdline_opts));
212
213 opts->max_idle_threads = UINT_MAX; /* new default in fuse version 3.12 */
214 opts->max_threads = 10;
215
216 if (fuse_opt_parse(args, opts, fuse_helper_opts,
217 fuse_helper_opt_proc) == -1)
218 return -1;
219
220 /* *Linux*: if neither -o subtype nor -o fsname are specified,
221 set subtype to program's basename.
222 *FreeBSD*: if fsname is not specified, set to program's
223 basename. */
224 if (!opts->nodefault_subtype)
225 if (add_default_subtype(args->argv[0], args) == -1)
226 return -1;
227
228 return 0;
229}
230
234int fuse_parse_cmdline_30(struct fuse_args *args,
235 struct fuse_cmdline_opts *opts);
236FUSE_SYMVER("fuse_parse_cmdline_30", "fuse_parse_cmdline@FUSE_3.0")
237int fuse_parse_cmdline_30(struct fuse_args *args,
238 struct fuse_cmdline_opts *out_opts)
239{
240 struct fuse_cmdline_opts opts;
241
242 int rc = fuse_parse_cmdline_312(args, &opts);
243 if (rc == 0) {
244 /* copy up to the size of the old pre 3.12 struct */
245 memcpy(out_opts, &opts,
246 offsetof(struct fuse_cmdline_opts, max_idle_threads) +
247 sizeof(opts.max_idle_threads));
248 }
249
250 return rc;
251}
252
253int fuse_daemonize(int foreground)
254{
255 if (!foreground) {
256 int nullfd;
257 int waiter[2];
258 char completed;
259
260 if (pipe(waiter)) {
261 perror("fuse_daemonize: pipe");
262 return -1;
263 }
264
265 /*
266 * demonize current process by forking it and killing the
267 * parent. This makes current process as a child of 'init'.
268 */
269 switch(fork()) {
270 case -1:
271 perror("fuse_daemonize: fork");
272 return -1;
273 case 0:
274 break;
275 default:
276 (void) read(waiter[0], &completed, sizeof(completed));
277 _exit(0);
278 }
279
280 if (setsid() == -1) {
281 perror("fuse_daemonize: setsid");
282 return -1;
283 }
284
285 (void) chdir("/");
286
287 nullfd = open("/dev/null", O_RDWR, 0);
288 if (nullfd != -1) {
289 (void) dup2(nullfd, 0);
290 (void) dup2(nullfd, 1);
291 (void) dup2(nullfd, 2);
292 if (nullfd > 2)
293 close(nullfd);
294 }
295
296 /* Propagate completion of daemon initialization */
297 completed = 1;
298 (void) write(waiter[1], &completed, sizeof(completed));
299 close(waiter[0]);
300 close(waiter[1]);
301 } else {
302 (void) chdir("/");
303 }
304 return 0;
305}
306
307int fuse_main_real_versioned(int argc, char *argv[],
308 const struct fuse_operations *op, size_t op_size,
309 struct libfuse_version *version, void *user_data)
310{
311 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
312 struct fuse *fuse;
313 struct fuse_cmdline_opts opts;
314 int res;
315 struct fuse_loop_config *loop_config = NULL;
316
317 if (fuse_parse_cmdline(&args, &opts) != 0)
318 return 1;
319
320 if (opts.show_version) {
321 printf("FUSE library version %s\n", PACKAGE_VERSION);
323 res = 0;
324 goto out1;
325 }
326
327 if (opts.show_help) {
328 if(args.argv[0][0] != '\0')
329 printf("usage: %s [options] <mountpoint>\n\n",
330 args.argv[0]);
331 printf("FUSE options:\n");
333 fuse_lib_help(&args);
334 res = 0;
335 goto out1;
336 }
337
338 if (!opts.show_help &&
339 !opts.mountpoint) {
340 fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n");
341 res = 2;
342 goto out1;
343 }
344
345 struct fuse *_fuse_new_31(struct fuse_args *args,
346 const struct fuse_operations *op, size_t op_size,
347 struct libfuse_version *version,
348 void *user_data);
349 fuse = _fuse_new_31(&args, op, op_size, version, user_data);
350 if (fuse == NULL) {
351 res = 3;
352 goto out1;
353 }
354
355 if (fuse_mount(fuse,opts.mountpoint) != 0) {
356 res = 4;
357 goto out2;
358 }
359
360 if (fuse_daemonize(opts.foreground) != 0) {
361 res = 5;
362 goto out3;
363 }
364
365 struct fuse_session *se = fuse_get_session(fuse);
366 if (fuse_set_signal_handlers(se) != 0) {
367 res = 6;
368 goto out3;
369 }
370
371 if (opts.singlethread)
372 res = fuse_loop(fuse);
373 else {
374 loop_config = fuse_loop_cfg_create();
375 if (loop_config == NULL) {
376 res = 7;
377 goto out3;
378 }
379
380 fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
381
382 fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);
383 fuse_loop_cfg_set_max_threads(loop_config, opts.max_threads);
384 res = fuse_loop_mt(fuse, loop_config);
385 }
386 if (res)
387 res = 8;
388
390out3:
391 fuse_unmount(fuse);
392out2:
393 fuse_destroy(fuse);
394out1:
395 fuse_loop_cfg_destroy(loop_config);
396 free(opts.mountpoint);
397 fuse_opt_free_args(&args);
398 return res;
399}
400
401/* Not symboled, as not part of the official API */
402int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
403 size_t op_size, void *user_data);
404int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
405 size_t op_size, void *user_data)
406{
407 struct libfuse_version version = { 0 };
408 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
409 user_data);
410}
411
412void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
413 struct fuse_conn_info *conn)
414{
415 if(opts->set_max_write)
416 conn->max_write = opts->max_write;
417 if(opts->set_max_background)
418 conn->max_background = opts->max_background;
419 if(opts->set_congestion_threshold)
420 conn->congestion_threshold = opts->congestion_threshold;
421 if(opts->set_time_gran)
422 conn->time_gran = opts->time_gran;
423 if(opts->set_max_readahead)
424 conn->max_readahead = opts->max_readahead;
425
426#define LL_ENABLE(cond, cap) \
427 do { \
428 if (cond) \
429 fuse_set_feature_flag(conn, cap); \
430 } while (0)
431
432#define LL_DISABLE(cond, cap) \
433 do { \
434 if (cond) \
435 fuse_unset_feature_flag(conn, cap); \
436 } while (0)
437
438 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
439 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
440
441 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
442 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
443
444 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
445 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
446
447 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
448 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
449
450 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
451 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
452
453 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
454 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
455
456 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
457 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
458
459 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
460 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
461
462 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
463 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
464}
465
466struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
467{
468 struct fuse_conn_info_opts *opts;
469
470 opts = calloc(1, sizeof(struct fuse_conn_info_opts));
471 if(opts == NULL) {
472 fuse_log(FUSE_LOG_ERR, "calloc failed\n");
473 return NULL;
474 }
475 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
476 free(opts);
477 return NULL;
478 }
479 return opts;
480}
481
482int fuse_open_channel(const char *mountpoint, const char* options)
483{
484 struct mount_opts *opts = NULL;
485 int fd = -1;
486 const char *argv[] = { "", "-o", options };
487 int argc = sizeof(argv) / sizeof(argv[0]);
488 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
489
490 opts = parse_mount_opts(&args);
491 if (opts == NULL)
492 return -1;
493
494 fd = fuse_kern_mount(mountpoint, opts);
495 destroy_mount_opts(opts);
496
497 return fd;
498}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
Definition helper.c:307
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_READDIRPLUS_AUTO
struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
Definition helper.c:466
#define FUSE_CAP_ASYNC_DIO
#define FUSE_CAP_READDIRPLUS
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_NONOPT
Definition fuse_opt.h:137
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
Definition helper.c:412
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
char ** argv
Definition fuse_opt.h:114
uint32_t time_gran
uint32_t congestion_threshold
uint32_t max_write
uint32_t max_readahead
uint32_t max_background
fuse-3.18.2/doc/html/lib_2helper_8c_source.html0000644000175000017500000032422515156613443020275 0ustar berndbernd libfuse: lib/helper.c Source File
libfuse
helper.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Helper functions to create (simple) standalone programs. With the
6 aid of these functions it should be possible to create full FUSE
7 file system by implementing nothing but the request handlers.
8
9 This program can be distributed under the terms of the GNU LGPLv2.
10 See the file LGPL2.txt.
11*/
12
13#include "fuse_config.h"
14#include "fuse_i.h"
15#include "fuse_misc.h"
16#include "fuse_opt.h"
17#include "fuse_lowlevel.h"
18#include "mount_util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <stddef.h>
23#include <unistd.h>
24#include <string.h>
25#include <limits.h>
26#include <errno.h>
27#include <sys/param.h>
28
29#define FUSE_HELPER_OPT(t, p) \
30 { t, offsetof(struct fuse_cmdline_opts, p), 1 }
31
32static const struct fuse_opt fuse_helper_opts[] = {
33 FUSE_HELPER_OPT("-h", show_help),
34 FUSE_HELPER_OPT("--help", show_help),
35 FUSE_HELPER_OPT("-V", show_version),
36 FUSE_HELPER_OPT("--version", show_version),
37 FUSE_HELPER_OPT("-d", debug),
38 FUSE_HELPER_OPT("debug", debug),
39 FUSE_HELPER_OPT("-d", foreground),
40 FUSE_HELPER_OPT("debug", foreground),
43 FUSE_HELPER_OPT("-f", foreground),
44 FUSE_HELPER_OPT("-s", singlethread),
45 FUSE_HELPER_OPT("fsname=", nodefault_subtype),
47#ifndef __FreeBSD__
48 FUSE_HELPER_OPT("subtype=", nodefault_subtype),
50#endif
51 FUSE_HELPER_OPT("clone_fd", clone_fd),
52 FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads),
53 FUSE_HELPER_OPT("max_threads=%u", max_threads),
55};
56
57struct fuse_conn_info_opts {
58 int atomic_o_trunc;
59 int no_remote_posix_lock;
60 int no_remote_flock;
61 int splice_write;
62 int splice_move;
63 int splice_read;
64 int no_splice_write;
65 int no_splice_move;
66 int no_splice_read;
67 int auto_inval_data;
68 int no_auto_inval_data;
69 int no_readdirplus;
70 int no_readdirplus_auto;
71 int async_dio;
72 int no_async_dio;
73 int writeback_cache;
74 int no_writeback_cache;
75 int async_read;
76 int sync_read;
77 unsigned max_write;
78 unsigned max_readahead;
79 unsigned max_background;
80 unsigned congestion_threshold;
81 unsigned time_gran;
82 int set_max_write;
83 int set_max_readahead;
84 int set_max_background;
85 int set_congestion_threshold;
86 int set_time_gran;
87};
88
89#define CONN_OPTION(t, p, v) \
90 { t, offsetof(struct fuse_conn_info_opts, p), v }
91static const struct fuse_opt conn_info_opt_spec[] = {
92 CONN_OPTION("max_write=%u", max_write, 0),
93 CONN_OPTION("max_write=", set_max_write, 1),
94 CONN_OPTION("max_readahead=%u", max_readahead, 0),
95 CONN_OPTION("max_readahead=", set_max_readahead, 1),
96 CONN_OPTION("max_background=%u", max_background, 0),
97 CONN_OPTION("max_background=", set_max_background, 1),
98 CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0),
99 CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1),
100 CONN_OPTION("sync_read", sync_read, 1),
101 CONN_OPTION("async_read", async_read, 1),
102 CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1),
103 CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1),
104 CONN_OPTION("no_remote_lock", no_remote_flock, 1),
105 CONN_OPTION("no_remote_flock", no_remote_flock, 1),
106 CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1),
107 CONN_OPTION("splice_write", splice_write, 1),
108 CONN_OPTION("no_splice_write", no_splice_write, 1),
109 CONN_OPTION("splice_move", splice_move, 1),
110 CONN_OPTION("no_splice_move", no_splice_move, 1),
111 CONN_OPTION("splice_read", splice_read, 1),
112 CONN_OPTION("no_splice_read", no_splice_read, 1),
113 CONN_OPTION("auto_inval_data", auto_inval_data, 1),
114 CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1),
115 CONN_OPTION("readdirplus=no", no_readdirplus, 1),
116 CONN_OPTION("readdirplus=yes", no_readdirplus, 0),
117 CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1),
118 CONN_OPTION("readdirplus=auto", no_readdirplus, 0),
119 CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0),
120 CONN_OPTION("async_dio", async_dio, 1),
121 CONN_OPTION("no_async_dio", no_async_dio, 1),
122 CONN_OPTION("writeback_cache", writeback_cache, 1),
123 CONN_OPTION("no_writeback_cache", no_writeback_cache, 1),
124 CONN_OPTION("time_gran=%u", time_gran, 0),
125 CONN_OPTION("time_gran=", set_time_gran, 1),
127};
128
129
130void fuse_cmdline_help(void)
131{
132 printf(" -h --help print help\n"
133 " -V --version print version\n"
134 " -d -o debug enable debug output (implies -f)\n"
135 " -f foreground operation\n"
136 " -s disable multi-threaded operation\n"
137 " -o clone_fd use separate fuse device fd for each thread\n"
138 " (may improve performance)\n"
139 " -o max_idle_threads the maximum number of idle worker threads\n"
140 " allowed (default: -1)\n"
141 " -o max_threads the maximum number of worker threads\n"
142 " allowed (default: 10)\n");
143}
144
145static int fuse_helper_opt_proc(void *data, const char *arg, int key,
146 struct fuse_args *outargs)
147{
148 (void) outargs;
149 struct fuse_cmdline_opts *opts = data;
150
151 switch (key) {
153 if (!opts->mountpoint) {
154 if (fuse_mnt_parse_fuse_fd(arg) != -1) {
155 return fuse_opt_add_opt(&opts->mountpoint, arg);
156 }
157
158 char mountpoint[PATH_MAX] = "";
159 if (realpath(arg, mountpoint) == NULL) {
160 fuse_log(FUSE_LOG_ERR,
161 "fuse: bad mount point `%s': %s\n",
162 arg, strerror(errno));
163 return -1;
164 }
165 return fuse_opt_add_opt(&opts->mountpoint, mountpoint);
166 } else {
167 fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg);
168 return -1;
169 }
170
171 default:
172 /* Pass through unknown options */
173 return 1;
174 }
175}
176
177/* Under FreeBSD, there is no subtype option so this
178 function actually sets the fsname */
179static int add_default_subtype(const char *progname, struct fuse_args *args)
180{
181 int res;
182 char *subtype_opt;
183
184 const char *basename = strrchr(progname, '/');
185 if (basename == NULL)
186 basename = progname;
187 else if (basename[1] != '\0')
188 basename++;
189
190 subtype_opt = (char *) malloc(strlen(basename) + 64);
191 if (subtype_opt == NULL) {
192 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
193 return -1;
194 }
195#ifdef __FreeBSD__
196 sprintf(subtype_opt, "-ofsname=%s", basename);
197#else
198 sprintf(subtype_opt, "-osubtype=%s", basename);
199#endif
200 res = fuse_opt_add_arg(args, subtype_opt);
201 free(subtype_opt);
202 return res;
203}
204
205int fuse_parse_cmdline_312(struct fuse_args *args,
206 struct fuse_cmdline_opts *opts);
207FUSE_SYMVER("fuse_parse_cmdline_312", "fuse_parse_cmdline@@FUSE_3.12")
208int fuse_parse_cmdline_312(struct fuse_args *args,
209 struct fuse_cmdline_opts *opts)
210{
211 memset(opts, 0, sizeof(struct fuse_cmdline_opts));
212
213 opts->max_idle_threads = UINT_MAX; /* new default in fuse version 3.12 */
214 opts->max_threads = 10;
215
216 if (fuse_opt_parse(args, opts, fuse_helper_opts,
217 fuse_helper_opt_proc) == -1)
218 return -1;
219
220 /* *Linux*: if neither -o subtype nor -o fsname are specified,
221 set subtype to program's basename.
222 *FreeBSD*: if fsname is not specified, set to program's
223 basename. */
224 if (!opts->nodefault_subtype)
225 if (add_default_subtype(args->argv[0], args) == -1)
226 return -1;
227
228 return 0;
229}
230
234int fuse_parse_cmdline_30(struct fuse_args *args,
235 struct fuse_cmdline_opts *opts);
236FUSE_SYMVER("fuse_parse_cmdline_30", "fuse_parse_cmdline@FUSE_3.0")
237int fuse_parse_cmdline_30(struct fuse_args *args,
238 struct fuse_cmdline_opts *out_opts)
239{
240 struct fuse_cmdline_opts opts;
241
242 int rc = fuse_parse_cmdline_312(args, &opts);
243 if (rc == 0) {
244 /* copy up to the size of the old pre 3.12 struct */
245 memcpy(out_opts, &opts,
246 offsetof(struct fuse_cmdline_opts, max_idle_threads) +
247 sizeof(opts.max_idle_threads));
248 }
249
250 return rc;
251}
252
253int fuse_daemonize(int foreground)
254{
255 if (!foreground) {
256 int nullfd;
257 int waiter[2];
258 char completed;
259
260 if (pipe(waiter)) {
261 perror("fuse_daemonize: pipe");
262 return -1;
263 }
264
265 /*
266 * demonize current process by forking it and killing the
267 * parent. This makes current process as a child of 'init'.
268 */
269 switch(fork()) {
270 case -1:
271 perror("fuse_daemonize: fork");
272 return -1;
273 case 0:
274 break;
275 default:
276 (void) read(waiter[0], &completed, sizeof(completed));
277 _exit(0);
278 }
279
280 if (setsid() == -1) {
281 perror("fuse_daemonize: setsid");
282 return -1;
283 }
284
285 (void) chdir("/");
286
287 nullfd = open("/dev/null", O_RDWR, 0);
288 if (nullfd != -1) {
289 (void) dup2(nullfd, 0);
290 (void) dup2(nullfd, 1);
291 (void) dup2(nullfd, 2);
292 if (nullfd > 2)
293 close(nullfd);
294 }
295
296 /* Propagate completion of daemon initialization */
297 completed = 1;
298 (void) write(waiter[1], &completed, sizeof(completed));
299 close(waiter[0]);
300 close(waiter[1]);
301 } else {
302 (void) chdir("/");
303 }
304 return 0;
305}
306
307int fuse_main_real_versioned(int argc, char *argv[],
308 const struct fuse_operations *op, size_t op_size,
309 struct libfuse_version *version, void *user_data)
310{
311 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
312 struct fuse *fuse;
313 struct fuse_cmdline_opts opts;
314 int res;
315 struct fuse_loop_config *loop_config = NULL;
316
317 if (fuse_parse_cmdline(&args, &opts) != 0)
318 return 1;
319
320 if (opts.show_version) {
321 printf("FUSE library version %s\n", PACKAGE_VERSION);
323 res = 0;
324 goto out1;
325 }
326
327 if (opts.show_help) {
328 if(args.argv[0][0] != '\0')
329 printf("usage: %s [options] <mountpoint>\n\n",
330 args.argv[0]);
331 printf("FUSE options:\n");
333 fuse_lib_help(&args);
334 res = 0;
335 goto out1;
336 }
337
338 if (!opts.show_help &&
339 !opts.mountpoint) {
340 fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n");
341 res = 2;
342 goto out1;
343 }
344
345 struct fuse *_fuse_new_31(struct fuse_args *args,
346 const struct fuse_operations *op, size_t op_size,
347 struct libfuse_version *version,
348 void *user_data);
349 fuse = _fuse_new_31(&args, op, op_size, version, user_data);
350 if (fuse == NULL) {
351 res = 3;
352 goto out1;
353 }
354
355 if (fuse_mount(fuse,opts.mountpoint) != 0) {
356 res = 4;
357 goto out2;
358 }
359
360 if (fuse_daemonize(opts.foreground) != 0) {
361 res = 5;
362 goto out3;
363 }
364
365 struct fuse_session *se = fuse_get_session(fuse);
366 if (fuse_set_signal_handlers(se) != 0) {
367 res = 6;
368 goto out3;
369 }
370
371 if (opts.singlethread)
372 res = fuse_loop(fuse);
373 else {
374 loop_config = fuse_loop_cfg_create();
375 if (loop_config == NULL) {
376 res = 7;
377 goto out3;
378 }
379
380 fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
381
382 fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);
383 fuse_loop_cfg_set_max_threads(loop_config, opts.max_threads);
384 res = fuse_loop_mt(fuse, loop_config);
385 }
386 if (res)
387 res = 8;
388
390out3:
391 fuse_unmount(fuse);
392out2:
393 fuse_destroy(fuse);
394out1:
395 fuse_loop_cfg_destroy(loop_config);
396 free(opts.mountpoint);
397 fuse_opt_free_args(&args);
398 return res;
399}
400
401/* Not symboled, as not part of the official API */
402int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
403 size_t op_size, void *user_data);
404int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
405 size_t op_size, void *user_data)
406{
407 struct libfuse_version version = { 0 };
408 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
409 user_data);
410}
411
412void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
413 struct fuse_conn_info *conn)
414{
415 if(opts->set_max_write)
416 conn->max_write = opts->max_write;
417 if(opts->set_max_background)
418 conn->max_background = opts->max_background;
419 if(opts->set_congestion_threshold)
420 conn->congestion_threshold = opts->congestion_threshold;
421 if(opts->set_time_gran)
422 conn->time_gran = opts->time_gran;
423 if(opts->set_max_readahead)
424 conn->max_readahead = opts->max_readahead;
425
426#define LL_ENABLE(cond, cap) \
427 do { \
428 if (cond) \
429 fuse_set_feature_flag(conn, cap); \
430 } while (0)
431
432#define LL_DISABLE(cond, cap) \
433 do { \
434 if (cond) \
435 fuse_unset_feature_flag(conn, cap); \
436 } while (0)
437
438 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
439 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
440
441 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
442 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
443
444 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
445 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
446
447 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
448 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
449
450 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
451 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
452
453 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
454 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
455
456 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
457 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
458
459 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
460 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
461
462 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
463 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
464}
465
466struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
467{
468 struct fuse_conn_info_opts *opts;
469
470 opts = calloc(1, sizeof(struct fuse_conn_info_opts));
471 if(opts == NULL) {
472 fuse_log(FUSE_LOG_ERR, "calloc failed\n");
473 return NULL;
474 }
475 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
476 free(opts);
477 return NULL;
478 }
479 return opts;
480}
481
482int fuse_open_channel(const char *mountpoint, const char* options)
483{
484 struct mount_opts *opts = NULL;
485 int fd = -1;
486 const char *argv[] = { "", "-o", options };
487 int argc = sizeof(argv) / sizeof(argv[0]);
488 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
489
490 opts = parse_mount_opts(&args);
491 if (opts == NULL)
492 return -1;
493
494 fd = fuse_kern_mount(mountpoint, opts);
495 destroy_mount_opts(opts);
496
497 return fd;
498}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
Definition helper.c:307
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_READDIRPLUS_AUTO
struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
Definition helper.c:466
#define FUSE_CAP_ASYNC_DIO
#define FUSE_CAP_READDIRPLUS
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_NONOPT
Definition fuse_opt.h:137
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
Definition helper.c:412
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
char ** argv
Definition fuse_opt.h:114
uint32_t time_gran
uint32_t congestion_threshold
uint32_t max_write
uint32_t max_readahead
uint32_t max_background
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2modules_2iconv_8c_source.html0000644000175000017500000034774415156613443024137 0ustar berndbernd libfuse: fuse-3.17.4/lib/modules/iconv.c Source File
libfuse
iconv.c
1/*
2 fuse iconv module: file name charset conversion
3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
7*/
8
9#include <fuse_config.h>
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17#include <iconv.h>
18#include <pthread.h>
19#include <locale.h>
20#include <langinfo.h>
21
22struct iconv {
23 struct fuse_fs *next;
24 pthread_mutex_t lock;
25 char *from_code;
26 char *to_code;
27 iconv_t tofs;
28 iconv_t fromfs;
29};
30
31struct iconv_dh {
32 struct iconv *ic;
33 void *prev_buf;
34 fuse_fill_dir_t prev_filler;
35};
36
37static struct iconv *iconv_get(void)
38{
40}
41
42static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
43 int fromfs)
44{
45 size_t pathlen;
46 size_t newpathlen;
47 char *newpath;
48 size_t plen;
49 char *p;
50 size_t res;
51 int err;
52
53 if (path == NULL) {
54 *newpathp = NULL;
55 return 0;
56 }
57
58 pathlen = strlen(path);
59 newpathlen = pathlen * 4;
60 newpath = malloc(newpathlen + 1);
61 if (!newpath)
62 return -ENOMEM;
63
64 plen = newpathlen;
65 p = newpath;
66 pthread_mutex_lock(&ic->lock);
67 do {
68 res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path,
69 &pathlen, &p, &plen);
70 if (res == (size_t) -1) {
71 char *tmp;
72 size_t inc;
73
74 err = -EILSEQ;
75 if (errno != E2BIG)
76 goto err;
77
78 inc = (pathlen + 1) * 4;
79 newpathlen += inc;
80 int dp = p - newpath;
81 tmp = realloc(newpath, newpathlen + 1);
82 err = -ENOMEM;
83 if (!tmp)
84 goto err;
85
86 p = tmp + dp;
87 plen += inc;
88 newpath = tmp;
89 }
90 } while (res == (size_t) -1);
91 pthread_mutex_unlock(&ic->lock);
92 *p = '\0';
93 *newpathp = newpath;
94 return 0;
95
96err:
97 iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL);
98 pthread_mutex_unlock(&ic->lock);
99 free(newpath);
100 return err;
101}
102
103static int iconv_getattr(const char *path, struct stat *stbuf,
104 struct fuse_file_info *fi)
105{
106 struct iconv *ic = iconv_get();
107 char *newpath;
108 int err = iconv_convpath(ic, path, &newpath, 0);
109 if (!err) {
110 err = fuse_fs_getattr(ic->next, newpath, stbuf, fi);
111 free(newpath);
112 }
113 return err;
114}
115
116static int iconv_access(const char *path, int mask)
117{
118 struct iconv *ic = iconv_get();
119 char *newpath;
120 int err = iconv_convpath(ic, path, &newpath, 0);
121 if (!err) {
122 err = fuse_fs_access(ic->next, newpath, mask);
123 free(newpath);
124 }
125 return err;
126}
127
128static int iconv_readlink(const char *path, char *buf, size_t size)
129{
130 struct iconv *ic = iconv_get();
131 char *newpath;
132 int err = iconv_convpath(ic, path, &newpath, 0);
133 if (!err) {
134 err = fuse_fs_readlink(ic->next, newpath, buf, size);
135 if (!err) {
136 char *newlink;
137 err = iconv_convpath(ic, buf, &newlink, 1);
138 if (!err) {
139 strncpy(buf, newlink, size - 1);
140 buf[size - 1] = '\0';
141 free(newlink);
142 }
143 }
144 free(newpath);
145 }
146 return err;
147}
148
149static int iconv_opendir(const char *path, struct fuse_file_info *fi)
150{
151 struct iconv *ic = iconv_get();
152 char *newpath;
153 int err = iconv_convpath(ic, path, &newpath, 0);
154 if (!err) {
155 err = fuse_fs_opendir(ic->next, newpath, fi);
156 free(newpath);
157 }
158 return err;
159}
160
161static int iconv_dir_fill(void *buf, const char *name,
162 const struct stat *stbuf, off_t off,
163 enum fuse_fill_dir_flags flags)
164{
165 struct iconv_dh *dh = buf;
166 char *newname;
167 int res = 0;
168 if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
169 res = dh->prev_filler(dh->prev_buf, newname, stbuf, off, flags);
170 free(newname);
171 }
172 return res;
173}
174
175static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
176 off_t offset, struct fuse_file_info *fi,
177 enum fuse_readdir_flags flags)
178{
179 struct iconv *ic = iconv_get();
180 char *newpath;
181 int err = iconv_convpath(ic, path, &newpath, 0);
182 if (!err) {
183 struct iconv_dh dh;
184 dh.ic = ic;
185 dh.prev_buf = buf;
186 dh.prev_filler = filler;
187 err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill,
188 offset, fi, flags);
189 free(newpath);
190 }
191 return err;
192}
193
194static int iconv_releasedir(const char *path, struct fuse_file_info *fi)
195{
196 struct iconv *ic = iconv_get();
197 char *newpath;
198 int err = iconv_convpath(ic, path, &newpath, 0);
199 if (!err) {
200 err = fuse_fs_releasedir(ic->next, newpath, fi);
201 free(newpath);
202 }
203 return err;
204}
205
206static int iconv_mknod(const char *path, mode_t mode, dev_t rdev)
207{
208 struct iconv *ic = iconv_get();
209 char *newpath;
210 int err = iconv_convpath(ic, path, &newpath, 0);
211 if (!err) {
212 err = fuse_fs_mknod(ic->next, newpath, mode, rdev);
213 free(newpath);
214 }
215 return err;
216}
217
218static int iconv_mkdir(const char *path, mode_t mode)
219{
220 struct iconv *ic = iconv_get();
221 char *newpath;
222 int err = iconv_convpath(ic, path, &newpath, 0);
223 if (!err) {
224 err = fuse_fs_mkdir(ic->next, newpath, mode);
225 free(newpath);
226 }
227 return err;
228}
229
230static int iconv_unlink(const char *path)
231{
232 struct iconv *ic = iconv_get();
233 char *newpath;
234 int err = iconv_convpath(ic, path, &newpath, 0);
235 if (!err) {
236 err = fuse_fs_unlink(ic->next, newpath);
237 free(newpath);
238 }
239 return err;
240}
241
242static int iconv_rmdir(const char *path)
243{
244 struct iconv *ic = iconv_get();
245 char *newpath;
246 int err = iconv_convpath(ic, path, &newpath, 0);
247 if (!err) {
248 err = fuse_fs_rmdir(ic->next, newpath);
249 free(newpath);
250 }
251 return err;
252}
253
254static int iconv_symlink(const char *from, const char *to)
255{
256 struct iconv *ic = iconv_get();
257 char *newfrom;
258 char *newto;
259 int err = iconv_convpath(ic, from, &newfrom, 0);
260 if (!err) {
261 err = iconv_convpath(ic, to, &newto, 0);
262 if (!err) {
263 err = fuse_fs_symlink(ic->next, newfrom, newto);
264 free(newto);
265 }
266 free(newfrom);
267 }
268 return err;
269}
270
271static int iconv_rename(const char *from, const char *to, unsigned int flags)
272{
273 struct iconv *ic = iconv_get();
274 char *newfrom;
275 char *newto;
276 int err = iconv_convpath(ic, from, &newfrom, 0);
277 if (!err) {
278 err = iconv_convpath(ic, to, &newto, 0);
279 if (!err) {
280 err = fuse_fs_rename(ic->next, newfrom, newto, flags);
281 free(newto);
282 }
283 free(newfrom);
284 }
285 return err;
286}
287
288static int iconv_link(const char *from, const char *to)
289{
290 struct iconv *ic = iconv_get();
291 char *newfrom;
292 char *newto;
293 int err = iconv_convpath(ic, from, &newfrom, 0);
294 if (!err) {
295 err = iconv_convpath(ic, to, &newto, 0);
296 if (!err) {
297 err = fuse_fs_link(ic->next, newfrom, newto);
298 free(newto);
299 }
300 free(newfrom);
301 }
302 return err;
303}
304
305static int iconv_chmod(const char *path, mode_t mode,
306 struct fuse_file_info *fi)
307{
308 struct iconv *ic = iconv_get();
309 char *newpath;
310 int err = iconv_convpath(ic, path, &newpath, 0);
311 if (!err) {
312 err = fuse_fs_chmod(ic->next, newpath, mode, fi);
313 free(newpath);
314 }
315 return err;
316}
317
318static int iconv_chown(const char *path, uid_t uid, gid_t gid,
319 struct fuse_file_info *fi)
320{
321 struct iconv *ic = iconv_get();
322 char *newpath;
323 int err = iconv_convpath(ic, path, &newpath, 0);
324 if (!err) {
325 err = fuse_fs_chown(ic->next, newpath, uid, gid, fi);
326 free(newpath);
327 }
328 return err;
329}
330
331static int iconv_truncate(const char *path, off_t size,
332 struct fuse_file_info *fi)
333{
334 struct iconv *ic = iconv_get();
335 char *newpath;
336 int err = iconv_convpath(ic, path, &newpath, 0);
337 if (!err) {
338 err = fuse_fs_truncate(ic->next, newpath, size, fi);
339 free(newpath);
340 }
341 return err;
342}
343
344static int iconv_utimens(const char *path, const struct timespec ts[2],
345 struct fuse_file_info *fi)
346{
347 struct iconv *ic = iconv_get();
348 char *newpath;
349 int err = iconv_convpath(ic, path, &newpath, 0);
350 if (!err) {
351 err = fuse_fs_utimens(ic->next, newpath, ts, fi);
352 free(newpath);
353 }
354 return err;
355}
356
357static int iconv_create(const char *path, mode_t mode,
358 struct fuse_file_info *fi)
359{
360 struct iconv *ic = iconv_get();
361 char *newpath;
362 int err = iconv_convpath(ic, path, &newpath, 0);
363 if (!err) {
364 err = fuse_fs_create(ic->next, newpath, mode, fi);
365 free(newpath);
366 }
367 return err;
368}
369
370static int iconv_open_file(const char *path, struct fuse_file_info *fi)
371{
372 struct iconv *ic = iconv_get();
373 char *newpath;
374 int err = iconv_convpath(ic, path, &newpath, 0);
375 if (!err) {
376 err = fuse_fs_open(ic->next, newpath, fi);
377 free(newpath);
378 }
379 return err;
380}
381
382static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
383 size_t size, off_t offset, struct fuse_file_info *fi)
384{
385 struct iconv *ic = iconv_get();
386 char *newpath;
387 int err = iconv_convpath(ic, path, &newpath, 0);
388 if (!err) {
389 err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi);
390 free(newpath);
391 }
392 return err;
393}
394
395static int iconv_write_buf(const char *path, struct fuse_bufvec *buf,
396 off_t offset, struct fuse_file_info *fi)
397{
398 struct iconv *ic = iconv_get();
399 char *newpath;
400 int err = iconv_convpath(ic, path, &newpath, 0);
401 if (!err) {
402 err = fuse_fs_write_buf(ic->next, newpath, buf, offset, fi);
403 free(newpath);
404 }
405 return err;
406}
407
408static int iconv_statfs(const char *path, struct statvfs *stbuf)
409{
410 struct iconv *ic = iconv_get();
411 char *newpath;
412 int err = iconv_convpath(ic, path, &newpath, 0);
413 if (!err) {
414 err = fuse_fs_statfs(ic->next, newpath, stbuf);
415 free(newpath);
416 }
417 return err;
418}
419
420static int iconv_flush(const char *path, struct fuse_file_info *fi)
421{
422 struct iconv *ic = iconv_get();
423 char *newpath;
424 int err = iconv_convpath(ic, path, &newpath, 0);
425 if (!err) {
426 err = fuse_fs_flush(ic->next, newpath, fi);
427 free(newpath);
428 }
429 return err;
430}
431
432static int iconv_release(const char *path, struct fuse_file_info *fi)
433{
434 struct iconv *ic = iconv_get();
435 char *newpath;
436 int err = iconv_convpath(ic, path, &newpath, 0);
437 if (!err) {
438 err = fuse_fs_release(ic->next, newpath, fi);
439 free(newpath);
440 }
441 return err;
442}
443
444static int iconv_fsync(const char *path, int isdatasync,
445 struct fuse_file_info *fi)
446{
447 struct iconv *ic = iconv_get();
448 char *newpath;
449 int err = iconv_convpath(ic, path, &newpath, 0);
450 if (!err) {
451 err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi);
452 free(newpath);
453 }
454 return err;
455}
456
457static int iconv_fsyncdir(const char *path, int isdatasync,
458 struct fuse_file_info *fi)
459{
460 struct iconv *ic = iconv_get();
461 char *newpath;
462 int err = iconv_convpath(ic, path, &newpath, 0);
463 if (!err) {
464 err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi);
465 free(newpath);
466 }
467 return err;
468}
469
470static int iconv_setxattr(const char *path, const char *name,
471 const char *value, size_t size, int flags)
472{
473 struct iconv *ic = iconv_get();
474 char *newpath;
475 int err = iconv_convpath(ic, path, &newpath, 0);
476 if (!err) {
477 err = fuse_fs_setxattr(ic->next, newpath, name, value, size,
478 flags);
479 free(newpath);
480 }
481 return err;
482}
483
484static int iconv_getxattr(const char *path, const char *name, char *value,
485 size_t size)
486{
487 struct iconv *ic = iconv_get();
488 char *newpath;
489 int err = iconv_convpath(ic, path, &newpath, 0);
490 if (!err) {
491 err = fuse_fs_getxattr(ic->next, newpath, name, value, size);
492 free(newpath);
493 }
494 return err;
495}
496
497static int iconv_listxattr(const char *path, char *list, size_t size)
498{
499 struct iconv *ic = iconv_get();
500 char *newpath;
501 int err = iconv_convpath(ic, path, &newpath, 0);
502 if (!err) {
503 err = fuse_fs_listxattr(ic->next, newpath, list, size);
504 free(newpath);
505 }
506 return err;
507}
508
509static int iconv_removexattr(const char *path, const char *name)
510{
511 struct iconv *ic = iconv_get();
512 char *newpath;
513 int err = iconv_convpath(ic, path, &newpath, 0);
514 if (!err) {
515 err = fuse_fs_removexattr(ic->next, newpath, name);
516 free(newpath);
517 }
518 return err;
519}
520
521static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd,
522 struct flock *lock)
523{
524 struct iconv *ic = iconv_get();
525 char *newpath;
526 int err = iconv_convpath(ic, path, &newpath, 0);
527 if (!err) {
528 err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock);
529 free(newpath);
530 }
531 return err;
532}
533
534static int iconv_flock(const char *path, struct fuse_file_info *fi, int op)
535{
536 struct iconv *ic = iconv_get();
537 char *newpath;
538 int err = iconv_convpath(ic, path, &newpath, 0);
539 if (!err) {
540 err = fuse_fs_flock(ic->next, newpath, fi, op);
541 free(newpath);
542 }
543 return err;
544}
545
546static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
547{
548 struct iconv *ic = iconv_get();
549 char *newpath;
550 int err = iconv_convpath(ic, path, &newpath, 0);
551 if (!err) {
552 err = fuse_fs_bmap(ic->next, newpath, blocksize, idx);
553 free(newpath);
554 }
555 return err;
556}
557
558static off_t iconv_lseek(const char *path, off_t off, int whence,
559 struct fuse_file_info *fi)
560{
561 struct iconv *ic = iconv_get();
562 char *newpath;
563 int res = iconv_convpath(ic, path, &newpath, 0);
564 if (!res) {
565 res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
566 free(newpath);
567 }
568 return res;
569}
570
571static void *iconv_init(struct fuse_conn_info *conn,
572 struct fuse_config *cfg)
573{
574 struct iconv *ic = iconv_get();
575 fuse_fs_init(ic->next, conn, cfg);
576 /* Don't touch cfg->nullpath_ok, we can work with
577 either */
578 return ic;
579}
580
581static void iconv_destroy(void *data)
582{
583 struct iconv *ic = data;
584 fuse_fs_destroy(ic->next);
585 iconv_close(ic->tofs);
586 iconv_close(ic->fromfs);
587 pthread_mutex_destroy(&ic->lock);
588 free(ic->from_code);
589 free(ic->to_code);
590 free(ic);
591}
592
593static const struct fuse_operations iconv_oper = {
594 .destroy = iconv_destroy,
595 .init = iconv_init,
596 .getattr = iconv_getattr,
597 .access = iconv_access,
598 .readlink = iconv_readlink,
599 .opendir = iconv_opendir,
600 .readdir = iconv_readdir,
601 .releasedir = iconv_releasedir,
602 .mknod = iconv_mknod,
603 .mkdir = iconv_mkdir,
604 .symlink = iconv_symlink,
605 .unlink = iconv_unlink,
606 .rmdir = iconv_rmdir,
607 .rename = iconv_rename,
608 .link = iconv_link,
609 .chmod = iconv_chmod,
610 .chown = iconv_chown,
611 .truncate = iconv_truncate,
612 .utimens = iconv_utimens,
613 .create = iconv_create,
614 .open = iconv_open_file,
615 .read_buf = iconv_read_buf,
616 .write_buf = iconv_write_buf,
617 .statfs = iconv_statfs,
618 .flush = iconv_flush,
619 .release = iconv_release,
620 .fsync = iconv_fsync,
621 .fsyncdir = iconv_fsyncdir,
622 .setxattr = iconv_setxattr,
623 .getxattr = iconv_getxattr,
624 .listxattr = iconv_listxattr,
625 .removexattr = iconv_removexattr,
626 .lock = iconv_lock,
627 .flock = iconv_flock,
628 .bmap = iconv_bmap,
629 .lseek = iconv_lseek,
630};
631
632static const struct fuse_opt iconv_opts[] = {
633 FUSE_OPT_KEY("-h", 0),
634 FUSE_OPT_KEY("--help", 0),
635 { "from_code=%s", offsetof(struct iconv, from_code), 0 },
636 { "to_code=%s", offsetof(struct iconv, to_code), 1 },
638};
639
640static void iconv_help(void)
641{
642 char *charmap;
643 const char *old = setlocale(LC_CTYPE, "");
644
645 charmap = strdup(nl_langinfo(CODESET));
646 if (old)
647 setlocale(LC_CTYPE, old);
648 else
649 perror("setlocale");
650
651 printf(
652" -o from_code=CHARSET original encoding of file names (default: UTF-8)\n"
653" -o to_code=CHARSET new encoding of the file names (default: %s)\n",
654 charmap);
655 free(charmap);
656}
657
658static int iconv_opt_proc(void *data, const char *arg, int key,
659 struct fuse_args *outargs)
660{
661 (void) data; (void) arg; (void) outargs;
662
663 if (!key) {
664 iconv_help();
665 return -1;
666 }
667
668 return 1;
669}
670
671static struct fuse_fs *iconv_new(struct fuse_args *args,
672 struct fuse_fs *next[])
673{
674 struct fuse_fs *fs;
675 struct iconv *ic;
676 const char *old = NULL;
677 const char *from;
678 const char *to;
679
680 ic = calloc(1, sizeof(struct iconv));
681 if (ic == NULL) {
682 fuse_log(FUSE_LOG_ERR, "fuse-iconv: memory allocation failed\n");
683 return NULL;
684 }
685
686 if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
687 goto out_free;
688
689 if (!next[0] || next[1]) {
690 fuse_log(FUSE_LOG_ERR, "fuse-iconv: exactly one next filesystem required\n");
691 goto out_free;
692 }
693
694 from = ic->from_code ? ic->from_code : "UTF-8";
695 to = ic->to_code ? ic->to_code : "";
696 /* FIXME: detect charset equivalence? */
697 if (!to[0])
698 old = setlocale(LC_CTYPE, "");
699 ic->tofs = iconv_open(from, to);
700 if (ic->tofs == (iconv_t) -1) {
701 fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
702 to, from);
703 goto out_free;
704 }
705 ic->fromfs = iconv_open(to, from);
706 if (ic->tofs == (iconv_t) -1) {
707 fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
708 from, to);
709 goto out_iconv_close_to;
710 }
711 if (old) {
712 setlocale(LC_CTYPE, old);
713 old = NULL;
714 }
715
716 ic->next = next[0];
717 fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
718 if (!fs)
719 goto out_iconv_close_from;
720
721 return fs;
722
723out_iconv_close_from:
724 iconv_close(ic->fromfs);
725out_iconv_close_to:
726 iconv_close(ic->tofs);
727out_free:
728 free(ic->from_code);
729 free(ic->to_code);
730 free(ic);
731 if (old) {
732 setlocale(LC_CTYPE, old);
733 }
734 return NULL;
735}
736
737FUSE_REGISTER_MODULE(iconv, iconv_new);
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
fuse_fill_dir_flags
Definition fuse.h:58
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition fuse.h:1395
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
void * private_data
Definition fuse.h:874
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2modules_2iconv_8c_source.html0000644000175000017500000035537415156613443024133 0ustar berndbernd libfuse: fuse-3.18.1/lib/modules/iconv.c Source File
libfuse
iconv.c
1/*
2 fuse iconv module: file name charset conversion
3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#include <fuse_config.h>
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17#include <iconv.h>
18#include <pthread.h>
19#include <locale.h>
20#include <langinfo.h>
21
22struct iconv {
23 struct fuse_fs *next;
24 pthread_mutex_t lock;
25 char *from_code;
26 char *to_code;
27 iconv_t tofs;
28 iconv_t fromfs;
29};
30
31struct iconv_dh {
32 struct iconv *ic;
33 void *prev_buf;
34 fuse_fill_dir_t prev_filler;
35};
36
37static struct iconv *iconv_get(void)
38{
40}
41
42static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
43 int fromfs)
44{
45 size_t pathlen;
46 size_t newpathlen;
47 char *newpath;
48 size_t plen;
49 char *p;
50 size_t res;
51 int err;
52
53 if (path == NULL) {
54 *newpathp = NULL;
55 return 0;
56 }
57
58 pathlen = strlen(path);
59 newpathlen = pathlen * 4;
60 newpath = malloc(newpathlen + 1);
61 if (!newpath)
62 return -ENOMEM;
63
64 plen = newpathlen;
65 p = newpath;
66 pthread_mutex_lock(&ic->lock);
67 do {
68 res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path,
69 &pathlen, &p, &plen);
70 if (res == (size_t) -1) {
71 char *tmp;
72 size_t inc;
73
74 err = -EILSEQ;
75 if (errno != E2BIG)
76 goto err;
77
78 inc = (pathlen + 1) * 4;
79 newpathlen += inc;
80 int dp = p - newpath;
81 tmp = realloc(newpath, newpathlen + 1);
82 err = -ENOMEM;
83 if (!tmp)
84 goto err;
85
86 p = tmp + dp;
87 plen += inc;
88 newpath = tmp;
89 }
90 } while (res == (size_t) -1);
91 pthread_mutex_unlock(&ic->lock);
92 *p = '\0';
93 *newpathp = newpath;
94 return 0;
95
96err:
97 iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL);
98 pthread_mutex_unlock(&ic->lock);
99 free(newpath);
100 return err;
101}
102
103static int iconv_getattr(const char *path, struct stat *stbuf,
104 struct fuse_file_info *fi)
105{
106 struct iconv *ic = iconv_get();
107 char *newpath;
108 int err = iconv_convpath(ic, path, &newpath, 0);
109 if (!err) {
110 err = fuse_fs_getattr(ic->next, newpath, stbuf, fi);
111 free(newpath);
112 }
113 return err;
114}
115
116static int iconv_access(const char *path, int mask)
117{
118 struct iconv *ic = iconv_get();
119 char *newpath;
120 int err = iconv_convpath(ic, path, &newpath, 0);
121 if (!err) {
122 err = fuse_fs_access(ic->next, newpath, mask);
123 free(newpath);
124 }
125 return err;
126}
127
128static int iconv_readlink(const char *path, char *buf, size_t size)
129{
130 struct iconv *ic = iconv_get();
131 char *newpath;
132 int err = iconv_convpath(ic, path, &newpath, 0);
133 if (!err) {
134 err = fuse_fs_readlink(ic->next, newpath, buf, size);
135 if (!err) {
136 char *newlink;
137 err = iconv_convpath(ic, buf, &newlink, 1);
138 if (!err) {
139 strncpy(buf, newlink, size - 1);
140 buf[size - 1] = '\0';
141 free(newlink);
142 }
143 }
144 free(newpath);
145 }
146 return err;
147}
148
149static int iconv_opendir(const char *path, struct fuse_file_info *fi)
150{
151 struct iconv *ic = iconv_get();
152 char *newpath;
153 int err = iconv_convpath(ic, path, &newpath, 0);
154 if (!err) {
155 err = fuse_fs_opendir(ic->next, newpath, fi);
156 free(newpath);
157 }
158 return err;
159}
160
161static int iconv_dir_fill(void *buf, const char *name,
162 const struct stat *stbuf, off_t off,
163 enum fuse_fill_dir_flags flags)
164{
165 struct iconv_dh *dh = buf;
166 char *newname;
167 int res = 0;
168 if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
169 res = dh->prev_filler(dh->prev_buf, newname, stbuf, off, flags);
170 free(newname);
171 }
172 return res;
173}
174
175static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
176 off_t offset, struct fuse_file_info *fi,
177 enum fuse_readdir_flags flags)
178{
179 struct iconv *ic = iconv_get();
180 char *newpath;
181 int err = iconv_convpath(ic, path, &newpath, 0);
182 if (!err) {
183 struct iconv_dh dh;
184 dh.ic = ic;
185 dh.prev_buf = buf;
186 dh.prev_filler = filler;
187 err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill,
188 offset, fi, flags);
189 free(newpath);
190 }
191 return err;
192}
193
194static int iconv_releasedir(const char *path, struct fuse_file_info *fi)
195{
196 struct iconv *ic = iconv_get();
197 char *newpath;
198 int err = iconv_convpath(ic, path, &newpath, 0);
199 if (!err) {
200 err = fuse_fs_releasedir(ic->next, newpath, fi);
201 free(newpath);
202 }
203 return err;
204}
205
206static int iconv_mknod(const char *path, mode_t mode, dev_t rdev)
207{
208 struct iconv *ic = iconv_get();
209 char *newpath;
210 int err = iconv_convpath(ic, path, &newpath, 0);
211 if (!err) {
212 err = fuse_fs_mknod(ic->next, newpath, mode, rdev);
213 free(newpath);
214 }
215 return err;
216}
217
218static int iconv_mkdir(const char *path, mode_t mode)
219{
220 struct iconv *ic = iconv_get();
221 char *newpath;
222 int err = iconv_convpath(ic, path, &newpath, 0);
223 if (!err) {
224 err = fuse_fs_mkdir(ic->next, newpath, mode);
225 free(newpath);
226 }
227 return err;
228}
229
230static int iconv_unlink(const char *path)
231{
232 struct iconv *ic = iconv_get();
233 char *newpath;
234 int err = iconv_convpath(ic, path, &newpath, 0);
235 if (!err) {
236 err = fuse_fs_unlink(ic->next, newpath);
237 free(newpath);
238 }
239 return err;
240}
241
242static int iconv_rmdir(const char *path)
243{
244 struct iconv *ic = iconv_get();
245 char *newpath;
246 int err = iconv_convpath(ic, path, &newpath, 0);
247 if (!err) {
248 err = fuse_fs_rmdir(ic->next, newpath);
249 free(newpath);
250 }
251 return err;
252}
253
254static int iconv_symlink(const char *from, const char *to)
255{
256 struct iconv *ic = iconv_get();
257 char *newfrom;
258 char *newto;
259 int err = iconv_convpath(ic, from, &newfrom, 0);
260 if (!err) {
261 err = iconv_convpath(ic, to, &newto, 0);
262 if (!err) {
263 err = fuse_fs_symlink(ic->next, newfrom, newto);
264 free(newto);
265 }
266 free(newfrom);
267 }
268 return err;
269}
270
271static int iconv_rename(const char *from, const char *to, unsigned int flags)
272{
273 struct iconv *ic = iconv_get();
274 char *newfrom;
275 char *newto;
276 int err = iconv_convpath(ic, from, &newfrom, 0);
277 if (!err) {
278 err = iconv_convpath(ic, to, &newto, 0);
279 if (!err) {
280 err = fuse_fs_rename(ic->next, newfrom, newto, flags);
281 free(newto);
282 }
283 free(newfrom);
284 }
285 return err;
286}
287
288static int iconv_link(const char *from, const char *to)
289{
290 struct iconv *ic = iconv_get();
291 char *newfrom;
292 char *newto;
293 int err = iconv_convpath(ic, from, &newfrom, 0);
294 if (!err) {
295 err = iconv_convpath(ic, to, &newto, 0);
296 if (!err) {
297 err = fuse_fs_link(ic->next, newfrom, newto);
298 free(newto);
299 }
300 free(newfrom);
301 }
302 return err;
303}
304
305static int iconv_chmod(const char *path, mode_t mode,
306 struct fuse_file_info *fi)
307{
308 struct iconv *ic = iconv_get();
309 char *newpath;
310 int err = iconv_convpath(ic, path, &newpath, 0);
311 if (!err) {
312 err = fuse_fs_chmod(ic->next, newpath, mode, fi);
313 free(newpath);
314 }
315 return err;
316}
317
318static int iconv_chown(const char *path, uid_t uid, gid_t gid,
319 struct fuse_file_info *fi)
320{
321 struct iconv *ic = iconv_get();
322 char *newpath;
323 int err = iconv_convpath(ic, path, &newpath, 0);
324 if (!err) {
325 err = fuse_fs_chown(ic->next, newpath, uid, gid, fi);
326 free(newpath);
327 }
328 return err;
329}
330
331static int iconv_truncate(const char *path, off_t size,
332 struct fuse_file_info *fi)
333{
334 struct iconv *ic = iconv_get();
335 char *newpath;
336 int err = iconv_convpath(ic, path, &newpath, 0);
337 if (!err) {
338 err = fuse_fs_truncate(ic->next, newpath, size, fi);
339 free(newpath);
340 }
341 return err;
342}
343
344static int iconv_utimens(const char *path, const struct timespec ts[2],
345 struct fuse_file_info *fi)
346{
347 struct iconv *ic = iconv_get();
348 char *newpath;
349 int err = iconv_convpath(ic, path, &newpath, 0);
350 if (!err) {
351 err = fuse_fs_utimens(ic->next, newpath, ts, fi);
352 free(newpath);
353 }
354 return err;
355}
356
357static int iconv_create(const char *path, mode_t mode,
358 struct fuse_file_info *fi)
359{
360 struct iconv *ic = iconv_get();
361 char *newpath;
362 int err = iconv_convpath(ic, path, &newpath, 0);
363 if (!err) {
364 err = fuse_fs_create(ic->next, newpath, mode, fi);
365 free(newpath);
366 }
367 return err;
368}
369
370static int iconv_open_file(const char *path, struct fuse_file_info *fi)
371{
372 struct iconv *ic = iconv_get();
373 char *newpath;
374 int err = iconv_convpath(ic, path, &newpath, 0);
375 if (!err) {
376 err = fuse_fs_open(ic->next, newpath, fi);
377 free(newpath);
378 }
379 return err;
380}
381
382static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
383 size_t size, off_t offset, struct fuse_file_info *fi)
384{
385 struct iconv *ic = iconv_get();
386 char *newpath;
387 int err = iconv_convpath(ic, path, &newpath, 0);
388 if (!err) {
389 err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi);
390 free(newpath);
391 }
392 return err;
393}
394
395static int iconv_write_buf(const char *path, struct fuse_bufvec *buf,
396 off_t offset, struct fuse_file_info *fi)
397{
398 struct iconv *ic = iconv_get();
399 char *newpath;
400 int err = iconv_convpath(ic, path, &newpath, 0);
401 if (!err) {
402 err = fuse_fs_write_buf(ic->next, newpath, buf, offset, fi);
403 free(newpath);
404 }
405 return err;
406}
407
408static int iconv_statfs(const char *path, struct statvfs *stbuf)
409{
410 struct iconv *ic = iconv_get();
411 char *newpath;
412 int err = iconv_convpath(ic, path, &newpath, 0);
413 if (!err) {
414 err = fuse_fs_statfs(ic->next, newpath, stbuf);
415 free(newpath);
416 }
417 return err;
418}
419
420static int iconv_flush(const char *path, struct fuse_file_info *fi)
421{
422 struct iconv *ic = iconv_get();
423 char *newpath;
424 int err = iconv_convpath(ic, path, &newpath, 0);
425 if (!err) {
426 err = fuse_fs_flush(ic->next, newpath, fi);
427 free(newpath);
428 }
429 return err;
430}
431
432static int iconv_release(const char *path, struct fuse_file_info *fi)
433{
434 struct iconv *ic = iconv_get();
435 char *newpath;
436 int err = iconv_convpath(ic, path, &newpath, 0);
437 if (!err) {
438 err = fuse_fs_release(ic->next, newpath, fi);
439 free(newpath);
440 }
441 return err;
442}
443
444static int iconv_fsync(const char *path, int isdatasync,
445 struct fuse_file_info *fi)
446{
447 struct iconv *ic = iconv_get();
448 char *newpath;
449 int err = iconv_convpath(ic, path, &newpath, 0);
450 if (!err) {
451 err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi);
452 free(newpath);
453 }
454 return err;
455}
456
457static int iconv_fsyncdir(const char *path, int isdatasync,
458 struct fuse_file_info *fi)
459{
460 struct iconv *ic = iconv_get();
461 char *newpath;
462 int err = iconv_convpath(ic, path, &newpath, 0);
463 if (!err) {
464 err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi);
465 free(newpath);
466 }
467 return err;
468}
469
470static int iconv_setxattr(const char *path, const char *name,
471 const char *value, size_t size, int flags)
472{
473 struct iconv *ic = iconv_get();
474 char *newpath;
475 int err = iconv_convpath(ic, path, &newpath, 0);
476 if (!err) {
477 err = fuse_fs_setxattr(ic->next, newpath, name, value, size,
478 flags);
479 free(newpath);
480 }
481 return err;
482}
483
484static int iconv_getxattr(const char *path, const char *name, char *value,
485 size_t size)
486{
487 struct iconv *ic = iconv_get();
488 char *newpath;
489 int err = iconv_convpath(ic, path, &newpath, 0);
490 if (!err) {
491 err = fuse_fs_getxattr(ic->next, newpath, name, value, size);
492 free(newpath);
493 }
494 return err;
495}
496
497static int iconv_listxattr(const char *path, char *list, size_t size)
498{
499 struct iconv *ic = iconv_get();
500 char *newpath;
501 int err = iconv_convpath(ic, path, &newpath, 0);
502 if (!err) {
503 err = fuse_fs_listxattr(ic->next, newpath, list, size);
504 free(newpath);
505 }
506 return err;
507}
508
509static int iconv_removexattr(const char *path, const char *name)
510{
511 struct iconv *ic = iconv_get();
512 char *newpath;
513 int err = iconv_convpath(ic, path, &newpath, 0);
514 if (!err) {
515 err = fuse_fs_removexattr(ic->next, newpath, name);
516 free(newpath);
517 }
518 return err;
519}
520
521static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd,
522 struct flock *lock)
523{
524 struct iconv *ic = iconv_get();
525 char *newpath;
526 int err = iconv_convpath(ic, path, &newpath, 0);
527 if (!err) {
528 err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock);
529 free(newpath);
530 }
531 return err;
532}
533
534static int iconv_flock(const char *path, struct fuse_file_info *fi, int op)
535{
536 struct iconv *ic = iconv_get();
537 char *newpath;
538 int err = iconv_convpath(ic, path, &newpath, 0);
539 if (!err) {
540 err = fuse_fs_flock(ic->next, newpath, fi, op);
541 free(newpath);
542 }
543 return err;
544}
545
546static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
547{
548 struct iconv *ic = iconv_get();
549 char *newpath;
550 int err = iconv_convpath(ic, path, &newpath, 0);
551 if (!err) {
552 err = fuse_fs_bmap(ic->next, newpath, blocksize, idx);
553 free(newpath);
554 }
555 return err;
556}
557
558static off_t iconv_lseek(const char *path, off_t off, int whence,
559 struct fuse_file_info *fi)
560{
561 struct iconv *ic = iconv_get();
562 char *newpath;
563 int res = iconv_convpath(ic, path, &newpath, 0);
564 if (!res) {
565 res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
566 free(newpath);
567 }
568 return res;
569}
570
571#ifdef HAVE_STATX
572static int iconv_statx(const char *path, int flags, int mask, struct statx *stxbuf,
573 struct fuse_file_info *fi)
574{
575 struct iconv *ic = iconv_get();
576 char *newpath;
577 int res = iconv_convpath(ic, path, &newpath, 0);
578
579 if (!res) {
580 res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi);
581 free(newpath);
582 }
583 return res;
584}
585#endif
586
587static void *iconv_init(struct fuse_conn_info *conn,
588 struct fuse_config *cfg)
589{
590 struct iconv *ic = iconv_get();
591 fuse_fs_init(ic->next, conn, cfg);
592 /* Don't touch cfg->nullpath_ok, we can work with
593 either */
594 return ic;
595}
596
597static void iconv_destroy(void *data)
598{
599 struct iconv *ic = data;
600 fuse_fs_destroy(ic->next);
601 iconv_close(ic->tofs);
602 iconv_close(ic->fromfs);
603 pthread_mutex_destroy(&ic->lock);
604 free(ic->from_code);
605 free(ic->to_code);
606 free(ic);
607}
608
609static const struct fuse_operations iconv_oper = {
610 .destroy = iconv_destroy,
611 .init = iconv_init,
612 .getattr = iconv_getattr,
613 .access = iconv_access,
614 .readlink = iconv_readlink,
615 .opendir = iconv_opendir,
616 .readdir = iconv_readdir,
617 .releasedir = iconv_releasedir,
618 .mknod = iconv_mknod,
619 .mkdir = iconv_mkdir,
620 .symlink = iconv_symlink,
621 .unlink = iconv_unlink,
622 .rmdir = iconv_rmdir,
623 .rename = iconv_rename,
624 .link = iconv_link,
625 .chmod = iconv_chmod,
626 .chown = iconv_chown,
627 .truncate = iconv_truncate,
628 .utimens = iconv_utimens,
629 .create = iconv_create,
630 .open = iconv_open_file,
631 .read_buf = iconv_read_buf,
632 .write_buf = iconv_write_buf,
633 .statfs = iconv_statfs,
634 .flush = iconv_flush,
635 .release = iconv_release,
636 .fsync = iconv_fsync,
637 .fsyncdir = iconv_fsyncdir,
638 .setxattr = iconv_setxattr,
639 .getxattr = iconv_getxattr,
640 .listxattr = iconv_listxattr,
641 .removexattr = iconv_removexattr,
642 .lock = iconv_lock,
643 .flock = iconv_flock,
644 .bmap = iconv_bmap,
645 .lseek = iconv_lseek,
646#ifdef HAVE_STATX
647 .statx = iconv_statx,
648#endif
649};
650
651static const struct fuse_opt iconv_opts[] = {
652 FUSE_OPT_KEY("-h", 0),
653 FUSE_OPT_KEY("--help", 0),
654 { "from_code=%s", offsetof(struct iconv, from_code), 0 },
655 { "to_code=%s", offsetof(struct iconv, to_code), 1 },
657};
658
659static void iconv_help(void)
660{
661 char *charmap;
662 const char *old = setlocale(LC_CTYPE, "");
663
664 charmap = strdup(nl_langinfo(CODESET));
665 if (old)
666 setlocale(LC_CTYPE, old);
667 else
668 perror("setlocale");
669
670 printf(
671" -o from_code=CHARSET original encoding of file names (default: UTF-8)\n"
672" -o to_code=CHARSET new encoding of the file names (default: %s)\n",
673 charmap);
674 free(charmap);
675}
676
677static int iconv_opt_proc(void *data, const char *arg, int key,
678 struct fuse_args *outargs)
679{
680 (void) data; (void) arg; (void) outargs;
681
682 if (!key) {
683 iconv_help();
684 return -1;
685 }
686
687 return 1;
688}
689
690static struct fuse_fs *iconv_new(struct fuse_args *args,
691 struct fuse_fs *next[])
692{
693 struct fuse_fs *fs;
694 struct iconv *ic;
695 const char *old = NULL;
696 const char *from;
697 const char *to;
698
699 ic = calloc(1, sizeof(struct iconv));
700 if (ic == NULL) {
701 fuse_log(FUSE_LOG_ERR, "fuse-iconv: memory allocation failed\n");
702 return NULL;
703 }
704
705 if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
706 goto out_free;
707
708 if (!next[0] || next[1]) {
709 fuse_log(FUSE_LOG_ERR, "fuse-iconv: exactly one next filesystem required\n");
710 goto out_free;
711 }
712
713 from = ic->from_code ? ic->from_code : "UTF-8";
714 to = ic->to_code ? ic->to_code : "";
715 /* FIXME: detect charset equivalence? */
716 if (!to[0])
717 old = setlocale(LC_CTYPE, "");
718 ic->tofs = iconv_open(from, to);
719 if (ic->tofs == (iconv_t) -1) {
720 fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
721 to, from);
722 goto out_free;
723 }
724 ic->fromfs = iconv_open(to, from);
725 if (ic->tofs == (iconv_t) -1) {
726 fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
727 from, to);
728 goto out_iconv_close_to;
729 }
730 if (old) {
731 setlocale(LC_CTYPE, old);
732 old = NULL;
733 }
734
735 ic->next = next[0];
736 fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
737 if (!fs)
738 goto out_iconv_close_from;
739
740 return fs;
741
742out_iconv_close_from:
743 iconv_close(ic->fromfs);
744out_iconv_close_to:
745 iconv_close(ic->tofs);
746out_free:
747 free(ic->from_code);
748 free(ic->to_code);
749 free(ic);
750 if (old) {
751 setlocale(LC_CTYPE, old);
752 }
753 return NULL;
754}
755
756FUSE_REGISTER_MODULE(iconv, iconv_new);
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
fuse_fill_dir_flags
Definition fuse.h:58
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition fuse.h:1395
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
void * private_data
Definition fuse.h:874
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/lib_2modules_2iconv_8c_source.html0000644000175000017500000035521115156613443021745 0ustar berndbernd libfuse: lib/modules/iconv.c Source File
libfuse
iconv.c
1/*
2 fuse iconv module: file name charset conversion
3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#include <fuse_config.h>
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17#include <iconv.h>
18#include <pthread.h>
19#include <locale.h>
20#include <langinfo.h>
21
22struct iconv {
23 struct fuse_fs *next;
24 pthread_mutex_t lock;
25 char *from_code;
26 char *to_code;
27 iconv_t tofs;
28 iconv_t fromfs;
29};
30
31struct iconv_dh {
32 struct iconv *ic;
33 void *prev_buf;
34 fuse_fill_dir_t prev_filler;
35};
36
37static struct iconv *iconv_get(void)
38{
40}
41
42static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
43 int fromfs)
44{
45 size_t pathlen;
46 size_t newpathlen;
47 char *newpath;
48 size_t plen;
49 char *p;
50 size_t res;
51 int err;
52
53 if (path == NULL) {
54 *newpathp = NULL;
55 return 0;
56 }
57
58 pathlen = strlen(path);
59 newpathlen = pathlen * 4;
60 newpath = malloc(newpathlen + 1);
61 if (!newpath)
62 return -ENOMEM;
63
64 plen = newpathlen;
65 p = newpath;
66 pthread_mutex_lock(&ic->lock);
67 do {
68 res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path,
69 &pathlen, &p, &plen);
70 if (res == (size_t) -1) {
71 char *tmp;
72 size_t inc;
73
74 err = -EILSEQ;
75 if (errno != E2BIG)
76 goto err;
77
78 inc = (pathlen + 1) * 4;
79 newpathlen += inc;
80 int dp = p - newpath;
81 tmp = realloc(newpath, newpathlen + 1);
82 err = -ENOMEM;
83 if (!tmp)
84 goto err;
85
86 p = tmp + dp;
87 plen += inc;
88 newpath = tmp;
89 }
90 } while (res == (size_t) -1);
91 pthread_mutex_unlock(&ic->lock);
92 *p = '\0';
93 *newpathp = newpath;
94 return 0;
95
96err:
97 iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL);
98 pthread_mutex_unlock(&ic->lock);
99 free(newpath);
100 return err;
101}
102
103static int iconv_getattr(const char *path, struct stat *stbuf,
104 struct fuse_file_info *fi)
105{
106 struct iconv *ic = iconv_get();
107 char *newpath;
108 int err = iconv_convpath(ic, path, &newpath, 0);
109 if (!err) {
110 err = fuse_fs_getattr(ic->next, newpath, stbuf, fi);
111 free(newpath);
112 }
113 return err;
114}
115
116static int iconv_access(const char *path, int mask)
117{
118 struct iconv *ic = iconv_get();
119 char *newpath;
120 int err = iconv_convpath(ic, path, &newpath, 0);
121 if (!err) {
122 err = fuse_fs_access(ic->next, newpath, mask);
123 free(newpath);
124 }
125 return err;
126}
127
128static int iconv_readlink(const char *path, char *buf, size_t size)
129{
130 struct iconv *ic = iconv_get();
131 char *newpath;
132 int err = iconv_convpath(ic, path, &newpath, 0);
133 if (!err) {
134 err = fuse_fs_readlink(ic->next, newpath, buf, size);
135 if (!err) {
136 char *newlink;
137 err = iconv_convpath(ic, buf, &newlink, 1);
138 if (!err) {
139 strncpy(buf, newlink, size - 1);
140 buf[size - 1] = '\0';
141 free(newlink);
142 }
143 }
144 free(newpath);
145 }
146 return err;
147}
148
149static int iconv_opendir(const char *path, struct fuse_file_info *fi)
150{
151 struct iconv *ic = iconv_get();
152 char *newpath;
153 int err = iconv_convpath(ic, path, &newpath, 0);
154 if (!err) {
155 err = fuse_fs_opendir(ic->next, newpath, fi);
156 free(newpath);
157 }
158 return err;
159}
160
161static int iconv_dir_fill(void *buf, const char *name,
162 const struct stat *stbuf, off_t off,
163 enum fuse_fill_dir_flags flags)
164{
165 struct iconv_dh *dh = buf;
166 char *newname;
167 int res = 0;
168 if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
169 res = dh->prev_filler(dh->prev_buf, newname, stbuf, off, flags);
170 free(newname);
171 }
172 return res;
173}
174
175static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
176 off_t offset, struct fuse_file_info *fi,
177 enum fuse_readdir_flags flags)
178{
179 struct iconv *ic = iconv_get();
180 char *newpath;
181 int err = iconv_convpath(ic, path, &newpath, 0);
182 if (!err) {
183 struct iconv_dh dh;
184 dh.ic = ic;
185 dh.prev_buf = buf;
186 dh.prev_filler = filler;
187 err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill,
188 offset, fi, flags);
189 free(newpath);
190 }
191 return err;
192}
193
194static int iconv_releasedir(const char *path, struct fuse_file_info *fi)
195{
196 struct iconv *ic = iconv_get();
197 char *newpath;
198 int err = iconv_convpath(ic, path, &newpath, 0);
199 if (!err) {
200 err = fuse_fs_releasedir(ic->next, newpath, fi);
201 free(newpath);
202 }
203 return err;
204}
205
206static int iconv_mknod(const char *path, mode_t mode, dev_t rdev)
207{
208 struct iconv *ic = iconv_get();
209 char *newpath;
210 int err = iconv_convpath(ic, path, &newpath, 0);
211 if (!err) {
212 err = fuse_fs_mknod(ic->next, newpath, mode, rdev);
213 free(newpath);
214 }
215 return err;
216}
217
218static int iconv_mkdir(const char *path, mode_t mode)
219{
220 struct iconv *ic = iconv_get();
221 char *newpath;
222 int err = iconv_convpath(ic, path, &newpath, 0);
223 if (!err) {
224 err = fuse_fs_mkdir(ic->next, newpath, mode);
225 free(newpath);
226 }
227 return err;
228}
229
230static int iconv_unlink(const char *path)
231{
232 struct iconv *ic = iconv_get();
233 char *newpath;
234 int err = iconv_convpath(ic, path, &newpath, 0);
235 if (!err) {
236 err = fuse_fs_unlink(ic->next, newpath);
237 free(newpath);
238 }
239 return err;
240}
241
242static int iconv_rmdir(const char *path)
243{
244 struct iconv *ic = iconv_get();
245 char *newpath;
246 int err = iconv_convpath(ic, path, &newpath, 0);
247 if (!err) {
248 err = fuse_fs_rmdir(ic->next, newpath);
249 free(newpath);
250 }
251 return err;
252}
253
254static int iconv_symlink(const char *from, const char *to)
255{
256 struct iconv *ic = iconv_get();
257 char *newfrom;
258 char *newto;
259 int err = iconv_convpath(ic, from, &newfrom, 0);
260 if (!err) {
261 err = iconv_convpath(ic, to, &newto, 0);
262 if (!err) {
263 err = fuse_fs_symlink(ic->next, newfrom, newto);
264 free(newto);
265 }
266 free(newfrom);
267 }
268 return err;
269}
270
271static int iconv_rename(const char *from, const char *to, unsigned int flags)
272{
273 struct iconv *ic = iconv_get();
274 char *newfrom;
275 char *newto;
276 int err = iconv_convpath(ic, from, &newfrom, 0);
277 if (!err) {
278 err = iconv_convpath(ic, to, &newto, 0);
279 if (!err) {
280 err = fuse_fs_rename(ic->next, newfrom, newto, flags);
281 free(newto);
282 }
283 free(newfrom);
284 }
285 return err;
286}
287
288static int iconv_link(const char *from, const char *to)
289{
290 struct iconv *ic = iconv_get();
291 char *newfrom;
292 char *newto;
293 int err = iconv_convpath(ic, from, &newfrom, 0);
294 if (!err) {
295 err = iconv_convpath(ic, to, &newto, 0);
296 if (!err) {
297 err = fuse_fs_link(ic->next, newfrom, newto);
298 free(newto);
299 }
300 free(newfrom);
301 }
302 return err;
303}
304
305static int iconv_chmod(const char *path, mode_t mode,
306 struct fuse_file_info *fi)
307{
308 struct iconv *ic = iconv_get();
309 char *newpath;
310 int err = iconv_convpath(ic, path, &newpath, 0);
311 if (!err) {
312 err = fuse_fs_chmod(ic->next, newpath, mode, fi);
313 free(newpath);
314 }
315 return err;
316}
317
318static int iconv_chown(const char *path, uid_t uid, gid_t gid,
319 struct fuse_file_info *fi)
320{
321 struct iconv *ic = iconv_get();
322 char *newpath;
323 int err = iconv_convpath(ic, path, &newpath, 0);
324 if (!err) {
325 err = fuse_fs_chown(ic->next, newpath, uid, gid, fi);
326 free(newpath);
327 }
328 return err;
329}
330
331static int iconv_truncate(const char *path, off_t size,
332 struct fuse_file_info *fi)
333{
334 struct iconv *ic = iconv_get();
335 char *newpath;
336 int err = iconv_convpath(ic, path, &newpath, 0);
337 if (!err) {
338 err = fuse_fs_truncate(ic->next, newpath, size, fi);
339 free(newpath);
340 }
341 return err;
342}
343
344static int iconv_utimens(const char *path, const struct timespec ts[2],
345 struct fuse_file_info *fi)
346{
347 struct iconv *ic = iconv_get();
348 char *newpath;
349 int err = iconv_convpath(ic, path, &newpath, 0);
350 if (!err) {
351 err = fuse_fs_utimens(ic->next, newpath, ts, fi);
352 free(newpath);
353 }
354 return err;
355}
356
357static int iconv_create(const char *path, mode_t mode,
358 struct fuse_file_info *fi)
359{
360 struct iconv *ic = iconv_get();
361 char *newpath;
362 int err = iconv_convpath(ic, path, &newpath, 0);
363 if (!err) {
364 err = fuse_fs_create(ic->next, newpath, mode, fi);
365 free(newpath);
366 }
367 return err;
368}
369
370static int iconv_open_file(const char *path, struct fuse_file_info *fi)
371{
372 struct iconv *ic = iconv_get();
373 char *newpath;
374 int err = iconv_convpath(ic, path, &newpath, 0);
375 if (!err) {
376 err = fuse_fs_open(ic->next, newpath, fi);
377 free(newpath);
378 }
379 return err;
380}
381
382static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
383 size_t size, off_t offset, struct fuse_file_info *fi)
384{
385 struct iconv *ic = iconv_get();
386 char *newpath;
387 int err = iconv_convpath(ic, path, &newpath, 0);
388 if (!err) {
389 err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi);
390 free(newpath);
391 }
392 return err;
393}
394
395static int iconv_write_buf(const char *path, struct fuse_bufvec *buf,
396 off_t offset, struct fuse_file_info *fi)
397{
398 struct iconv *ic = iconv_get();
399 char *newpath;
400 int err = iconv_convpath(ic, path, &newpath, 0);
401 if (!err) {
402 err = fuse_fs_write_buf(ic->next, newpath, buf, offset, fi);
403 free(newpath);
404 }
405 return err;
406}
407
408static int iconv_statfs(const char *path, struct statvfs *stbuf)
409{
410 struct iconv *ic = iconv_get();
411 char *newpath;
412 int err = iconv_convpath(ic, path, &newpath, 0);
413 if (!err) {
414 err = fuse_fs_statfs(ic->next, newpath, stbuf);
415 free(newpath);
416 }
417 return err;
418}
419
420static int iconv_flush(const char *path, struct fuse_file_info *fi)
421{
422 struct iconv *ic = iconv_get();
423 char *newpath;
424 int err = iconv_convpath(ic, path, &newpath, 0);
425 if (!err) {
426 err = fuse_fs_flush(ic->next, newpath, fi);
427 free(newpath);
428 }
429 return err;
430}
431
432static int iconv_release(const char *path, struct fuse_file_info *fi)
433{
434 struct iconv *ic = iconv_get();
435 char *newpath;
436 int err = iconv_convpath(ic, path, &newpath, 0);
437 if (!err) {
438 err = fuse_fs_release(ic->next, newpath, fi);
439 free(newpath);
440 }
441 return err;
442}
443
444static int iconv_fsync(const char *path, int isdatasync,
445 struct fuse_file_info *fi)
446{
447 struct iconv *ic = iconv_get();
448 char *newpath;
449 int err = iconv_convpath(ic, path, &newpath, 0);
450 if (!err) {
451 err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi);
452 free(newpath);
453 }
454 return err;
455}
456
457static int iconv_fsyncdir(const char *path, int isdatasync,
458 struct fuse_file_info *fi)
459{
460 struct iconv *ic = iconv_get();
461 char *newpath;
462 int err = iconv_convpath(ic, path, &newpath, 0);
463 if (!err) {
464 err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi);
465 free(newpath);
466 }
467 return err;
468}
469
470static int iconv_setxattr(const char *path, const char *name,
471 const char *value, size_t size, int flags)
472{
473 struct iconv *ic = iconv_get();
474 char *newpath;
475 int err = iconv_convpath(ic, path, &newpath, 0);
476 if (!err) {
477 err = fuse_fs_setxattr(ic->next, newpath, name, value, size,
478 flags);
479 free(newpath);
480 }
481 return err;
482}
483
484static int iconv_getxattr(const char *path, const char *name, char *value,
485 size_t size)
486{
487 struct iconv *ic = iconv_get();
488 char *newpath;
489 int err = iconv_convpath(ic, path, &newpath, 0);
490 if (!err) {
491 err = fuse_fs_getxattr(ic->next, newpath, name, value, size);
492 free(newpath);
493 }
494 return err;
495}
496
497static int iconv_listxattr(const char *path, char *list, size_t size)
498{
499 struct iconv *ic = iconv_get();
500 char *newpath;
501 int err = iconv_convpath(ic, path, &newpath, 0);
502 if (!err) {
503 err = fuse_fs_listxattr(ic->next, newpath, list, size);
504 free(newpath);
505 }
506 return err;
507}
508
509static int iconv_removexattr(const char *path, const char *name)
510{
511 struct iconv *ic = iconv_get();
512 char *newpath;
513 int err = iconv_convpath(ic, path, &newpath, 0);
514 if (!err) {
515 err = fuse_fs_removexattr(ic->next, newpath, name);
516 free(newpath);
517 }
518 return err;
519}
520
521static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd,
522 struct flock *lock)
523{
524 struct iconv *ic = iconv_get();
525 char *newpath;
526 int err = iconv_convpath(ic, path, &newpath, 0);
527 if (!err) {
528 err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock);
529 free(newpath);
530 }
531 return err;
532}
533
534static int iconv_flock(const char *path, struct fuse_file_info *fi, int op)
535{
536 struct iconv *ic = iconv_get();
537 char *newpath;
538 int err = iconv_convpath(ic, path, &newpath, 0);
539 if (!err) {
540 err = fuse_fs_flock(ic->next, newpath, fi, op);
541 free(newpath);
542 }
543 return err;
544}
545
546static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
547{
548 struct iconv *ic = iconv_get();
549 char *newpath;
550 int err = iconv_convpath(ic, path, &newpath, 0);
551 if (!err) {
552 err = fuse_fs_bmap(ic->next, newpath, blocksize, idx);
553 free(newpath);
554 }
555 return err;
556}
557
558static off_t iconv_lseek(const char *path, off_t off, int whence,
559 struct fuse_file_info *fi)
560{
561 struct iconv *ic = iconv_get();
562 char *newpath;
563 int res = iconv_convpath(ic, path, &newpath, 0);
564 if (!res) {
565 res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
566 free(newpath);
567 }
568 return res;
569}
570
571#ifdef HAVE_STATX
572static int iconv_statx(const char *path, int flags, int mask, struct statx *stxbuf,
573 struct fuse_file_info *fi)
574{
575 struct iconv *ic = iconv_get();
576 char *newpath;
577 int res = iconv_convpath(ic, path, &newpath, 0);
578
579 if (!res) {
580 res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi);
581 free(newpath);
582 }
583 return res;
584}
585#endif
586
587static void *iconv_init(struct fuse_conn_info *conn,
588 struct fuse_config *cfg)
589{
590 struct iconv *ic = iconv_get();
591 fuse_fs_init(ic->next, conn, cfg);
592 /* Don't touch cfg->nullpath_ok, we can work with
593 either */
594 return ic;
595}
596
597static void iconv_destroy(void *data)
598{
599 struct iconv *ic = data;
600 fuse_fs_destroy(ic->next);
601 iconv_close(ic->tofs);
602 iconv_close(ic->fromfs);
603 pthread_mutex_destroy(&ic->lock);
604 free(ic->from_code);
605 free(ic->to_code);
606 free(ic);
607}
608
609static const struct fuse_operations iconv_oper = {
610 .destroy = iconv_destroy,
611 .init = iconv_init,
612 .getattr = iconv_getattr,
613 .access = iconv_access,
614 .readlink = iconv_readlink,
615 .opendir = iconv_opendir,
616 .readdir = iconv_readdir,
617 .releasedir = iconv_releasedir,
618 .mknod = iconv_mknod,
619 .mkdir = iconv_mkdir,
620 .symlink = iconv_symlink,
621 .unlink = iconv_unlink,
622 .rmdir = iconv_rmdir,
623 .rename = iconv_rename,
624 .link = iconv_link,
625 .chmod = iconv_chmod,
626 .chown = iconv_chown,
627 .truncate = iconv_truncate,
628 .utimens = iconv_utimens,
629 .create = iconv_create,
630 .open = iconv_open_file,
631 .read_buf = iconv_read_buf,
632 .write_buf = iconv_write_buf,
633 .statfs = iconv_statfs,
634 .flush = iconv_flush,
635 .release = iconv_release,
636 .fsync = iconv_fsync,
637 .fsyncdir = iconv_fsyncdir,
638 .setxattr = iconv_setxattr,
639 .getxattr = iconv_getxattr,
640 .listxattr = iconv_listxattr,
641 .removexattr = iconv_removexattr,
642 .lock = iconv_lock,
643 .flock = iconv_flock,
644 .bmap = iconv_bmap,
645 .lseek = iconv_lseek,
646#ifdef HAVE_STATX
647 .statx = iconv_statx,
648#endif
649};
650
651static const struct fuse_opt iconv_opts[] = {
652 FUSE_OPT_KEY("-h", 0),
653 FUSE_OPT_KEY("--help", 0),
654 { "from_code=%s", offsetof(struct iconv, from_code), 0 },
655 { "to_code=%s", offsetof(struct iconv, to_code), 1 },
657};
658
659static void iconv_help(void)
660{
661 char *charmap;
662 const char *old = setlocale(LC_CTYPE, "");
663
664 charmap = strdup(nl_langinfo(CODESET));
665 if (old)
666 setlocale(LC_CTYPE, old);
667 else
668 perror("setlocale");
669
670 printf(
671" -o from_code=CHARSET original encoding of file names (default: UTF-8)\n"
672" -o to_code=CHARSET new encoding of the file names (default: %s)\n",
673 charmap);
674 free(charmap);
675}
676
677static int iconv_opt_proc(void *data, const char *arg, int key,
678 struct fuse_args *outargs)
679{
680 (void) data; (void) arg; (void) outargs;
681
682 if (!key) {
683 iconv_help();
684 return -1;
685 }
686
687 return 1;
688}
689
690static struct fuse_fs *iconv_new(struct fuse_args *args,
691 struct fuse_fs *next[])
692{
693 struct fuse_fs *fs;
694 struct iconv *ic;
695 const char *old = NULL;
696 const char *from;
697 const char *to;
698
699 ic = calloc(1, sizeof(struct iconv));
700 if (ic == NULL) {
701 fuse_log(FUSE_LOG_ERR, "fuse-iconv: memory allocation failed\n");
702 return NULL;
703 }
704
705 if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
706 goto out_free;
707
708 if (!next[0] || next[1]) {
709 fuse_log(FUSE_LOG_ERR, "fuse-iconv: exactly one next filesystem required\n");
710 goto out_free;
711 }
712
713 from = ic->from_code ? ic->from_code : "UTF-8";
714 to = ic->to_code ? ic->to_code : "";
715 /* FIXME: detect charset equivalence? */
716 if (!to[0])
717 old = setlocale(LC_CTYPE, "");
718 ic->tofs = iconv_open(from, to);
719 if (ic->tofs == (iconv_t) -1) {
720 fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
721 to, from);
722 goto out_free;
723 }
724 ic->fromfs = iconv_open(to, from);
725 if (ic->tofs == (iconv_t) -1) {
726 fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
727 from, to);
728 goto out_iconv_close_to;
729 }
730 if (old) {
731 setlocale(LC_CTYPE, old);
732 old = NULL;
733 }
734
735 ic->next = next[0];
736 fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
737 if (!fs)
738 goto out_iconv_close_from;
739
740 return fs;
741
742out_iconv_close_from:
743 iconv_close(ic->fromfs);
744out_iconv_close_to:
745 iconv_close(ic->tofs);
746out_free:
747 free(ic->from_code);
748 free(ic->to_code);
749 free(ic);
750 if (old) {
751 setlocale(LC_CTYPE, old);
752 }
753 return NULL;
754}
755
756FUSE_REGISTER_MODULE(iconv, iconv_new);
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
fuse_fill_dir_flags
Definition fuse.h:58
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition fuse.h:1395
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
void * private_data
Definition fuse.h:874
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2modules_2subdir_8c_source.html0000644000175000017500000033336215156613443024300 0ustar berndbernd libfuse: fuse-3.17.4/lib/modules/subdir.c Source File
libfuse
subdir.c
1/*
2 fuse subdir module: offset paths with a base directory
3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
7*/
8
9#include <fuse_config.h>
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17
18struct subdir {
19 char *base;
20 size_t baselen;
21 int rellinks;
22 struct fuse_fs *next;
23};
24
25static struct subdir *subdir_get(void)
26{
28}
29
30static int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
31{
32 char *newpath = NULL;
33
34 if (path != NULL) {
35 unsigned newlen = d->baselen + strlen(path);
36
37 newpath = malloc(newlen + 2);
38 if (!newpath)
39 return -ENOMEM;
40
41 if (path[0] == '/')
42 path++;
43 strcpy(newpath, d->base);
44 strcpy(newpath + d->baselen, path);
45 if (!newpath[0])
46 strcpy(newpath, ".");
47 }
48 *newpathp = newpath;
49
50 return 0;
51}
52
53static int subdir_getattr(const char *path, struct stat *stbuf,
54 struct fuse_file_info *fi)
55{
56 struct subdir *d = subdir_get();
57 char *newpath;
58 int err = subdir_addpath(d, path, &newpath);
59 if (!err) {
60 err = fuse_fs_getattr(d->next, newpath, stbuf, fi);
61 free(newpath);
62 }
63 return err;
64}
65
66static int subdir_access(const char *path, int mask)
67{
68 struct subdir *d = subdir_get();
69 char *newpath;
70 int err = subdir_addpath(d, path, &newpath);
71 if (!err) {
72 err = fuse_fs_access(d->next, newpath, mask);
73 free(newpath);
74 }
75 return err;
76}
77
78
79static int count_components(const char *p)
80{
81 int ctr;
82
83 for (; *p == '/'; p++);
84 for (ctr = 0; *p; ctr++) {
85 for (; *p && *p != '/'; p++);
86 for (; *p == '/'; p++);
87 }
88 return ctr;
89}
90
91static void strip_common(const char **sp, const char **tp)
92{
93 const char *s = *sp;
94 const char *t = *tp;
95 do {
96 for (; *s == '/'; s++);
97 for (; *t == '/'; t++);
98 *tp = t;
99 *sp = s;
100 for (; *s == *t && *s && *s != '/'; s++, t++);
101 } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
102}
103
104static void transform_symlink(struct subdir *d, const char *path,
105 char *buf, size_t size)
106{
107 const char *l = buf;
108 size_t llen;
109 char *s;
110 int dotdots;
111 int i;
112
113 if (l[0] != '/' || d->base[0] != '/')
114 return;
115
116 strip_common(&l, &path);
117 if (l - buf < (long) d->baselen)
118 return;
119
120 dotdots = count_components(path);
121 if (!dotdots)
122 return;
123 dotdots--;
124
125 llen = strlen(l);
126 if (dotdots * 3 + llen + 2 > size)
127 return;
128
129 s = buf + dotdots * 3;
130 if (llen)
131 memmove(s, l, llen + 1);
132 else if (!dotdots)
133 strcpy(s, ".");
134 else
135 *s = '\0';
136
137 for (s = buf, i = 0; i < dotdots; i++, s += 3)
138 memcpy(s, "../", 3);
139}
140
141
142static int subdir_readlink(const char *path, char *buf, size_t size)
143{
144 struct subdir *d = subdir_get();
145 char *newpath;
146 int err = subdir_addpath(d, path, &newpath);
147 if (!err) {
148 err = fuse_fs_readlink(d->next, newpath, buf, size);
149 if (!err && d->rellinks)
150 transform_symlink(d, newpath, buf, size);
151 free(newpath);
152 }
153 return err;
154}
155
156static int subdir_opendir(const char *path, struct fuse_file_info *fi)
157{
158 struct subdir *d = subdir_get();
159 char *newpath;
160 int err = subdir_addpath(d, path, &newpath);
161 if (!err) {
162 err = fuse_fs_opendir(d->next, newpath, fi);
163 free(newpath);
164 }
165 return err;
166}
167
168static int subdir_readdir(const char *path, void *buf,
169 fuse_fill_dir_t filler, off_t offset,
170 struct fuse_file_info *fi,
171 enum fuse_readdir_flags flags)
172{
173 struct subdir *d = subdir_get();
174 char *newpath;
175 int err = subdir_addpath(d, path, &newpath);
176 if (!err) {
177 err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
178 fi, flags);
179 free(newpath);
180 }
181 return err;
182}
183
184static int subdir_releasedir(const char *path, struct fuse_file_info *fi)
185{
186 struct subdir *d = subdir_get();
187 char *newpath;
188 int err = subdir_addpath(d, path, &newpath);
189 if (!err) {
190 err = fuse_fs_releasedir(d->next, newpath, fi);
191 free(newpath);
192 }
193 return err;
194}
195
196static int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
197{
198 struct subdir *d = subdir_get();
199 char *newpath;
200 int err = subdir_addpath(d, path, &newpath);
201 if (!err) {
202 err = fuse_fs_mknod(d->next, newpath, mode, rdev);
203 free(newpath);
204 }
205 return err;
206}
207
208static int subdir_mkdir(const char *path, mode_t mode)
209{
210 struct subdir *d = subdir_get();
211 char *newpath;
212 int err = subdir_addpath(d, path, &newpath);
213 if (!err) {
214 err = fuse_fs_mkdir(d->next, newpath, mode);
215 free(newpath);
216 }
217 return err;
218}
219
220static int subdir_unlink(const char *path)
221{
222 struct subdir *d = subdir_get();
223 char *newpath;
224 int err = subdir_addpath(d, path, &newpath);
225 if (!err) {
226 err = fuse_fs_unlink(d->next, newpath);
227 free(newpath);
228 }
229 return err;
230}
231
232static int subdir_rmdir(const char *path)
233{
234 struct subdir *d = subdir_get();
235 char *newpath;
236 int err = subdir_addpath(d, path, &newpath);
237 if (!err) {
238 err = fuse_fs_rmdir(d->next, newpath);
239 free(newpath);
240 }
241 return err;
242}
243
244static int subdir_symlink(const char *from, const char *path)
245{
246 struct subdir *d = subdir_get();
247 char *newpath;
248 int err = subdir_addpath(d, path, &newpath);
249 if (!err) {
250 err = fuse_fs_symlink(d->next, from, newpath);
251 free(newpath);
252 }
253 return err;
254}
255
256static int subdir_rename(const char *from, const char *to, unsigned int flags)
257{
258 struct subdir *d = subdir_get();
259 char *newfrom;
260 char *newto;
261 int err = subdir_addpath(d, from, &newfrom);
262 if (!err) {
263 err = subdir_addpath(d, to, &newto);
264 if (!err) {
265 err = fuse_fs_rename(d->next, newfrom, newto, flags);
266 free(newto);
267 }
268 free(newfrom);
269 }
270 return err;
271}
272
273static int subdir_link(const char *from, const char *to)
274{
275 struct subdir *d = subdir_get();
276 char *newfrom;
277 char *newto;
278 int err = subdir_addpath(d, from, &newfrom);
279 if (!err) {
280 err = subdir_addpath(d, to, &newto);
281 if (!err) {
282 err = fuse_fs_link(d->next, newfrom, newto);
283 free(newto);
284 }
285 free(newfrom);
286 }
287 return err;
288}
289
290static int subdir_chmod(const char *path, mode_t mode,
291 struct fuse_file_info *fi)
292{
293 struct subdir *d = subdir_get();
294 char *newpath;
295 int err = subdir_addpath(d, path, &newpath);
296 if (!err) {
297 err = fuse_fs_chmod(d->next, newpath, mode, fi);
298 free(newpath);
299 }
300 return err;
301}
302
303static int subdir_chown(const char *path, uid_t uid, gid_t gid,
304 struct fuse_file_info *fi)
305{
306 struct subdir *d = subdir_get();
307 char *newpath;
308 int err = subdir_addpath(d, path, &newpath);
309 if (!err) {
310 err = fuse_fs_chown(d->next, newpath, uid, gid, fi);
311 free(newpath);
312 }
313 return err;
314}
315
316static int subdir_truncate(const char *path, off_t size,
317 struct fuse_file_info *fi)
318{
319 struct subdir *d = subdir_get();
320 char *newpath;
321 int err = subdir_addpath(d, path, &newpath);
322 if (!err) {
323 err = fuse_fs_truncate(d->next, newpath, size, fi);
324 free(newpath);
325 }
326 return err;
327}
328
329static int subdir_utimens(const char *path, const struct timespec ts[2],
330 struct fuse_file_info *fi)
331{
332 struct subdir *d = subdir_get();
333 char *newpath;
334 int err = subdir_addpath(d, path, &newpath);
335 if (!err) {
336 err = fuse_fs_utimens(d->next, newpath, ts, fi);
337 free(newpath);
338 }
339 return err;
340}
341
342static int subdir_create(const char *path, mode_t mode,
343 struct fuse_file_info *fi)
344{
345 struct subdir *d = subdir_get();
346 char *newpath;
347 int err = subdir_addpath(d, path, &newpath);
348 if (!err) {
349 err = fuse_fs_create(d->next, newpath, mode, fi);
350 free(newpath);
351 }
352 return err;
353}
354
355static int subdir_open(const char *path, struct fuse_file_info *fi)
356{
357 struct subdir *d = subdir_get();
358 char *newpath;
359 int err = subdir_addpath(d, path, &newpath);
360 if (!err) {
361 err = fuse_fs_open(d->next, newpath, fi);
362 free(newpath);
363 }
364 return err;
365}
366
367static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
368 size_t size, off_t offset, struct fuse_file_info *fi)
369{
370 struct subdir *d = subdir_get();
371 char *newpath;
372 int err = subdir_addpath(d, path, &newpath);
373 if (!err) {
374 err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
375 free(newpath);
376 }
377 return err;
378}
379
380static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
381 off_t offset, struct fuse_file_info *fi)
382{
383 struct subdir *d = subdir_get();
384 char *newpath;
385 int err = subdir_addpath(d, path, &newpath);
386 if (!err) {
387 err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
388 free(newpath);
389 }
390 return err;
391}
392
393static int subdir_statfs(const char *path, struct statvfs *stbuf)
394{
395 struct subdir *d = subdir_get();
396 char *newpath;
397 int err = subdir_addpath(d, path, &newpath);
398 if (!err) {
399 err = fuse_fs_statfs(d->next, newpath, stbuf);
400 free(newpath);
401 }
402 return err;
403}
404
405static int subdir_flush(const char *path, struct fuse_file_info *fi)
406{
407 struct subdir *d = subdir_get();
408 char *newpath;
409 int err = subdir_addpath(d, path, &newpath);
410 if (!err) {
411 err = fuse_fs_flush(d->next, newpath, fi);
412 free(newpath);
413 }
414 return err;
415}
416
417static int subdir_release(const char *path, struct fuse_file_info *fi)
418{
419 struct subdir *d = subdir_get();
420 char *newpath;
421 int err = subdir_addpath(d, path, &newpath);
422 if (!err) {
423 err = fuse_fs_release(d->next, newpath, fi);
424 free(newpath);
425 }
426 return err;
427}
428
429static int subdir_fsync(const char *path, int isdatasync,
430 struct fuse_file_info *fi)
431{
432 struct subdir *d = subdir_get();
433 char *newpath;
434 int err = subdir_addpath(d, path, &newpath);
435 if (!err) {
436 err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
437 free(newpath);
438 }
439 return err;
440}
441
442static int subdir_fsyncdir(const char *path, int isdatasync,
443 struct fuse_file_info *fi)
444{
445 struct subdir *d = subdir_get();
446 char *newpath;
447 int err = subdir_addpath(d, path, &newpath);
448 if (!err) {
449 err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
450 free(newpath);
451 }
452 return err;
453}
454
455static int subdir_setxattr(const char *path, const char *name,
456 const char *value, size_t size, int flags)
457{
458 struct subdir *d = subdir_get();
459 char *newpath;
460 int err = subdir_addpath(d, path, &newpath);
461 if (!err) {
462 err = fuse_fs_setxattr(d->next, newpath, name, value, size,
463 flags);
464 free(newpath);
465 }
466 return err;
467}
468
469static int subdir_getxattr(const char *path, const char *name, char *value,
470 size_t size)
471{
472 struct subdir *d = subdir_get();
473 char *newpath;
474 int err = subdir_addpath(d, path, &newpath);
475 if (!err) {
476 err = fuse_fs_getxattr(d->next, newpath, name, value, size);
477 free(newpath);
478 }
479 return err;
480}
481
482static int subdir_listxattr(const char *path, char *list, size_t size)
483{
484 struct subdir *d = subdir_get();
485 char *newpath;
486 int err = subdir_addpath(d, path, &newpath);
487 if (!err) {
488 err = fuse_fs_listxattr(d->next, newpath, list, size);
489 free(newpath);
490 }
491 return err;
492}
493
494static int subdir_removexattr(const char *path, const char *name)
495{
496 struct subdir *d = subdir_get();
497 char *newpath;
498 int err = subdir_addpath(d, path, &newpath);
499 if (!err) {
500 err = fuse_fs_removexattr(d->next, newpath, name);
501 free(newpath);
502 }
503 return err;
504}
505
506static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
507 struct flock *lock)
508{
509 struct subdir *d = subdir_get();
510 char *newpath;
511 int err = subdir_addpath(d, path, &newpath);
512 if (!err) {
513 err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
514 free(newpath);
515 }
516 return err;
517}
518
519static int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
520{
521 struct subdir *d = subdir_get();
522 char *newpath;
523 int err = subdir_addpath(d, path, &newpath);
524 if (!err) {
525 err = fuse_fs_flock(d->next, newpath, fi, op);
526 free(newpath);
527 }
528 return err;
529}
530
531static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
532{
533 struct subdir *d = subdir_get();
534 char *newpath;
535 int err = subdir_addpath(d, path, &newpath);
536 if (!err) {
537 err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
538 free(newpath);
539 }
540 return err;
541}
542
543static off_t subdir_lseek(const char *path, off_t off, int whence,
544 struct fuse_file_info *fi)
545{
546 struct subdir *ic = subdir_get();
547 char *newpath;
548 int res = subdir_addpath(ic, path, &newpath);
549 if (!res) {
550 res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
551 free(newpath);
552 }
553 return res;
554}
555
556static void *subdir_init(struct fuse_conn_info *conn,
557 struct fuse_config *cfg)
558{
559 struct subdir *d = subdir_get();
560 fuse_fs_init(d->next, conn, cfg);
561 /* Don't touch cfg->nullpath_ok, we can work with
562 either */
563 return d;
564}
565
566static void subdir_destroy(void *data)
567{
568 struct subdir *d = data;
569 fuse_fs_destroy(d->next);
570 free(d->base);
571 free(d);
572}
573
574static const struct fuse_operations subdir_oper = {
575 .destroy = subdir_destroy,
576 .init = subdir_init,
577 .getattr = subdir_getattr,
578 .access = subdir_access,
579 .readlink = subdir_readlink,
580 .opendir = subdir_opendir,
581 .readdir = subdir_readdir,
582 .releasedir = subdir_releasedir,
583 .mknod = subdir_mknod,
584 .mkdir = subdir_mkdir,
585 .symlink = subdir_symlink,
586 .unlink = subdir_unlink,
587 .rmdir = subdir_rmdir,
588 .rename = subdir_rename,
589 .link = subdir_link,
590 .chmod = subdir_chmod,
591 .chown = subdir_chown,
592 .truncate = subdir_truncate,
593 .utimens = subdir_utimens,
594 .create = subdir_create,
595 .open = subdir_open,
596 .read_buf = subdir_read_buf,
597 .write_buf = subdir_write_buf,
598 .statfs = subdir_statfs,
599 .flush = subdir_flush,
600 .release = subdir_release,
601 .fsync = subdir_fsync,
602 .fsyncdir = subdir_fsyncdir,
603 .setxattr = subdir_setxattr,
604 .getxattr = subdir_getxattr,
605 .listxattr = subdir_listxattr,
606 .removexattr = subdir_removexattr,
607 .lock = subdir_lock,
608 .flock = subdir_flock,
609 .bmap = subdir_bmap,
610 .lseek = subdir_lseek,
611};
612
613static const struct fuse_opt subdir_opts[] = {
614 FUSE_OPT_KEY("-h", 0),
615 FUSE_OPT_KEY("--help", 0),
616 { "subdir=%s", offsetof(struct subdir, base), 0 },
617 { "rellinks", offsetof(struct subdir, rellinks), 1 },
618 { "norellinks", offsetof(struct subdir, rellinks), 0 },
620};
621
622static void subdir_help(void)
623{
624 printf(
625" -o subdir=DIR prepend this directory to all paths (mandatory)\n"
626" -o [no]rellinks transform absolute symlinks to relative\n");
627}
628
629static int subdir_opt_proc(void *data, const char *arg, int key,
630 struct fuse_args *outargs)
631{
632 (void) data; (void) arg; (void) outargs;
633
634 if (!key) {
635 subdir_help();
636 return -1;
637 }
638
639 return 1;
640}
641
642static struct fuse_fs *subdir_new(struct fuse_args *args,
643 struct fuse_fs *next[])
644{
645 struct fuse_fs *fs;
646 struct subdir *d;
647
648 d = calloc(1, sizeof(struct subdir));
649 if (d == NULL) {
650 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
651 return NULL;
652 }
653
654 if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
655 goto out_free;
656
657 if (!next[0] || next[1]) {
658 fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n");
659 goto out_free;
660 }
661
662 if (!d->base) {
663 fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n");
664 goto out_free;
665 }
666
667 if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
668 char *tmp = realloc(d->base, strlen(d->base) + 2);
669 if (!tmp) {
670 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
671 goto out_free;
672 }
673 d->base = tmp;
674 strcat(d->base, "/");
675 }
676 d->baselen = strlen(d->base);
677 d->next = next[0];
678 fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
679 if (!fs)
680 goto out_free;
681 return fs;
682
683out_free:
684 free(d->base);
685 free(d);
686 return NULL;
687}
688
689FUSE_REGISTER_MODULE(subdir, subdir_new);
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition fuse.h:1395
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
void * private_data
Definition fuse.h:874
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2modules_2subdir_8c_source.html0000644000175000017500000034101215156613443024265 0ustar berndbernd libfuse: fuse-3.18.1/lib/modules/subdir.c Source File
libfuse
subdir.c
1/*
2 fuse subdir module: offset paths with a base directory
3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#include <fuse_config.h>
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17
18struct subdir {
19 char *base;
20 size_t baselen;
21 int rellinks;
22 struct fuse_fs *next;
23};
24
25static struct subdir *subdir_get(void)
26{
28}
29
30static int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
31{
32 char *newpath = NULL;
33
34 if (path != NULL) {
35 unsigned newlen = d->baselen + strlen(path);
36
37 newpath = malloc(newlen + 2);
38 if (!newpath)
39 return -ENOMEM;
40
41 if (path[0] == '/')
42 path++;
43 strcpy(newpath, d->base);
44 strcpy(newpath + d->baselen, path);
45 if (!newpath[0])
46 strcpy(newpath, ".");
47 }
48 *newpathp = newpath;
49
50 return 0;
51}
52
53static int subdir_getattr(const char *path, struct stat *stbuf,
54 struct fuse_file_info *fi)
55{
56 struct subdir *d = subdir_get();
57 char *newpath;
58 int err = subdir_addpath(d, path, &newpath);
59 if (!err) {
60 err = fuse_fs_getattr(d->next, newpath, stbuf, fi);
61 free(newpath);
62 }
63 return err;
64}
65
66static int subdir_access(const char *path, int mask)
67{
68 struct subdir *d = subdir_get();
69 char *newpath;
70 int err = subdir_addpath(d, path, &newpath);
71 if (!err) {
72 err = fuse_fs_access(d->next, newpath, mask);
73 free(newpath);
74 }
75 return err;
76}
77
78
79static int count_components(const char *p)
80{
81 int ctr;
82
83 for (; *p == '/'; p++);
84 for (ctr = 0; *p; ctr++) {
85 for (; *p && *p != '/'; p++);
86 for (; *p == '/'; p++);
87 }
88 return ctr;
89}
90
91static void strip_common(const char **sp, const char **tp)
92{
93 const char *s = *sp;
94 const char *t = *tp;
95 do {
96 for (; *s == '/'; s++);
97 for (; *t == '/'; t++);
98 *tp = t;
99 *sp = s;
100 for (; *s == *t && *s && *s != '/'; s++, t++);
101 } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
102}
103
104static void transform_symlink(struct subdir *d, const char *path,
105 char *buf, size_t size)
106{
107 const char *l = buf;
108 size_t llen;
109 char *s;
110 int dotdots;
111 int i;
112
113 if (l[0] != '/' || d->base[0] != '/')
114 return;
115
116 strip_common(&l, &path);
117 if (l - buf < (long) d->baselen)
118 return;
119
120 dotdots = count_components(path);
121 if (!dotdots)
122 return;
123 dotdots--;
124
125 llen = strlen(l);
126 if (dotdots * 3 + llen + 2 > size)
127 return;
128
129 s = buf + dotdots * 3;
130 if (llen)
131 memmove(s, l, llen + 1);
132 else if (!dotdots)
133 strcpy(s, ".");
134 else
135 *s = '\0';
136
137 for (s = buf, i = 0; i < dotdots; i++, s += 3)
138 memcpy(s, "../", 3);
139}
140
141
142static int subdir_readlink(const char *path, char *buf, size_t size)
143{
144 struct subdir *d = subdir_get();
145 char *newpath;
146 int err = subdir_addpath(d, path, &newpath);
147 if (!err) {
148 err = fuse_fs_readlink(d->next, newpath, buf, size);
149 if (!err && d->rellinks)
150 transform_symlink(d, newpath, buf, size);
151 free(newpath);
152 }
153 return err;
154}
155
156static int subdir_opendir(const char *path, struct fuse_file_info *fi)
157{
158 struct subdir *d = subdir_get();
159 char *newpath;
160 int err = subdir_addpath(d, path, &newpath);
161 if (!err) {
162 err = fuse_fs_opendir(d->next, newpath, fi);
163 free(newpath);
164 }
165 return err;
166}
167
168static int subdir_readdir(const char *path, void *buf,
169 fuse_fill_dir_t filler, off_t offset,
170 struct fuse_file_info *fi,
171 enum fuse_readdir_flags flags)
172{
173 struct subdir *d = subdir_get();
174 char *newpath;
175 int err = subdir_addpath(d, path, &newpath);
176 if (!err) {
177 err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
178 fi, flags);
179 free(newpath);
180 }
181 return err;
182}
183
184static int subdir_releasedir(const char *path, struct fuse_file_info *fi)
185{
186 struct subdir *d = subdir_get();
187 char *newpath;
188 int err = subdir_addpath(d, path, &newpath);
189 if (!err) {
190 err = fuse_fs_releasedir(d->next, newpath, fi);
191 free(newpath);
192 }
193 return err;
194}
195
196static int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
197{
198 struct subdir *d = subdir_get();
199 char *newpath;
200 int err = subdir_addpath(d, path, &newpath);
201 if (!err) {
202 err = fuse_fs_mknod(d->next, newpath, mode, rdev);
203 free(newpath);
204 }
205 return err;
206}
207
208static int subdir_mkdir(const char *path, mode_t mode)
209{
210 struct subdir *d = subdir_get();
211 char *newpath;
212 int err = subdir_addpath(d, path, &newpath);
213 if (!err) {
214 err = fuse_fs_mkdir(d->next, newpath, mode);
215 free(newpath);
216 }
217 return err;
218}
219
220static int subdir_unlink(const char *path)
221{
222 struct subdir *d = subdir_get();
223 char *newpath;
224 int err = subdir_addpath(d, path, &newpath);
225 if (!err) {
226 err = fuse_fs_unlink(d->next, newpath);
227 free(newpath);
228 }
229 return err;
230}
231
232static int subdir_rmdir(const char *path)
233{
234 struct subdir *d = subdir_get();
235 char *newpath;
236 int err = subdir_addpath(d, path, &newpath);
237 if (!err) {
238 err = fuse_fs_rmdir(d->next, newpath);
239 free(newpath);
240 }
241 return err;
242}
243
244static int subdir_symlink(const char *from, const char *path)
245{
246 struct subdir *d = subdir_get();
247 char *newpath;
248 int err = subdir_addpath(d, path, &newpath);
249 if (!err) {
250 err = fuse_fs_symlink(d->next, from, newpath);
251 free(newpath);
252 }
253 return err;
254}
255
256static int subdir_rename(const char *from, const char *to, unsigned int flags)
257{
258 struct subdir *d = subdir_get();
259 char *newfrom;
260 char *newto;
261 int err = subdir_addpath(d, from, &newfrom);
262 if (!err) {
263 err = subdir_addpath(d, to, &newto);
264 if (!err) {
265 err = fuse_fs_rename(d->next, newfrom, newto, flags);
266 free(newto);
267 }
268 free(newfrom);
269 }
270 return err;
271}
272
273static int subdir_link(const char *from, const char *to)
274{
275 struct subdir *d = subdir_get();
276 char *newfrom;
277 char *newto;
278 int err = subdir_addpath(d, from, &newfrom);
279 if (!err) {
280 err = subdir_addpath(d, to, &newto);
281 if (!err) {
282 err = fuse_fs_link(d->next, newfrom, newto);
283 free(newto);
284 }
285 free(newfrom);
286 }
287 return err;
288}
289
290static int subdir_chmod(const char *path, mode_t mode,
291 struct fuse_file_info *fi)
292{
293 struct subdir *d = subdir_get();
294 char *newpath;
295 int err = subdir_addpath(d, path, &newpath);
296 if (!err) {
297 err = fuse_fs_chmod(d->next, newpath, mode, fi);
298 free(newpath);
299 }
300 return err;
301}
302
303static int subdir_chown(const char *path, uid_t uid, gid_t gid,
304 struct fuse_file_info *fi)
305{
306 struct subdir *d = subdir_get();
307 char *newpath;
308 int err = subdir_addpath(d, path, &newpath);
309 if (!err) {
310 err = fuse_fs_chown(d->next, newpath, uid, gid, fi);
311 free(newpath);
312 }
313 return err;
314}
315
316static int subdir_truncate(const char *path, off_t size,
317 struct fuse_file_info *fi)
318{
319 struct subdir *d = subdir_get();
320 char *newpath;
321 int err = subdir_addpath(d, path, &newpath);
322 if (!err) {
323 err = fuse_fs_truncate(d->next, newpath, size, fi);
324 free(newpath);
325 }
326 return err;
327}
328
329static int subdir_utimens(const char *path, const struct timespec ts[2],
330 struct fuse_file_info *fi)
331{
332 struct subdir *d = subdir_get();
333 char *newpath;
334 int err = subdir_addpath(d, path, &newpath);
335 if (!err) {
336 err = fuse_fs_utimens(d->next, newpath, ts, fi);
337 free(newpath);
338 }
339 return err;
340}
341
342static int subdir_create(const char *path, mode_t mode,
343 struct fuse_file_info *fi)
344{
345 struct subdir *d = subdir_get();
346 char *newpath;
347 int err = subdir_addpath(d, path, &newpath);
348 if (!err) {
349 err = fuse_fs_create(d->next, newpath, mode, fi);
350 free(newpath);
351 }
352 return err;
353}
354
355static int subdir_open(const char *path, struct fuse_file_info *fi)
356{
357 struct subdir *d = subdir_get();
358 char *newpath;
359 int err = subdir_addpath(d, path, &newpath);
360 if (!err) {
361 err = fuse_fs_open(d->next, newpath, fi);
362 free(newpath);
363 }
364 return err;
365}
366
367static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
368 size_t size, off_t offset, struct fuse_file_info *fi)
369{
370 struct subdir *d = subdir_get();
371 char *newpath;
372 int err = subdir_addpath(d, path, &newpath);
373 if (!err) {
374 err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
375 free(newpath);
376 }
377 return err;
378}
379
380static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
381 off_t offset, struct fuse_file_info *fi)
382{
383 struct subdir *d = subdir_get();
384 char *newpath;
385 int err = subdir_addpath(d, path, &newpath);
386 if (!err) {
387 err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
388 free(newpath);
389 }
390 return err;
391}
392
393static int subdir_statfs(const char *path, struct statvfs *stbuf)
394{
395 struct subdir *d = subdir_get();
396 char *newpath;
397 int err = subdir_addpath(d, path, &newpath);
398 if (!err) {
399 err = fuse_fs_statfs(d->next, newpath, stbuf);
400 free(newpath);
401 }
402 return err;
403}
404
405static int subdir_flush(const char *path, struct fuse_file_info *fi)
406{
407 struct subdir *d = subdir_get();
408 char *newpath;
409 int err = subdir_addpath(d, path, &newpath);
410 if (!err) {
411 err = fuse_fs_flush(d->next, newpath, fi);
412 free(newpath);
413 }
414 return err;
415}
416
417static int subdir_release(const char *path, struct fuse_file_info *fi)
418{
419 struct subdir *d = subdir_get();
420 char *newpath;
421 int err = subdir_addpath(d, path, &newpath);
422 if (!err) {
423 err = fuse_fs_release(d->next, newpath, fi);
424 free(newpath);
425 }
426 return err;
427}
428
429static int subdir_fsync(const char *path, int isdatasync,
430 struct fuse_file_info *fi)
431{
432 struct subdir *d = subdir_get();
433 char *newpath;
434 int err = subdir_addpath(d, path, &newpath);
435 if (!err) {
436 err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
437 free(newpath);
438 }
439 return err;
440}
441
442static int subdir_fsyncdir(const char *path, int isdatasync,
443 struct fuse_file_info *fi)
444{
445 struct subdir *d = subdir_get();
446 char *newpath;
447 int err = subdir_addpath(d, path, &newpath);
448 if (!err) {
449 err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
450 free(newpath);
451 }
452 return err;
453}
454
455static int subdir_setxattr(const char *path, const char *name,
456 const char *value, size_t size, int flags)
457{
458 struct subdir *d = subdir_get();
459 char *newpath;
460 int err = subdir_addpath(d, path, &newpath);
461 if (!err) {
462 err = fuse_fs_setxattr(d->next, newpath, name, value, size,
463 flags);
464 free(newpath);
465 }
466 return err;
467}
468
469static int subdir_getxattr(const char *path, const char *name, char *value,
470 size_t size)
471{
472 struct subdir *d = subdir_get();
473 char *newpath;
474 int err = subdir_addpath(d, path, &newpath);
475 if (!err) {
476 err = fuse_fs_getxattr(d->next, newpath, name, value, size);
477 free(newpath);
478 }
479 return err;
480}
481
482static int subdir_listxattr(const char *path, char *list, size_t size)
483{
484 struct subdir *d = subdir_get();
485 char *newpath;
486 int err = subdir_addpath(d, path, &newpath);
487 if (!err) {
488 err = fuse_fs_listxattr(d->next, newpath, list, size);
489 free(newpath);
490 }
491 return err;
492}
493
494static int subdir_removexattr(const char *path, const char *name)
495{
496 struct subdir *d = subdir_get();
497 char *newpath;
498 int err = subdir_addpath(d, path, &newpath);
499 if (!err) {
500 err = fuse_fs_removexattr(d->next, newpath, name);
501 free(newpath);
502 }
503 return err;
504}
505
506static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
507 struct flock *lock)
508{
509 struct subdir *d = subdir_get();
510 char *newpath;
511 int err = subdir_addpath(d, path, &newpath);
512 if (!err) {
513 err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
514 free(newpath);
515 }
516 return err;
517}
518
519static int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
520{
521 struct subdir *d = subdir_get();
522 char *newpath;
523 int err = subdir_addpath(d, path, &newpath);
524 if (!err) {
525 err = fuse_fs_flock(d->next, newpath, fi, op);
526 free(newpath);
527 }
528 return err;
529}
530
531static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
532{
533 struct subdir *d = subdir_get();
534 char *newpath;
535 int err = subdir_addpath(d, path, &newpath);
536 if (!err) {
537 err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
538 free(newpath);
539 }
540 return err;
541}
542
543static off_t subdir_lseek(const char *path, off_t off, int whence,
544 struct fuse_file_info *fi)
545{
546 struct subdir *ic = subdir_get();
547 char *newpath;
548 int res = subdir_addpath(ic, path, &newpath);
549 if (!res) {
550 res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
551 free(newpath);
552 }
553 return res;
554}
555
556#ifdef HAVE_STATX
557static int subdir_statx(const char *path, int flags, int mask, struct statx *stxbuf,
558 struct fuse_file_info *fi)
559{
560 struct subdir *ic = subdir_get();
561 char *newpath;
562 int res = subdir_addpath(ic, path, &newpath);
563
564 if (!res) {
565 res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi);
566 free(newpath);
567 }
568 return res;
569}
570#endif
571
572static void *subdir_init(struct fuse_conn_info *conn,
573 struct fuse_config *cfg)
574{
575 struct subdir *d = subdir_get();
576 fuse_fs_init(d->next, conn, cfg);
577 /* Don't touch cfg->nullpath_ok, we can work with
578 either */
579 return d;
580}
581
582static void subdir_destroy(void *data)
583{
584 struct subdir *d = data;
585 fuse_fs_destroy(d->next);
586 free(d->base);
587 free(d);
588}
589
590static const struct fuse_operations subdir_oper = {
591 .destroy = subdir_destroy,
592 .init = subdir_init,
593 .getattr = subdir_getattr,
594 .access = subdir_access,
595 .readlink = subdir_readlink,
596 .opendir = subdir_opendir,
597 .readdir = subdir_readdir,
598 .releasedir = subdir_releasedir,
599 .mknod = subdir_mknod,
600 .mkdir = subdir_mkdir,
601 .symlink = subdir_symlink,
602 .unlink = subdir_unlink,
603 .rmdir = subdir_rmdir,
604 .rename = subdir_rename,
605 .link = subdir_link,
606 .chmod = subdir_chmod,
607 .chown = subdir_chown,
608 .truncate = subdir_truncate,
609 .utimens = subdir_utimens,
610 .create = subdir_create,
611 .open = subdir_open,
612 .read_buf = subdir_read_buf,
613 .write_buf = subdir_write_buf,
614 .statfs = subdir_statfs,
615 .flush = subdir_flush,
616 .release = subdir_release,
617 .fsync = subdir_fsync,
618 .fsyncdir = subdir_fsyncdir,
619 .setxattr = subdir_setxattr,
620 .getxattr = subdir_getxattr,
621 .listxattr = subdir_listxattr,
622 .removexattr = subdir_removexattr,
623 .lock = subdir_lock,
624 .flock = subdir_flock,
625 .bmap = subdir_bmap,
626 .lseek = subdir_lseek,
627#ifdef HAVE_STATX
628 .statx = subdir_statx,
629#endif
630};
631
632static const struct fuse_opt subdir_opts[] = {
633 FUSE_OPT_KEY("-h", 0),
634 FUSE_OPT_KEY("--help", 0),
635 { "subdir=%s", offsetof(struct subdir, base), 0 },
636 { "rellinks", offsetof(struct subdir, rellinks), 1 },
637 { "norellinks", offsetof(struct subdir, rellinks), 0 },
639};
640
641static void subdir_help(void)
642{
643 printf(
644" -o subdir=DIR prepend this directory to all paths (mandatory)\n"
645" -o [no]rellinks transform absolute symlinks to relative\n");
646}
647
648static int subdir_opt_proc(void *data, const char *arg, int key,
649 struct fuse_args *outargs)
650{
651 (void) data; (void) arg; (void) outargs;
652
653 if (!key) {
654 subdir_help();
655 return -1;
656 }
657
658 return 1;
659}
660
661static struct fuse_fs *subdir_new(struct fuse_args *args,
662 struct fuse_fs *next[])
663{
664 struct fuse_fs *fs;
665 struct subdir *d;
666
667 d = calloc(1, sizeof(struct subdir));
668 if (d == NULL) {
669 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
670 return NULL;
671 }
672
673 if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
674 goto out_free;
675
676 if (!next[0] || next[1]) {
677 fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n");
678 goto out_free;
679 }
680
681 if (!d->base) {
682 fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n");
683 goto out_free;
684 }
685
686 if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
687 char *tmp = realloc(d->base, strlen(d->base) + 2);
688 if (!tmp) {
689 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
690 goto out_free;
691 }
692 d->base = tmp;
693 strcat(d->base, "/");
694 }
695 d->baselen = strlen(d->base);
696 d->next = next[0];
697 fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
698 if (!fs)
699 goto out_free;
700 return fs;
701
702out_free:
703 free(d->base);
704 free(d);
705 return NULL;
706}
707
708FUSE_REGISTER_MODULE(subdir, subdir_new);
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition fuse.h:1395
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
void * private_data
Definition fuse.h:874
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/lib_2modules_2subdir_8c_source.html0000644000175000017500000034062715156613443022124 0ustar berndbernd libfuse: lib/modules/subdir.c Source File
libfuse
subdir.c
1/*
2 fuse subdir module: offset paths with a base directory
3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#include <fuse_config.h>
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17
18struct subdir {
19 char *base;
20 size_t baselen;
21 int rellinks;
22 struct fuse_fs *next;
23};
24
25static struct subdir *subdir_get(void)
26{
28}
29
30static int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
31{
32 char *newpath = NULL;
33
34 if (path != NULL) {
35 unsigned newlen = d->baselen + strlen(path);
36
37 newpath = malloc(newlen + 2);
38 if (!newpath)
39 return -ENOMEM;
40
41 if (path[0] == '/')
42 path++;
43 strcpy(newpath, d->base);
44 strcpy(newpath + d->baselen, path);
45 if (!newpath[0])
46 strcpy(newpath, ".");
47 }
48 *newpathp = newpath;
49
50 return 0;
51}
52
53static int subdir_getattr(const char *path, struct stat *stbuf,
54 struct fuse_file_info *fi)
55{
56 struct subdir *d = subdir_get();
57 char *newpath;
58 int err = subdir_addpath(d, path, &newpath);
59 if (!err) {
60 err = fuse_fs_getattr(d->next, newpath, stbuf, fi);
61 free(newpath);
62 }
63 return err;
64}
65
66static int subdir_access(const char *path, int mask)
67{
68 struct subdir *d = subdir_get();
69 char *newpath;
70 int err = subdir_addpath(d, path, &newpath);
71 if (!err) {
72 err = fuse_fs_access(d->next, newpath, mask);
73 free(newpath);
74 }
75 return err;
76}
77
78
79static int count_components(const char *p)
80{
81 int ctr;
82
83 for (; *p == '/'; p++);
84 for (ctr = 0; *p; ctr++) {
85 for (; *p && *p != '/'; p++);
86 for (; *p == '/'; p++);
87 }
88 return ctr;
89}
90
91static void strip_common(const char **sp, const char **tp)
92{
93 const char *s = *sp;
94 const char *t = *tp;
95 do {
96 for (; *s == '/'; s++);
97 for (; *t == '/'; t++);
98 *tp = t;
99 *sp = s;
100 for (; *s == *t && *s && *s != '/'; s++, t++);
101 } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
102}
103
104static void transform_symlink(struct subdir *d, const char *path,
105 char *buf, size_t size)
106{
107 const char *l = buf;
108 size_t llen;
109 char *s;
110 int dotdots;
111 int i;
112
113 if (l[0] != '/' || d->base[0] != '/')
114 return;
115
116 strip_common(&l, &path);
117 if (l - buf < (long) d->baselen)
118 return;
119
120 dotdots = count_components(path);
121 if (!dotdots)
122 return;
123 dotdots--;
124
125 llen = strlen(l);
126 if (dotdots * 3 + llen + 2 > size)
127 return;
128
129 s = buf + dotdots * 3;
130 if (llen)
131 memmove(s, l, llen + 1);
132 else if (!dotdots)
133 strcpy(s, ".");
134 else
135 *s = '\0';
136
137 for (s = buf, i = 0; i < dotdots; i++, s += 3)
138 memcpy(s, "../", 3);
139}
140
141
142static int subdir_readlink(const char *path, char *buf, size_t size)
143{
144 struct subdir *d = subdir_get();
145 char *newpath;
146 int err = subdir_addpath(d, path, &newpath);
147 if (!err) {
148 err = fuse_fs_readlink(d->next, newpath, buf, size);
149 if (!err && d->rellinks)
150 transform_symlink(d, newpath, buf, size);
151 free(newpath);
152 }
153 return err;
154}
155
156static int subdir_opendir(const char *path, struct fuse_file_info *fi)
157{
158 struct subdir *d = subdir_get();
159 char *newpath;
160 int err = subdir_addpath(d, path, &newpath);
161 if (!err) {
162 err = fuse_fs_opendir(d->next, newpath, fi);
163 free(newpath);
164 }
165 return err;
166}
167
168static int subdir_readdir(const char *path, void *buf,
169 fuse_fill_dir_t filler, off_t offset,
170 struct fuse_file_info *fi,
171 enum fuse_readdir_flags flags)
172{
173 struct subdir *d = subdir_get();
174 char *newpath;
175 int err = subdir_addpath(d, path, &newpath);
176 if (!err) {
177 err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
178 fi, flags);
179 free(newpath);
180 }
181 return err;
182}
183
184static int subdir_releasedir(const char *path, struct fuse_file_info *fi)
185{
186 struct subdir *d = subdir_get();
187 char *newpath;
188 int err = subdir_addpath(d, path, &newpath);
189 if (!err) {
190 err = fuse_fs_releasedir(d->next, newpath, fi);
191 free(newpath);
192 }
193 return err;
194}
195
196static int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
197{
198 struct subdir *d = subdir_get();
199 char *newpath;
200 int err = subdir_addpath(d, path, &newpath);
201 if (!err) {
202 err = fuse_fs_mknod(d->next, newpath, mode, rdev);
203 free(newpath);
204 }
205 return err;
206}
207
208static int subdir_mkdir(const char *path, mode_t mode)
209{
210 struct subdir *d = subdir_get();
211 char *newpath;
212 int err = subdir_addpath(d, path, &newpath);
213 if (!err) {
214 err = fuse_fs_mkdir(d->next, newpath, mode);
215 free(newpath);
216 }
217 return err;
218}
219
220static int subdir_unlink(const char *path)
221{
222 struct subdir *d = subdir_get();
223 char *newpath;
224 int err = subdir_addpath(d, path, &newpath);
225 if (!err) {
226 err = fuse_fs_unlink(d->next, newpath);
227 free(newpath);
228 }
229 return err;
230}
231
232static int subdir_rmdir(const char *path)
233{
234 struct subdir *d = subdir_get();
235 char *newpath;
236 int err = subdir_addpath(d, path, &newpath);
237 if (!err) {
238 err = fuse_fs_rmdir(d->next, newpath);
239 free(newpath);
240 }
241 return err;
242}
243
244static int subdir_symlink(const char *from, const char *path)
245{
246 struct subdir *d = subdir_get();
247 char *newpath;
248 int err = subdir_addpath(d, path, &newpath);
249 if (!err) {
250 err = fuse_fs_symlink(d->next, from, newpath);
251 free(newpath);
252 }
253 return err;
254}
255
256static int subdir_rename(const char *from, const char *to, unsigned int flags)
257{
258 struct subdir *d = subdir_get();
259 char *newfrom;
260 char *newto;
261 int err = subdir_addpath(d, from, &newfrom);
262 if (!err) {
263 err = subdir_addpath(d, to, &newto);
264 if (!err) {
265 err = fuse_fs_rename(d->next, newfrom, newto, flags);
266 free(newto);
267 }
268 free(newfrom);
269 }
270 return err;
271}
272
273static int subdir_link(const char *from, const char *to)
274{
275 struct subdir *d = subdir_get();
276 char *newfrom;
277 char *newto;
278 int err = subdir_addpath(d, from, &newfrom);
279 if (!err) {
280 err = subdir_addpath(d, to, &newto);
281 if (!err) {
282 err = fuse_fs_link(d->next, newfrom, newto);
283 free(newto);
284 }
285 free(newfrom);
286 }
287 return err;
288}
289
290static int subdir_chmod(const char *path, mode_t mode,
291 struct fuse_file_info *fi)
292{
293 struct subdir *d = subdir_get();
294 char *newpath;
295 int err = subdir_addpath(d, path, &newpath);
296 if (!err) {
297 err = fuse_fs_chmod(d->next, newpath, mode, fi);
298 free(newpath);
299 }
300 return err;
301}
302
303static int subdir_chown(const char *path, uid_t uid, gid_t gid,
304 struct fuse_file_info *fi)
305{
306 struct subdir *d = subdir_get();
307 char *newpath;
308 int err = subdir_addpath(d, path, &newpath);
309 if (!err) {
310 err = fuse_fs_chown(d->next, newpath, uid, gid, fi);
311 free(newpath);
312 }
313 return err;
314}
315
316static int subdir_truncate(const char *path, off_t size,
317 struct fuse_file_info *fi)
318{
319 struct subdir *d = subdir_get();
320 char *newpath;
321 int err = subdir_addpath(d, path, &newpath);
322 if (!err) {
323 err = fuse_fs_truncate(d->next, newpath, size, fi);
324 free(newpath);
325 }
326 return err;
327}
328
329static int subdir_utimens(const char *path, const struct timespec ts[2],
330 struct fuse_file_info *fi)
331{
332 struct subdir *d = subdir_get();
333 char *newpath;
334 int err = subdir_addpath(d, path, &newpath);
335 if (!err) {
336 err = fuse_fs_utimens(d->next, newpath, ts, fi);
337 free(newpath);
338 }
339 return err;
340}
341
342static int subdir_create(const char *path, mode_t mode,
343 struct fuse_file_info *fi)
344{
345 struct subdir *d = subdir_get();
346 char *newpath;
347 int err = subdir_addpath(d, path, &newpath);
348 if (!err) {
349 err = fuse_fs_create(d->next, newpath, mode, fi);
350 free(newpath);
351 }
352 return err;
353}
354
355static int subdir_open(const char *path, struct fuse_file_info *fi)
356{
357 struct subdir *d = subdir_get();
358 char *newpath;
359 int err = subdir_addpath(d, path, &newpath);
360 if (!err) {
361 err = fuse_fs_open(d->next, newpath, fi);
362 free(newpath);
363 }
364 return err;
365}
366
367static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
368 size_t size, off_t offset, struct fuse_file_info *fi)
369{
370 struct subdir *d = subdir_get();
371 char *newpath;
372 int err = subdir_addpath(d, path, &newpath);
373 if (!err) {
374 err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
375 free(newpath);
376 }
377 return err;
378}
379
380static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
381 off_t offset, struct fuse_file_info *fi)
382{
383 struct subdir *d = subdir_get();
384 char *newpath;
385 int err = subdir_addpath(d, path, &newpath);
386 if (!err) {
387 err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
388 free(newpath);
389 }
390 return err;
391}
392
393static int subdir_statfs(const char *path, struct statvfs *stbuf)
394{
395 struct subdir *d = subdir_get();
396 char *newpath;
397 int err = subdir_addpath(d, path, &newpath);
398 if (!err) {
399 err = fuse_fs_statfs(d->next, newpath, stbuf);
400 free(newpath);
401 }
402 return err;
403}
404
405static int subdir_flush(const char *path, struct fuse_file_info *fi)
406{
407 struct subdir *d = subdir_get();
408 char *newpath;
409 int err = subdir_addpath(d, path, &newpath);
410 if (!err) {
411 err = fuse_fs_flush(d->next, newpath, fi);
412 free(newpath);
413 }
414 return err;
415}
416
417static int subdir_release(const char *path, struct fuse_file_info *fi)
418{
419 struct subdir *d = subdir_get();
420 char *newpath;
421 int err = subdir_addpath(d, path, &newpath);
422 if (!err) {
423 err = fuse_fs_release(d->next, newpath, fi);
424 free(newpath);
425 }
426 return err;
427}
428
429static int subdir_fsync(const char *path, int isdatasync,
430 struct fuse_file_info *fi)
431{
432 struct subdir *d = subdir_get();
433 char *newpath;
434 int err = subdir_addpath(d, path, &newpath);
435 if (!err) {
436 err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
437 free(newpath);
438 }
439 return err;
440}
441
442static int subdir_fsyncdir(const char *path, int isdatasync,
443 struct fuse_file_info *fi)
444{
445 struct subdir *d = subdir_get();
446 char *newpath;
447 int err = subdir_addpath(d, path, &newpath);
448 if (!err) {
449 err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
450 free(newpath);
451 }
452 return err;
453}
454
455static int subdir_setxattr(const char *path, const char *name,
456 const char *value, size_t size, int flags)
457{
458 struct subdir *d = subdir_get();
459 char *newpath;
460 int err = subdir_addpath(d, path, &newpath);
461 if (!err) {
462 err = fuse_fs_setxattr(d->next, newpath, name, value, size,
463 flags);
464 free(newpath);
465 }
466 return err;
467}
468
469static int subdir_getxattr(const char *path, const char *name, char *value,
470 size_t size)
471{
472 struct subdir *d = subdir_get();
473 char *newpath;
474 int err = subdir_addpath(d, path, &newpath);
475 if (!err) {
476 err = fuse_fs_getxattr(d->next, newpath, name, value, size);
477 free(newpath);
478 }
479 return err;
480}
481
482static int subdir_listxattr(const char *path, char *list, size_t size)
483{
484 struct subdir *d = subdir_get();
485 char *newpath;
486 int err = subdir_addpath(d, path, &newpath);
487 if (!err) {
488 err = fuse_fs_listxattr(d->next, newpath, list, size);
489 free(newpath);
490 }
491 return err;
492}
493
494static int subdir_removexattr(const char *path, const char *name)
495{
496 struct subdir *d = subdir_get();
497 char *newpath;
498 int err = subdir_addpath(d, path, &newpath);
499 if (!err) {
500 err = fuse_fs_removexattr(d->next, newpath, name);
501 free(newpath);
502 }
503 return err;
504}
505
506static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
507 struct flock *lock)
508{
509 struct subdir *d = subdir_get();
510 char *newpath;
511 int err = subdir_addpath(d, path, &newpath);
512 if (!err) {
513 err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
514 free(newpath);
515 }
516 return err;
517}
518
519static int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
520{
521 struct subdir *d = subdir_get();
522 char *newpath;
523 int err = subdir_addpath(d, path, &newpath);
524 if (!err) {
525 err = fuse_fs_flock(d->next, newpath, fi, op);
526 free(newpath);
527 }
528 return err;
529}
530
531static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
532{
533 struct subdir *d = subdir_get();
534 char *newpath;
535 int err = subdir_addpath(d, path, &newpath);
536 if (!err) {
537 err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
538 free(newpath);
539 }
540 return err;
541}
542
543static off_t subdir_lseek(const char *path, off_t off, int whence,
544 struct fuse_file_info *fi)
545{
546 struct subdir *ic = subdir_get();
547 char *newpath;
548 int res = subdir_addpath(ic, path, &newpath);
549 if (!res) {
550 res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
551 free(newpath);
552 }
553 return res;
554}
555
556#ifdef HAVE_STATX
557static int subdir_statx(const char *path, int flags, int mask, struct statx *stxbuf,
558 struct fuse_file_info *fi)
559{
560 struct subdir *ic = subdir_get();
561 char *newpath;
562 int res = subdir_addpath(ic, path, &newpath);
563
564 if (!res) {
565 res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi);
566 free(newpath);
567 }
568 return res;
569}
570#endif
571
572static void *subdir_init(struct fuse_conn_info *conn,
573 struct fuse_config *cfg)
574{
575 struct subdir *d = subdir_get();
576 fuse_fs_init(d->next, conn, cfg);
577 /* Don't touch cfg->nullpath_ok, we can work with
578 either */
579 return d;
580}
581
582static void subdir_destroy(void *data)
583{
584 struct subdir *d = data;
585 fuse_fs_destroy(d->next);
586 free(d->base);
587 free(d);
588}
589
590static const struct fuse_operations subdir_oper = {
591 .destroy = subdir_destroy,
592 .init = subdir_init,
593 .getattr = subdir_getattr,
594 .access = subdir_access,
595 .readlink = subdir_readlink,
596 .opendir = subdir_opendir,
597 .readdir = subdir_readdir,
598 .releasedir = subdir_releasedir,
599 .mknod = subdir_mknod,
600 .mkdir = subdir_mkdir,
601 .symlink = subdir_symlink,
602 .unlink = subdir_unlink,
603 .rmdir = subdir_rmdir,
604 .rename = subdir_rename,
605 .link = subdir_link,
606 .chmod = subdir_chmod,
607 .chown = subdir_chown,
608 .truncate = subdir_truncate,
609 .utimens = subdir_utimens,
610 .create = subdir_create,
611 .open = subdir_open,
612 .read_buf = subdir_read_buf,
613 .write_buf = subdir_write_buf,
614 .statfs = subdir_statfs,
615 .flush = subdir_flush,
616 .release = subdir_release,
617 .fsync = subdir_fsync,
618 .fsyncdir = subdir_fsyncdir,
619 .setxattr = subdir_setxattr,
620 .getxattr = subdir_getxattr,
621 .listxattr = subdir_listxattr,
622 .removexattr = subdir_removexattr,
623 .lock = subdir_lock,
624 .flock = subdir_flock,
625 .bmap = subdir_bmap,
626 .lseek = subdir_lseek,
627#ifdef HAVE_STATX
628 .statx = subdir_statx,
629#endif
630};
631
632static const struct fuse_opt subdir_opts[] = {
633 FUSE_OPT_KEY("-h", 0),
634 FUSE_OPT_KEY("--help", 0),
635 { "subdir=%s", offsetof(struct subdir, base), 0 },
636 { "rellinks", offsetof(struct subdir, rellinks), 1 },
637 { "norellinks", offsetof(struct subdir, rellinks), 0 },
639};
640
641static void subdir_help(void)
642{
643 printf(
644" -o subdir=DIR prepend this directory to all paths (mandatory)\n"
645" -o [no]rellinks transform absolute symlinks to relative\n");
646}
647
648static int subdir_opt_proc(void *data, const char *arg, int key,
649 struct fuse_args *outargs)
650{
651 (void) data; (void) arg; (void) outargs;
652
653 if (!key) {
654 subdir_help();
655 return -1;
656 }
657
658 return 1;
659}
660
661static struct fuse_fs *subdir_new(struct fuse_args *args,
662 struct fuse_fs *next[])
663{
664 struct fuse_fs *fs;
665 struct subdir *d;
666
667 d = calloc(1, sizeof(struct subdir));
668 if (d == NULL) {
669 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
670 return NULL;
671 }
672
673 if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
674 goto out_free;
675
676 if (!next[0] || next[1]) {
677 fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n");
678 goto out_free;
679 }
680
681 if (!d->base) {
682 fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n");
683 goto out_free;
684 }
685
686 if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
687 char *tmp = realloc(d->base, strlen(d->base) + 2);
688 if (!tmp) {
689 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
690 goto out_free;
691 }
692 d->base = tmp;
693 strcat(d->base, "/");
694 }
695 d->baselen = strlen(d->base);
696 d->next = next[0];
697 fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
698 if (!fs)
699 goto out_free;
700 return fs;
701
702out_free:
703 free(d->base);
704 free(d);
705 return NULL;
706}
707
708FUSE_REGISTER_MODULE(subdir, subdir_new);
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition fuse.h:1395
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
void * private_data
Definition fuse.h:874
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2mount_8c_source.html0000644000175000017500000035335015156613443022337 0ustar berndbernd libfuse: fuse-3.17.4/lib/mount.c Source File
libfuse
mount.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture specific file system mounting (Linux).
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB.
9*/
10
11/* For environ */
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_misc.h"
17#include "fuse_opt.h"
18#include "mount_util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <stddef.h>
24#include <string.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <poll.h>
28#include <spawn.h>
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <sys/wait.h>
32
33#include "fuse_mount_compat.h"
34
35#ifdef __NetBSD__
36#include <perfuse.h>
37
38#define MS_RDONLY MNT_RDONLY
39#define MS_NOSUID MNT_NOSUID
40#define MS_NODEV MNT_NODEV
41#define MS_NOEXEC MNT_NOEXEC
42#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
43#define MS_NOATIME MNT_NOATIME
44#define MS_NOSYMFOLLOW MNT_NOSYMFOLLOW
45
46#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
47#endif
48
49#define FUSERMOUNT_PROG "fusermount3"
50#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
51#define FUSE_COMMFD2_ENV "_FUSE_COMMFD2"
52
53#ifndef MS_DIRSYNC
54#define MS_DIRSYNC 128
55#endif
56
57enum {
58 KEY_KERN_FLAG,
59 KEY_KERN_OPT,
60 KEY_FUSERMOUNT_OPT,
61 KEY_SUBTYPE_OPT,
62 KEY_MTAB_OPT,
63 KEY_ALLOW_OTHER,
64 KEY_RO,
65};
66
67struct mount_opts {
68 int allow_other;
69 int flags;
70 int auto_unmount;
71 int blkdev;
72 char *fsname;
73 char *subtype;
74 char *subtype_opt;
75 char *mtab_opts;
76 char *fusermount_opts;
77 char *kernel_opts;
78 unsigned max_read;
79};
80
81#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
82
83static const struct fuse_opt fuse_mount_opts[] = {
84 FUSE_MOUNT_OPT("allow_other", allow_other),
85 FUSE_MOUNT_OPT("blkdev", blkdev),
86 FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
87 FUSE_MOUNT_OPT("fsname=%s", fsname),
88 FUSE_MOUNT_OPT("max_read=%u", max_read),
89 FUSE_MOUNT_OPT("subtype=%s", subtype),
90 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
91 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
92 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
93 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
94 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
95 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
96 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
97 FUSE_OPT_KEY("context=", KEY_KERN_OPT),
98 FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
99 FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
100 FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
101 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
102 FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
103 FUSE_OPT_KEY("-n", KEY_MTAB_OPT),
104 FUSE_OPT_KEY("-r", KEY_RO),
105 FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
106 FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
107 FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
108 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
109 FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
110 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
111 FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
112 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
113 FUSE_OPT_KEY("async", KEY_KERN_FLAG),
114 FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
115 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
116 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
117 FUSE_OPT_KEY("nodiratime", KEY_KERN_FLAG),
118 FUSE_OPT_KEY("nostrictatime", KEY_KERN_FLAG),
119 FUSE_OPT_KEY("symfollow", KEY_KERN_FLAG),
120 FUSE_OPT_KEY("nosymfollow", KEY_KERN_FLAG),
122};
123
124/*
125 * Running fusermount by calling 'posix_spawn'
126 *
127 * @param out_pid might be NULL
128 */
129static int fusermount_posix_spawn(posix_spawn_file_actions_t *action,
130 char const * const argv[], pid_t *out_pid)
131{
132 const char *full_path = FUSERMOUNT_DIR "/" FUSERMOUNT_PROG;
133 pid_t pid;
134
135 /* See man 7 environ for the global environ pointer */
136
137 /* first try the install path */
138 int status = posix_spawn(&pid, full_path, action, NULL,
139 (char * const *) argv, environ);
140 if (status != 0) {
141 /* if that fails, try a system install */
142 status = posix_spawnp(&pid, FUSERMOUNT_PROG, action, NULL,
143 (char * const *) argv, environ);
144 }
145
146 if (status != 0) {
147 fuse_log(FUSE_LOG_ERR,
148 "On calling fusermount posix_spawn failed: %s\n",
149 strerror(status));
150 return -status;
151 }
152
153 if (out_pid)
154 *out_pid = pid;
155 else
156 waitpid(pid, NULL, 0); /* FIXME: check exit code and return error if any */
157
158 return 0;
159}
160
161void fuse_mount_version(void)
162{
163 char const *const argv[] = {FUSERMOUNT_PROG, "--version", NULL};
164 int status = fusermount_posix_spawn(NULL, argv, NULL);
165
166 if(status != 0)
167 fuse_log(FUSE_LOG_ERR, "Running '%s --version' failed",
168 FUSERMOUNT_PROG);
169}
170
171struct mount_flags {
172 const char *opt;
173 unsigned long flag;
174 int on;
175};
176
177static const struct mount_flags mount_flags[] = {
178 {"rw", MS_RDONLY, 0},
179 {"ro", MS_RDONLY, 1},
180 {"suid", MS_NOSUID, 0},
181 {"nosuid", MS_NOSUID, 1},
182 {"dev", MS_NODEV, 0},
183 {"nodev", MS_NODEV, 1},
184 {"exec", MS_NOEXEC, 0},
185 {"noexec", MS_NOEXEC, 1},
186 {"async", MS_SYNCHRONOUS, 0},
187 {"sync", MS_SYNCHRONOUS, 1},
188 {"noatime", MS_NOATIME, 1},
189 {"nodiratime", MS_NODIRATIME, 1},
190 {"norelatime", MS_RELATIME, 0},
191 {"nostrictatime", MS_STRICTATIME, 0},
192 {"symfollow", MS_NOSYMFOLLOW, 0},
193 {"nosymfollow", MS_NOSYMFOLLOW, 1},
194#ifndef __NetBSD__
195 {"dirsync", MS_DIRSYNC, 1},
196#endif
197 {NULL, 0, 0}
198};
199
200unsigned get_max_read(struct mount_opts *o)
201{
202 return o->max_read;
203}
204
205static void set_mount_flag(const char *s, int *flags)
206{
207 int i;
208
209 for (i = 0; mount_flags[i].opt != NULL; i++) {
210 const char *opt = mount_flags[i].opt;
211 if (strcmp(opt, s) == 0) {
212 if (mount_flags[i].on)
213 *flags |= mount_flags[i].flag;
214 else
215 *flags &= ~mount_flags[i].flag;
216 return;
217 }
218 }
219 fuse_log(FUSE_LOG_ERR, "fuse: internal error, can't find mount flag\n");
220 abort();
221}
222
223static int fuse_mount_opt_proc(void *data, const char *arg, int key,
224 struct fuse_args *outargs)
225{
226 (void) outargs;
227 struct mount_opts *mo = data;
228
229 switch (key) {
230 case KEY_RO:
231 arg = "ro";
232 /* fall through */
233 case KEY_KERN_FLAG:
234 set_mount_flag(arg, &mo->flags);
235 return 0;
236
237 case KEY_KERN_OPT:
238 return fuse_opt_add_opt(&mo->kernel_opts, arg);
239
240 case KEY_FUSERMOUNT_OPT:
241 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
242
243 case KEY_SUBTYPE_OPT:
244 return fuse_opt_add_opt(&mo->subtype_opt, arg);
245
246 case KEY_MTAB_OPT:
247 return fuse_opt_add_opt(&mo->mtab_opts, arg);
248
249 /* Third party options like 'x-gvfs-notrash' */
250 case FUSE_OPT_KEY_OPT:
251 return (strncmp("x-", arg, 2) == 0) ?
252 fuse_opt_add_opt(&mo->mtab_opts, arg) :
253 1;
254 }
255
256 /* Pass through unknown options */
257 return 1;
258}
259
260/* return value:
261 * >= 0 => fd
262 * -1 => error
263 */
264static int receive_fd(int fd)
265{
266 struct msghdr msg;
267 struct iovec iov;
268 char buf[1];
269 int rv;
270 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
271 struct cmsghdr *cmsg;
272
273 iov.iov_base = buf;
274 iov.iov_len = 1;
275
276 memset(&msg, 0, sizeof(msg));
277 msg.msg_name = 0;
278 msg.msg_namelen = 0;
279 msg.msg_iov = &iov;
280 msg.msg_iovlen = 1;
281 /* old BSD implementations should use msg_accrights instead of
282 * msg_control; the interface is different. */
283 msg.msg_control = ccmsg;
284 msg.msg_controllen = sizeof(ccmsg);
285
286 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
287 if (rv == -1) {
288 fuse_log(FUSE_LOG_ERR, "recvmsg failed: %s", strerror(errno));
289 return -1;
290 }
291 if(!rv) {
292 /* EOF */
293 return -1;
294 }
295
296 cmsg = CMSG_FIRSTHDR(&msg);
297 if (cmsg->cmsg_type != SCM_RIGHTS) {
298 fuse_log(FUSE_LOG_ERR, "got control message of unknown type %d\n",
299 cmsg->cmsg_type);
300 return -1;
301 }
302 return *(int*)CMSG_DATA(cmsg);
303}
304
305void fuse_kern_unmount(const char *mountpoint, int fd)
306{
307 int res;
308
309 if (fd != -1) {
310 struct pollfd pfd;
311
312 pfd.fd = fd;
313 pfd.events = 0;
314 res = poll(&pfd, 1, 0);
315
316 /* Need to close file descriptor, otherwise synchronous umount
317 would recurse into filesystem, and deadlock.
318
319 Caller expects fuse_kern_unmount to close the fd, so close it
320 anyway. */
321 close(fd);
322
323 /* If file poll returns POLLERR on the device file descriptor,
324 then the filesystem is already unmounted or the connection
325 was severed via /sys/fs/fuse/connections/NNN/abort */
326 if (res == 1 && (pfd.revents & POLLERR))
327 return;
328 }
329
330 if (geteuid() == 0) {
331 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
332 return;
333 }
334
335 res = umount2(mountpoint, 2);
336 if (res == 0)
337 return;
338
339 char const * const argv[] =
340 { FUSERMOUNT_PROG, "--unmount", "--quiet", "--lazy",
341 "--", mountpoint, NULL };
342 int status = fusermount_posix_spawn(NULL, argv, NULL);
343 if(status != 0) {
344 fuse_log(FUSE_LOG_ERR, "Spawning %s to unmount failed: %s",
345 FUSERMOUNT_PROG, strerror(-status));
346 return;
347 }
348}
349
350static int setup_auto_unmount(const char *mountpoint, int quiet)
351{
352 int fds[2];
353 pid_t pid;
354 int res;
355
356 if (!mountpoint) {
357 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
358 return -1;
359 }
360
361 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
362 if(res == -1) {
363 fuse_log(FUSE_LOG_ERR, "Setting up auto-unmountsocketpair() failed",
364 strerror(errno));
365 return -1;
366 }
367
368 char arg_fd_entry[30];
369 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
370 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
371 /*
372 * This helps to identify the FD hold by parent process.
373 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
374 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
375 * One potential use case is to satisfy FD-Leak checks.
376 */
377 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
378 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
379
380 char const *const argv[] = {
381 FUSERMOUNT_PROG,
382 "--auto-unmount",
383 "--",
384 mountpoint,
385 NULL,
386 };
387
388 // TODO: add error handling for all manipulations of action.
389 posix_spawn_file_actions_t action;
390 posix_spawn_file_actions_init(&action);
391
392 if (quiet) {
393 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
394 posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
395 }
396 posix_spawn_file_actions_addclose(&action, fds[1]);
397
398 /*
399 * auto-umount runs in the background - it is not waiting for the
400 * process
401 */
402 int status = fusermount_posix_spawn(&action, argv, &pid);
403
404 posix_spawn_file_actions_destroy(&action);
405
406 if(status != 0) {
407 close(fds[0]);
408 close(fds[1]);
409 fuse_log(FUSE_LOG_ERR, "fuse: Setting up auto-unmount failed (spawn): %s",
410 strerror(-status));
411 return -1;
412 }
413 // passed to child now, so can close here.
414 close(fds[0]);
415
416 // Now fusermount3 will only exit when fds[1] closes automatically when our
417 // process exits.
418 return 0;
419 // Note: fds[1] is leakend and doesn't get FD_CLOEXEC
420}
421
422static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
423 const char *opts, int quiet)
424{
425 int fds[2];
426 pid_t pid;
427 int res;
428
429 if (!mountpoint) {
430 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
431 return -1;
432 }
433
434 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
435 if(res == -1) {
436 fuse_log(FUSE_LOG_ERR, "Running %s: socketpair() failed: %s\n",
437 FUSERMOUNT_PROG, strerror(errno));
438 return -1;
439 }
440
441 char arg_fd_entry[30];
442 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
443 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
444 /*
445 * This helps to identify the FD hold by parent process.
446 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
447 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
448 * One potential use case is to satisfy FD-Leak checks.
449 */
450 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
451 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
452
453 char const *const argv[] = {
454 FUSERMOUNT_PROG,
455 "-o", opts ? opts : "",
456 "--",
457 mountpoint,
458 NULL,
459 };
460
461
462 posix_spawn_file_actions_t action;
463 posix_spawn_file_actions_init(&action);
464
465 if (quiet) {
466 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
467 posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
468 }
469 posix_spawn_file_actions_addclose(&action, fds[1]);
470
471 int status = fusermount_posix_spawn(&action, argv, &pid);
472
473 posix_spawn_file_actions_destroy(&action);
474
475 if(status != 0) {
476 close(fds[0]);
477 close(fds[1]);
478 fuse_log(FUSE_LOG_ERR, "posix_spawn(p)() for %s failed: %s",
479 FUSERMOUNT_PROG, strerror(-status));
480 return -1;
481 }
482
483 // passed to child now, so can close here.
484 close(fds[0]);
485
486 int fd = receive_fd(fds[1]);
487
488 if (!mo->auto_unmount) {
489 /* with auto_unmount option fusermount3 will not exit until
490 this socket is closed */
491 close(fds[1]);
492 waitpid(pid, NULL, 0); /* bury zombie */
493 }
494
495 if (fd >= 0)
496 fcntl(fd, F_SETFD, FD_CLOEXEC);
497
498 return fd;
499}
500
501#ifndef O_CLOEXEC
502#define O_CLOEXEC 0
503#endif
504
505static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
506 const char *mnt_opts)
507{
508 char tmp[128];
509 const char *devname = "/dev/fuse";
510 char *source = NULL;
511 char *type = NULL;
512 struct stat stbuf;
513 int fd;
514 int res;
515
516 if (!mnt) {
517 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
518 return -1;
519 }
520
521 res = stat(mnt, &stbuf);
522 if (res == -1) {
523 fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n",
524 mnt, strerror(errno));
525 return -1;
526 }
527
528 fd = open(devname, O_RDWR | O_CLOEXEC);
529 if (fd == -1) {
530 if (errno == ENODEV || errno == ENOENT)
531 fuse_log(FUSE_LOG_ERR, "fuse: device not found, try 'modprobe fuse' first\n");
532 else
533 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n",
534 devname, strerror(errno));
535 return -1;
536 }
537 if (!O_CLOEXEC)
538 fcntl(fd, F_SETFD, FD_CLOEXEC);
539
540 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
541 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
542
543 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
544 if (res == -1)
545 goto out_close;
546
547 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
548 (mo->subtype ? strlen(mo->subtype) : 0) +
549 strlen(devname) + 32);
550
551 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
552 if (!type || !source) {
553 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n");
554 goto out_close;
555 }
556
557 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
558 if (mo->subtype) {
559 strcat(type, ".");
560 strcat(type, mo->subtype);
561 }
562 strcpy(source,
563 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
564
565 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
566 if (res == -1 && errno == ENODEV && mo->subtype) {
567 /* Probably missing subtype support */
568 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
569 if (mo->fsname) {
570 if (!mo->blkdev)
571 sprintf(source, "%s#%s", mo->subtype,
572 mo->fsname);
573 } else {
574 strcpy(source, type);
575 }
576 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
577 }
578 if (res == -1) {
579 /*
580 * Maybe kernel doesn't support unprivileged mounts, in this
581 * case try falling back to fusermount3
582 */
583 if (errno == EPERM) {
584 res = -2;
585 } else {
586 int errno_save = errno;
587 if (mo->blkdev && errno == ENODEV &&
588 !fuse_mnt_check_fuseblk())
589 fuse_log(FUSE_LOG_ERR,
590 "fuse: 'fuseblk' support missing\n");
591 else
592 fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n",
593 strerror(errno_save));
594 }
595
596 goto out_close;
597 }
598
599#ifndef IGNORE_MTAB
600 if (geteuid() == 0) {
601 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
602 res = -1;
603 if (!newmnt)
604 goto out_umount;
605
606 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
607 mnt_opts);
608 free(newmnt);
609 if (res == -1)
610 goto out_umount;
611 }
612#endif /* IGNORE_MTAB */
613 free(type);
614 free(source);
615
616 return fd;
617
618out_umount:
619 umount2(mnt, 2); /* lazy umount */
620out_close:
621 free(type);
622 free(source);
623 close(fd);
624 return res;
625}
626
627static int get_mnt_flag_opts(char **mnt_optsp, int flags)
628{
629 int i;
630
631 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
632 return -1;
633
634 for (i = 0; mount_flags[i].opt != NULL; i++) {
635 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
636 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
637 return -1;
638 }
639 return 0;
640}
641
642struct mount_opts *parse_mount_opts(struct fuse_args *args)
643{
644 struct mount_opts *mo;
645
646 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
647 if (mo == NULL)
648 return NULL;
649
650 memset(mo, 0, sizeof(struct mount_opts));
651 mo->flags = MS_NOSUID | MS_NODEV;
652
653 if (args &&
654 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
655 goto err_out;
656
657 return mo;
658
659err_out:
660 destroy_mount_opts(mo);
661 return NULL;
662}
663
664void destroy_mount_opts(struct mount_opts *mo)
665{
666 free(mo->fsname);
667 free(mo->subtype);
668 free(mo->fusermount_opts);
669 free(mo->subtype_opt);
670 free(mo->kernel_opts);
671 free(mo->mtab_opts);
672 free(mo);
673}
674
675
676int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
677{
678 int res = -1;
679 char *mnt_opts = NULL;
680
681 res = -1;
682 if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
683 goto out;
684 if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1)
685 goto out;
686 if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1)
687 goto out;
688
689 res = fuse_mount_sys(mountpoint, mo, mnt_opts);
690 if (res >= 0 && mo->auto_unmount) {
691 if(0 > setup_auto_unmount(mountpoint, 0)) {
692 // Something went wrong, let's umount like in fuse_mount_sys.
693 umount2(mountpoint, MNT_DETACH); /* lazy umount */
694 res = -1;
695 }
696 } else if (res == -2) {
697 if (mo->fusermount_opts &&
698 fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
699 goto out;
700
701 if (mo->subtype) {
702 char *tmp_opts = NULL;
703
704 res = -1;
705 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
706 fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
707 free(tmp_opts);
708 goto out;
709 }
710
711 res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
712 free(tmp_opts);
713 if (res == -1)
714 res = fuse_mount_fusermount(mountpoint, mo,
715 mnt_opts, 0);
716 } else {
717 res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
718 }
719 }
720out:
721 free(mnt_opts);
722 return res;
723}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
#define FUSE_OPT_KEY_OPT
Definition fuse_opt.h:129
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2mount_8c_source.html0000644000175000017500000035411715156613443022337 0ustar berndbernd libfuse: fuse-3.18.1/lib/mount.c Source File
libfuse
mount.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture specific file system mounting (Linux).
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11/* For environ */
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_misc.h"
17#include "fuse_opt.h"
18#include "mount_util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <stddef.h>
24#include <string.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <poll.h>
28#include <spawn.h>
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <sys/wait.h>
32
33#include "fuse_mount_compat.h"
34
35#ifdef __NetBSD__
36#include <perfuse.h>
37
38#define MS_RDONLY MNT_RDONLY
39#define MS_NOSUID MNT_NOSUID
40#define MS_NODEV MNT_NODEV
41#define MS_NOEXEC MNT_NOEXEC
42#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
43#define MS_NOATIME MNT_NOATIME
44#define MS_NOSYMFOLLOW MNT_NOSYMFOLLOW
45
46#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
47#endif
48
49#define FUSERMOUNT_PROG "fusermount3"
50#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
51#define FUSE_COMMFD2_ENV "_FUSE_COMMFD2"
52#define FUSE_KERN_DEVICE_ENV "FUSE_KERN_DEVICE"
53
54#ifndef MS_DIRSYNC
55#define MS_DIRSYNC 128
56#endif
57
58enum {
59 KEY_KERN_FLAG,
60 KEY_KERN_OPT,
61 KEY_FUSERMOUNT_OPT,
62 KEY_SUBTYPE_OPT,
63 KEY_MTAB_OPT,
64 KEY_ALLOW_OTHER,
65 KEY_RO,
66};
67
68struct mount_opts {
69 int allow_other;
70 int flags;
71 int auto_unmount;
72 int blkdev;
73 char *fsname;
74 char *subtype;
75 char *subtype_opt;
76 char *mtab_opts;
77 char *fusermount_opts;
78 char *kernel_opts;
79 unsigned max_read;
80};
81
82#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
83
84static const struct fuse_opt fuse_mount_opts[] = {
85 FUSE_MOUNT_OPT("allow_other", allow_other),
86 FUSE_MOUNT_OPT("blkdev", blkdev),
87 FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
88 FUSE_MOUNT_OPT("fsname=%s", fsname),
89 FUSE_MOUNT_OPT("max_read=%u", max_read),
90 FUSE_MOUNT_OPT("subtype=%s", subtype),
91 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
92 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
93 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
94 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
95 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
96 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
97 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
98 FUSE_OPT_KEY("context=", KEY_KERN_OPT),
99 FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
100 FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
101 FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
102 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
103 FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
104 FUSE_OPT_KEY("-n", KEY_MTAB_OPT),
105 FUSE_OPT_KEY("-r", KEY_RO),
106 FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
107 FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
108 FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
109 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
110 FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
111 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
112 FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
113 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
114 FUSE_OPT_KEY("async", KEY_KERN_FLAG),
115 FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
116 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
117 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
118 FUSE_OPT_KEY("nodiratime", KEY_KERN_FLAG),
119 FUSE_OPT_KEY("nostrictatime", KEY_KERN_FLAG),
120 FUSE_OPT_KEY("symfollow", KEY_KERN_FLAG),
121 FUSE_OPT_KEY("nosymfollow", KEY_KERN_FLAG),
123};
124
125/*
126 * Running fusermount by calling 'posix_spawn'
127 *
128 * @param out_pid might be NULL
129 */
130static int fusermount_posix_spawn(posix_spawn_file_actions_t *action,
131 char const * const argv[], pid_t *out_pid)
132{
133 const char *full_path = FUSERMOUNT_DIR "/" FUSERMOUNT_PROG;
134 pid_t pid;
135
136 /* See man 7 environ for the global environ pointer */
137
138 /* first try the install path */
139 int status = posix_spawn(&pid, full_path, action, NULL,
140 (char * const *) argv, environ);
141 if (status != 0) {
142 /* if that fails, try a system install */
143 status = posix_spawnp(&pid, FUSERMOUNT_PROG, action, NULL,
144 (char * const *) argv, environ);
145 }
146
147 if (status != 0) {
148 fuse_log(FUSE_LOG_ERR, "Failed to call '%s': %s\n",
149 FUSERMOUNT_PROG, strerror(status));
150 return -status;
151 }
152
153 if (out_pid)
154 *out_pid = pid;
155 else
156 waitpid(pid, NULL, 0); /* FIXME: check exit code and return error if any */
157
158 return 0;
159}
160
161void fuse_mount_version(void)
162{
163 char const *const argv[] = {FUSERMOUNT_PROG, "--version", NULL};
164 int status = fusermount_posix_spawn(NULL, argv, NULL);
165
166 if(status != 0)
167 fuse_log(FUSE_LOG_ERR, "Running '%s --version' failed",
168 FUSERMOUNT_PROG);
169}
170
171struct mount_flags {
172 const char *opt;
173 unsigned long flag;
174 int on;
175};
176
177static const struct mount_flags mount_flags[] = {
178 {"rw", MS_RDONLY, 0},
179 {"ro", MS_RDONLY, 1},
180 {"suid", MS_NOSUID, 0},
181 {"nosuid", MS_NOSUID, 1},
182 {"dev", MS_NODEV, 0},
183 {"nodev", MS_NODEV, 1},
184 {"exec", MS_NOEXEC, 0},
185 {"noexec", MS_NOEXEC, 1},
186 {"async", MS_SYNCHRONOUS, 0},
187 {"sync", MS_SYNCHRONOUS, 1},
188 {"noatime", MS_NOATIME, 1},
189 {"nodiratime", MS_NODIRATIME, 1},
190 {"norelatime", MS_RELATIME, 0},
191 {"nostrictatime", MS_STRICTATIME, 0},
192 {"symfollow", MS_NOSYMFOLLOW, 0},
193 {"nosymfollow", MS_NOSYMFOLLOW, 1},
194#ifndef __NetBSD__
195 {"dirsync", MS_DIRSYNC, 1},
196#endif
197 {NULL, 0, 0}
198};
199
200unsigned get_max_read(struct mount_opts *o)
201{
202 return o->max_read;
203}
204
205static void set_mount_flag(const char *s, int *flags)
206{
207 int i;
208
209 for (i = 0; mount_flags[i].opt != NULL; i++) {
210 const char *opt = mount_flags[i].opt;
211 if (strcmp(opt, s) == 0) {
212 if (mount_flags[i].on)
213 *flags |= mount_flags[i].flag;
214 else
215 *flags &= ~mount_flags[i].flag;
216 return;
217 }
218 }
219 fuse_log(FUSE_LOG_ERR, "fuse: internal error, can't find mount flag\n");
220 abort();
221}
222
223static int fuse_mount_opt_proc(void *data, const char *arg, int key,
224 struct fuse_args *outargs)
225{
226 (void) outargs;
227 struct mount_opts *mo = data;
228
229 switch (key) {
230 case KEY_RO:
231 arg = "ro";
232 /* fall through */
233 case KEY_KERN_FLAG:
234 set_mount_flag(arg, &mo->flags);
235 return 0;
236
237 case KEY_KERN_OPT:
238 return fuse_opt_add_opt(&mo->kernel_opts, arg);
239
240 case KEY_FUSERMOUNT_OPT:
241 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
242
243 case KEY_SUBTYPE_OPT:
244 return fuse_opt_add_opt(&mo->subtype_opt, arg);
245
246 case KEY_MTAB_OPT:
247 return fuse_opt_add_opt(&mo->mtab_opts, arg);
248
249 /* Third party options like 'x-gvfs-notrash' */
250 case FUSE_OPT_KEY_OPT:
251 return (strncmp("x-", arg, 2) == 0) ?
252 fuse_opt_add_opt(&mo->mtab_opts, arg) :
253 1;
254 }
255
256 /* Pass through unknown options */
257 return 1;
258}
259
260/* return value:
261 * >= 0 => fd
262 * -1 => error
263 */
264static int receive_fd(int fd)
265{
266 struct msghdr msg;
267 struct iovec iov;
268 char buf[1];
269 int rv;
270 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
271 struct cmsghdr *cmsg;
272
273 iov.iov_base = buf;
274 iov.iov_len = 1;
275
276 memset(&msg, 0, sizeof(msg));
277 msg.msg_name = 0;
278 msg.msg_namelen = 0;
279 msg.msg_iov = &iov;
280 msg.msg_iovlen = 1;
281 /* old BSD implementations should use msg_accrights instead of
282 * msg_control; the interface is different. */
283 msg.msg_control = ccmsg;
284 msg.msg_controllen = sizeof(ccmsg);
285
286 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
287 if (rv == -1) {
288 fuse_log(FUSE_LOG_ERR, "recvmsg failed: %s", strerror(errno));
289 return -1;
290 }
291 if(!rv) {
292 /* EOF */
293 return -1;
294 }
295
296 cmsg = CMSG_FIRSTHDR(&msg);
297 if (cmsg->cmsg_type != SCM_RIGHTS) {
298 fuse_log(FUSE_LOG_ERR, "got control message of unknown type %d\n",
299 cmsg->cmsg_type);
300 return -1;
301 }
302 return *(int*)CMSG_DATA(cmsg);
303}
304
305void fuse_kern_unmount(const char *mountpoint, int fd)
306{
307 int res;
308
309 if (fd != -1) {
310 struct pollfd pfd;
311
312 pfd.fd = fd;
313 pfd.events = 0;
314 res = poll(&pfd, 1, 0);
315
316 /* Need to close file descriptor, otherwise synchronous umount
317 would recurse into filesystem, and deadlock.
318
319 Caller expects fuse_kern_unmount to close the fd, so close it
320 anyway. */
321 close(fd);
322
323 /* If file poll returns POLLERR on the device file descriptor,
324 then the filesystem is already unmounted or the connection
325 was severed via /sys/fs/fuse/connections/NNN/abort */
326 if (res == 1 && (pfd.revents & POLLERR))
327 return;
328 }
329
330 if (geteuid() == 0) {
331 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
332 return;
333 }
334
335 res = umount2(mountpoint, 2);
336 if (res == 0)
337 return;
338
339 char const * const argv[] =
340 { FUSERMOUNT_PROG, "--unmount", "--quiet", "--lazy",
341 "--", mountpoint, NULL };
342 int status = fusermount_posix_spawn(NULL, argv, NULL);
343 if(status != 0) {
344 fuse_log(FUSE_LOG_ERR, "Spawning %s to unmount failed: %s",
345 FUSERMOUNT_PROG, strerror(-status));
346 return;
347 }
348}
349
350static int setup_auto_unmount(const char *mountpoint, int quiet)
351{
352 int fds[2];
353 pid_t pid;
354 int res;
355
356 if (!mountpoint) {
357 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
358 return -1;
359 }
360
361 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
362 if(res == -1) {
363 fuse_log(FUSE_LOG_ERR, "Setting up auto-unmount socketpair() failed: %s\n",
364 strerror(errno));
365 return -1;
366 }
367
368 char arg_fd_entry[30];
369 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
370 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
371 /*
372 * This helps to identify the FD hold by parent process.
373 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
374 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
375 * One potential use case is to satisfy FD-Leak checks.
376 */
377 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
378 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
379
380 char const *const argv[] = {
381 FUSERMOUNT_PROG,
382 "--auto-unmount",
383 "--",
384 mountpoint,
385 NULL,
386 };
387
388 // TODO: add error handling for all manipulations of action.
389 posix_spawn_file_actions_t action;
390 posix_spawn_file_actions_init(&action);
391
392 if (quiet) {
393 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
394 posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
395 }
396 posix_spawn_file_actions_addclose(&action, fds[1]);
397
398 /*
399 * auto-umount runs in the background - it is not waiting for the
400 * process
401 */
402 int status = fusermount_posix_spawn(&action, argv, &pid);
403
404 posix_spawn_file_actions_destroy(&action);
405
406 if(status != 0) {
407 close(fds[0]);
408 close(fds[1]);
409 fuse_log(FUSE_LOG_ERR, "fuse: Setting up auto-unmount failed (spawn): %s",
410 strerror(-status));
411 return -1;
412 }
413 // passed to child now, so can close here.
414 close(fds[0]);
415
416 // Now fusermount3 will only exit when fds[1] closes automatically when our
417 // process exits.
418 return 0;
419 // Note: fds[1] is leakend and doesn't get FD_CLOEXEC
420}
421
422static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
423 const char *opts, int quiet)
424{
425 int fds[2];
426 pid_t pid;
427 int res;
428
429 if (!mountpoint) {
430 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
431 return -1;
432 }
433
434 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
435 if(res == -1) {
436 fuse_log(FUSE_LOG_ERR, "Running %s: socketpair() failed: %s\n",
437 FUSERMOUNT_PROG, strerror(errno));
438 return -1;
439 }
440
441 char arg_fd_entry[30];
442 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
443 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
444 /*
445 * This helps to identify the FD hold by parent process.
446 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
447 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
448 * One potential use case is to satisfy FD-Leak checks.
449 */
450 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
451 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
452
453 char const *const argv[] = {
454 FUSERMOUNT_PROG,
455 "-o", opts ? opts : "",
456 "--",
457 mountpoint,
458 NULL,
459 };
460
461
462 posix_spawn_file_actions_t action;
463 posix_spawn_file_actions_init(&action);
464
465 if (quiet) {
466 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
467 posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
468 }
469 posix_spawn_file_actions_addclose(&action, fds[1]);
470
471 int status = fusermount_posix_spawn(&action, argv, &pid);
472
473 posix_spawn_file_actions_destroy(&action);
474
475 if(status != 0) {
476 close(fds[0]);
477 close(fds[1]);
478 fuse_log(FUSE_LOG_ERR, "posix_spawn(p)() for %s failed: %s",
479 FUSERMOUNT_PROG, strerror(-status));
480 return -1;
481 }
482
483 // passed to child now, so can close here.
484 close(fds[0]);
485
486 int fd = receive_fd(fds[1]);
487
488 if (!mo->auto_unmount) {
489 /* with auto_unmount option fusermount3 will not exit until
490 this socket is closed */
491 close(fds[1]);
492 waitpid(pid, NULL, 0); /* bury zombie */
493 }
494
495 if (fd >= 0)
496 fcntl(fd, F_SETFD, FD_CLOEXEC);
497
498 return fd;
499}
500
501#ifndef O_CLOEXEC
502#define O_CLOEXEC 0
503#endif
504
505static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
506 const char *mnt_opts)
507{
508 char tmp[128];
509 const char *devname = getenv(FUSE_KERN_DEVICE_ENV) ?: "/dev/fuse";
510 char *source = NULL;
511 char *type = NULL;
512 struct stat stbuf;
513 int fd;
514 int res;
515
516 if (!mnt) {
517 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
518 return -1;
519 }
520
521 res = stat(mnt, &stbuf);
522 if (res == -1) {
523 fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n",
524 mnt, strerror(errno));
525 return -1;
526 }
527
528 fd = open(devname, O_RDWR | O_CLOEXEC);
529 if (fd == -1) {
530 if (errno == ENODEV || errno == ENOENT)
531 fuse_log(FUSE_LOG_ERR,
532 "fuse: device %s not found. Kernel module not loaded?\n",
533 devname);
534 else
535 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n",
536 devname, strerror(errno));
537 return -1;
538 }
539 if (!O_CLOEXEC)
540 fcntl(fd, F_SETFD, FD_CLOEXEC);
541
542 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
543 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
544
545 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
546 if (res == -1)
547 goto out_close;
548
549 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
550 (mo->subtype ? strlen(mo->subtype) : 0) +
551 strlen(devname) + 32);
552
553 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
554 if (!type || !source) {
555 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n");
556 goto out_close;
557 }
558
559 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
560 if (mo->subtype) {
561 strcat(type, ".");
562 strcat(type, mo->subtype);
563 }
564 strcpy(source,
565 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
566
567 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
568 if (res == -1 && errno == ENODEV && mo->subtype) {
569 /* Probably missing subtype support */
570 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
571 if (mo->fsname) {
572 if (!mo->blkdev)
573 sprintf(source, "%s#%s", mo->subtype,
574 mo->fsname);
575 } else {
576 strcpy(source, type);
577 }
578 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
579 }
580 if (res == -1) {
581 /*
582 * Maybe kernel doesn't support unprivileged mounts, in this
583 * case try falling back to fusermount3
584 */
585 if (errno == EPERM) {
586 res = -2;
587 } else {
588 int errno_save = errno;
589 if (mo->blkdev && errno == ENODEV &&
590 !fuse_mnt_check_fuseblk())
591 fuse_log(FUSE_LOG_ERR,
592 "fuse: 'fuseblk' support missing\n");
593 else
594 fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n",
595 strerror(errno_save));
596 }
597
598 goto out_close;
599 }
600
601#ifndef IGNORE_MTAB
602 if (geteuid() == 0) {
603 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
604 res = -1;
605 if (!newmnt)
606 goto out_umount;
607
608 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
609 mnt_opts);
610 free(newmnt);
611 if (res == -1)
612 goto out_umount;
613 }
614#endif /* IGNORE_MTAB */
615 free(type);
616 free(source);
617
618 return fd;
619
620out_umount:
621 umount2(mnt, 2); /* lazy umount */
622out_close:
623 free(type);
624 free(source);
625 close(fd);
626 return res;
627}
628
629static int get_mnt_flag_opts(char **mnt_optsp, int flags)
630{
631 int i;
632
633 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
634 return -1;
635
636 for (i = 0; mount_flags[i].opt != NULL; i++) {
637 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
638 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
639 return -1;
640 }
641 return 0;
642}
643
644struct mount_opts *parse_mount_opts(struct fuse_args *args)
645{
646 struct mount_opts *mo;
647
648 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
649 if (mo == NULL)
650 return NULL;
651
652 memset(mo, 0, sizeof(struct mount_opts));
653 mo->flags = MS_NOSUID | MS_NODEV;
654
655 if (args &&
656 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
657 goto err_out;
658
659 return mo;
660
661err_out:
662 destroy_mount_opts(mo);
663 return NULL;
664}
665
666void destroy_mount_opts(struct mount_opts *mo)
667{
668 free(mo->fsname);
669 free(mo->subtype);
670 free(mo->fusermount_opts);
671 free(mo->subtype_opt);
672 free(mo->kernel_opts);
673 free(mo->mtab_opts);
674 free(mo);
675}
676
677
678int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
679{
680 int res = -1;
681 char *mnt_opts = NULL;
682
683 res = -1;
684 if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
685 goto out;
686 if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1)
687 goto out;
688 if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1)
689 goto out;
690
691 res = fuse_mount_sys(mountpoint, mo, mnt_opts);
692 if (res >= 0 && mo->auto_unmount) {
693 if(0 > setup_auto_unmount(mountpoint, 0)) {
694 // Something went wrong, let's umount like in fuse_mount_sys.
695 umount2(mountpoint, MNT_DETACH); /* lazy umount */
696 res = -1;
697 }
698 } else if (res == -2) {
699 if (mo->fusermount_opts &&
700 fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
701 goto out;
702
703 if (mo->subtype) {
704 char *tmp_opts = NULL;
705
706 res = -1;
707 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
708 fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
709 free(tmp_opts);
710 goto out;
711 }
712
713 res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
714 free(tmp_opts);
715 if (res == -1)
716 res = fuse_mount_fusermount(mountpoint, mo,
717 mnt_opts, 0);
718 } else {
719 res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
720 }
721 }
722out:
723 free(mnt_opts);
724 return res;
725}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
#define FUSE_OPT_KEY_OPT
Definition fuse_opt.h:129
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
fuse-3.18.2/doc/html/lib_2mount_8c_source.html0000644000175000017500000035373415156613443020167 0ustar berndbernd libfuse: lib/mount.c Source File
libfuse
mount.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture specific file system mounting (Linux).
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11/* For environ */
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_misc.h"
17#include "fuse_opt.h"
18#include "mount_util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <stddef.h>
24#include <string.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <poll.h>
28#include <spawn.h>
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <sys/wait.h>
32
33#include "fuse_mount_compat.h"
34
35#ifdef __NetBSD__
36#include <perfuse.h>
37
38#define MS_RDONLY MNT_RDONLY
39#define MS_NOSUID MNT_NOSUID
40#define MS_NODEV MNT_NODEV
41#define MS_NOEXEC MNT_NOEXEC
42#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
43#define MS_NOATIME MNT_NOATIME
44#define MS_NOSYMFOLLOW MNT_NOSYMFOLLOW
45
46#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
47#endif
48
49#define FUSERMOUNT_PROG "fusermount3"
50#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
51#define FUSE_COMMFD2_ENV "_FUSE_COMMFD2"
52#define FUSE_KERN_DEVICE_ENV "FUSE_KERN_DEVICE"
53
54#ifndef MS_DIRSYNC
55#define MS_DIRSYNC 128
56#endif
57
58enum {
59 KEY_KERN_FLAG,
60 KEY_KERN_OPT,
61 KEY_FUSERMOUNT_OPT,
62 KEY_SUBTYPE_OPT,
63 KEY_MTAB_OPT,
64 KEY_ALLOW_OTHER,
65 KEY_RO,
66};
67
68struct mount_opts {
69 int allow_other;
70 int flags;
71 int auto_unmount;
72 int blkdev;
73 char *fsname;
74 char *subtype;
75 char *subtype_opt;
76 char *mtab_opts;
77 char *fusermount_opts;
78 char *kernel_opts;
79 unsigned max_read;
80};
81
82#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
83
84static const struct fuse_opt fuse_mount_opts[] = {
85 FUSE_MOUNT_OPT("allow_other", allow_other),
86 FUSE_MOUNT_OPT("blkdev", blkdev),
87 FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
88 FUSE_MOUNT_OPT("fsname=%s", fsname),
89 FUSE_MOUNT_OPT("max_read=%u", max_read),
90 FUSE_MOUNT_OPT("subtype=%s", subtype),
91 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
92 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
93 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
94 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
95 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
96 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
97 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
98 FUSE_OPT_KEY("context=", KEY_KERN_OPT),
99 FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
100 FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
101 FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
102 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
103 FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
104 FUSE_OPT_KEY("-n", KEY_MTAB_OPT),
105 FUSE_OPT_KEY("-r", KEY_RO),
106 FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
107 FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
108 FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
109 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
110 FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
111 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
112 FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
113 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
114 FUSE_OPT_KEY("async", KEY_KERN_FLAG),
115 FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
116 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
117 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
118 FUSE_OPT_KEY("nodiratime", KEY_KERN_FLAG),
119 FUSE_OPT_KEY("nostrictatime", KEY_KERN_FLAG),
120 FUSE_OPT_KEY("symfollow", KEY_KERN_FLAG),
121 FUSE_OPT_KEY("nosymfollow", KEY_KERN_FLAG),
123};
124
125/*
126 * Running fusermount by calling 'posix_spawn'
127 *
128 * @param out_pid might be NULL
129 */
130static int fusermount_posix_spawn(posix_spawn_file_actions_t *action,
131 char const * const argv[], pid_t *out_pid)
132{
133 const char *full_path = FUSERMOUNT_DIR "/" FUSERMOUNT_PROG;
134 pid_t pid;
135
136 /* See man 7 environ for the global environ pointer */
137
138 /* first try the install path */
139 int status = posix_spawn(&pid, full_path, action, NULL,
140 (char * const *) argv, environ);
141 if (status != 0) {
142 /* if that fails, try a system install */
143 status = posix_spawnp(&pid, FUSERMOUNT_PROG, action, NULL,
144 (char * const *) argv, environ);
145 }
146
147 if (status != 0) {
148 fuse_log(FUSE_LOG_ERR, "Failed to call '%s': %s\n",
149 FUSERMOUNT_PROG, strerror(status));
150 return -status;
151 }
152
153 if (out_pid)
154 *out_pid = pid;
155 else
156 waitpid(pid, NULL, 0); /* FIXME: check exit code and return error if any */
157
158 return 0;
159}
160
161void fuse_mount_version(void)
162{
163 char const *const argv[] = {FUSERMOUNT_PROG, "--version", NULL};
164 int status = fusermount_posix_spawn(NULL, argv, NULL);
165
166 if(status != 0)
167 fuse_log(FUSE_LOG_ERR, "Running '%s --version' failed",
168 FUSERMOUNT_PROG);
169}
170
171struct mount_flags {
172 const char *opt;
173 unsigned long flag;
174 int on;
175};
176
177static const struct mount_flags mount_flags[] = {
178 {"rw", MS_RDONLY, 0},
179 {"ro", MS_RDONLY, 1},
180 {"suid", MS_NOSUID, 0},
181 {"nosuid", MS_NOSUID, 1},
182 {"dev", MS_NODEV, 0},
183 {"nodev", MS_NODEV, 1},
184 {"exec", MS_NOEXEC, 0},
185 {"noexec", MS_NOEXEC, 1},
186 {"async", MS_SYNCHRONOUS, 0},
187 {"sync", MS_SYNCHRONOUS, 1},
188 {"noatime", MS_NOATIME, 1},
189 {"nodiratime", MS_NODIRATIME, 1},
190 {"norelatime", MS_RELATIME, 0},
191 {"nostrictatime", MS_STRICTATIME, 0},
192 {"symfollow", MS_NOSYMFOLLOW, 0},
193 {"nosymfollow", MS_NOSYMFOLLOW, 1},
194#ifndef __NetBSD__
195 {"dirsync", MS_DIRSYNC, 1},
196#endif
197 {NULL, 0, 0}
198};
199
200unsigned get_max_read(struct mount_opts *o)
201{
202 return o->max_read;
203}
204
205static void set_mount_flag(const char *s, int *flags)
206{
207 int i;
208
209 for (i = 0; mount_flags[i].opt != NULL; i++) {
210 const char *opt = mount_flags[i].opt;
211 if (strcmp(opt, s) == 0) {
212 if (mount_flags[i].on)
213 *flags |= mount_flags[i].flag;
214 else
215 *flags &= ~mount_flags[i].flag;
216 return;
217 }
218 }
219 fuse_log(FUSE_LOG_ERR, "fuse: internal error, can't find mount flag\n");
220 abort();
221}
222
223static int fuse_mount_opt_proc(void *data, const char *arg, int key,
224 struct fuse_args *outargs)
225{
226 (void) outargs;
227 struct mount_opts *mo = data;
228
229 switch (key) {
230 case KEY_RO:
231 arg = "ro";
232 /* fall through */
233 case KEY_KERN_FLAG:
234 set_mount_flag(arg, &mo->flags);
235 return 0;
236
237 case KEY_KERN_OPT:
238 return fuse_opt_add_opt(&mo->kernel_opts, arg);
239
240 case KEY_FUSERMOUNT_OPT:
241 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
242
243 case KEY_SUBTYPE_OPT:
244 return fuse_opt_add_opt(&mo->subtype_opt, arg);
245
246 case KEY_MTAB_OPT:
247 return fuse_opt_add_opt(&mo->mtab_opts, arg);
248
249 /* Third party options like 'x-gvfs-notrash' */
250 case FUSE_OPT_KEY_OPT:
251 return (strncmp("x-", arg, 2) == 0) ?
252 fuse_opt_add_opt(&mo->mtab_opts, arg) :
253 1;
254 }
255
256 /* Pass through unknown options */
257 return 1;
258}
259
260/* return value:
261 * >= 0 => fd
262 * -1 => error
263 */
264static int receive_fd(int fd)
265{
266 struct msghdr msg;
267 struct iovec iov;
268 char buf[1];
269 int rv;
270 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
271 struct cmsghdr *cmsg;
272
273 iov.iov_base = buf;
274 iov.iov_len = 1;
275
276 memset(&msg, 0, sizeof(msg));
277 msg.msg_name = 0;
278 msg.msg_namelen = 0;
279 msg.msg_iov = &iov;
280 msg.msg_iovlen = 1;
281 /* old BSD implementations should use msg_accrights instead of
282 * msg_control; the interface is different. */
283 msg.msg_control = ccmsg;
284 msg.msg_controllen = sizeof(ccmsg);
285
286 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
287 if (rv == -1) {
288 fuse_log(FUSE_LOG_ERR, "recvmsg failed: %s", strerror(errno));
289 return -1;
290 }
291 if(!rv) {
292 /* EOF */
293 return -1;
294 }
295
296 cmsg = CMSG_FIRSTHDR(&msg);
297 if (cmsg->cmsg_type != SCM_RIGHTS) {
298 fuse_log(FUSE_LOG_ERR, "got control message of unknown type %d\n",
299 cmsg->cmsg_type);
300 return -1;
301 }
302 return *(int*)CMSG_DATA(cmsg);
303}
304
305void fuse_kern_unmount(const char *mountpoint, int fd)
306{
307 int res;
308
309 if (fd != -1) {
310 struct pollfd pfd;
311
312 pfd.fd = fd;
313 pfd.events = 0;
314 res = poll(&pfd, 1, 0);
315
316 /* Need to close file descriptor, otherwise synchronous umount
317 would recurse into filesystem, and deadlock.
318
319 Caller expects fuse_kern_unmount to close the fd, so close it
320 anyway. */
321 close(fd);
322
323 /* If file poll returns POLLERR on the device file descriptor,
324 then the filesystem is already unmounted or the connection
325 was severed via /sys/fs/fuse/connections/NNN/abort */
326 if (res == 1 && (pfd.revents & POLLERR))
327 return;
328 }
329
330 if (geteuid() == 0) {
331 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
332 return;
333 }
334
335 res = umount2(mountpoint, 2);
336 if (res == 0)
337 return;
338
339 char const * const argv[] =
340 { FUSERMOUNT_PROG, "--unmount", "--quiet", "--lazy",
341 "--", mountpoint, NULL };
342 int status = fusermount_posix_spawn(NULL, argv, NULL);
343 if(status != 0) {
344 fuse_log(FUSE_LOG_ERR, "Spawning %s to unmount failed: %s",
345 FUSERMOUNT_PROG, strerror(-status));
346 return;
347 }
348}
349
350static int setup_auto_unmount(const char *mountpoint, int quiet)
351{
352 int fds[2];
353 pid_t pid;
354 int res;
355
356 if (!mountpoint) {
357 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
358 return -1;
359 }
360
361 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
362 if(res == -1) {
363 fuse_log(FUSE_LOG_ERR, "Setting up auto-unmount socketpair() failed: %s\n",
364 strerror(errno));
365 return -1;
366 }
367
368 char arg_fd_entry[30];
369 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
370 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
371 /*
372 * This helps to identify the FD hold by parent process.
373 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
374 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
375 * One potential use case is to satisfy FD-Leak checks.
376 */
377 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
378 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
379
380 char const *const argv[] = {
381 FUSERMOUNT_PROG,
382 "--auto-unmount",
383 "--",
384 mountpoint,
385 NULL,
386 };
387
388 // TODO: add error handling for all manipulations of action.
389 posix_spawn_file_actions_t action;
390 posix_spawn_file_actions_init(&action);
391
392 if (quiet) {
393 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
394 posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
395 }
396 posix_spawn_file_actions_addclose(&action, fds[1]);
397
398 /*
399 * auto-umount runs in the background - it is not waiting for the
400 * process
401 */
402 int status = fusermount_posix_spawn(&action, argv, &pid);
403
404 posix_spawn_file_actions_destroy(&action);
405
406 if(status != 0) {
407 close(fds[0]);
408 close(fds[1]);
409 fuse_log(FUSE_LOG_ERR, "fuse: Setting up auto-unmount failed (spawn): %s",
410 strerror(-status));
411 return -1;
412 }
413 // passed to child now, so can close here.
414 close(fds[0]);
415
416 // Now fusermount3 will only exit when fds[1] closes automatically when our
417 // process exits.
418 return 0;
419 // Note: fds[1] is leakend and doesn't get FD_CLOEXEC
420}
421
422static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
423 const char *opts, int quiet)
424{
425 int fds[2];
426 pid_t pid;
427 int res;
428
429 if (!mountpoint) {
430 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
431 return -1;
432 }
433
434 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
435 if(res == -1) {
436 fuse_log(FUSE_LOG_ERR, "Running %s: socketpair() failed: %s\n",
437 FUSERMOUNT_PROG, strerror(errno));
438 return -1;
439 }
440
441 char arg_fd_entry[30];
442 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
443 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
444 /*
445 * This helps to identify the FD hold by parent process.
446 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
447 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
448 * One potential use case is to satisfy FD-Leak checks.
449 */
450 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
451 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
452
453 char const *const argv[] = {
454 FUSERMOUNT_PROG,
455 "-o", opts ? opts : "",
456 "--",
457 mountpoint,
458 NULL,
459 };
460
461
462 posix_spawn_file_actions_t action;
463 posix_spawn_file_actions_init(&action);
464
465 if (quiet) {
466 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
467 posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
468 }
469 posix_spawn_file_actions_addclose(&action, fds[1]);
470
471 int status = fusermount_posix_spawn(&action, argv, &pid);
472
473 posix_spawn_file_actions_destroy(&action);
474
475 if(status != 0) {
476 close(fds[0]);
477 close(fds[1]);
478 fuse_log(FUSE_LOG_ERR, "posix_spawn(p)() for %s failed: %s",
479 FUSERMOUNT_PROG, strerror(-status));
480 return -1;
481 }
482
483 // passed to child now, so can close here.
484 close(fds[0]);
485
486 int fd = receive_fd(fds[1]);
487
488 if (!mo->auto_unmount) {
489 /* with auto_unmount option fusermount3 will not exit until
490 this socket is closed */
491 close(fds[1]);
492 waitpid(pid, NULL, 0); /* bury zombie */
493 }
494
495 if (fd >= 0)
496 fcntl(fd, F_SETFD, FD_CLOEXEC);
497
498 return fd;
499}
500
501#ifndef O_CLOEXEC
502#define O_CLOEXEC 0
503#endif
504
505static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
506 const char *mnt_opts)
507{
508 char tmp[128];
509 const char *devname = getenv(FUSE_KERN_DEVICE_ENV) ?: "/dev/fuse";
510 char *source = NULL;
511 char *type = NULL;
512 struct stat stbuf;
513 int fd;
514 int res;
515
516 if (!mnt) {
517 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
518 return -1;
519 }
520
521 res = stat(mnt, &stbuf);
522 if (res == -1) {
523 fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n",
524 mnt, strerror(errno));
525 return -1;
526 }
527
528 fd = open(devname, O_RDWR | O_CLOEXEC);
529 if (fd == -1) {
530 if (errno == ENODEV || errno == ENOENT)
531 fuse_log(FUSE_LOG_ERR,
532 "fuse: device %s not found. Kernel module not loaded?\n",
533 devname);
534 else
535 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n",
536 devname, strerror(errno));
537 return -1;
538 }
539 if (!O_CLOEXEC)
540 fcntl(fd, F_SETFD, FD_CLOEXEC);
541
542 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
543 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
544
545 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
546 if (res == -1)
547 goto out_close;
548
549 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
550 (mo->subtype ? strlen(mo->subtype) : 0) +
551 strlen(devname) + 32);
552
553 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
554 if (!type || !source) {
555 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n");
556 goto out_close;
557 }
558
559 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
560 if (mo->subtype) {
561 strcat(type, ".");
562 strcat(type, mo->subtype);
563 }
564 strcpy(source,
565 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
566
567 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
568 if (res == -1 && errno == ENODEV && mo->subtype) {
569 /* Probably missing subtype support */
570 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
571 if (mo->fsname) {
572 if (!mo->blkdev)
573 sprintf(source, "%s#%s", mo->subtype,
574 mo->fsname);
575 } else {
576 strcpy(source, type);
577 }
578 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
579 }
580 if (res == -1) {
581 /*
582 * Maybe kernel doesn't support unprivileged mounts, in this
583 * case try falling back to fusermount3
584 */
585 if (errno == EPERM) {
586 res = -2;
587 } else {
588 int errno_save = errno;
589 if (mo->blkdev && errno == ENODEV &&
590 !fuse_mnt_check_fuseblk())
591 fuse_log(FUSE_LOG_ERR,
592 "fuse: 'fuseblk' support missing\n");
593 else
594 fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n",
595 strerror(errno_save));
596 }
597
598 goto out_close;
599 }
600
601#ifndef IGNORE_MTAB
602 if (geteuid() == 0) {
603 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
604 res = -1;
605 if (!newmnt)
606 goto out_umount;
607
608 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
609 mnt_opts);
610 free(newmnt);
611 if (res == -1)
612 goto out_umount;
613 }
614#endif /* IGNORE_MTAB */
615 free(type);
616 free(source);
617
618 return fd;
619
620out_umount:
621 umount2(mnt, 2); /* lazy umount */
622out_close:
623 free(type);
624 free(source);
625 close(fd);
626 return res;
627}
628
629static int get_mnt_flag_opts(char **mnt_optsp, int flags)
630{
631 int i;
632
633 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
634 return -1;
635
636 for (i = 0; mount_flags[i].opt != NULL; i++) {
637 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
638 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
639 return -1;
640 }
641 return 0;
642}
643
644struct mount_opts *parse_mount_opts(struct fuse_args *args)
645{
646 struct mount_opts *mo;
647
648 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
649 if (mo == NULL)
650 return NULL;
651
652 memset(mo, 0, sizeof(struct mount_opts));
653 mo->flags = MS_NOSUID | MS_NODEV;
654
655 if (args &&
656 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
657 goto err_out;
658
659 return mo;
660
661err_out:
662 destroy_mount_opts(mo);
663 return NULL;
664}
665
666void destroy_mount_opts(struct mount_opts *mo)
667{
668 free(mo->fsname);
669 free(mo->subtype);
670 free(mo->fusermount_opts);
671 free(mo->subtype_opt);
672 free(mo->kernel_opts);
673 free(mo->mtab_opts);
674 free(mo);
675}
676
677
678int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
679{
680 int res = -1;
681 char *mnt_opts = NULL;
682
683 res = -1;
684 if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
685 goto out;
686 if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1)
687 goto out;
688 if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1)
689 goto out;
690
691 res = fuse_mount_sys(mountpoint, mo, mnt_opts);
692 if (res >= 0 && mo->auto_unmount) {
693 if(0 > setup_auto_unmount(mountpoint, 0)) {
694 // Something went wrong, let's umount like in fuse_mount_sys.
695 umount2(mountpoint, MNT_DETACH); /* lazy umount */
696 res = -1;
697 }
698 } else if (res == -2) {
699 if (mo->fusermount_opts &&
700 fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
701 goto out;
702
703 if (mo->subtype) {
704 char *tmp_opts = NULL;
705
706 res = -1;
707 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
708 fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
709 free(tmp_opts);
710 goto out;
711 }
712
713 res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
714 free(tmp_opts);
715 if (res == -1)
716 res = fuse_mount_fusermount(mountpoint, mo,
717 mnt_opts, 0);
718 } else {
719 res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
720 }
721 }
722out:
723 free(mnt_opts);
724 return res;
725}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
#define FUSE_OPT_KEY_OPT
Definition fuse_opt.h:129
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2mount__bsd_8c_source.html0000644000175000017500000013131215156613443023316 0ustar berndbernd libfuse: fuse-3.17.4/lib/mount_bsd.c Source File
libfuse
mount_bsd.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
4
5 Architecture specific file system mounting (FreeBSD).
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB.
9*/
10
11#include "fuse_config.h"
12#include "fuse_i.h"
13#include "fuse_misc.h"
14#include "fuse_opt.h"
15#include "util.h"
16
17#include <sys/param.h>
18#include "fuse_mount_compat.h"
19
20#include <sys/wait.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <stddef.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <string.h>
28
29#define FUSERMOUNT_PROG "mount_fusefs"
30#define FUSE_DEV_TRUNK "/dev/fuse"
31
32enum {
33 KEY_RO,
34 KEY_KERN
35};
36
37struct mount_opts {
38 int allow_other;
39 char *kernel_opts;
40 unsigned max_read;
41};
42
43#define FUSE_DUAL_OPT_KEY(templ, key) \
44 FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
45
46static const struct fuse_opt fuse_mount_opts[] = {
47 { "allow_other", offsetof(struct mount_opts, allow_other), 1 },
48 { "max_read=%u", offsetof(struct mount_opts, max_read), 1 },
49 FUSE_OPT_KEY("-r", KEY_RO),
50 /* standard FreeBSD mount options */
51 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
52 FUSE_DUAL_OPT_KEY("async", KEY_KERN),
53 FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
54 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
55 FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
56 FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
57 FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
58 FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
59 FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
60 FUSE_DUAL_OPT_KEY("union", KEY_KERN),
61 FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
62 FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
63 FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
64 FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
65 FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
66 FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
67 FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
68 FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
69 FUSE_DUAL_OPT_KEY("force", KEY_KERN),
70 FUSE_DUAL_OPT_KEY("update", KEY_KERN),
71 FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
72 FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
73 FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
74 FUSE_DUAL_OPT_KEY("automounted", KEY_KERN),
75 /* options supported under both Linux and FBSD */
76 FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
77 FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
78 FUSE_OPT_KEY("max_read=", KEY_KERN),
79 FUSE_OPT_KEY("subtype=", KEY_KERN),
80 /* FBSD FUSE specific mount options */
81 FUSE_DUAL_OPT_KEY("private", KEY_KERN),
82 FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
83 FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
84#if __FreeBSD_version >= 1200519
85 FUSE_DUAL_OPT_KEY("intr", KEY_KERN),
86#endif
87 /* stock FBSD mountopt parsing routine lets anything be negated... */
88 /*
89 * Linux specific mount options, but let just the mount util
90 * handle them
91 */
92 FUSE_OPT_KEY("fsname=", KEY_KERN),
94};
95
96void fuse_mount_version(void)
97{
98 system(FUSERMOUNT_PROG " --version");
99}
100
101unsigned get_max_read(struct mount_opts *o)
102{
103 return o->max_read;
104}
105
106static int fuse_mount_opt_proc(void *data, const char *arg, int key,
107 struct fuse_args *outargs)
108{
109 (void) outargs;
110 struct mount_opts *mo = data;
111
112 switch (key) {
113 case KEY_RO:
114 arg = "ro";
115 /* fall through */
116
117 case KEY_KERN:
118 return fuse_opt_add_opt(&mo->kernel_opts, arg);
119 }
120
121 /* Pass through unknown options */
122 return 1;
123}
124
125void fuse_kern_unmount(const char *mountpoint, int fd)
126{
127 if (close(fd) < 0)
128 fuse_log(FUSE_LOG_ERR, "closing FD %d failed: %s", fd, strerror(errno));
129 if (unmount(mountpoint, MNT_FORCE) < 0)
130 fuse_log(FUSE_LOG_ERR, "unmounting %s failed: %s",
131 mountpoint, strerror(errno));
132}
133
134static int fuse_mount_core(const char *mountpoint, const char *opts)
135{
136 const char *mountprog = FUSERMOUNT_PROG;
137 long fd;
138 char *fdnam, *dev;
139 pid_t pid, cpid;
140 int status;
141 int err;
142
143 fdnam = getenv("FUSE_DEV_FD");
144
145 if (fdnam) {
146 err = libfuse_strtol(fdnam, &fd);
147 if (err || fd < 0) {
148 fuse_log(FUSE_LOG_ERR, "invalid value given in FUSE_DEV_FD\n");
149 return -1;
150 }
151
152 goto mount;
153 }
154
155 dev = getenv("FUSE_DEV_NAME");
156
157 if (! dev)
158 dev = (char *)FUSE_DEV_TRUNK;
159
160 if ((fd = open(dev, O_RDWR)) < 0) {
161 perror("fuse: failed to open fuse device");
162 return -1;
163 }
164
165mount:
166 if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
167 goto out;
168
169 pid = fork();
170 cpid = pid;
171
172 if (pid == -1) {
173 perror("fuse: fork() failed");
174 close(fd);
175 return -1;
176 }
177
178 if (pid == 0) {
179 pid = fork();
180
181 if (pid == -1) {
182 perror("fuse: fork() failed");
183 close(fd);
184 _exit(EXIT_FAILURE);
185 }
186
187 if (pid == 0) {
188 const char *argv[32];
189 int a = 0;
190 int ret = -1;
191
192 if (! fdnam)
193 {
194 ret = asprintf(&fdnam, "%ld", fd);
195 if(ret == -1)
196 {
197 perror("fuse: failed to assemble mount arguments");
198 close(fd);
199 _exit(EXIT_FAILURE);
200 }
201 }
202
203 argv[a++] = mountprog;
204 if (opts) {
205 argv[a++] = "-o";
206 argv[a++] = opts;
207 }
208 argv[a++] = fdnam;
209 argv[a++] = mountpoint;
210 argv[a++] = NULL;
211 execvp(mountprog, (char **) argv);
212 perror("fuse: failed to exec mount program");
213 free(fdnam);
214 _exit(EXIT_FAILURE);
215 }
216
217 _exit(EXIT_SUCCESS);
218 }
219
220 if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
221 perror("fuse: failed to mount file system");
222 if (close(fd) < 0)
223 perror("fuse: closing FD");
224 return -1;
225 }
226
227out:
228 return fd;
229}
230
231struct mount_opts *parse_mount_opts(struct fuse_args *args)
232{
233 struct mount_opts *mo;
234
235 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
236 if (mo == NULL)
237 return NULL;
238
239 memset(mo, 0, sizeof(struct mount_opts));
240
241 if (args &&
242 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
243 goto err_out;
244
245 return mo;
246
247err_out:
248 destroy_mount_opts(mo);
249 return NULL;
250}
251
252void destroy_mount_opts(struct mount_opts *mo)
253{
254 free(mo->kernel_opts);
255 free(mo);
256}
257
258int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
259{
260 /* mount util should not try to spawn the daemon */
261 setenv("MOUNT_FUSEFS_SAFE", "1", 1);
262 /* to notify the mount util it's called from lib */
263 setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
264
265 return fuse_mount_core(mountpoint, mo->kernel_opts);
266}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2mount__bsd_8c_source.html0000644000175000017500000013220015156613443023311 0ustar berndbernd libfuse: fuse-3.18.1/lib/mount_bsd.c Source File
libfuse
mount_bsd.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
4
5 Architecture specific file system mounting (FreeBSD).
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#include "fuse_config.h"
12#include "fuse_i.h"
13#include "fuse_misc.h"
14#include "fuse_opt.h"
15#include "util.h"
16
17#include <sys/param.h>
18#include "fuse_mount_compat.h"
19
20#include <sys/wait.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <stddef.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <string.h>
28
29#define FUSERMOUNT_PROG "mount_fusefs"
30#define FUSE_DEV_TRUNK "/dev/fuse"
31
32enum {
33 KEY_RO,
34 KEY_KERN
35};
36
37struct mount_opts {
38 int allow_other;
39 char *kernel_opts;
40 unsigned max_read;
41};
42
43#define FUSE_DUAL_OPT_KEY(templ, key) \
44 FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
45
46static const struct fuse_opt fuse_mount_opts[] = {
47 { "allow_other", offsetof(struct mount_opts, allow_other), 1 },
48 { "max_read=%u", offsetof(struct mount_opts, max_read), 1 },
49 FUSE_OPT_KEY("-r", KEY_RO),
50 /* standard FreeBSD mount options */
51 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
52 FUSE_DUAL_OPT_KEY("async", KEY_KERN),
53 FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
54 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
55 FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
56 FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
57 FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
58 FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
59 FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
60 FUSE_DUAL_OPT_KEY("union", KEY_KERN),
61 FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
62 FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
63 FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
64 FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
65 FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
66 FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
67 FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
68 FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
69 FUSE_DUAL_OPT_KEY("force", KEY_KERN),
70 FUSE_DUAL_OPT_KEY("update", KEY_KERN),
71 FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
72 FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
73 FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
74 FUSE_DUAL_OPT_KEY("automounted", KEY_KERN),
75 /* options supported under both Linux and FBSD */
76 FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
77 FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
78 FUSE_OPT_KEY("max_read=", KEY_KERN),
79 FUSE_OPT_KEY("subtype=", KEY_KERN),
80 /* FBSD FUSE specific mount options */
81 FUSE_DUAL_OPT_KEY("private", KEY_KERN),
82 FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
83 FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
84#if __FreeBSD_version >= 1200519
85 FUSE_DUAL_OPT_KEY("intr", KEY_KERN),
86#endif
87 /* stock FBSD mountopt parsing routine lets anything be negated... */
88 /*
89 * Linux specific mount options, but let just the mount util
90 * handle them
91 */
92 FUSE_OPT_KEY("fsname=", KEY_KERN),
94};
95
96void fuse_mount_version(void)
97{
98 system(FUSERMOUNT_PROG " --version");
99}
100
101unsigned get_max_read(struct mount_opts *o)
102{
103 return o->max_read;
104}
105
106static int fuse_mount_opt_proc(void *data, const char *arg, int key,
107 struct fuse_args *outargs)
108{
109 (void) outargs;
110 struct mount_opts *mo = data;
111
112 switch (key) {
113 case KEY_RO:
114 arg = "ro";
115 /* fall through */
116
117 case KEY_KERN:
118 return fuse_opt_add_opt(&mo->kernel_opts, arg);
119 }
120
121 /* Pass through unknown options */
122 return 1;
123}
124
125void fuse_kern_unmount(const char *mountpoint, int fd)
126{
127 if (close(fd) < 0)
128 fuse_log(FUSE_LOG_ERR, "closing FD %d failed: %s", fd, strerror(errno));
129 if (unmount(mountpoint, MNT_FORCE) < 0)
130 fuse_log(FUSE_LOG_ERR, "unmounting %s failed: %s",
131 mountpoint, strerror(errno));
132}
133
134static int fuse_mount_core(const char *mountpoint, const char *opts)
135{
136 const char *mountprog = FUSERMOUNT_PROG;
137 long fd;
138 char *fdnam, *dev;
139 pid_t pid, cpid;
140 int status;
141 int err;
142
143 fdnam = getenv("FUSE_DEV_FD");
144
145 if (fdnam) {
146 err = libfuse_strtol(fdnam, &fd);
147 if (err || fd < 0) {
148 fuse_log(FUSE_LOG_ERR, "invalid value given in FUSE_DEV_FD\n");
149 return -1;
150 }
151
152 goto mount;
153 }
154
155 dev = getenv("FUSE_DEV_NAME");
156
157 if (! dev)
158 dev = (char *)FUSE_DEV_TRUNK;
159
160 if ((fd = open(dev, O_RDWR)) < 0) {
161 perror("fuse: failed to open fuse device");
162 return -1;
163 }
164
165mount:
166 if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
167 goto out;
168
169 pid = fork();
170 cpid = pid;
171
172 if (pid == -1) {
173 perror("fuse: fork() failed");
174 close(fd);
175 return -1;
176 }
177
178 if (pid == 0) {
179 pid = fork();
180
181 if (pid == -1) {
182 perror("fuse: fork() failed");
183 close(fd);
184 _exit(EXIT_FAILURE);
185 }
186
187 if (pid == 0) {
188 const char *argv[32];
189 int a = 0;
190 int ret = -1;
191
192 if (! fdnam)
193 {
194 ret = asprintf(&fdnam, "%ld", fd);
195 if(ret == -1)
196 {
197 perror("fuse: failed to assemble mount arguments");
198 close(fd);
199 _exit(EXIT_FAILURE);
200 }
201 }
202
203 argv[a++] = mountprog;
204 if (opts) {
205 argv[a++] = "-o";
206 argv[a++] = opts;
207 }
208 argv[a++] = fdnam;
209 argv[a++] = mountpoint;
210 argv[a++] = NULL;
211 execvp(mountprog, (char **) argv);
212 perror("fuse: failed to exec mount program");
213 free(fdnam);
214 _exit(EXIT_FAILURE);
215 }
216
217 waitpid(pid, &status, 0);
218 if (!WIFEXITED(status))
219 _exit(EXIT_FAILURE);
220 _exit(WEXITSTATUS(status));
221 }
222
223 if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
224 perror("fuse: failed to mount file system");
225 if (close(fd) < 0)
226 perror("fuse: closing FD");
227 return -1;
228 }
229
230out:
231 return fd;
232}
233
234struct mount_opts *parse_mount_opts(struct fuse_args *args)
235{
236 struct mount_opts *mo;
237
238 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
239 if (mo == NULL)
240 return NULL;
241
242 memset(mo, 0, sizeof(struct mount_opts));
243
244 if (args &&
245 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
246 goto err_out;
247
248 return mo;
249
250err_out:
251 destroy_mount_opts(mo);
252 return NULL;
253}
254
255void destroy_mount_opts(struct mount_opts *mo)
256{
257 free(mo->kernel_opts);
258 free(mo);
259}
260
261int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
262{
263 /* mount util should not try to spawn the daemon */
264 setenv("MOUNT_FUSEFS_SAFE", "1", 1);
265 /* to notify the mount util it's called from lib */
266 setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
267
268 return fuse_mount_core(mountpoint, mo->kernel_opts);
269}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
fuse-3.18.2/doc/html/lib_2mount__bsd_8c_source.html0000644000175000017500000013201515156613443021141 0ustar berndbernd libfuse: lib/mount_bsd.c Source File
libfuse
mount_bsd.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
4
5 Architecture specific file system mounting (FreeBSD).
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#include "fuse_config.h"
12#include "fuse_i.h"
13#include "fuse_misc.h"
14#include "fuse_opt.h"
15#include "util.h"
16
17#include <sys/param.h>
18#include "fuse_mount_compat.h"
19
20#include <sys/wait.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <stddef.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <string.h>
28
29#define FUSERMOUNT_PROG "mount_fusefs"
30#define FUSE_DEV_TRUNK "/dev/fuse"
31
32enum {
33 KEY_RO,
34 KEY_KERN
35};
36
37struct mount_opts {
38 int allow_other;
39 char *kernel_opts;
40 unsigned max_read;
41};
42
43#define FUSE_DUAL_OPT_KEY(templ, key) \
44 FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
45
46static const struct fuse_opt fuse_mount_opts[] = {
47 { "allow_other", offsetof(struct mount_opts, allow_other), 1 },
48 { "max_read=%u", offsetof(struct mount_opts, max_read), 1 },
49 FUSE_OPT_KEY("-r", KEY_RO),
50 /* standard FreeBSD mount options */
51 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
52 FUSE_DUAL_OPT_KEY("async", KEY_KERN),
53 FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
54 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
55 FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
56 FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
57 FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
58 FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
59 FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
60 FUSE_DUAL_OPT_KEY("union", KEY_KERN),
61 FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
62 FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
63 FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
64 FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
65 FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
66 FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
67 FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
68 FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
69 FUSE_DUAL_OPT_KEY("force", KEY_KERN),
70 FUSE_DUAL_OPT_KEY("update", KEY_KERN),
71 FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
72 FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
73 FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
74 FUSE_DUAL_OPT_KEY("automounted", KEY_KERN),
75 /* options supported under both Linux and FBSD */
76 FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
77 FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
78 FUSE_OPT_KEY("max_read=", KEY_KERN),
79 FUSE_OPT_KEY("subtype=", KEY_KERN),
80 /* FBSD FUSE specific mount options */
81 FUSE_DUAL_OPT_KEY("private", KEY_KERN),
82 FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
83 FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
84#if __FreeBSD_version >= 1200519
85 FUSE_DUAL_OPT_KEY("intr", KEY_KERN),
86#endif
87 /* stock FBSD mountopt parsing routine lets anything be negated... */
88 /*
89 * Linux specific mount options, but let just the mount util
90 * handle them
91 */
92 FUSE_OPT_KEY("fsname=", KEY_KERN),
94};
95
96void fuse_mount_version(void)
97{
98 system(FUSERMOUNT_PROG " --version");
99}
100
101unsigned get_max_read(struct mount_opts *o)
102{
103 return o->max_read;
104}
105
106static int fuse_mount_opt_proc(void *data, const char *arg, int key,
107 struct fuse_args *outargs)
108{
109 (void) outargs;
110 struct mount_opts *mo = data;
111
112 switch (key) {
113 case KEY_RO:
114 arg = "ro";
115 /* fall through */
116
117 case KEY_KERN:
118 return fuse_opt_add_opt(&mo->kernel_opts, arg);
119 }
120
121 /* Pass through unknown options */
122 return 1;
123}
124
125void fuse_kern_unmount(const char *mountpoint, int fd)
126{
127 if (close(fd) < 0)
128 fuse_log(FUSE_LOG_ERR, "closing FD %d failed: %s", fd, strerror(errno));
129 if (unmount(mountpoint, MNT_FORCE) < 0)
130 fuse_log(FUSE_LOG_ERR, "unmounting %s failed: %s",
131 mountpoint, strerror(errno));
132}
133
134static int fuse_mount_core(const char *mountpoint, const char *opts)
135{
136 const char *mountprog = FUSERMOUNT_PROG;
137 long fd;
138 char *fdnam, *dev;
139 pid_t pid, cpid;
140 int status;
141 int err;
142
143 fdnam = getenv("FUSE_DEV_FD");
144
145 if (fdnam) {
146 err = libfuse_strtol(fdnam, &fd);
147 if (err || fd < 0) {
148 fuse_log(FUSE_LOG_ERR, "invalid value given in FUSE_DEV_FD\n");
149 return -1;
150 }
151
152 goto mount;
153 }
154
155 dev = getenv("FUSE_DEV_NAME");
156
157 if (! dev)
158 dev = (char *)FUSE_DEV_TRUNK;
159
160 if ((fd = open(dev, O_RDWR)) < 0) {
161 perror("fuse: failed to open fuse device");
162 return -1;
163 }
164
165mount:
166 if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
167 goto out;
168
169 pid = fork();
170 cpid = pid;
171
172 if (pid == -1) {
173 perror("fuse: fork() failed");
174 close(fd);
175 return -1;
176 }
177
178 if (pid == 0) {
179 pid = fork();
180
181 if (pid == -1) {
182 perror("fuse: fork() failed");
183 close(fd);
184 _exit(EXIT_FAILURE);
185 }
186
187 if (pid == 0) {
188 const char *argv[32];
189 int a = 0;
190 int ret = -1;
191
192 if (! fdnam)
193 {
194 ret = asprintf(&fdnam, "%ld", fd);
195 if(ret == -1)
196 {
197 perror("fuse: failed to assemble mount arguments");
198 close(fd);
199 _exit(EXIT_FAILURE);
200 }
201 }
202
203 argv[a++] = mountprog;
204 if (opts) {
205 argv[a++] = "-o";
206 argv[a++] = opts;
207 }
208 argv[a++] = fdnam;
209 argv[a++] = mountpoint;
210 argv[a++] = NULL;
211 execvp(mountprog, (char **) argv);
212 perror("fuse: failed to exec mount program");
213 free(fdnam);
214 _exit(EXIT_FAILURE);
215 }
216
217 waitpid(pid, &status, 0);
218 if (!WIFEXITED(status))
219 _exit(EXIT_FAILURE);
220 _exit(WEXITSTATUS(status));
221 }
222
223 if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
224 perror("fuse: failed to mount file system");
225 if (close(fd) < 0)
226 perror("fuse: closing FD");
227 return -1;
228 }
229
230out:
231 return fd;
232}
233
234struct mount_opts *parse_mount_opts(struct fuse_args *args)
235{
236 struct mount_opts *mo;
237
238 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
239 if (mo == NULL)
240 return NULL;
241
242 memset(mo, 0, sizeof(struct mount_opts));
243
244 if (args &&
245 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
246 goto err_out;
247
248 return mo;
249
250err_out:
251 destroy_mount_opts(mo);
252 return NULL;
253}
254
255void destroy_mount_opts(struct mount_opts *mo)
256{
257 free(mo->kernel_opts);
258 free(mo);
259}
260
261int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
262{
263 /* mount util should not try to spawn the daemon */
264 setenv("MOUNT_FUSEFS_SAFE", "1", 1);
265 /* to notify the mount util it's called from lib */
266 setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
267
268 return fuse_mount_core(mountpoint, mo->kernel_opts);
269}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2mount__util_8c_source.html0000644000175000017500000016025315156613443023531 0ustar berndbernd libfuse: fuse-3.17.4/lib/mount_util.c Source File
libfuse
mount_util.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture-independent mounting code.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB.
9*/
10
11#include "fuse_config.h"
12#include "mount_util.h"
13
14#include <stdio.h>
15#include <unistd.h>
16#include <stdlib.h>
17#include <string.h>
18#include <signal.h>
19#include <dirent.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <limits.h>
23#include <paths.h>
24#if !defined( __NetBSD__) && !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__)
25#include <mntent.h>
26#else
27#define IGNORE_MTAB
28#endif
29#include <sys/stat.h>
30#include <sys/wait.h>
31
32#include "fuse_mount_compat.h"
33
34#include <sys/param.h>
35
36#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
37#define umount2(mnt, flags) unmount(mnt, ((flags) == 2) ? MNT_FORCE : 0)
38#endif
39
40#ifdef IGNORE_MTAB
41#define mtab_needs_update(mnt) 0
42#else
43static int mtab_needs_update(const char *mnt)
44{
45 int res;
46 struct stat stbuf;
47
48 /* If mtab is within new mount, don't touch it */
49 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
50 _PATH_MOUNTED[strlen(mnt)] == '/')
51 return 0;
52
53 /*
54 * Skip mtab update if /etc/mtab:
55 *
56 * - doesn't exist,
57 * - is on a read-only filesystem.
58 */
59 res = lstat(_PATH_MOUNTED, &stbuf);
60 if (res == -1) {
61 if (errno == ENOENT)
62 return 0;
63 } else {
64 uid_t ruid;
65 int err;
66
67 ruid = getuid();
68 if (ruid != 0)
69 setreuid(0, -1);
70
71 res = access(_PATH_MOUNTED, W_OK);
72 err = (res == -1) ? errno : 0;
73 if (ruid != 0)
74 setreuid(ruid, -1);
75
76 if (err == EROFS)
77 return 0;
78
79 res = access("/run/mount/utab", F_OK);
80 if (res == -1)
81 return 0;
82 }
83
84 return 1;
85}
86#endif /* IGNORE_MTAB */
87
88static int add_mount(const char *progname, const char *fsname,
89 const char *mnt, const char *type, const char *opts)
90{
91 int res;
92 int status;
93 sigset_t blockmask;
94 sigset_t oldmask;
95
96 sigemptyset(&blockmask);
97 sigaddset(&blockmask, SIGCHLD);
98 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
99 if (res == -1) {
100 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
101 return -1;
102 }
103
104 res = fork();
105 if (res == -1) {
106 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
107 goto out_restore;
108 }
109 if (res == 0) {
110 char *env = NULL;
111
112 sigprocmask(SIG_SETMASK, &oldmask, NULL);
113
114 if(setuid(geteuid()) == -1) {
115 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
116 res = -1;
117 goto out_restore;
118 }
119
120 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
121 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
122 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
123 progname, strerror(errno));
124 exit(1);
125 }
126 res = waitpid(res, &status, 0);
127 if (res == -1)
128 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
129
130 if (status != 0)
131 res = -1;
132
133 out_restore:
134 sigprocmask(SIG_SETMASK, &oldmask, NULL);
135
136 return res;
137}
138
139int fuse_mnt_add_mount(const char *progname, const char *fsname,
140 const char *mnt, const char *type, const char *opts)
141{
142 if (!mtab_needs_update(mnt))
143 return 0;
144
145 return add_mount(progname, fsname, mnt, type, opts);
146}
147
148static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
149{
150 int res;
151 int status;
152 sigset_t blockmask;
153 sigset_t oldmask;
154
155 sigemptyset(&blockmask);
156 sigaddset(&blockmask, SIGCHLD);
157 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
158 if (res == -1) {
159 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
160 return -1;
161 }
162
163 res = fork();
164 if (res == -1) {
165 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
166 goto out_restore;
167 }
168 if (res == 0) {
169 char *env = NULL;
170
171 sigprocmask(SIG_SETMASK, &oldmask, NULL);
172
173 if(setuid(geteuid()) == -1) {
174 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
175 res = -1;
176 goto out_restore;
177 }
178
179 if (lazy) {
180 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
181 "-l", NULL, &env);
182 } else {
183 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
184 NULL, &env);
185 }
186 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
187 progname, strerror(errno));
188 exit(1);
189 }
190 res = waitpid(res, &status, 0);
191 if (res == -1)
192 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
193
194 if (status != 0) {
195 res = -1;
196 }
197
198 out_restore:
199 sigprocmask(SIG_SETMASK, &oldmask, NULL);
200 return res;
201
202}
203
204int fuse_mnt_umount(const char *progname, const char *abs_mnt,
205 const char *rel_mnt, int lazy)
206{
207 int res;
208
209 if (!mtab_needs_update(abs_mnt)) {
210 res = umount2(rel_mnt, lazy ? 2 : 0);
211 if (res == -1)
212 fprintf(stderr, "%s: failed to unmount %s: %s\n",
213 progname, abs_mnt, strerror(errno));
214 return res;
215 }
216
217 return exec_umount(progname, rel_mnt, lazy);
218}
219
220static int remove_mount(const char *progname, const char *mnt)
221{
222 int res;
223 int status;
224 sigset_t blockmask;
225 sigset_t oldmask;
226
227 sigemptyset(&blockmask);
228 sigaddset(&blockmask, SIGCHLD);
229 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
230 if (res == -1) {
231 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
232 return -1;
233 }
234
235 res = fork();
236 if (res == -1) {
237 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
238 goto out_restore;
239 }
240 if (res == 0) {
241 char *env = NULL;
242
243 sigprocmask(SIG_SETMASK, &oldmask, NULL);
244
245 if(setuid(geteuid()) == -1) {
246 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
247 res = -1;
248 goto out_restore;
249 }
250
251 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
252 "--fake", mnt, NULL, &env);
253 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
254 progname, strerror(errno));
255 exit(1);
256 }
257 res = waitpid(res, &status, 0);
258 if (res == -1)
259 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
260
261 if (status != 0)
262 res = -1;
263
264 out_restore:
265 sigprocmask(SIG_SETMASK, &oldmask, NULL);
266 return res;
267}
268
269int fuse_mnt_remove_mount(const char *progname, const char *mnt)
270{
271 if (!mtab_needs_update(mnt))
272 return 0;
273
274 return remove_mount(progname, mnt);
275}
276
277char *fuse_mnt_resolve_path(const char *progname, const char *orig)
278{
279 char buf[PATH_MAX];
280 char *copy;
281 char *dst;
282 char *end;
283 char *lastcomp;
284 const char *toresolv;
285
286 if (!orig[0]) {
287 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
288 orig);
289 return NULL;
290 }
291
292 copy = strdup(orig);
293 if (copy == NULL) {
294 fprintf(stderr, "%s: failed to allocate memory\n", progname);
295 return NULL;
296 }
297
298 toresolv = copy;
299 lastcomp = NULL;
300 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
301 if (end[0] != '/') {
302 char *tmp;
303 end[1] = '\0';
304 tmp = strrchr(copy, '/');
305 if (tmp == NULL) {
306 lastcomp = copy;
307 toresolv = ".";
308 } else {
309 lastcomp = tmp + 1;
310 if (tmp == copy)
311 toresolv = "/";
312 }
313 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
314 lastcomp = NULL;
315 toresolv = copy;
316 }
317 else if (tmp)
318 tmp[0] = '\0';
319 }
320 if (realpath(toresolv, buf) == NULL) {
321 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
322 strerror(errno));
323 free(copy);
324 return NULL;
325 }
326 if (lastcomp == NULL)
327 dst = strdup(buf);
328 else {
329 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
330 if (dst) {
331 unsigned buflen = strlen(buf);
332 if (buflen && buf[buflen-1] == '/')
333 sprintf(dst, "%s%s", buf, lastcomp);
334 else
335 sprintf(dst, "%s/%s", buf, lastcomp);
336 }
337 }
338 free(copy);
339 if (dst == NULL)
340 fprintf(stderr, "%s: failed to allocate memory\n", progname);
341 return dst;
342}
343
344int fuse_mnt_check_fuseblk(void)
345{
346 char buf[256];
347 FILE *f = fopen("/proc/filesystems", "r");
348 if (!f)
349 return 1;
350
351 while (fgets(buf, sizeof(buf), f))
352 if (strstr(buf, "fuseblk\n")) {
353 fclose(f);
354 return 1;
355 }
356
357 fclose(f);
358 return 0;
359}
360
361int fuse_mnt_parse_fuse_fd(const char *mountpoint)
362{
363 int fd = -1;
364 int len = 0;
365
366 if (mountpoint == NULL) {
367 fprintf(stderr, "Invalid null-ptr mount-point!\n");
368 return -1;
369 }
370
371 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
372 len == strlen(mountpoint)) {
373 return fd;
374 }
375
376 return -1;
377}
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2mount__util_8c_source.html0000644000175000017500000016025115156613443023525 0ustar berndbernd libfuse: fuse-3.18.1/lib/mount_util.c Source File
libfuse
mount_util.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture-independent mounting code.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#include "fuse_config.h"
12#include "mount_util.h"
13
14#include <stdio.h>
15#include <unistd.h>
16#include <stdlib.h>
17#include <string.h>
18#include <signal.h>
19#include <dirent.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <limits.h>
23#include <paths.h>
24#if !defined( __NetBSD__) && !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__)
25#include <mntent.h>
26#else
27#define IGNORE_MTAB
28#endif
29#include <sys/stat.h>
30#include <sys/wait.h>
31
32#include "fuse_mount_compat.h"
33
34#include <sys/param.h>
35
36#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
37#define umount2(mnt, flags) unmount(mnt, ((flags) == 2) ? MNT_FORCE : 0)
38#endif
39
40#ifdef IGNORE_MTAB
41#define mtab_needs_update(mnt) 0
42#else
43static int mtab_needs_update(const char *mnt)
44{
45 int res;
46 struct stat stbuf;
47
48 /* If mtab is within new mount, don't touch it */
49 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
50 _PATH_MOUNTED[strlen(mnt)] == '/')
51 return 0;
52
53 /*
54 * Skip mtab update if /etc/mtab:
55 *
56 * - doesn't exist,
57 * - is on a read-only filesystem.
58 */
59 res = lstat(_PATH_MOUNTED, &stbuf);
60 if (res == -1) {
61 if (errno == ENOENT)
62 return 0;
63 } else {
64 uid_t ruid;
65 int err;
66
67 ruid = getuid();
68 if (ruid != 0)
69 setreuid(0, -1);
70
71 res = access(_PATH_MOUNTED, W_OK);
72 err = (res == -1) ? errno : 0;
73 if (ruid != 0)
74 setreuid(ruid, -1);
75
76 if (err == EROFS)
77 return 0;
78
79 res = access("/run/mount/utab", F_OK);
80 if (res == -1)
81 return 0;
82 }
83
84 return 1;
85}
86#endif /* IGNORE_MTAB */
87
88static int add_mount(const char *progname, const char *fsname,
89 const char *mnt, const char *type, const char *opts)
90{
91 int res;
92 int status;
93 sigset_t blockmask;
94 sigset_t oldmask;
95
96 sigemptyset(&blockmask);
97 sigaddset(&blockmask, SIGCHLD);
98 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
99 if (res == -1) {
100 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
101 return -1;
102 }
103
104 res = fork();
105 if (res == -1) {
106 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
107 goto out_restore;
108 }
109 if (res == 0) {
110 char *env = NULL;
111
112 sigprocmask(SIG_SETMASK, &oldmask, NULL);
113
114 if(setuid(geteuid()) == -1) {
115 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
116 res = -1;
117 goto out_restore;
118 }
119
120 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
121 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
122 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
123 progname, strerror(errno));
124 exit(1);
125 }
126 res = waitpid(res, &status, 0);
127 if (res == -1)
128 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
129
130 if (status != 0)
131 res = -1;
132
133 out_restore:
134 sigprocmask(SIG_SETMASK, &oldmask, NULL);
135
136 return res;
137}
138
139int fuse_mnt_add_mount(const char *progname, const char *fsname,
140 const char *mnt, const char *type, const char *opts)
141{
142 if (!mtab_needs_update(mnt))
143 return 0;
144
145 return add_mount(progname, fsname, mnt, type, opts);
146}
147
148static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
149{
150 int res;
151 int status;
152 sigset_t blockmask;
153 sigset_t oldmask;
154
155 sigemptyset(&blockmask);
156 sigaddset(&blockmask, SIGCHLD);
157 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
158 if (res == -1) {
159 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
160 return -1;
161 }
162
163 res = fork();
164 if (res == -1) {
165 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
166 goto out_restore;
167 }
168 if (res == 0) {
169 char *env = NULL;
170
171 sigprocmask(SIG_SETMASK, &oldmask, NULL);
172
173 if(setuid(geteuid()) == -1) {
174 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
175 res = -1;
176 goto out_restore;
177 }
178
179 if (lazy) {
180 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
181 "-l", NULL, &env);
182 } else {
183 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
184 NULL, &env);
185 }
186 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
187 progname, strerror(errno));
188 exit(1);
189 }
190 res = waitpid(res, &status, 0);
191 if (res == -1)
192 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
193
194 if (status != 0) {
195 res = -1;
196 }
197
198 out_restore:
199 sigprocmask(SIG_SETMASK, &oldmask, NULL);
200 return res;
201
202}
203
204int fuse_mnt_umount(const char *progname, const char *abs_mnt,
205 const char *rel_mnt, int lazy)
206{
207 int res;
208
209 if (!mtab_needs_update(abs_mnt)) {
210 res = umount2(rel_mnt, lazy ? 2 : 0);
211 if (res == -1)
212 fprintf(stderr, "%s: failed to unmount %s: %s\n",
213 progname, abs_mnt, strerror(errno));
214 return res;
215 }
216
217 return exec_umount(progname, rel_mnt, lazy);
218}
219
220static int remove_mount(const char *progname, const char *mnt)
221{
222 int res;
223 int status;
224 sigset_t blockmask;
225 sigset_t oldmask;
226
227 sigemptyset(&blockmask);
228 sigaddset(&blockmask, SIGCHLD);
229 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
230 if (res == -1) {
231 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
232 return -1;
233 }
234
235 res = fork();
236 if (res == -1) {
237 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
238 goto out_restore;
239 }
240 if (res == 0) {
241 char *env = NULL;
242
243 sigprocmask(SIG_SETMASK, &oldmask, NULL);
244
245 if(setuid(geteuid()) == -1) {
246 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
247 res = -1;
248 goto out_restore;
249 }
250
251 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
252 "--fake", mnt, NULL, &env);
253 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
254 progname, strerror(errno));
255 exit(1);
256 }
257 res = waitpid(res, &status, 0);
258 if (res == -1)
259 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
260
261 if (status != 0)
262 res = -1;
263
264 out_restore:
265 sigprocmask(SIG_SETMASK, &oldmask, NULL);
266 return res;
267}
268
269int fuse_mnt_remove_mount(const char *progname, const char *mnt)
270{
271 if (!mtab_needs_update(mnt))
272 return 0;
273
274 return remove_mount(progname, mnt);
275}
276
277char *fuse_mnt_resolve_path(const char *progname, const char *orig)
278{
279 char buf[PATH_MAX];
280 char *copy;
281 char *dst;
282 char *end;
283 char *lastcomp;
284 const char *toresolv;
285
286 if (!orig[0]) {
287 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
288 orig);
289 return NULL;
290 }
291
292 copy = strdup(orig);
293 if (copy == NULL) {
294 fprintf(stderr, "%s: failed to allocate memory\n", progname);
295 return NULL;
296 }
297
298 toresolv = copy;
299 lastcomp = NULL;
300 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
301 if (end[0] != '/') {
302 char *tmp;
303 end[1] = '\0';
304 tmp = strrchr(copy, '/');
305 if (tmp == NULL) {
306 lastcomp = copy;
307 toresolv = ".";
308 } else {
309 lastcomp = tmp + 1;
310 if (tmp == copy)
311 toresolv = "/";
312 }
313 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
314 lastcomp = NULL;
315 toresolv = copy;
316 }
317 else if (tmp)
318 tmp[0] = '\0';
319 }
320 if (realpath(toresolv, buf) == NULL) {
321 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
322 strerror(errno));
323 free(copy);
324 return NULL;
325 }
326 if (lastcomp == NULL)
327 dst = strdup(buf);
328 else {
329 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
330 if (dst) {
331 unsigned buflen = strlen(buf);
332 if (buflen && buf[buflen-1] == '/')
333 sprintf(dst, "%s%s", buf, lastcomp);
334 else
335 sprintf(dst, "%s/%s", buf, lastcomp);
336 }
337 }
338 free(copy);
339 if (dst == NULL)
340 fprintf(stderr, "%s: failed to allocate memory\n", progname);
341 return dst;
342}
343
344int fuse_mnt_check_fuseblk(void)
345{
346 char buf[256];
347 FILE *f = fopen("/proc/filesystems", "r");
348 if (!f)
349 return 1;
350
351 while (fgets(buf, sizeof(buf), f))
352 if (strstr(buf, "fuseblk\n")) {
353 fclose(f);
354 return 1;
355 }
356
357 fclose(f);
358 return 0;
359}
360
361int fuse_mnt_parse_fuse_fd(const char *mountpoint)
362{
363 int fd = -1;
364 int len = 0;
365
366 if (mountpoint == NULL) {
367 fprintf(stderr, "Invalid null-ptr mount-point!\n");
368 return -1;
369 }
370
371 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
372 len == strlen(mountpoint)) {
373 return fd;
374 }
375
376 return -1;
377}
fuse-3.18.2/doc/html/lib_2mount__util_8c_source.html0000644000175000017500000016006615156613443021355 0ustar berndbernd libfuse: lib/mount_util.c Source File
libfuse
mount_util.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture-independent mounting code.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#include "fuse_config.h"
12#include "mount_util.h"
13
14#include <stdio.h>
15#include <unistd.h>
16#include <stdlib.h>
17#include <string.h>
18#include <signal.h>
19#include <dirent.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <limits.h>
23#include <paths.h>
24#if !defined( __NetBSD__) && !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__)
25#include <mntent.h>
26#else
27#define IGNORE_MTAB
28#endif
29#include <sys/stat.h>
30#include <sys/wait.h>
31
32#include "fuse_mount_compat.h"
33
34#include <sys/param.h>
35
36#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
37#define umount2(mnt, flags) unmount(mnt, ((flags) == 2) ? MNT_FORCE : 0)
38#endif
39
40#ifdef IGNORE_MTAB
41#define mtab_needs_update(mnt) 0
42#else
43static int mtab_needs_update(const char *mnt)
44{
45 int res;
46 struct stat stbuf;
47
48 /* If mtab is within new mount, don't touch it */
49 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
50 _PATH_MOUNTED[strlen(mnt)] == '/')
51 return 0;
52
53 /*
54 * Skip mtab update if /etc/mtab:
55 *
56 * - doesn't exist,
57 * - is on a read-only filesystem.
58 */
59 res = lstat(_PATH_MOUNTED, &stbuf);
60 if (res == -1) {
61 if (errno == ENOENT)
62 return 0;
63 } else {
64 uid_t ruid;
65 int err;
66
67 ruid = getuid();
68 if (ruid != 0)
69 setreuid(0, -1);
70
71 res = access(_PATH_MOUNTED, W_OK);
72 err = (res == -1) ? errno : 0;
73 if (ruid != 0)
74 setreuid(ruid, -1);
75
76 if (err == EROFS)
77 return 0;
78
79 res = access("/run/mount/utab", F_OK);
80 if (res == -1)
81 return 0;
82 }
83
84 return 1;
85}
86#endif /* IGNORE_MTAB */
87
88static int add_mount(const char *progname, const char *fsname,
89 const char *mnt, const char *type, const char *opts)
90{
91 int res;
92 int status;
93 sigset_t blockmask;
94 sigset_t oldmask;
95
96 sigemptyset(&blockmask);
97 sigaddset(&blockmask, SIGCHLD);
98 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
99 if (res == -1) {
100 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
101 return -1;
102 }
103
104 res = fork();
105 if (res == -1) {
106 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
107 goto out_restore;
108 }
109 if (res == 0) {
110 char *env = NULL;
111
112 sigprocmask(SIG_SETMASK, &oldmask, NULL);
113
114 if(setuid(geteuid()) == -1) {
115 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
116 res = -1;
117 goto out_restore;
118 }
119
120 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
121 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
122 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
123 progname, strerror(errno));
124 exit(1);
125 }
126 res = waitpid(res, &status, 0);
127 if (res == -1)
128 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
129
130 if (status != 0)
131 res = -1;
132
133 out_restore:
134 sigprocmask(SIG_SETMASK, &oldmask, NULL);
135
136 return res;
137}
138
139int fuse_mnt_add_mount(const char *progname, const char *fsname,
140 const char *mnt, const char *type, const char *opts)
141{
142 if (!mtab_needs_update(mnt))
143 return 0;
144
145 return add_mount(progname, fsname, mnt, type, opts);
146}
147
148static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
149{
150 int res;
151 int status;
152 sigset_t blockmask;
153 sigset_t oldmask;
154
155 sigemptyset(&blockmask);
156 sigaddset(&blockmask, SIGCHLD);
157 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
158 if (res == -1) {
159 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
160 return -1;
161 }
162
163 res = fork();
164 if (res == -1) {
165 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
166 goto out_restore;
167 }
168 if (res == 0) {
169 char *env = NULL;
170
171 sigprocmask(SIG_SETMASK, &oldmask, NULL);
172
173 if(setuid(geteuid()) == -1) {
174 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
175 res = -1;
176 goto out_restore;
177 }
178
179 if (lazy) {
180 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
181 "-l", NULL, &env);
182 } else {
183 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
184 NULL, &env);
185 }
186 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
187 progname, strerror(errno));
188 exit(1);
189 }
190 res = waitpid(res, &status, 0);
191 if (res == -1)
192 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
193
194 if (status != 0) {
195 res = -1;
196 }
197
198 out_restore:
199 sigprocmask(SIG_SETMASK, &oldmask, NULL);
200 return res;
201
202}
203
204int fuse_mnt_umount(const char *progname, const char *abs_mnt,
205 const char *rel_mnt, int lazy)
206{
207 int res;
208
209 if (!mtab_needs_update(abs_mnt)) {
210 res = umount2(rel_mnt, lazy ? 2 : 0);
211 if (res == -1)
212 fprintf(stderr, "%s: failed to unmount %s: %s\n",
213 progname, abs_mnt, strerror(errno));
214 return res;
215 }
216
217 return exec_umount(progname, rel_mnt, lazy);
218}
219
220static int remove_mount(const char *progname, const char *mnt)
221{
222 int res;
223 int status;
224 sigset_t blockmask;
225 sigset_t oldmask;
226
227 sigemptyset(&blockmask);
228 sigaddset(&blockmask, SIGCHLD);
229 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
230 if (res == -1) {
231 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
232 return -1;
233 }
234
235 res = fork();
236 if (res == -1) {
237 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
238 goto out_restore;
239 }
240 if (res == 0) {
241 char *env = NULL;
242
243 sigprocmask(SIG_SETMASK, &oldmask, NULL);
244
245 if(setuid(geteuid()) == -1) {
246 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
247 res = -1;
248 goto out_restore;
249 }
250
251 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
252 "--fake", mnt, NULL, &env);
253 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
254 progname, strerror(errno));
255 exit(1);
256 }
257 res = waitpid(res, &status, 0);
258 if (res == -1)
259 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
260
261 if (status != 0)
262 res = -1;
263
264 out_restore:
265 sigprocmask(SIG_SETMASK, &oldmask, NULL);
266 return res;
267}
268
269int fuse_mnt_remove_mount(const char *progname, const char *mnt)
270{
271 if (!mtab_needs_update(mnt))
272 return 0;
273
274 return remove_mount(progname, mnt);
275}
276
277char *fuse_mnt_resolve_path(const char *progname, const char *orig)
278{
279 char buf[PATH_MAX];
280 char *copy;
281 char *dst;
282 char *end;
283 char *lastcomp;
284 const char *toresolv;
285
286 if (!orig[0]) {
287 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
288 orig);
289 return NULL;
290 }
291
292 copy = strdup(orig);
293 if (copy == NULL) {
294 fprintf(stderr, "%s: failed to allocate memory\n", progname);
295 return NULL;
296 }
297
298 toresolv = copy;
299 lastcomp = NULL;
300 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
301 if (end[0] != '/') {
302 char *tmp;
303 end[1] = '\0';
304 tmp = strrchr(copy, '/');
305 if (tmp == NULL) {
306 lastcomp = copy;
307 toresolv = ".";
308 } else {
309 lastcomp = tmp + 1;
310 if (tmp == copy)
311 toresolv = "/";
312 }
313 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
314 lastcomp = NULL;
315 toresolv = copy;
316 }
317 else if (tmp)
318 tmp[0] = '\0';
319 }
320 if (realpath(toresolv, buf) == NULL) {
321 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
322 strerror(errno));
323 free(copy);
324 return NULL;
325 }
326 if (lastcomp == NULL)
327 dst = strdup(buf);
328 else {
329 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
330 if (dst) {
331 unsigned buflen = strlen(buf);
332 if (buflen && buf[buflen-1] == '/')
333 sprintf(dst, "%s%s", buf, lastcomp);
334 else
335 sprintf(dst, "%s/%s", buf, lastcomp);
336 }
337 }
338 free(copy);
339 if (dst == NULL)
340 fprintf(stderr, "%s: failed to allocate memory\n", progname);
341 return dst;
342}
343
344int fuse_mnt_check_fuseblk(void)
345{
346 char buf[256];
347 FILE *f = fopen("/proc/filesystems", "r");
348 if (!f)
349 return 1;
350
351 while (fgets(buf, sizeof(buf), f))
352 if (strstr(buf, "fuseblk\n")) {
353 fclose(f);
354 return 1;
355 }
356
357 fclose(f);
358 return 0;
359}
360
361int fuse_mnt_parse_fuse_fd(const char *mountpoint)
362{
363 int fd = -1;
364 int len = 0;
365
366 if (mountpoint == NULL) {
367 fprintf(stderr, "Invalid null-ptr mount-point!\n");
368 return -1;
369 }
370
371 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
372 len == strlen(mountpoint)) {
373 return fd;
374 }
375
376 return -1;
377}
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2mount__util_8h_source.html0000644000175000017500000001363415156613443023536 0ustar berndbernd libfuse: fuse-3.17.4/lib/mount_util.h Source File
libfuse
mount_util.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
7*/
8
9#include <sys/types.h>
10
11int fuse_mnt_add_mount(const char *progname, const char *fsname,
12 const char *mnt, const char *type, const char *opts);
13int fuse_mnt_remove_mount(const char *progname, const char *mnt);
14int fuse_mnt_umount(const char *progname, const char *abs_mnt,
15 const char *rel_mnt, int lazy);
16char *fuse_mnt_resolve_path(const char *progname, const char *orig);
17int fuse_mnt_check_fuseblk(void);
18int fuse_mnt_parse_fuse_fd(const char *mountpoint);
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2mount__util_8h_source.html0000644000175000017500000001363215156613443023532 0ustar berndbernd libfuse: fuse-3.18.1/lib/mount_util.h Source File
libfuse
mount_util.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#include <sys/types.h>
10
11int fuse_mnt_add_mount(const char *progname, const char *fsname,
12 const char *mnt, const char *type, const char *opts);
13int fuse_mnt_remove_mount(const char *progname, const char *mnt);
14int fuse_mnt_umount(const char *progname, const char *abs_mnt,
15 const char *rel_mnt, int lazy);
16char *fuse_mnt_resolve_path(const char *progname, const char *orig);
17int fuse_mnt_check_fuseblk(void);
18int fuse_mnt_parse_fuse_fd(const char *mountpoint);
fuse-3.18.2/doc/html/lib_2mount__util_8h_source.html0000644000175000017500000001344715156613443021362 0ustar berndbernd libfuse: lib/mount_util.h Source File
libfuse
mount_util.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#include <sys/types.h>
10
11int fuse_mnt_add_mount(const char *progname, const char *fsname,
12 const char *mnt, const char *type, const char *opts);
13int fuse_mnt_remove_mount(const char *progname, const char *mnt);
14int fuse_mnt_umount(const char *progname, const char *abs_mnt,
15 const char *rel_mnt, int lazy);
16char *fuse_mnt_resolve_path(const char *progname, const char *orig);
17int fuse_mnt_check_fuseblk(void);
18int fuse_mnt_parse_fuse_fd(const char *mountpoint);
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2usdt_8h_source.html0000644000175000017500000027402315156613443022156 0ustar berndbernd libfuse: fuse-3.18.1/lib/usdt.h Source File
libfuse
usdt.h
1/*
2 * Copied from https://github.com/libbpf/usdt/
3 */
4
5// SPDX-License-Identifier: BSD-2-Clause
6/*
7 * This single-header library defines a collection of variadic macros for
8 * defining and triggering USDTs (User Statically-Defined Tracepoints):
9 *
10 * - For USDTs without associated semaphore:
11 * USDT(group, name, args...)
12 *
13 * - For USDTs with implicit (transparent to the user) semaphore:
14 * USDT_WITH_SEMA(group, name, args...)
15 * USDT_IS_ACTIVE(group, name)
16 *
17 * - For USDTs with explicit (user-defined and provided) semaphore:
18 * USDT_WITH_EXPLICIT_SEMA(sema, group, name, args...)
19 * USDT_SEMA_IS_ACTIVE(sema)
20 *
21 * all of which emit a NOP instruction into the instruction stream, and so
22 * have *zero* overhead for the surrounding code. USDTs are identified by
23 * a combination of `group` and `name` identifiers, which is used by external
24 * tracing tooling (tracers) for identifying exact USDTs of interest.
25 *
26 * USDTs can have an associated (2-byte) activity counter (USDT semaphore),
27 * automatically maintained by Linux kernel whenever any correctly written
28 * BPF-based tracer is attached to the USDT. This USDT semaphore can be used
29 * to check whether there is a need to do any extra data collection and
30 * processing for a given USDT (if necessary), and otherwise avoid extra work
31 * for a common case of USDT not being traced ("active").
32 *
33 * See documentation for USDT_WITH_SEMA()/USDT_IS_ACTIVE() or
34 * USDT_WITH_EXPLICIT_SEMA()/USDT_SEMA_IS_ACTIVE() APIs below for details on
35 * working with USDTs with implicitly or explicitly associated
36 * USDT semaphores, respectively.
37 *
38 * There is also some additional data recorded into an auxiliary note
39 * section. The data in the note section describes the operands, in terms of
40 * size and location, used by tracing tooling to know where to find USDT
41 * arguments. Each location is encoded as an assembler operand string.
42 * Tracing tools (bpftrace and BPF-based tracers, systemtap, etc) insert
43 * breakpoints on top of the nop, and decode the location operand-strings,
44 * like an assembler, to find the values being passed.
45 *
46 * The operand strings are selected by the compiler for each operand.
47 * They are constrained by inline-assembler codes.The default is:
48 *
49 * #define USDT_ARG_CONSTRAINT nor
50 *
51 * This is a good default if the operands tend to be integral and
52 * moderate in number (smaller than number of registers). In other
53 * cases, the compiler may report "'asm' requires impossible reload" or
54 * similar. In this case, consider simplifying the macro call (fewer
55 * and simpler operands), reduce optimization, or override the default
56 * constraints string via:
57 *
58 * #define USDT_ARG_CONSTRAINT g
59 * #include <usdt.h>
60 *
61 * For some historical description of USDT v3 format (the one used by this
62 * library and generally recognized and assumed by BPF-based tracing tools)
63 * see [0]. The more formal specification can be found at [1]. Additional
64 * argument constraints information can be found at [2].
65 *
66 * Original SystemTap's sys/sdt.h implementation ([3]) was used as a base for
67 * this USDT library implementation. Current implementation differs *a lot* in
68 * terms of exposed user API and general usability, which was the main goal
69 * and focus of the reimplementation work. Nevertheless, underlying recorded
70 * USDT definitions are fully binary compatible and any USDT-based tooling
71 * should work equally well with USDTs defined by either SystemTap's or this
72 * library's USDT implementation.
73 *
74 * [0] https://ecos.sourceware.org/ml/systemtap/2010-q3/msg00145.html
75 * [1] https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation
76 * [2] https://gcc.gnu.org/onlinedocs/gcc/Constraints.html
77 * [3] https://sourceware.org/git/?p=systemtap.git;a=blob;f=includes/sys/sdt.h
78 */
79#ifndef __USDT_H
80#define __USDT_H
81
82/*
83 * Changelog:
84 *
85 * 0.1.0
86 * -----
87 * - Initial release
88 */
89#define USDT_MAJOR_VERSION 0
90#define USDT_MINOR_VERSION 1
91#define USDT_PATCH_VERSION 0
92
93/* C++20 and C23 added __VA_OPT__ as a standard replacement for non-standard `##__VA_ARGS__` extension */
94#if (defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L) || (defined(__cplusplus) && __cplusplus > 201703L)
95#define __usdt_va_opt 1
96#define __usdt_va_args(...) __VA_OPT__(,) __VA_ARGS__
97#else
98#define __usdt_va_args(...) , ##__VA_ARGS__
99#endif
100
101/*
102 * Trigger USDT with `group`:`name` identifier and pass through `args` as its
103 * arguments. Zero arguments are acceptable as well. No USDT semaphore is
104 * associated with this USDT.
105 *
106 * Such "semaphoreless" USDTs are commonly used when there is no extra data
107 * collection or processing needed to collect and prepare USDT arguments and
108 * they are just available in the surrounding code. USDT() macro will just
109 * record their locations in CPU registers or in memory for tracing tooling to
110 * be able to access them, if necessary.
111 */
112#ifdef __usdt_va_opt
113#define USDT(group, name, ...) \
114 __usdt_probe(group, name, __usdt_sema_none, 0 __VA_OPT__(,) __VA_ARGS__)
115#else
116#define USDT(group, name, ...) \
117 __usdt_probe(group, name, __usdt_sema_none, 0, ##__VA_ARGS__)
118#endif
119
120/*
121 * Trigger USDT with `group`:`name` identifier and pass through `args` as its
122 * arguments. Zero arguments are acceptable as well. USDT also get an
123 * implicitly-defined associated USDT semaphore, which will be "activated" by
124 * tracing tooling and can be used to check whether USDT is being actively
125 * observed.
126 *
127 * USDTs with semaphore are commonly used when there is a need to perform
128 * additional data collection and processing to prepare USDT arguments, which
129 * otherwise might not be necessary for the rest of application logic. In such
130 * case, USDT semaphore can be used to avoid unnecessary extra work. If USDT
131 * is not traced (which is presumed to be a common situation), the associated
132 * USDT semaphore is "inactive", and so there is no need to waste resources to
133 * prepare USDT arguments. Use USDT_IS_ACTIVE(group, name) to check whether
134 * USDT is "active".
135 *
136 * N.B. There is an inherent (albeit short) gap between checking whether USDT
137 * is active and triggering corresponding USDT, in which external tracer can
138 * be attached to an USDT and activate USDT semaphore after the activity check.
139 * If such a race occurs, tracers might miss one USDT execution. Tracers are
140 * expected to accommodate such possibility and this is expected to not be
141 * a problem for applications and tracers.
142 *
143 * N.B. Implicit USDT semaphore defined by USDT_WITH_SEMA() is contained
144 * within a single executable or shared library and is not shared outside
145 * them. I.e., if you use USDT_WITH_SEMA() with the same USDT group and name
146 * identifier across executable and shared library, it will work and won't
147 * conflict, per se, but will define independent USDT semaphores, one for each
148 * shared library/executable in which USDT_WITH_SEMA(group, name) is used.
149 * That is, if you attach to this USDT in one shared library (or executable),
150 * then only USDT semaphore within that shared library (or executable) will be
151 * updated by the kernel, while other libraries (or executable) will not see
152 * activated USDT semaphore. In short, it's best to use unique USDT group:name
153 * identifiers across different shared libraries (and, equivalently, between
154 * executable and shared library). This is advanced consideration and is
155 * rarely (if ever) seen in practice, but just to avoid surprises this is
156 * called out here. (Static libraries become a part of final executable, once
157 * linked by linker, so the above considerations don't apply to them.)
158 */
159#ifdef __usdt_va_opt
160#define USDT_WITH_SEMA(group, name, ...) \
161 __usdt_probe(group, name, \
162 __usdt_sema_implicit, __usdt_sema_name(group, name) \
163 __VA_OPT__(,) __VA_ARGS__)
164#else
165#define USDT_WITH_SEMA(group, name, ...) \
166 __usdt_probe(group, name, \
167 __usdt_sema_implicit, __usdt_sema_name(group, name), \
168 ##__VA_ARGS__)
169#endif
170
171struct usdt_sema { volatile unsigned short active; };
172
173/*
174 * Check if USDT with `group`:`name` identifier is "active" (i.e., whether it
175 * is attached to by external tracing tooling and is actively observed).
176 *
177 * This macro can be used to decide whether any additional and potentially
178 * expensive data collection or processing should be done to pass extra
179 * information into the given USDT. It is assumed that USDT is triggered with
180 * USDT_WITH_SEMA() macro which will implicitly define associated USDT
181 * semaphore. (If one needs more control over USDT semaphore, see
182 * USDT_DEFINE_SEMA() and USDT_WITH_EXPLICIT_SEMA() macros below.)
183 *
184 * N.B. Such checks are necessarily racy and speculative. Between checking
185 * whether USDT is active and triggering the USDT itself, tracer can be
186 * detached with no notification. This race should be extremely rare and worst
187 * case should result in one-time wasted extra data collection and processing.
188 */
189#define USDT_IS_ACTIVE(group, name) ({ \
190 extern struct usdt_sema __usdt_sema_name(group, name) \
191 __usdt_asm_name(__usdt_sema_name(group, name)); \
192 __usdt_sema_implicit(__usdt_sema_name(group, name)); \
193 __usdt_sema_name(group, name).active > 0; \
194})
195
196/*
197 * APIs for working with user-defined explicit USDT semaphores.
198 *
199 * This is a less commonly used advanced API for use cases in which user needs
200 * an explicit control over (potentially shared across multiple USDTs) USDT
201 * semaphore instance. This can be used when there is a group of logically
202 * related USDTs that all need extra data collection and processing whenever
203 * any of a family of related USDTs are "activated" (i.e., traced). In such
204 * a case, all such related USDTs will be associated with the same shared USDT
205 * semaphore defined with USDT_DEFINE_SEMA() and the USDTs themselves will be
206 * triggered with USDT_WITH_EXPLICIT_SEMA() macros, taking an explicit extra
207 * USDT semaphore identifier as an extra parameter.
208 */
209
215#define USDT_SEMA(sema) __usdt_sema_##sema
216
217/*
218 * Define storage for user-defined USDT semaphore `sema`.
219 *
220 * Should be used only once in non-header source file to let compiler allocate
221 * space for the semaphore variable. Just like with any other global variable.
222 *
223 * This macro can be used anywhere where global variable declaration is
224 * allowed. Just like with global variable definitions, there should be only
225 * one definition of user-defined USDT semaphore with given `sema` identifier,
226 * otherwise compiler or linker will complain about duplicate variable
227 * definition.
228 *
229 * For C++, it is allowed to use USDT_DEFINE_SEMA() both in global namespace
230 * and inside namespaces (including nested namespaces). Just make sure that
231 * USDT_DECLARE_SEMA() is placed within the namespace where this semaphore is
232 * referenced, or any of its parent namespaces, so the C++ language-level
233 * identifier is visible to the code that needs to reference the semaphore.
234 * At the lowest layer, USDT semaphores have global naming and visibility
235 * (they have a corresponding `__usdt_sema_<name>` symbol, which can be linked
236 * against from C or C++ code, if necessary). To keep it simple, putting
237 * USDT_DECLARE_SEMA() declarations into global namespaces is the simplest
238 * no-brainer solution. All these aspects are irrelevant for plain C, because
239 * C doesn't have namespaces and everything is always in the global namespace.
240 *
241 * N.B. Due to USDT metadata being recorded in non-allocatable ELF note
242 * section, it has limitations when it comes to relocations, which, in
243 * practice, means that it's not possible to correctly share USDT semaphores
244 * between main executable and shared libraries, or even between multiple
245 * shared libraries. USDT semaphore has to be contained to individual shared
246 * library or executable to avoid unpleasant surprises with half-working USDT
247 * semaphores. We enforce this by marking semaphore ELF symbols as having
248 * a hidden visibility. This is quite an advanced use case and consideration
249 * and for most users this should have no consequences whatsoever.
250 */
251#define USDT_DEFINE_SEMA(sema) \
252 struct usdt_sema __usdt_sema_sec USDT_SEMA(sema) \
253 __usdt_asm_name(USDT_SEMA(sema)) \
254 __attribute__((visibility("hidden"))) = { 0 }
255
256/*
257 * Declare extern reference to user-defined USDT semaphore `sema`.
258 *
259 * Refers to a variable defined in another compilation unit by
260 * USDT_DEFINE_SEMA() and allows to use the same USDT semaphore across
261 * multiple compilation units (i.e., .c and .cpp files).
262 *
263 * See USDT_DEFINE_SEMA() notes above for C++ language usage peculiarities.
264 */
265#define USDT_DECLARE_SEMA(sema) \
266 extern struct usdt_sema USDT_SEMA(sema) __usdt_asm_name(USDT_SEMA(sema))
267
268/*
269 * Check if user-defined USDT semaphore `sema` is "active" (i.e., whether it
270 * is attached to by external tracing tooling and is actively observed).
271 *
272 * This macro can be used to decide whether any additional and potentially
273 * expensive data collection or processing should be done to pass extra
274 * information into USDT(s) associated with USDT semaphore `sema`.
275 *
276 * N.B. Such checks are necessarily racy. Between checking the state of USDT
277 * semaphore and triggering associated USDT(s), the active tracer might attach
278 * or detach. This race should be extremely rare and worst case should result
279 * in one-time missed USDT event or wasted extra data collection and
280 * processing. USDT-using tracers should be written with this in mind and is
281 * not a concern of the application defining USDTs with associated semaphore.
282 */
283#define USDT_SEMA_IS_ACTIVE(sema) (USDT_SEMA(sema).active > 0)
284
285/*
286 * Invoke USDT specified by `group` and `name` identifiers and associate
287 * explicitly user-defined semaphore `sema` with it. Pass through `args` as
288 * USDT arguments. `args` are optional and zero arguments are acceptable.
289 *
290 * Semaphore is defined with the help of USDT_DEFINE_SEMA() macro and can be
291 * checked whether active with USDT_SEMA_IS_ACTIVE().
292 */
293#ifdef __usdt_va_opt
294#define USDT_WITH_EXPLICIT_SEMA(sema, group, name, ...) \
295 __usdt_probe(group, name, __usdt_sema_explicit, USDT_SEMA(sema), ##__VA_ARGS__)
296#else
297#define USDT_WITH_EXPLICIT_SEMA(sema, group, name, ...) \
298 __usdt_probe(group, name, __usdt_sema_explicit, USDT_SEMA(sema) __VA_OPT__(,) __VA_ARGS__)
299#endif
300
301/*
302 * Adjustable implementation aspects
303 */
304#ifndef USDT_ARG_CONSTRAINT
305#if defined __powerpc__
306#define USDT_ARG_CONSTRAINT nZr
307#elif defined __arm__
308#define USDT_ARG_CONSTRAINT g
309#elif defined __loongarch__
310#define USDT_ARG_CONSTRAINT nmr
311#else
312#define USDT_ARG_CONSTRAINT nor
313#endif
314#endif /* USDT_ARG_CONSTRAINT */
315
316#ifndef USDT_NOP
317#if defined(__ia64__) || defined(__s390__) || defined(__s390x__)
318#define USDT_NOP nop 0
319#else
320#define USDT_NOP nop
321#endif
322#endif /* USDT_NOP */
323
324/*
325 * Implementation details
326 */
327/* USDT name for implicitly-defined USDT semaphore, derived from group:name */
328#define __usdt_sema_name(group, name) __usdt_sema_##group##__##name
329/* ELF section into which USDT semaphores are put */
330#define __usdt_sema_sec __attribute__((section(".probes")))
331
332#define __usdt_concat(a, b) a ## b
333#define __usdt_apply(fn, n) __usdt_concat(fn, n)
334
335#ifndef __usdt_nth
336#define __usdt_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, N, ...) N
337#endif
338
339#ifndef __usdt_narg
340#ifdef __usdt_va_opt
341#define __usdt_narg(...) __usdt_nth(_ __VA_OPT__(,) __VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
342#else
343#define __usdt_narg(...) __usdt_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
344#endif
345#endif /* __usdt_narg */
346
347#define __usdt_hash #
348#define __usdt_str_(x) #x
349#define __usdt_str(x) __usdt_str_(x)
350
351#ifndef __usdt_asm_name
352#define __usdt_asm_name(name) __asm__(__usdt_str(name))
353#endif
354
355#define __usdt_asm1(a) __usdt_str(a) "\n"
356#define __usdt_asm2(a,b) __usdt_str(a) "," __usdt_str(b) "\n"
357#define __usdt_asm3(a,b,c) __usdt_str(a) "," __usdt_str(b) "," __usdt_str(c) "\n"
358#define __usdt_asm5(a,b,c,d,e) __usdt_str(a) "," __usdt_str(b) "," __usdt_str(c) "," \
359 __usdt_str(d) "," __usdt_str(e) "\n"
360
361#ifdef __LP64__
362#define __usdt_asm_addr .8byte
363#else
364#define __usdt_asm_addr .4byte
365#endif
366
367#define __usdt_asm_strz_(x) __usdt_asm1(.asciz #x)
368#define __usdt_asm_strz(x) __usdt_asm_strz_(x)
369#define __usdt_asm_str_(x) __usdt_asm1(.ascii #x)
370#define __usdt_asm_str(x) __usdt_asm_str_(x)
371
372/* "semaphoreless" USDT case */
373#ifndef __usdt_sema_none
374#define __usdt_sema_none(sema)
375#endif
376
377/* implicitly defined __usdt_sema__group__name semaphore (using weak symbols) */
378#ifndef __usdt_sema_implicit
379#define __usdt_sema_implicit(sema) \
380 __asm__ __volatile__ ( \
381 __usdt_asm1(.ifndef sema) \
382 __usdt_asm3( .pushsection .probes, "aw", "progbits") \
383 __usdt_asm1( .weak sema) \
384 __usdt_asm1( .hidden sema) \
385 __usdt_asm1( .align 2) \
386 __usdt_asm1(sema:) \
387 __usdt_asm1( .zero 2) \
388 __usdt_asm2( .type sema, @object) \
389 __usdt_asm2( .size sema, 2) \
390 __usdt_asm1( .popsection) \
391 __usdt_asm1(.endif) \
392 );
393#endif
394
395/* externally defined semaphore using USDT_DEFINE_SEMA() and passed explicitly by user */
396#ifndef __usdt_sema_explicit
397#define __usdt_sema_explicit(sema) \
398 __asm__ __volatile__ ("" :: "m" (sema));
399#endif
400
401/* main USDT definition (nop and .note.stapsdt metadata) */
402#define __usdt_probe(group, name, sema_def, sema, ...) do { \
403 sema_def(sema) \
404 __asm__ __volatile__ ( \
405 __usdt_asm1(990: USDT_NOP) \
406 __usdt_asm3( .pushsection .note.stapsdt, "", "note") \
407 __usdt_asm1( .balign 4) \
408 __usdt_asm3( .4byte 992f-991f,994f-993f,3) \
409 __usdt_asm1(991: .asciz "stapsdt") \
410 __usdt_asm1(992: .balign 4) \
411 __usdt_asm1(993: __usdt_asm_addr 990b) \
412 __usdt_asm1( __usdt_asm_addr _.stapsdt.base) \
413 __usdt_asm1( __usdt_asm_addr sema) \
414 __usdt_asm_strz(group) \
415 __usdt_asm_strz(name) \
416 __usdt_asm_args(__VA_ARGS__) \
417 __usdt_asm1( .ascii "\0") \
418 __usdt_asm1(994: .balign 4) \
419 __usdt_asm1( .popsection) \
420 __usdt_asm1(.ifndef _.stapsdt.base) \
421 __usdt_asm5( .pushsection .stapsdt.base,"aG","progbits",.stapsdt.base,comdat)\
422 __usdt_asm1( .weak _.stapsdt.base) \
423 __usdt_asm1( .hidden _.stapsdt.base) \
424 __usdt_asm1(_.stapsdt.base:) \
425 __usdt_asm1( .space 1) \
426 __usdt_asm2( .size _.stapsdt.base, 1) \
427 __usdt_asm1( .popsection) \
428 __usdt_asm1(.endif) \
429 :: __usdt_asm_ops(__VA_ARGS__) \
430 ); \
431} while (0)
432
433/*
434 * NB: gdb PR24541 highlighted an unspecified corner of the sdt.h
435 * operand note format.
436 *
437 * The named register may be a longer or shorter (!) alias for the
438 * storage where the value in question is found. For example, on
439 * i386, 64-bit value may be put in register pairs, and a register
440 * name stored would identify just one of them. Previously, gcc was
441 * asked to emit the %w[id] (16-bit alias of some registers holding
442 * operands), even when a wider 32-bit value was used.
443 *
444 * Bottom line: the byte-width given before the @ sign governs. If
445 * there is a mismatch between that width and that of the named
446 * register, then a sys/sdt.h note consumer may need to employ
447 * architecture-specific heuristics to figure out where the compiler
448 * has actually put the complete value.
449 */
450#if defined(__powerpc__) || defined(__powerpc64__)
451#define __usdt_argref(id) %I[id]%[id]
452#elif defined(__i386__)
453#define __usdt_argref(id) %k[id] /* gcc.gnu.org/PR80115 sourceware.org/PR24541 */
454#else
455#define __usdt_argref(id) %[id]
456#endif
457
458#define __usdt_asm_arg(n) __usdt_asm_str(%c[__usdt_asz##n]) \
459 __usdt_asm1(.ascii "@") \
460 __usdt_asm_str(__usdt_argref(__usdt_aval##n))
461
462#define __usdt_asm_args0 /* no arguments */
463#define __usdt_asm_args1 __usdt_asm_arg(1)
464#define __usdt_asm_args2 __usdt_asm_args1 __usdt_asm1(.ascii " ") __usdt_asm_arg(2)
465#define __usdt_asm_args3 __usdt_asm_args2 __usdt_asm1(.ascii " ") __usdt_asm_arg(3)
466#define __usdt_asm_args4 __usdt_asm_args3 __usdt_asm1(.ascii " ") __usdt_asm_arg(4)
467#define __usdt_asm_args5 __usdt_asm_args4 __usdt_asm1(.ascii " ") __usdt_asm_arg(5)
468#define __usdt_asm_args6 __usdt_asm_args5 __usdt_asm1(.ascii " ") __usdt_asm_arg(6)
469#define __usdt_asm_args7 __usdt_asm_args6 __usdt_asm1(.ascii " ") __usdt_asm_arg(7)
470#define __usdt_asm_args8 __usdt_asm_args7 __usdt_asm1(.ascii " ") __usdt_asm_arg(8)
471#define __usdt_asm_args9 __usdt_asm_args8 __usdt_asm1(.ascii " ") __usdt_asm_arg(9)
472#define __usdt_asm_args10 __usdt_asm_args9 __usdt_asm1(.ascii " ") __usdt_asm_arg(10)
473#define __usdt_asm_args11 __usdt_asm_args10 __usdt_asm1(.ascii " ") __usdt_asm_arg(11)
474#define __usdt_asm_args12 __usdt_asm_args11 __usdt_asm1(.ascii " ") __usdt_asm_arg(12)
475#define __usdt_asm_args(...) __usdt_apply(__usdt_asm_args, __usdt_narg(__VA_ARGS__))
476
477#define __usdt_is_arr(x) (__builtin_classify_type(x) == 14 || __builtin_classify_type(x) == 5)
478#define __usdt_arg_size(x) (__usdt_is_arr(x) ? sizeof(void *) : sizeof(x))
479
480/*
481 * We can't use __builtin_choose_expr() in C++, so fall back to table-based
482 * signedness determination for known types, utilizing templates magic.
483 */
484#ifdef __cplusplus
485
486#define __usdt_is_signed(x) (!__usdt_is_arr(x) && __usdt_t<__typeof(x)>::is_signed)
487
488#include <cstddef>
489
490template<typename T> struct __usdt_t { static const bool is_signed = false; };
491template<typename A> struct __usdt_t<A[]> : public __usdt_t<A *> {};
492template<typename A, size_t N> struct __usdt_t<A[N]> : public __usdt_t<A *> {};
493
494#define __usdt_def_signed(T) \
495template<> struct __usdt_t<T> { static const bool is_signed = true; }; \
496template<> struct __usdt_t<const T> { static const bool is_signed = true; }; \
497template<> struct __usdt_t<volatile T> { static const bool is_signed = true; }; \
498template<> struct __usdt_t<const volatile T> { static const bool is_signed = true; }
499#define __usdt_maybe_signed(T) \
500template<> struct __usdt_t<T> { static const bool is_signed = (T)-1 < (T)1; }; \
501template<> struct __usdt_t<const T> { static const bool is_signed = (T)-1 < (T)1; }; \
502template<> struct __usdt_t<volatile T> { static const bool is_signed = (T)-1 < (T)1; }; \
503template<> struct __usdt_t<const volatile T> { static const bool is_signed = (T)-1 < (T)1; }
504
505__usdt_def_signed(signed char);
506__usdt_def_signed(short);
507__usdt_def_signed(int);
508__usdt_def_signed(long);
509__usdt_def_signed(long long);
510__usdt_maybe_signed(char);
511__usdt_maybe_signed(wchar_t);
512
513#else /* !__cplusplus */
514
515#define __usdt_is_inttype(x) (__builtin_classify_type(x) >= 1 && __builtin_classify_type(x) <= 4)
516#define __usdt_inttype(x) __typeof(__builtin_choose_expr(__usdt_is_inttype(x), (x), 0U))
517#define __usdt_is_signed(x) ((__usdt_inttype(x))-1 < (__usdt_inttype(x))1)
518
519#endif /* __cplusplus */
520
521#define __usdt_asm_op(n, x) \
522 [__usdt_asz##n] "n" ((__usdt_is_signed(x) ? (int)-1 : 1) * (int)__usdt_arg_size(x)), \
523 [__usdt_aval##n] __usdt_str(USDT_ARG_CONSTRAINT)(x)
524
525#define __usdt_asm_ops0() [__usdt_dummy] "g" (0)
526#define __usdt_asm_ops1(x) __usdt_asm_op(1, x)
527#define __usdt_asm_ops2(a,x) __usdt_asm_ops1(a), __usdt_asm_op(2, x)
528#define __usdt_asm_ops3(a,b,x) __usdt_asm_ops2(a,b), __usdt_asm_op(3, x)
529#define __usdt_asm_ops4(a,b,c,x) __usdt_asm_ops3(a,b,c), __usdt_asm_op(4, x)
530#define __usdt_asm_ops5(a,b,c,d,x) __usdt_asm_ops4(a,b,c,d), __usdt_asm_op(5, x)
531#define __usdt_asm_ops6(a,b,c,d,e,x) __usdt_asm_ops5(a,b,c,d,e), __usdt_asm_op(6, x)
532#define __usdt_asm_ops7(a,b,c,d,e,f,x) __usdt_asm_ops6(a,b,c,d,e,f), __usdt_asm_op(7, x)
533#define __usdt_asm_ops8(a,b,c,d,e,f,g,x) __usdt_asm_ops7(a,b,c,d,e,f,g), __usdt_asm_op(8, x)
534#define __usdt_asm_ops9(a,b,c,d,e,f,g,h,x) __usdt_asm_ops8(a,b,c,d,e,f,g,h), __usdt_asm_op(9, x)
535#define __usdt_asm_ops10(a,b,c,d,e,f,g,h,i,x) __usdt_asm_ops9(a,b,c,d,e,f,g,h,i), __usdt_asm_op(10, x)
536#define __usdt_asm_ops11(a,b,c,d,e,f,g,h,i,j,x) __usdt_asm_ops10(a,b,c,d,e,f,g,h,i,j), __usdt_asm_op(11, x)
537#define __usdt_asm_ops12(a,b,c,d,e,f,g,h,i,j,k,x) __usdt_asm_ops11(a,b,c,d,e,f,g,h,i,j,k), __usdt_asm_op(12, x)
538#define __usdt_asm_ops(...) __usdt_apply(__usdt_asm_ops, __usdt_narg(__VA_ARGS__))(__VA_ARGS__)
539
540#endif /* __USDT_H */
fuse-3.18.2/doc/html/lib_2usdt_8h_source.html0000644000175000017500000027364015156613443020006 0ustar berndbernd libfuse: lib/usdt.h Source File
libfuse
usdt.h
1/*
2 * Copied from https://github.com/libbpf/usdt/
3 */
4
5// SPDX-License-Identifier: BSD-2-Clause
6/*
7 * This single-header library defines a collection of variadic macros for
8 * defining and triggering USDTs (User Statically-Defined Tracepoints):
9 *
10 * - For USDTs without associated semaphore:
11 * USDT(group, name, args...)
12 *
13 * - For USDTs with implicit (transparent to the user) semaphore:
14 * USDT_WITH_SEMA(group, name, args...)
15 * USDT_IS_ACTIVE(group, name)
16 *
17 * - For USDTs with explicit (user-defined and provided) semaphore:
18 * USDT_WITH_EXPLICIT_SEMA(sema, group, name, args...)
19 * USDT_SEMA_IS_ACTIVE(sema)
20 *
21 * all of which emit a NOP instruction into the instruction stream, and so
22 * have *zero* overhead for the surrounding code. USDTs are identified by
23 * a combination of `group` and `name` identifiers, which is used by external
24 * tracing tooling (tracers) for identifying exact USDTs of interest.
25 *
26 * USDTs can have an associated (2-byte) activity counter (USDT semaphore),
27 * automatically maintained by Linux kernel whenever any correctly written
28 * BPF-based tracer is attached to the USDT. This USDT semaphore can be used
29 * to check whether there is a need to do any extra data collection and
30 * processing for a given USDT (if necessary), and otherwise avoid extra work
31 * for a common case of USDT not being traced ("active").
32 *
33 * See documentation for USDT_WITH_SEMA()/USDT_IS_ACTIVE() or
34 * USDT_WITH_EXPLICIT_SEMA()/USDT_SEMA_IS_ACTIVE() APIs below for details on
35 * working with USDTs with implicitly or explicitly associated
36 * USDT semaphores, respectively.
37 *
38 * There is also some additional data recorded into an auxiliary note
39 * section. The data in the note section describes the operands, in terms of
40 * size and location, used by tracing tooling to know where to find USDT
41 * arguments. Each location is encoded as an assembler operand string.
42 * Tracing tools (bpftrace and BPF-based tracers, systemtap, etc) insert
43 * breakpoints on top of the nop, and decode the location operand-strings,
44 * like an assembler, to find the values being passed.
45 *
46 * The operand strings are selected by the compiler for each operand.
47 * They are constrained by inline-assembler codes.The default is:
48 *
49 * #define USDT_ARG_CONSTRAINT nor
50 *
51 * This is a good default if the operands tend to be integral and
52 * moderate in number (smaller than number of registers). In other
53 * cases, the compiler may report "'asm' requires impossible reload" or
54 * similar. In this case, consider simplifying the macro call (fewer
55 * and simpler operands), reduce optimization, or override the default
56 * constraints string via:
57 *
58 * #define USDT_ARG_CONSTRAINT g
59 * #include <usdt.h>
60 *
61 * For some historical description of USDT v3 format (the one used by this
62 * library and generally recognized and assumed by BPF-based tracing tools)
63 * see [0]. The more formal specification can be found at [1]. Additional
64 * argument constraints information can be found at [2].
65 *
66 * Original SystemTap's sys/sdt.h implementation ([3]) was used as a base for
67 * this USDT library implementation. Current implementation differs *a lot* in
68 * terms of exposed user API and general usability, which was the main goal
69 * and focus of the reimplementation work. Nevertheless, underlying recorded
70 * USDT definitions are fully binary compatible and any USDT-based tooling
71 * should work equally well with USDTs defined by either SystemTap's or this
72 * library's USDT implementation.
73 *
74 * [0] https://ecos.sourceware.org/ml/systemtap/2010-q3/msg00145.html
75 * [1] https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation
76 * [2] https://gcc.gnu.org/onlinedocs/gcc/Constraints.html
77 * [3] https://sourceware.org/git/?p=systemtap.git;a=blob;f=includes/sys/sdt.h
78 */
79#ifndef __USDT_H
80#define __USDT_H
81
82/*
83 * Changelog:
84 *
85 * 0.1.0
86 * -----
87 * - Initial release
88 */
89#define USDT_MAJOR_VERSION 0
90#define USDT_MINOR_VERSION 1
91#define USDT_PATCH_VERSION 0
92
93/* C++20 and C23 added __VA_OPT__ as a standard replacement for non-standard `##__VA_ARGS__` extension */
94#if (defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L) || (defined(__cplusplus) && __cplusplus > 201703L)
95#define __usdt_va_opt 1
96#define __usdt_va_args(...) __VA_OPT__(,) __VA_ARGS__
97#else
98#define __usdt_va_args(...) , ##__VA_ARGS__
99#endif
100
101/*
102 * Trigger USDT with `group`:`name` identifier and pass through `args` as its
103 * arguments. Zero arguments are acceptable as well. No USDT semaphore is
104 * associated with this USDT.
105 *
106 * Such "semaphoreless" USDTs are commonly used when there is no extra data
107 * collection or processing needed to collect and prepare USDT arguments and
108 * they are just available in the surrounding code. USDT() macro will just
109 * record their locations in CPU registers or in memory for tracing tooling to
110 * be able to access them, if necessary.
111 */
112#ifdef __usdt_va_opt
113#define USDT(group, name, ...) \
114 __usdt_probe(group, name, __usdt_sema_none, 0 __VA_OPT__(,) __VA_ARGS__)
115#else
116#define USDT(group, name, ...) \
117 __usdt_probe(group, name, __usdt_sema_none, 0, ##__VA_ARGS__)
118#endif
119
120/*
121 * Trigger USDT with `group`:`name` identifier and pass through `args` as its
122 * arguments. Zero arguments are acceptable as well. USDT also get an
123 * implicitly-defined associated USDT semaphore, which will be "activated" by
124 * tracing tooling and can be used to check whether USDT is being actively
125 * observed.
126 *
127 * USDTs with semaphore are commonly used when there is a need to perform
128 * additional data collection and processing to prepare USDT arguments, which
129 * otherwise might not be necessary for the rest of application logic. In such
130 * case, USDT semaphore can be used to avoid unnecessary extra work. If USDT
131 * is not traced (which is presumed to be a common situation), the associated
132 * USDT semaphore is "inactive", and so there is no need to waste resources to
133 * prepare USDT arguments. Use USDT_IS_ACTIVE(group, name) to check whether
134 * USDT is "active".
135 *
136 * N.B. There is an inherent (albeit short) gap between checking whether USDT
137 * is active and triggering corresponding USDT, in which external tracer can
138 * be attached to an USDT and activate USDT semaphore after the activity check.
139 * If such a race occurs, tracers might miss one USDT execution. Tracers are
140 * expected to accommodate such possibility and this is expected to not be
141 * a problem for applications and tracers.
142 *
143 * N.B. Implicit USDT semaphore defined by USDT_WITH_SEMA() is contained
144 * within a single executable or shared library and is not shared outside
145 * them. I.e., if you use USDT_WITH_SEMA() with the same USDT group and name
146 * identifier across executable and shared library, it will work and won't
147 * conflict, per se, but will define independent USDT semaphores, one for each
148 * shared library/executable in which USDT_WITH_SEMA(group, name) is used.
149 * That is, if you attach to this USDT in one shared library (or executable),
150 * then only USDT semaphore within that shared library (or executable) will be
151 * updated by the kernel, while other libraries (or executable) will not see
152 * activated USDT semaphore. In short, it's best to use unique USDT group:name
153 * identifiers across different shared libraries (and, equivalently, between
154 * executable and shared library). This is advanced consideration and is
155 * rarely (if ever) seen in practice, but just to avoid surprises this is
156 * called out here. (Static libraries become a part of final executable, once
157 * linked by linker, so the above considerations don't apply to them.)
158 */
159#ifdef __usdt_va_opt
160#define USDT_WITH_SEMA(group, name, ...) \
161 __usdt_probe(group, name, \
162 __usdt_sema_implicit, __usdt_sema_name(group, name) \
163 __VA_OPT__(,) __VA_ARGS__)
164#else
165#define USDT_WITH_SEMA(group, name, ...) \
166 __usdt_probe(group, name, \
167 __usdt_sema_implicit, __usdt_sema_name(group, name), \
168 ##__VA_ARGS__)
169#endif
170
171struct usdt_sema { volatile unsigned short active; };
172
173/*
174 * Check if USDT with `group`:`name` identifier is "active" (i.e., whether it
175 * is attached to by external tracing tooling and is actively observed).
176 *
177 * This macro can be used to decide whether any additional and potentially
178 * expensive data collection or processing should be done to pass extra
179 * information into the given USDT. It is assumed that USDT is triggered with
180 * USDT_WITH_SEMA() macro which will implicitly define associated USDT
181 * semaphore. (If one needs more control over USDT semaphore, see
182 * USDT_DEFINE_SEMA() and USDT_WITH_EXPLICIT_SEMA() macros below.)
183 *
184 * N.B. Such checks are necessarily racy and speculative. Between checking
185 * whether USDT is active and triggering the USDT itself, tracer can be
186 * detached with no notification. This race should be extremely rare and worst
187 * case should result in one-time wasted extra data collection and processing.
188 */
189#define USDT_IS_ACTIVE(group, name) ({ \
190 extern struct usdt_sema __usdt_sema_name(group, name) \
191 __usdt_asm_name(__usdt_sema_name(group, name)); \
192 __usdt_sema_implicit(__usdt_sema_name(group, name)); \
193 __usdt_sema_name(group, name).active > 0; \
194})
195
196/*
197 * APIs for working with user-defined explicit USDT semaphores.
198 *
199 * This is a less commonly used advanced API for use cases in which user needs
200 * an explicit control over (potentially shared across multiple USDTs) USDT
201 * semaphore instance. This can be used when there is a group of logically
202 * related USDTs that all need extra data collection and processing whenever
203 * any of a family of related USDTs are "activated" (i.e., traced). In such
204 * a case, all such related USDTs will be associated with the same shared USDT
205 * semaphore defined with USDT_DEFINE_SEMA() and the USDTs themselves will be
206 * triggered with USDT_WITH_EXPLICIT_SEMA() macros, taking an explicit extra
207 * USDT semaphore identifier as an extra parameter.
208 */
209
215#define USDT_SEMA(sema) __usdt_sema_##sema
216
217/*
218 * Define storage for user-defined USDT semaphore `sema`.
219 *
220 * Should be used only once in non-header source file to let compiler allocate
221 * space for the semaphore variable. Just like with any other global variable.
222 *
223 * This macro can be used anywhere where global variable declaration is
224 * allowed. Just like with global variable definitions, there should be only
225 * one definition of user-defined USDT semaphore with given `sema` identifier,
226 * otherwise compiler or linker will complain about duplicate variable
227 * definition.
228 *
229 * For C++, it is allowed to use USDT_DEFINE_SEMA() both in global namespace
230 * and inside namespaces (including nested namespaces). Just make sure that
231 * USDT_DECLARE_SEMA() is placed within the namespace where this semaphore is
232 * referenced, or any of its parent namespaces, so the C++ language-level
233 * identifier is visible to the code that needs to reference the semaphore.
234 * At the lowest layer, USDT semaphores have global naming and visibility
235 * (they have a corresponding `__usdt_sema_<name>` symbol, which can be linked
236 * against from C or C++ code, if necessary). To keep it simple, putting
237 * USDT_DECLARE_SEMA() declarations into global namespaces is the simplest
238 * no-brainer solution. All these aspects are irrelevant for plain C, because
239 * C doesn't have namespaces and everything is always in the global namespace.
240 *
241 * N.B. Due to USDT metadata being recorded in non-allocatable ELF note
242 * section, it has limitations when it comes to relocations, which, in
243 * practice, means that it's not possible to correctly share USDT semaphores
244 * between main executable and shared libraries, or even between multiple
245 * shared libraries. USDT semaphore has to be contained to individual shared
246 * library or executable to avoid unpleasant surprises with half-working USDT
247 * semaphores. We enforce this by marking semaphore ELF symbols as having
248 * a hidden visibility. This is quite an advanced use case and consideration
249 * and for most users this should have no consequences whatsoever.
250 */
251#define USDT_DEFINE_SEMA(sema) \
252 struct usdt_sema __usdt_sema_sec USDT_SEMA(sema) \
253 __usdt_asm_name(USDT_SEMA(sema)) \
254 __attribute__((visibility("hidden"))) = { 0 }
255
256/*
257 * Declare extern reference to user-defined USDT semaphore `sema`.
258 *
259 * Refers to a variable defined in another compilation unit by
260 * USDT_DEFINE_SEMA() and allows to use the same USDT semaphore across
261 * multiple compilation units (i.e., .c and .cpp files).
262 *
263 * See USDT_DEFINE_SEMA() notes above for C++ language usage peculiarities.
264 */
265#define USDT_DECLARE_SEMA(sema) \
266 extern struct usdt_sema USDT_SEMA(sema) __usdt_asm_name(USDT_SEMA(sema))
267
268/*
269 * Check if user-defined USDT semaphore `sema` is "active" (i.e., whether it
270 * is attached to by external tracing tooling and is actively observed).
271 *
272 * This macro can be used to decide whether any additional and potentially
273 * expensive data collection or processing should be done to pass extra
274 * information into USDT(s) associated with USDT semaphore `sema`.
275 *
276 * N.B. Such checks are necessarily racy. Between checking the state of USDT
277 * semaphore and triggering associated USDT(s), the active tracer might attach
278 * or detach. This race should be extremely rare and worst case should result
279 * in one-time missed USDT event or wasted extra data collection and
280 * processing. USDT-using tracers should be written with this in mind and is
281 * not a concern of the application defining USDTs with associated semaphore.
282 */
283#define USDT_SEMA_IS_ACTIVE(sema) (USDT_SEMA(sema).active > 0)
284
285/*
286 * Invoke USDT specified by `group` and `name` identifiers and associate
287 * explicitly user-defined semaphore `sema` with it. Pass through `args` as
288 * USDT arguments. `args` are optional and zero arguments are acceptable.
289 *
290 * Semaphore is defined with the help of USDT_DEFINE_SEMA() macro and can be
291 * checked whether active with USDT_SEMA_IS_ACTIVE().
292 */
293#ifdef __usdt_va_opt
294#define USDT_WITH_EXPLICIT_SEMA(sema, group, name, ...) \
295 __usdt_probe(group, name, __usdt_sema_explicit, USDT_SEMA(sema), ##__VA_ARGS__)
296#else
297#define USDT_WITH_EXPLICIT_SEMA(sema, group, name, ...) \
298 __usdt_probe(group, name, __usdt_sema_explicit, USDT_SEMA(sema) __VA_OPT__(,) __VA_ARGS__)
299#endif
300
301/*
302 * Adjustable implementation aspects
303 */
304#ifndef USDT_ARG_CONSTRAINT
305#if defined __powerpc__
306#define USDT_ARG_CONSTRAINT nZr
307#elif defined __arm__
308#define USDT_ARG_CONSTRAINT g
309#elif defined __loongarch__
310#define USDT_ARG_CONSTRAINT nmr
311#else
312#define USDT_ARG_CONSTRAINT nor
313#endif
314#endif /* USDT_ARG_CONSTRAINT */
315
316#ifndef USDT_NOP
317#if defined(__ia64__) || defined(__s390__) || defined(__s390x__)
318#define USDT_NOP nop 0
319#else
320#define USDT_NOP nop
321#endif
322#endif /* USDT_NOP */
323
324/*
325 * Implementation details
326 */
327/* USDT name for implicitly-defined USDT semaphore, derived from group:name */
328#define __usdt_sema_name(group, name) __usdt_sema_##group##__##name
329/* ELF section into which USDT semaphores are put */
330#define __usdt_sema_sec __attribute__((section(".probes")))
331
332#define __usdt_concat(a, b) a ## b
333#define __usdt_apply(fn, n) __usdt_concat(fn, n)
334
335#ifndef __usdt_nth
336#define __usdt_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, N, ...) N
337#endif
338
339#ifndef __usdt_narg
340#ifdef __usdt_va_opt
341#define __usdt_narg(...) __usdt_nth(_ __VA_OPT__(,) __VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
342#else
343#define __usdt_narg(...) __usdt_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
344#endif
345#endif /* __usdt_narg */
346
347#define __usdt_hash #
348#define __usdt_str_(x) #x
349#define __usdt_str(x) __usdt_str_(x)
350
351#ifndef __usdt_asm_name
352#define __usdt_asm_name(name) __asm__(__usdt_str(name))
353#endif
354
355#define __usdt_asm1(a) __usdt_str(a) "\n"
356#define __usdt_asm2(a,b) __usdt_str(a) "," __usdt_str(b) "\n"
357#define __usdt_asm3(a,b,c) __usdt_str(a) "," __usdt_str(b) "," __usdt_str(c) "\n"
358#define __usdt_asm5(a,b,c,d,e) __usdt_str(a) "," __usdt_str(b) "," __usdt_str(c) "," \
359 __usdt_str(d) "," __usdt_str(e) "\n"
360
361#ifdef __LP64__
362#define __usdt_asm_addr .8byte
363#else
364#define __usdt_asm_addr .4byte
365#endif
366
367#define __usdt_asm_strz_(x) __usdt_asm1(.asciz #x)
368#define __usdt_asm_strz(x) __usdt_asm_strz_(x)
369#define __usdt_asm_str_(x) __usdt_asm1(.ascii #x)
370#define __usdt_asm_str(x) __usdt_asm_str_(x)
371
372/* "semaphoreless" USDT case */
373#ifndef __usdt_sema_none
374#define __usdt_sema_none(sema)
375#endif
376
377/* implicitly defined __usdt_sema__group__name semaphore (using weak symbols) */
378#ifndef __usdt_sema_implicit
379#define __usdt_sema_implicit(sema) \
380 __asm__ __volatile__ ( \
381 __usdt_asm1(.ifndef sema) \
382 __usdt_asm3( .pushsection .probes, "aw", "progbits") \
383 __usdt_asm1( .weak sema) \
384 __usdt_asm1( .hidden sema) \
385 __usdt_asm1( .align 2) \
386 __usdt_asm1(sema:) \
387 __usdt_asm1( .zero 2) \
388 __usdt_asm2( .type sema, @object) \
389 __usdt_asm2( .size sema, 2) \
390 __usdt_asm1( .popsection) \
391 __usdt_asm1(.endif) \
392 );
393#endif
394
395/* externally defined semaphore using USDT_DEFINE_SEMA() and passed explicitly by user */
396#ifndef __usdt_sema_explicit
397#define __usdt_sema_explicit(sema) \
398 __asm__ __volatile__ ("" :: "m" (sema));
399#endif
400
401/* main USDT definition (nop and .note.stapsdt metadata) */
402#define __usdt_probe(group, name, sema_def, sema, ...) do { \
403 sema_def(sema) \
404 __asm__ __volatile__ ( \
405 __usdt_asm1(990: USDT_NOP) \
406 __usdt_asm3( .pushsection .note.stapsdt, "", "note") \
407 __usdt_asm1( .balign 4) \
408 __usdt_asm3( .4byte 992f-991f,994f-993f,3) \
409 __usdt_asm1(991: .asciz "stapsdt") \
410 __usdt_asm1(992: .balign 4) \
411 __usdt_asm1(993: __usdt_asm_addr 990b) \
412 __usdt_asm1( __usdt_asm_addr _.stapsdt.base) \
413 __usdt_asm1( __usdt_asm_addr sema) \
414 __usdt_asm_strz(group) \
415 __usdt_asm_strz(name) \
416 __usdt_asm_args(__VA_ARGS__) \
417 __usdt_asm1( .ascii "\0") \
418 __usdt_asm1(994: .balign 4) \
419 __usdt_asm1( .popsection) \
420 __usdt_asm1(.ifndef _.stapsdt.base) \
421 __usdt_asm5( .pushsection .stapsdt.base,"aG","progbits",.stapsdt.base,comdat)\
422 __usdt_asm1( .weak _.stapsdt.base) \
423 __usdt_asm1( .hidden _.stapsdt.base) \
424 __usdt_asm1(_.stapsdt.base:) \
425 __usdt_asm1( .space 1) \
426 __usdt_asm2( .size _.stapsdt.base, 1) \
427 __usdt_asm1( .popsection) \
428 __usdt_asm1(.endif) \
429 :: __usdt_asm_ops(__VA_ARGS__) \
430 ); \
431} while (0)
432
433/*
434 * NB: gdb PR24541 highlighted an unspecified corner of the sdt.h
435 * operand note format.
436 *
437 * The named register may be a longer or shorter (!) alias for the
438 * storage where the value in question is found. For example, on
439 * i386, 64-bit value may be put in register pairs, and a register
440 * name stored would identify just one of them. Previously, gcc was
441 * asked to emit the %w[id] (16-bit alias of some registers holding
442 * operands), even when a wider 32-bit value was used.
443 *
444 * Bottom line: the byte-width given before the @ sign governs. If
445 * there is a mismatch between that width and that of the named
446 * register, then a sys/sdt.h note consumer may need to employ
447 * architecture-specific heuristics to figure out where the compiler
448 * has actually put the complete value.
449 */
450#if defined(__powerpc__) || defined(__powerpc64__)
451#define __usdt_argref(id) %I[id]%[id]
452#elif defined(__i386__)
453#define __usdt_argref(id) %k[id] /* gcc.gnu.org/PR80115 sourceware.org/PR24541 */
454#else
455#define __usdt_argref(id) %[id]
456#endif
457
458#define __usdt_asm_arg(n) __usdt_asm_str(%c[__usdt_asz##n]) \
459 __usdt_asm1(.ascii "@") \
460 __usdt_asm_str(__usdt_argref(__usdt_aval##n))
461
462#define __usdt_asm_args0 /* no arguments */
463#define __usdt_asm_args1 __usdt_asm_arg(1)
464#define __usdt_asm_args2 __usdt_asm_args1 __usdt_asm1(.ascii " ") __usdt_asm_arg(2)
465#define __usdt_asm_args3 __usdt_asm_args2 __usdt_asm1(.ascii " ") __usdt_asm_arg(3)
466#define __usdt_asm_args4 __usdt_asm_args3 __usdt_asm1(.ascii " ") __usdt_asm_arg(4)
467#define __usdt_asm_args5 __usdt_asm_args4 __usdt_asm1(.ascii " ") __usdt_asm_arg(5)
468#define __usdt_asm_args6 __usdt_asm_args5 __usdt_asm1(.ascii " ") __usdt_asm_arg(6)
469#define __usdt_asm_args7 __usdt_asm_args6 __usdt_asm1(.ascii " ") __usdt_asm_arg(7)
470#define __usdt_asm_args8 __usdt_asm_args7 __usdt_asm1(.ascii " ") __usdt_asm_arg(8)
471#define __usdt_asm_args9 __usdt_asm_args8 __usdt_asm1(.ascii " ") __usdt_asm_arg(9)
472#define __usdt_asm_args10 __usdt_asm_args9 __usdt_asm1(.ascii " ") __usdt_asm_arg(10)
473#define __usdt_asm_args11 __usdt_asm_args10 __usdt_asm1(.ascii " ") __usdt_asm_arg(11)
474#define __usdt_asm_args12 __usdt_asm_args11 __usdt_asm1(.ascii " ") __usdt_asm_arg(12)
475#define __usdt_asm_args(...) __usdt_apply(__usdt_asm_args, __usdt_narg(__VA_ARGS__))
476
477#define __usdt_is_arr(x) (__builtin_classify_type(x) == 14 || __builtin_classify_type(x) == 5)
478#define __usdt_arg_size(x) (__usdt_is_arr(x) ? sizeof(void *) : sizeof(x))
479
480/*
481 * We can't use __builtin_choose_expr() in C++, so fall back to table-based
482 * signedness determination for known types, utilizing templates magic.
483 */
484#ifdef __cplusplus
485
486#define __usdt_is_signed(x) (!__usdt_is_arr(x) && __usdt_t<__typeof(x)>::is_signed)
487
488#include <cstddef>
489
490template<typename T> struct __usdt_t { static const bool is_signed = false; };
491template<typename A> struct __usdt_t<A[]> : public __usdt_t<A *> {};
492template<typename A, size_t N> struct __usdt_t<A[N]> : public __usdt_t<A *> {};
493
494#define __usdt_def_signed(T) \
495template<> struct __usdt_t<T> { static const bool is_signed = true; }; \
496template<> struct __usdt_t<const T> { static const bool is_signed = true; }; \
497template<> struct __usdt_t<volatile T> { static const bool is_signed = true; }; \
498template<> struct __usdt_t<const volatile T> { static const bool is_signed = true; }
499#define __usdt_maybe_signed(T) \
500template<> struct __usdt_t<T> { static const bool is_signed = (T)-1 < (T)1; }; \
501template<> struct __usdt_t<const T> { static const bool is_signed = (T)-1 < (T)1; }; \
502template<> struct __usdt_t<volatile T> { static const bool is_signed = (T)-1 < (T)1; }; \
503template<> struct __usdt_t<const volatile T> { static const bool is_signed = (T)-1 < (T)1; }
504
505__usdt_def_signed(signed char);
506__usdt_def_signed(short);
507__usdt_def_signed(int);
508__usdt_def_signed(long);
509__usdt_def_signed(long long);
510__usdt_maybe_signed(char);
511__usdt_maybe_signed(wchar_t);
512
513#else /* !__cplusplus */
514
515#define __usdt_is_inttype(x) (__builtin_classify_type(x) >= 1 && __builtin_classify_type(x) <= 4)
516#define __usdt_inttype(x) __typeof(__builtin_choose_expr(__usdt_is_inttype(x), (x), 0U))
517#define __usdt_is_signed(x) ((__usdt_inttype(x))-1 < (__usdt_inttype(x))1)
518
519#endif /* __cplusplus */
520
521#define __usdt_asm_op(n, x) \
522 [__usdt_asz##n] "n" ((__usdt_is_signed(x) ? (int)-1 : 1) * (int)__usdt_arg_size(x)), \
523 [__usdt_aval##n] __usdt_str(USDT_ARG_CONSTRAINT)(x)
524
525#define __usdt_asm_ops0() [__usdt_dummy] "g" (0)
526#define __usdt_asm_ops1(x) __usdt_asm_op(1, x)
527#define __usdt_asm_ops2(a,x) __usdt_asm_ops1(a), __usdt_asm_op(2, x)
528#define __usdt_asm_ops3(a,b,x) __usdt_asm_ops2(a,b), __usdt_asm_op(3, x)
529#define __usdt_asm_ops4(a,b,c,x) __usdt_asm_ops3(a,b,c), __usdt_asm_op(4, x)
530#define __usdt_asm_ops5(a,b,c,d,x) __usdt_asm_ops4(a,b,c,d), __usdt_asm_op(5, x)
531#define __usdt_asm_ops6(a,b,c,d,e,x) __usdt_asm_ops5(a,b,c,d,e), __usdt_asm_op(6, x)
532#define __usdt_asm_ops7(a,b,c,d,e,f,x) __usdt_asm_ops6(a,b,c,d,e,f), __usdt_asm_op(7, x)
533#define __usdt_asm_ops8(a,b,c,d,e,f,g,x) __usdt_asm_ops7(a,b,c,d,e,f,g), __usdt_asm_op(8, x)
534#define __usdt_asm_ops9(a,b,c,d,e,f,g,h,x) __usdt_asm_ops8(a,b,c,d,e,f,g,h), __usdt_asm_op(9, x)
535#define __usdt_asm_ops10(a,b,c,d,e,f,g,h,i,x) __usdt_asm_ops9(a,b,c,d,e,f,g,h,i), __usdt_asm_op(10, x)
536#define __usdt_asm_ops11(a,b,c,d,e,f,g,h,i,j,x) __usdt_asm_ops10(a,b,c,d,e,f,g,h,i,j), __usdt_asm_op(11, x)
537#define __usdt_asm_ops12(a,b,c,d,e,f,g,h,i,j,k,x) __usdt_asm_ops11(a,b,c,d,e,f,g,h,i,j,k), __usdt_asm_op(12, x)
538#define __usdt_asm_ops(...) __usdt_apply(__usdt_asm_ops, __usdt_narg(__VA_ARGS__))(__VA_ARGS__)
539
540#endif /* __USDT_H */
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2util_8c_source.html0000644000175000017500000001554715156613443022155 0ustar berndbernd libfuse: fuse-3.17.4/lib/util.c Source File
libfuse
util.c
1#include <stdlib.h>
2#include <errno.h>
3
4#ifndef FUSE_USE_VERSION
5#define FUSE_USE_VERSION (FUSE_MAKE_VERSION(3, 18))
6#endif
7
8#include "util.h"
9#include "fuse_log.h"
10#include "fuse_lowlevel.h"
11#include <stdio.h>
12
13int libfuse_strtol(const char *str, long *res)
14{
15 char *endptr;
16 int base = 10;
17 long val;
18
19 errno = 0;
20
21 if (!str)
22 return -EINVAL;
23
24 val = strtol(str, &endptr, base);
25
26 if (errno)
27 return -errno;
28
29 if (endptr == str || *endptr != '\0')
30 return -EINVAL;
31
32 *res = val;
33 return 0;
34}
35
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2util_8c_source.html0000644000175000017500000002220715156613443022142 0ustar berndbernd libfuse: fuse-3.18.1/lib/util.c Source File
libfuse
util.c
1
2#include "fuse_config.h"
3
4#ifdef HAVE_PTHREAD_SETNAME_NP
5#define _GNU_SOURCE
6#include <pthread.h>
7#endif
8
9#include <errno.h>
10#include <stdlib.h>
11#include <errno.h>
12
13#ifndef FUSE_USE_VERSION
14#define FUSE_USE_VERSION (FUSE_MAKE_VERSION(3, 18))
15#endif
16
17#include "util.h"
18#include "fuse_log.h"
19#include "fuse_lowlevel.h"
20#include <stdio.h>
21
22int libfuse_strtol(const char *str, long *res)
23{
24 char *endptr;
25 int base = 10;
26 long val;
27
28 errno = 0;
29
30 if (!str)
31 return -EINVAL;
32
33 val = strtol(str, &endptr, base);
34
35 if (errno)
36 return -errno;
37
38 if (endptr == str || *endptr != '\0')
39 return -EINVAL;
40
41 *res = val;
42 return 0;
43}
44
45void fuse_set_thread_name(const char *name)
46{
47#ifdef HAVE_PTHREAD_SETNAME_NP
48 pthread_setname_np(pthread_self(), name);
49#else
50 (void)name;
51#endif
52}
53
fuse-3.18.2/doc/html/lib_2util_8c_source.html0000644000175000017500000002202415156613443017763 0ustar berndbernd libfuse: lib/util.c Source File
libfuse
util.c
1
2#include "fuse_config.h"
3
4#ifdef HAVE_PTHREAD_SETNAME_NP
5#define _GNU_SOURCE
6#include <pthread.h>
7#endif
8
9#include <errno.h>
10#include <stdlib.h>
11#include <errno.h>
12
13#ifndef FUSE_USE_VERSION
14#define FUSE_USE_VERSION (FUSE_MAKE_VERSION(3, 18))
15#endif
16
17#include "util.h"
18#include "fuse_log.h"
19#include "fuse_lowlevel.h"
20#include <stdio.h>
21
22int libfuse_strtol(const char *str, long *res)
23{
24 char *endptr;
25 int base = 10;
26 long val;
27
28 errno = 0;
29
30 if (!str)
31 return -EINVAL;
32
33 val = strtol(str, &endptr, base);
34
35 if (errno)
36 return -errno;
37
38 if (endptr == str || *endptr != '\0')
39 return -EINVAL;
40
41 *res = val;
42 return 0;
43}
44
45void fuse_set_thread_name(const char *name)
46{
47#ifdef HAVE_PTHREAD_SETNAME_NP
48 pthread_setname_np(pthread_self(), name);
49#else
50 (void)name;
51#endif
52}
53
fuse-3.18.2/doc/html/fuse-3_817_84_2lib_2util_8h_source.html0000644000175000017500000001714015156613443022151 0ustar berndbernd libfuse: fuse-3.17.4/lib/util.h Source File
libfuse
util.h
1#ifndef FUSE_UTIL_H_
2#define FUSE_UTIL_H_
3
4#include <stdint.h>
5#include <stdbool.h>
6
7#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
8
9#define likely(x) __builtin_expect(!!(x), 1)
10#define unlikely(x) __builtin_expect(!!(x), 0)
11
12struct fuse_conn_info;
13
14int libfuse_strtol(const char *str, long *res);
15
19static inline uint32_t fuse_lower_32_bits(uint64_t nr)
20{
21 return (uint32_t)(nr & 0xffffffff);
22}
23
27static inline uint64_t fuse_higher_32_bits(uint64_t nr)
28{
29 return nr & ~0xffffffffULL;
30}
31
32#ifndef FUSE_VAR_UNUSED
33#define FUSE_VAR_UNUSED(var) (__attribute__((unused)) var)
34#endif
35
36#define container_of(ptr, type, member) \
37 ({ \
38 unsigned long __mptr = (unsigned long)(ptr); \
39 ((type *)(__mptr - offsetof(type, member))); \
40 })
41
42#endif
fuse-3.18.2/doc/html/fuse-3_818_81_2lib_2util_8h_source.html0000644000175000017500000002134415156613443022150 0ustar berndbernd libfuse: fuse-3.18.1/lib/util.h Source File
libfuse
util.h
1#ifndef FUSE_UTIL_H_
2#define FUSE_UTIL_H_
3
4#include <stdint.h>
5#include <stdbool.h>
6
7#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
8
9#define likely(x) __builtin_expect(!!(x), 1)
10#define unlikely(x) __builtin_expect(!!(x), 0)
11
12struct fuse_conn_info;
13
14int libfuse_strtol(const char *str, long *res);
15void fuse_set_thread_name(const char *name);
16
20static inline uint32_t fuse_lower_32_bits(uint64_t nr)
21{
22 return (uint32_t)(nr & 0xffffffff);
23}
24
28static inline uint64_t fuse_higher_32_bits(uint64_t nr)
29{
30 return nr & ~0xffffffffULL;
31}
32
33#ifndef FUSE_VAR_UNUSED
34#define FUSE_VAR_UNUSED __attribute__((__unused__))
35#endif
36
37#define container_of(ptr, type, member) \
38 ({ \
39 unsigned long __mptr = (unsigned long)(ptr); \
40 ((type *)(__mptr - offsetof(type, member))); \
41 })
42
43#if __has_attribute(__fallthrough__)
44#define fallthrough __attribute__((__fallthrough__))
45#else
46#define fallthrough do {} while (0)
47#endif
48
49#endif /* FUSE_UTIL_H_ */
fuse-3.18.2/doc/html/lib_2util_8h_source.html0000644000175000017500000002116115156613443017771 0ustar berndbernd libfuse: lib/util.h Source File
libfuse
util.h
1#ifndef FUSE_UTIL_H_
2#define FUSE_UTIL_H_
3
4#include <stdint.h>
5#include <stdbool.h>
6
7#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
8
9#define likely(x) __builtin_expect(!!(x), 1)
10#define unlikely(x) __builtin_expect(!!(x), 0)
11
12struct fuse_conn_info;
13
14int libfuse_strtol(const char *str, long *res);
15void fuse_set_thread_name(const char *name);
16
20static inline uint32_t fuse_lower_32_bits(uint64_t nr)
21{
22 return (uint32_t)(nr & 0xffffffff);
23}
24
28static inline uint64_t fuse_higher_32_bits(uint64_t nr)
29{
30 return nr & ~0xffffffffULL;
31}
32
33#ifndef FUSE_VAR_UNUSED
34#define FUSE_VAR_UNUSED __attribute__((__unused__))
35#endif
36
37#define container_of(ptr, type, member) \
38 ({ \
39 unsigned long __mptr = (unsigned long)(ptr); \
40 ((type *)(__mptr - offsetof(type, member))); \
41 })
42
43#if __has_attribute(__fallthrough__)
44#define fallthrough __attribute__((__fallthrough__))
45#else
46#define fallthrough do {} while (0)
47#endif
48
49#endif /* FUSE_UTIL_H_ */
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2readdir__inode_8c_source.html0000644000175000017500000002531415156613443024331 0ustar berndbernd libfuse: fuse-3.17.4/test/readdir_inode.c Source File
libfuse
readdir_inode.c
1/*
2 * Prints each directory entry, its inode and d_type as returned by 'readdir'.
3 * Skips '.' and '..' because readdir is not required to return them and
4 * some of our examples don't. However if they are returned, their d_type
5 * should be valid.
6 */
7
8#include <stdio.h>
9#include <string.h>
10#include <sys/types.h>
11#include <dirent.h>
12#include <errno.h>
13
14int main(int argc, char* argv[])
15{
16 DIR* dirp;
17 struct dirent* dent;
18
19 if (argc != 2) {
20 fprintf(stderr, "Usage: readdir_inode dir\n");
21 return 1;
22 }
23
24 dirp = opendir(argv[1]);
25 if (dirp == NULL) {
26 perror("failed to open directory");
27 return 2;
28 }
29
30 errno = 0;
31 dent = readdir(dirp);
32 while (dent != NULL) {
33 if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
34 printf("%llu %d %s\n", (unsigned long long)dent->d_ino,
35 (int)dent->d_type, dent->d_name);
36 if ((long long)dent->d_ino < 0)
37 fprintf(stderr,"%s : bad d_ino %llu\n",
38 dent->d_name, (unsigned long long)dent->d_ino);
39 if ((dent->d_type < 1) || (dent->d_type > 15))
40 fprintf(stderr,"%s : bad d_type %d\n",
41 dent->d_name, (int)dent->d_type);
42 } else {
43 if (dent->d_type != DT_DIR)
44 fprintf(stderr,"%s : bad d_type %d\n",
45 dent->d_name, (int)dent->d_type);
46 }
47 dent = readdir(dirp);
48 }
49 if (errno != 0) {
50 perror("failed to read directory entry");
51 return 3;
52 }
53
54 closedir(dirp);
55
56 return 0;
57}
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2readdir__inode_8c_source.html0000644000175000017500000002531415156613443024327 0ustar berndbernd libfuse: fuse-3.18.1/test/readdir_inode.c Source File
libfuse
readdir_inode.c
1/*
2 * Prints each directory entry, its inode and d_type as returned by 'readdir'.
3 * Skips '.' and '..' because readdir is not required to return them and
4 * some of our examples don't. However if they are returned, their d_type
5 * should be valid.
6 */
7
8#include <stdio.h>
9#include <string.h>
10#include <sys/types.h>
11#include <dirent.h>
12#include <errno.h>
13
14int main(int argc, char* argv[])
15{
16 DIR* dirp;
17 struct dirent* dent;
18
19 if (argc != 2) {
20 fprintf(stderr, "Usage: readdir_inode dir\n");
21 return 1;
22 }
23
24 dirp = opendir(argv[1]);
25 if (dirp == NULL) {
26 perror("failed to open directory");
27 return 2;
28 }
29
30 errno = 0;
31 dent = readdir(dirp);
32 while (dent != NULL) {
33 if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
34 printf("%llu %d %s\n", (unsigned long long)dent->d_ino,
35 (int)dent->d_type, dent->d_name);
36 if ((long long)dent->d_ino < 0)
37 fprintf(stderr,"%s : bad d_ino %llu\n",
38 dent->d_name, (unsigned long long)dent->d_ino);
39 if ((dent->d_type < 1) || (dent->d_type > 15))
40 fprintf(stderr,"%s : bad d_type %d\n",
41 dent->d_name, (int)dent->d_type);
42 } else {
43 if (dent->d_type != DT_DIR)
44 fprintf(stderr,"%s : bad d_type %d\n",
45 dent->d_name, (int)dent->d_type);
46 }
47 dent = readdir(dirp);
48 }
49 if (errno != 0) {
50 perror("failed to read directory entry");
51 return 3;
52 }
53
54 closedir(dirp);
55
56 return 0;
57}
fuse-3.18.2/doc/html/test_2readdir__inode_8c_source.html0000644000175000017500000002513115156613443022150 0ustar berndbernd libfuse: test/readdir_inode.c Source File
libfuse
readdir_inode.c
1/*
2 * Prints each directory entry, its inode and d_type as returned by 'readdir'.
3 * Skips '.' and '..' because readdir is not required to return them and
4 * some of our examples don't. However if they are returned, their d_type
5 * should be valid.
6 */
7
8#include <stdio.h>
9#include <string.h>
10#include <sys/types.h>
11#include <dirent.h>
12#include <errno.h>
13
14int main(int argc, char* argv[])
15{
16 DIR* dirp;
17 struct dirent* dent;
18
19 if (argc != 2) {
20 fprintf(stderr, "Usage: readdir_inode dir\n");
21 return 1;
22 }
23
24 dirp = opendir(argv[1]);
25 if (dirp == NULL) {
26 perror("failed to open directory");
27 return 2;
28 }
29
30 errno = 0;
31 dent = readdir(dirp);
32 while (dent != NULL) {
33 if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
34 printf("%llu %d %s\n", (unsigned long long)dent->d_ino,
35 (int)dent->d_type, dent->d_name);
36 if ((long long)dent->d_ino < 0)
37 fprintf(stderr,"%s : bad d_ino %llu\n",
38 dent->d_name, (unsigned long long)dent->d_ino);
39 if ((dent->d_type < 1) || (dent->d_type > 15))
40 fprintf(stderr,"%s : bad d_type %d\n",
41 dent->d_name, (int)dent->d_type);
42 } else {
43 if (dent->d_type != DT_DIR)
44 fprintf(stderr,"%s : bad d_type %d\n",
45 dent->d_name, (int)dent->d_type);
46 }
47 dent = readdir(dirp);
48 }
49 if (errno != 0) {
50 perror("failed to read directory entry");
51 return 3;
52 }
53
54 closedir(dirp);
55
56 return 0;
57}
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2release__unlink__race_8c_source.html0000644000175000017500000005417415156613443025700 0ustar berndbernd libfuse: fuse-3.17.4/test/release_unlink_race.c Source File
libfuse
release_unlink_race.c
1/*
2 This program can be distributed under the terms of the GNU GPLv2.
3 See the file COPYING.
4*/
5
6#define FUSE_USE_VERSION 31
7
8#define _GNU_SOURCE
9
10#include <fuse.h>
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <errno.h>
16
17static void *xmp_init(struct fuse_conn_info *conn,
18 struct fuse_config *cfg)
19{
20 (void) conn;
21
22 cfg->use_ino = 1;
23 cfg->nullpath_ok = 1;
24 cfg->entry_timeout = 0;
25 cfg->attr_timeout = 0;
26 cfg->negative_timeout = 0;
27
28 return NULL;
29}
30
31static int xmp_getattr(const char *path, struct stat *stbuf,
32 struct fuse_file_info *fi)
33{
34 int res;
35
36 (void) path;
37
38 if(fi)
39 res = fstat(fi->fh, stbuf);
40 else
41 res = lstat(path, stbuf);
42 if (res == -1)
43 return -errno;
44
45 return 0;
46}
47
48static int xmp_unlink(const char *path)
49{
50 int res;
51
52 res = unlink(path);
53 if (res == -1)
54 return -errno;
55
56 return 0;
57}
58
59static int xmp_rename(const char *from, const char *to, unsigned int flags)
60{
61 int res;
62
63 if (flags)
64 return -EINVAL;
65
66 if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
67
68 res = rename(from, to);
69 if (res == -1)
70 return -errno;
71
72 return 0;
73}
74
75static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
76{
77 int fd;
78
79 fd = open(path, fi->flags, mode);
80 if (fd == -1)
81 return -errno;
82
83 fi->fh = fd;
84 return 0;
85}
86
87static int xmp_release(const char *path, struct fuse_file_info *fi)
88{
89 (void) path;
90
91 if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
92
93 close(fi->fh);
94
95 return 0;
96}
97
98static const struct fuse_operations xmp_oper = {
99 .init = xmp_init,
100 .getattr = xmp_getattr,
101 .unlink = xmp_unlink,
102 .rename = xmp_rename,
103 .create = xmp_create,
104 .release = xmp_release,
105};
106
107int main(int argc, char *argv[])
108{
109 umask(0);
110 return fuse_main(argc, argv, &xmp_oper, NULL);
111}
int32_t nullpath_ok
Definition fuse.h:273
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2release__unlink__race_8c_source.html0000644000175000017500000005417515156613443025677 0ustar berndbernd libfuse: fuse-3.18.1/test/release_unlink_race.c Source File
libfuse
release_unlink_race.c
1/*
2 This program can be distributed under the terms of the GNU GPLv2.
3 See the file GPL2.txt.
4*/
5
6#define FUSE_USE_VERSION 31
7
8#define _GNU_SOURCE
9
10#include <fuse.h>
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <errno.h>
16
17static void *xmp_init(struct fuse_conn_info *conn,
18 struct fuse_config *cfg)
19{
20 (void) conn;
21
22 cfg->use_ino = 1;
23 cfg->nullpath_ok = 1;
24 cfg->entry_timeout = 0;
25 cfg->attr_timeout = 0;
26 cfg->negative_timeout = 0;
27
28 return NULL;
29}
30
31static int xmp_getattr(const char *path, struct stat *stbuf,
32 struct fuse_file_info *fi)
33{
34 int res;
35
36 (void) path;
37
38 if(fi)
39 res = fstat(fi->fh, stbuf);
40 else
41 res = lstat(path, stbuf);
42 if (res == -1)
43 return -errno;
44
45 return 0;
46}
47
48static int xmp_unlink(const char *path)
49{
50 int res;
51
52 res = unlink(path);
53 if (res == -1)
54 return -errno;
55
56 return 0;
57}
58
59static int xmp_rename(const char *from, const char *to, unsigned int flags)
60{
61 int res;
62
63 if (flags)
64 return -EINVAL;
65
66 if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
67
68 res = rename(from, to);
69 if (res == -1)
70 return -errno;
71
72 return 0;
73}
74
75static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
76{
77 int fd;
78
79 fd = open(path, fi->flags, mode);
80 if (fd == -1)
81 return -errno;
82
83 fi->fh = fd;
84 return 0;
85}
86
87static int xmp_release(const char *path, struct fuse_file_info *fi)
88{
89 (void) path;
90
91 if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
92
93 close(fi->fh);
94
95 return 0;
96}
97
98static const struct fuse_operations xmp_oper = {
99 .init = xmp_init,
100 .getattr = xmp_getattr,
101 .unlink = xmp_unlink,
102 .rename = xmp_rename,
103 .create = xmp_create,
104 .release = xmp_release,
105};
106
107int main(int argc, char *argv[])
108{
109 umask(0);
110 return fuse_main(argc, argv, &xmp_oper, NULL);
111}
int32_t nullpath_ok
Definition fuse.h:273
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/test_2release__unlink__race_8c_source.html0000644000175000017500000005401215156613443023511 0ustar berndbernd libfuse: test/release_unlink_race.c Source File
libfuse
release_unlink_race.c
1/*
2 This program can be distributed under the terms of the GNU GPLv2.
3 See the file GPL2.txt.
4*/
5
6#define FUSE_USE_VERSION 31
7
8#define _GNU_SOURCE
9
10#include <fuse.h>
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <errno.h>
16
17static void *xmp_init(struct fuse_conn_info *conn,
18 struct fuse_config *cfg)
19{
20 (void) conn;
21
22 cfg->use_ino = 1;
23 cfg->nullpath_ok = 1;
24 cfg->entry_timeout = 0;
25 cfg->attr_timeout = 0;
26 cfg->negative_timeout = 0;
27
28 return NULL;
29}
30
31static int xmp_getattr(const char *path, struct stat *stbuf,
32 struct fuse_file_info *fi)
33{
34 int res;
35
36 (void) path;
37
38 if(fi)
39 res = fstat(fi->fh, stbuf);
40 else
41 res = lstat(path, stbuf);
42 if (res == -1)
43 return -errno;
44
45 return 0;
46}
47
48static int xmp_unlink(const char *path)
49{
50 int res;
51
52 res = unlink(path);
53 if (res == -1)
54 return -errno;
55
56 return 0;
57}
58
59static int xmp_rename(const char *from, const char *to, unsigned int flags)
60{
61 int res;
62
63 if (flags)
64 return -EINVAL;
65
66 if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
67
68 res = rename(from, to);
69 if (res == -1)
70 return -errno;
71
72 return 0;
73}
74
75static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
76{
77 int fd;
78
79 fd = open(path, fi->flags, mode);
80 if (fd == -1)
81 return -errno;
82
83 fi->fh = fd;
84 return 0;
85}
86
87static int xmp_release(const char *path, struct fuse_file_info *fi)
88{
89 (void) path;
90
91 if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
92
93 close(fi->fh);
94
95 return 0;
96}
97
98static const struct fuse_operations xmp_oper = {
99 .init = xmp_init,
100 .getattr = xmp_getattr,
101 .unlink = xmp_unlink,
102 .rename = xmp_rename,
103 .create = xmp_create,
104 .release = xmp_release,
105};
106
107int main(int argc, char *argv[])
108{
109 umask(0);
110 return fuse_main(argc, argv, &xmp_oper, NULL);
111}
int32_t nullpath_ok
Definition fuse.h:273
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2stracedecode_8c_source.html0000644000175000017500000010622215156613443024025 0ustar berndbernd libfuse: fuse-3.17.4/test/stracedecode.c Source File
libfuse
stracedecode.c
1#include <stdio.h>
2#include <string.h>
3#include "fuse_kernel.h"
4
5static struct {
6 const char *name;
7} fuse_ll_ops[] = {
8 [FUSE_LOOKUP] = { "LOOKUP" },
9 [FUSE_FORGET] = { "FORGET" },
10 [FUSE_GETATTR] = { "GETATTR" },
11 [FUSE_SETATTR] = { "SETATTR" },
12 [FUSE_READLINK] = { "READLINK" },
13 [FUSE_SYMLINK] = { "SYMLINK" },
14 [FUSE_MKNOD] = { "MKNOD" },
15 [FUSE_MKDIR] = { "MKDIR" },
16 [FUSE_UNLINK] = { "UNLINK" },
17 [FUSE_RMDIR] = { "RMDIR" },
18 [FUSE_RENAME] = { "RENAME" },
19 [FUSE_LINK] = { "LINK" },
20 [FUSE_OPEN] = { "OPEN" },
21 [FUSE_READ] = { "READ" },
22 [FUSE_WRITE] = { "WRITE" },
23 [FUSE_STATFS] = { "STATFS" },
24 [FUSE_RELEASE] = { "RELEASE" },
25 [FUSE_FSYNC] = { "FSYNC" },
26 [FUSE_SETXATTR] = { "SETXATTR" },
27 [FUSE_GETXATTR] = { "GETXATTR" },
28 [FUSE_LISTXATTR] = { "LISTXATTR" },
29 [FUSE_REMOVEXATTR] = { "REMOVEXATTR" },
30 [FUSE_FLUSH] = { "FLUSH" },
31 [FUSE_INIT] = { "INIT" },
32 [FUSE_OPENDIR] = { "OPENDIR" },
33 [FUSE_READDIR] = { "READDIR" },
34 [FUSE_RELEASEDIR] = { "RELEASEDIR" },
35 [FUSE_FSYNCDIR] = { "FSYNCDIR" },
36 [FUSE_GETLK] = { "GETLK" },
37 [FUSE_SETLK] = { "SETLK" },
38 [FUSE_SETLKW] = { "SETLKW" },
39 [FUSE_ACCESS] = { "ACCESS" },
40 [FUSE_CREATE] = { "CREATE" },
41 [FUSE_TMPFILE] = { "TMPFILE" },
42 [FUSE_INTERRUPT] = { "INTERRUPT" },
43 [FUSE_BMAP] = { "BMAP" },
44 [FUSE_DESTROY] = { "DESTROY" },
45 [FUSE_READDIRPLUS] = { "READDIRPLUS" },
46};
47
48#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
49
50static const char *opname(enum fuse_opcode opcode)
51{
52 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
53 return "???";
54 else
55 return fuse_ll_ops[opcode].name;
56}
57
58
59static void process_buf(int dir, char *buf, int len)
60{
61 static unsigned long long prevuniq = -1;
62 static int prevopcode;
63
64 if (!dir) {
65 struct fuse_in_header *in = (struct fuse_in_header *) buf;
66 buf += sizeof(struct fuse_in_header);
67
68 printf("unique: %llu, opcode: %s (%i), nodeid: %lu, len: %i, insize: %i\n",
69 (unsigned long long) in->unique,
70 opname((enum fuse_opcode) in->opcode), in->opcode,
71 (unsigned long) in->nodeid, in->len, len);
72
73 switch (in->opcode) {
74 case FUSE_READ: {
75 struct fuse_read_in *arg = (struct fuse_read_in *) buf;
76 printf("-READ fh:%llu off:%llu siz:%u rfl:%u own:%llu fl:%u\n",
77 arg->fh, arg->offset, arg->size, arg->read_flags,
78 arg->lock_owner, arg->flags);
79 break;
80 }
81 case FUSE_WRITE: {
82 struct fuse_write_in *arg = (struct fuse_write_in *) buf;
83 printf("-WRITE fh:%llu off:%llu siz:%u wfl:%u own:%llu fl:%u\n",
84 arg->fh, arg->offset, arg->size, arg->write_flags,
85 arg->lock_owner, arg->flags);
86 break;
87 }
88 }
89 prevuniq = in->unique;
90 prevopcode = in->opcode;
91 } else {
92 struct fuse_out_header *out = (struct fuse_out_header *) buf;
93 buf += sizeof(struct fuse_out_header);
94
95 printf(" unique: %llu, error: %i (%s), len: %i, outsize: %i\n",
96 (unsigned long long) out->unique, out->error,
97 strerror(-out->error), out->len, len);
98
99 if (out->unique == prevuniq) {
100 switch (prevopcode) {
101 case FUSE_GETATTR: {
102 struct fuse_attr_out *arg = (struct fuse_attr_out *) buf;
103 printf("+ATTR v:%llu.%09u i:%llu s:%llu b:%llu\n",
104 arg->attr_valid, arg->attr_valid_nsec,
105 arg->attr.ino, arg->attr.size, arg->attr.blocks);
106 break;
107 }
108 case FUSE_LOOKUP: {
109 struct fuse_entry_out *arg = (struct fuse_entry_out *) buf;
110 printf("+ENTRY nodeid:%llu v:%llu.%09u i:%llu s:%llu b:%llu\n",
111 arg->nodeid, arg->attr_valid, arg->attr_valid_nsec,
112 arg->attr.ino, arg->attr.size, arg->attr.blocks);
113 break;
114 }
115 }
116 }
117 }
118
119}
120
121int main(void)
122{
123 FILE *in = stdin;
124 while (1) {
125 int dir;
126 int res;
127 char buf[1048576];
128 unsigned len = 0;
129
130 memset(buf, 0, sizeof(buf));
131 while (1) {
132 char str[32];
133
134 res = fscanf(in, "%30s", str);
135 if (res != 1 && feof(in))
136 return 0;
137
138 if (res == 0)
139 continue;
140
141 if (strncmp(str, "read(", 5) == 0) {
142 dir = 0;
143 break;
144 } else if (strncmp(str, "writev(", 7) == 0) {
145 dir = 1;
146 break;
147 }
148 }
149
150 while (1) {
151 int c = getc(in);
152 if (c == '"') {
153 while (1) {
154 int val;
155
156 c = getc(in);
157 if (c == EOF) {
158 fprintf(stderr, "eof in string\n");
159 break;
160 }
161 if (c == '\n') {
162 fprintf(stderr, "eol in string\n");
163 break;
164 }
165 if (c == '"')
166 break;
167 if (c != '\\') {
168 val = c;
169 } else {
170 c = getc(in);
171 switch (c) {
172 case 'n': val = '\n'; break;
173 case 'r': val = '\r'; break;
174 case 't': val = '\t'; break;
175 case '"': val = '"'; break;
176 case '\\': val = '\\'; break;
177 case 'x':
178 res = scanf("%x", &val);
179 if (res != 1) {
180 fprintf(stderr, "parse error\n");
181 continue;
182 }
183 break;
184 default:
185 fprintf(stderr, "unknown sequence: '\\%c'\n", c);
186 continue;
187 }
188 }
189 buf[len++] = val;
190 }
191 }
192 if (c == '\n')
193 break;
194 }
195 process_buf(dir, buf, len);
196 memset(buf, 0, len);
197 len = 0;
198 }
199}
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2stracedecode_8c_source.html0000644000175000017500000010622215156613443024023 0ustar berndbernd libfuse: fuse-3.18.1/test/stracedecode.c Source File
libfuse
stracedecode.c
1#include <stdio.h>
2#include <string.h>
3#include "fuse_kernel.h"
4
5static struct {
6 const char *name;
7} fuse_ll_ops[] = {
8 [FUSE_LOOKUP] = { "LOOKUP" },
9 [FUSE_FORGET] = { "FORGET" },
10 [FUSE_GETATTR] = { "GETATTR" },
11 [FUSE_SETATTR] = { "SETATTR" },
12 [FUSE_READLINK] = { "READLINK" },
13 [FUSE_SYMLINK] = { "SYMLINK" },
14 [FUSE_MKNOD] = { "MKNOD" },
15 [FUSE_MKDIR] = { "MKDIR" },
16 [FUSE_UNLINK] = { "UNLINK" },
17 [FUSE_RMDIR] = { "RMDIR" },
18 [FUSE_RENAME] = { "RENAME" },
19 [FUSE_LINK] = { "LINK" },
20 [FUSE_OPEN] = { "OPEN" },
21 [FUSE_READ] = { "READ" },
22 [FUSE_WRITE] = { "WRITE" },
23 [FUSE_STATFS] = { "STATFS" },
24 [FUSE_RELEASE] = { "RELEASE" },
25 [FUSE_FSYNC] = { "FSYNC" },
26 [FUSE_SETXATTR] = { "SETXATTR" },
27 [FUSE_GETXATTR] = { "GETXATTR" },
28 [FUSE_LISTXATTR] = { "LISTXATTR" },
29 [FUSE_REMOVEXATTR] = { "REMOVEXATTR" },
30 [FUSE_FLUSH] = { "FLUSH" },
31 [FUSE_INIT] = { "INIT" },
32 [FUSE_OPENDIR] = { "OPENDIR" },
33 [FUSE_READDIR] = { "READDIR" },
34 [FUSE_RELEASEDIR] = { "RELEASEDIR" },
35 [FUSE_FSYNCDIR] = { "FSYNCDIR" },
36 [FUSE_GETLK] = { "GETLK" },
37 [FUSE_SETLK] = { "SETLK" },
38 [FUSE_SETLKW] = { "SETLKW" },
39 [FUSE_ACCESS] = { "ACCESS" },
40 [FUSE_CREATE] = { "CREATE" },
41 [FUSE_TMPFILE] = { "TMPFILE" },
42 [FUSE_INTERRUPT] = { "INTERRUPT" },
43 [FUSE_BMAP] = { "BMAP" },
44 [FUSE_DESTROY] = { "DESTROY" },
45 [FUSE_READDIRPLUS] = { "READDIRPLUS" },
46};
47
48#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
49
50static const char *opname(enum fuse_opcode opcode)
51{
52 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
53 return "???";
54 else
55 return fuse_ll_ops[opcode].name;
56}
57
58
59static void process_buf(int dir, char *buf, int len)
60{
61 static unsigned long long prevuniq = -1;
62 static int prevopcode;
63
64 if (!dir) {
65 struct fuse_in_header *in = (struct fuse_in_header *) buf;
66 buf += sizeof(struct fuse_in_header);
67
68 printf("unique: %llu, opcode: %s (%i), nodeid: %lu, len: %i, insize: %i\n",
69 (unsigned long long) in->unique,
70 opname((enum fuse_opcode) in->opcode), in->opcode,
71 (unsigned long) in->nodeid, in->len, len);
72
73 switch (in->opcode) {
74 case FUSE_READ: {
75 struct fuse_read_in *arg = (struct fuse_read_in *) buf;
76 printf("-READ fh:%llu off:%llu siz:%u rfl:%u own:%llu fl:%u\n",
77 arg->fh, arg->offset, arg->size, arg->read_flags,
78 arg->lock_owner, arg->flags);
79 break;
80 }
81 case FUSE_WRITE: {
82 struct fuse_write_in *arg = (struct fuse_write_in *) buf;
83 printf("-WRITE fh:%llu off:%llu siz:%u wfl:%u own:%llu fl:%u\n",
84 arg->fh, arg->offset, arg->size, arg->write_flags,
85 arg->lock_owner, arg->flags);
86 break;
87 }
88 }
89 prevuniq = in->unique;
90 prevopcode = in->opcode;
91 } else {
92 struct fuse_out_header *out = (struct fuse_out_header *) buf;
93 buf += sizeof(struct fuse_out_header);
94
95 printf(" unique: %llu, error: %i (%s), len: %i, outsize: %i\n",
96 (unsigned long long) out->unique, out->error,
97 strerror(-out->error), out->len, len);
98
99 if (out->unique == prevuniq) {
100 switch (prevopcode) {
101 case FUSE_GETATTR: {
102 struct fuse_attr_out *arg = (struct fuse_attr_out *) buf;
103 printf("+ATTR v:%llu.%09u i:%llu s:%llu b:%llu\n",
104 arg->attr_valid, arg->attr_valid_nsec,
105 arg->attr.ino, arg->attr.size, arg->attr.blocks);
106 break;
107 }
108 case FUSE_LOOKUP: {
109 struct fuse_entry_out *arg = (struct fuse_entry_out *) buf;
110 printf("+ENTRY nodeid:%llu v:%llu.%09u i:%llu s:%llu b:%llu\n",
111 arg->nodeid, arg->attr_valid, arg->attr_valid_nsec,
112 arg->attr.ino, arg->attr.size, arg->attr.blocks);
113 break;
114 }
115 }
116 }
117 }
118
119}
120
121int main(void)
122{
123 FILE *in = stdin;
124 while (1) {
125 int dir;
126 int res;
127 char buf[1048576];
128 unsigned len = 0;
129
130 memset(buf, 0, sizeof(buf));
131 while (1) {
132 char str[32];
133
134 res = fscanf(in, "%30s", str);
135 if (res != 1 && feof(in))
136 return 0;
137
138 if (res == 0)
139 continue;
140
141 if (strncmp(str, "read(", 5) == 0) {
142 dir = 0;
143 break;
144 } else if (strncmp(str, "writev(", 7) == 0) {
145 dir = 1;
146 break;
147 }
148 }
149
150 while (1) {
151 int c = getc(in);
152 if (c == '"') {
153 while (1) {
154 int val;
155
156 c = getc(in);
157 if (c == EOF) {
158 fprintf(stderr, "eof in string\n");
159 break;
160 }
161 if (c == '\n') {
162 fprintf(stderr, "eol in string\n");
163 break;
164 }
165 if (c == '"')
166 break;
167 if (c != '\\') {
168 val = c;
169 } else {
170 c = getc(in);
171 switch (c) {
172 case 'n': val = '\n'; break;
173 case 'r': val = '\r'; break;
174 case 't': val = '\t'; break;
175 case '"': val = '"'; break;
176 case '\\': val = '\\'; break;
177 case 'x':
178 res = scanf("%x", &val);
179 if (res != 1) {
180 fprintf(stderr, "parse error\n");
181 continue;
182 }
183 break;
184 default:
185 fprintf(stderr, "unknown sequence: '\\%c'\n", c);
186 continue;
187 }
188 }
189 buf[len++] = val;
190 }
191 }
192 if (c == '\n')
193 break;
194 }
195 process_buf(dir, buf, len);
196 memset(buf, 0, len);
197 len = 0;
198 }
199}
fuse-3.18.2/doc/html/test_2stracedecode_8c_source.html0000644000175000017500000010603715156613443021653 0ustar berndbernd libfuse: test/stracedecode.c Source File
libfuse
stracedecode.c
1#include <stdio.h>
2#include <string.h>
3#include "fuse_kernel.h"
4
5static struct {
6 const char *name;
7} fuse_ll_ops[] = {
8 [FUSE_LOOKUP] = { "LOOKUP" },
9 [FUSE_FORGET] = { "FORGET" },
10 [FUSE_GETATTR] = { "GETATTR" },
11 [FUSE_SETATTR] = { "SETATTR" },
12 [FUSE_READLINK] = { "READLINK" },
13 [FUSE_SYMLINK] = { "SYMLINK" },
14 [FUSE_MKNOD] = { "MKNOD" },
15 [FUSE_MKDIR] = { "MKDIR" },
16 [FUSE_UNLINK] = { "UNLINK" },
17 [FUSE_RMDIR] = { "RMDIR" },
18 [FUSE_RENAME] = { "RENAME" },
19 [FUSE_LINK] = { "LINK" },
20 [FUSE_OPEN] = { "OPEN" },
21 [FUSE_READ] = { "READ" },
22 [FUSE_WRITE] = { "WRITE" },
23 [FUSE_STATFS] = { "STATFS" },
24 [FUSE_RELEASE] = { "RELEASE" },
25 [FUSE_FSYNC] = { "FSYNC" },
26 [FUSE_SETXATTR] = { "SETXATTR" },
27 [FUSE_GETXATTR] = { "GETXATTR" },
28 [FUSE_LISTXATTR] = { "LISTXATTR" },
29 [FUSE_REMOVEXATTR] = { "REMOVEXATTR" },
30 [FUSE_FLUSH] = { "FLUSH" },
31 [FUSE_INIT] = { "INIT" },
32 [FUSE_OPENDIR] = { "OPENDIR" },
33 [FUSE_READDIR] = { "READDIR" },
34 [FUSE_RELEASEDIR] = { "RELEASEDIR" },
35 [FUSE_FSYNCDIR] = { "FSYNCDIR" },
36 [FUSE_GETLK] = { "GETLK" },
37 [FUSE_SETLK] = { "SETLK" },
38 [FUSE_SETLKW] = { "SETLKW" },
39 [FUSE_ACCESS] = { "ACCESS" },
40 [FUSE_CREATE] = { "CREATE" },
41 [FUSE_TMPFILE] = { "TMPFILE" },
42 [FUSE_INTERRUPT] = { "INTERRUPT" },
43 [FUSE_BMAP] = { "BMAP" },
44 [FUSE_DESTROY] = { "DESTROY" },
45 [FUSE_READDIRPLUS] = { "READDIRPLUS" },
46};
47
48#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
49
50static const char *opname(enum fuse_opcode opcode)
51{
52 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
53 return "???";
54 else
55 return fuse_ll_ops[opcode].name;
56}
57
58
59static void process_buf(int dir, char *buf, int len)
60{
61 static unsigned long long prevuniq = -1;
62 static int prevopcode;
63
64 if (!dir) {
65 struct fuse_in_header *in = (struct fuse_in_header *) buf;
66 buf += sizeof(struct fuse_in_header);
67
68 printf("unique: %llu, opcode: %s (%i), nodeid: %lu, len: %i, insize: %i\n",
69 (unsigned long long) in->unique,
70 opname((enum fuse_opcode) in->opcode), in->opcode,
71 (unsigned long) in->nodeid, in->len, len);
72
73 switch (in->opcode) {
74 case FUSE_READ: {
75 struct fuse_read_in *arg = (struct fuse_read_in *) buf;
76 printf("-READ fh:%llu off:%llu siz:%u rfl:%u own:%llu fl:%u\n",
77 arg->fh, arg->offset, arg->size, arg->read_flags,
78 arg->lock_owner, arg->flags);
79 break;
80 }
81 case FUSE_WRITE: {
82 struct fuse_write_in *arg = (struct fuse_write_in *) buf;
83 printf("-WRITE fh:%llu off:%llu siz:%u wfl:%u own:%llu fl:%u\n",
84 arg->fh, arg->offset, arg->size, arg->write_flags,
85 arg->lock_owner, arg->flags);
86 break;
87 }
88 }
89 prevuniq = in->unique;
90 prevopcode = in->opcode;
91 } else {
92 struct fuse_out_header *out = (struct fuse_out_header *) buf;
93 buf += sizeof(struct fuse_out_header);
94
95 printf(" unique: %llu, error: %i (%s), len: %i, outsize: %i\n",
96 (unsigned long long) out->unique, out->error,
97 strerror(-out->error), out->len, len);
98
99 if (out->unique == prevuniq) {
100 switch (prevopcode) {
101 case FUSE_GETATTR: {
102 struct fuse_attr_out *arg = (struct fuse_attr_out *) buf;
103 printf("+ATTR v:%llu.%09u i:%llu s:%llu b:%llu\n",
104 arg->attr_valid, arg->attr_valid_nsec,
105 arg->attr.ino, arg->attr.size, arg->attr.blocks);
106 break;
107 }
108 case FUSE_LOOKUP: {
109 struct fuse_entry_out *arg = (struct fuse_entry_out *) buf;
110 printf("+ENTRY nodeid:%llu v:%llu.%09u i:%llu s:%llu b:%llu\n",
111 arg->nodeid, arg->attr_valid, arg->attr_valid_nsec,
112 arg->attr.ino, arg->attr.size, arg->attr.blocks);
113 break;
114 }
115 }
116 }
117 }
118
119}
120
121int main(void)
122{
123 FILE *in = stdin;
124 while (1) {
125 int dir;
126 int res;
127 char buf[1048576];
128 unsigned len = 0;
129
130 memset(buf, 0, sizeof(buf));
131 while (1) {
132 char str[32];
133
134 res = fscanf(in, "%30s", str);
135 if (res != 1 && feof(in))
136 return 0;
137
138 if (res == 0)
139 continue;
140
141 if (strncmp(str, "read(", 5) == 0) {
142 dir = 0;
143 break;
144 } else if (strncmp(str, "writev(", 7) == 0) {
145 dir = 1;
146 break;
147 }
148 }
149
150 while (1) {
151 int c = getc(in);
152 if (c == '"') {
153 while (1) {
154 int val;
155
156 c = getc(in);
157 if (c == EOF) {
158 fprintf(stderr, "eof in string\n");
159 break;
160 }
161 if (c == '\n') {
162 fprintf(stderr, "eol in string\n");
163 break;
164 }
165 if (c == '"')
166 break;
167 if (c != '\\') {
168 val = c;
169 } else {
170 c = getc(in);
171 switch (c) {
172 case 'n': val = '\n'; break;
173 case 'r': val = '\r'; break;
174 case 't': val = '\t'; break;
175 case '"': val = '"'; break;
176 case '\\': val = '\\'; break;
177 case 'x':
178 res = scanf("%x", &val);
179 if (res != 1) {
180 fprintf(stderr, "parse error\n");
181 continue;
182 }
183 break;
184 default:
185 fprintf(stderr, "unknown sequence: '\\%c'\n", c);
186 continue;
187 }
188 }
189 buf[len++] = val;
190 }
191 }
192 if (c == '\n')
193 break;
194 }
195 process_buf(dir, buf, len);
196 memset(buf, 0, len);
197 len = 0;
198 }
199}
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2test__abi_8c_source.html0000644000175000017500000001276515156613443023341 0ustar berndbernd libfuse: fuse-3.17.4/test/test_abi.c Source File
libfuse
test_abi.c
1#define FUSE_USE_VERSION 30
2
3#include "fuse.h"
4
5#include <stdio.h>
6#include <stdlib.h>
7
8int main(void)
9{
10 if (sizeof(struct fuse_file_info) != 64) {
11 fprintf(stderr, "struct fuse_file_info size mismatch\n");
12 exit(1);
13 }
14 if (sizeof(struct fuse_conn_info) != 128) {
15 fprintf(stderr, "struct fuse_conn_info size mismatch\n");
16 exit(1);
17 }
18}
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2test__abi_8c_source.html0000644000175000017500000001276515156613443023337 0ustar berndbernd libfuse: fuse-3.18.1/test/test_abi.c Source File
libfuse
test_abi.c
1#define FUSE_USE_VERSION 30
2
3#include "fuse.h"
4
5#include <stdio.h>
6#include <stdlib.h>
7
8int main(void)
9{
10 if (sizeof(struct fuse_file_info) != 64) {
11 fprintf(stderr, "struct fuse_file_info size mismatch\n");
12 exit(1);
13 }
14 if (sizeof(struct fuse_conn_info) != 128) {
15 fprintf(stderr, "struct fuse_conn_info size mismatch\n");
16 exit(1);
17 }
18}
fuse-3.18.2/doc/html/test_2test__abi_8c_source.html0000644000175000017500000001260215156613443021151 0ustar berndbernd libfuse: test/test_abi.c Source File
libfuse
test_abi.c
1#define FUSE_USE_VERSION 30
2
3#include "fuse.h"
4
5#include <stdio.h>
6#include <stdlib.h>
7
8int main(void)
9{
10 if (sizeof(struct fuse_file_info) != 64) {
11 fprintf(stderr, "struct fuse_file_info size mismatch\n");
12 exit(1);
13 }
14 if (sizeof(struct fuse_conn_info) != 128) {
15 fprintf(stderr, "struct fuse_conn_info size mismatch\n");
16 exit(1);
17 }
18}
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2test__setattr_8c_source.html0000644000175000017500000012116515156613443024267 0ustar berndbernd libfuse: fuse-3.17.4/test/test_setattr.c Source File
libfuse
test_setattr.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file COPYING.
7*/
8
9
10#define FUSE_USE_VERSION 30
11
12/* Not really needed - just to test build with FUSE_USE_VERSION == 30 */
13#include <fuse.h>
14
15#include <fuse_config.h>
16#include <fuse_lowlevel.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <assert.h>
23#include <stddef.h>
24#include <unistd.h>
25#include <pthread.h>
26
27#ifndef __linux__
28#include <limits.h>
29#else
30#include <linux/limits.h>
31#endif
32
33#define FILE_INO 2
34#define FILE_NAME "truncate_me"
35
36static int got_fh;
37static mode_t file_mode = S_IFREG | 0644;
38
39static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
40 stbuf->st_ino = ino;
41 if (ino == FUSE_ROOT_ID) {
42 stbuf->st_mode = S_IFDIR | 0755;
43 stbuf->st_nlink = 1;
44 }
45
46 else if (ino == FILE_INO) {
47 stbuf->st_mode = file_mode;
48 stbuf->st_nlink = 1;
49 stbuf->st_size = 0;
50 }
51
52 else
53 return -1;
54
55 return 0;
56}
57
58static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
59 const char *name) {
60 struct fuse_entry_param e;
61 memset(&e, 0, sizeof(e));
62
63 if (parent != FUSE_ROOT_ID)
64 goto err_out;
65 else if (strcmp(name, FILE_NAME) == 0)
66 e.ino = FILE_INO;
67 else
68 goto err_out;
69
70 if (tfs_stat(e.ino, &e.attr) != 0)
71 goto err_out;
72 fuse_reply_entry(req, &e);
73 return;
74
75err_out:
76 fuse_reply_err(req, ENOENT);
77}
78
79static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
80 struct fuse_file_info *fi) {
81 struct stat stbuf;
82
83 (void) fi;
84
85 memset(&stbuf, 0, sizeof(stbuf));
86 if (tfs_stat(ino, &stbuf) != 0)
87 fuse_reply_err(req, ENOENT);
88 else
89 fuse_reply_attr(req, &stbuf, 5);
90}
91
92static void tfs_open(fuse_req_t req, fuse_ino_t ino,
93 struct fuse_file_info *fi) {
94 if (ino == FUSE_ROOT_ID)
95 fuse_reply_err(req, EISDIR);
96 else {
97 assert(ino == FILE_INO);
98 fi->fh = FILE_INO;
99 fuse_reply_open(req, fi);
100 }
101}
102
103static void tfs_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
104 int to_set, struct fuse_file_info *fi) {
105 if(ino != FILE_INO ||
106 !(to_set & FUSE_SET_ATTR_MODE)) {
107 fuse_reply_err(req, EINVAL);
108 return;
109 }
110
111 if(fi == NULL)
112 fprintf(stderr, "setattr with fi == NULL\n");
113 else if (fi->fh != FILE_INO)
114 fprintf(stderr, "setattr with wrong fi->fh\n");
115 else {
116 fprintf(stderr, "setattr ok\n");
117 got_fh = 1;
118 file_mode = attr->st_mode;
119 }
120
121 tfs_getattr(req, ino, fi);
122}
123
124static struct fuse_lowlevel_ops tfs_oper = {
125 .lookup = tfs_lookup,
126 .getattr = tfs_getattr,
127 .open = tfs_open,
128 .setattr = tfs_setattr,
129};
130
131static void* run_fs(void *data) {
132 struct fuse_session *se = (struct fuse_session*) data;
133 assert(fuse_session_loop(se) == 0);
134 return NULL;
135}
136
137static void test_fs(char *mountpoint) {
138 char fname[PATH_MAX];
139 int fd;
140
141 assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME,
142 mountpoint) > 0);
143 fd = open(fname, O_WRONLY);
144 if (fd == -1) {
145 perror(fname);
146 assert(0);
147 }
148
149 assert(fchmod(fd, 0600) == 0);
150 close(fd);
151}
152
153int main(int argc, char *argv[]) {
154 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
155 struct fuse_session *se;
156 struct fuse_cmdline_opts fuse_opts;
157 pthread_t fs_thread;
158
159 assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
160#ifndef __FreeBSD__
161 assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
162#endif
163 se = fuse_session_new(&args, &tfs_oper,
164 sizeof(tfs_oper), NULL);
165 assert (se != NULL);
166 assert(fuse_set_signal_handlers(se) == 0);
167 assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
168
169 /* Start file-system thread */
170 assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
171
172 /* Do test */
173 test_fs(fuse_opts.mountpoint);
174
175 /* Stop file system */
176 assert(pthread_cancel(fs_thread) == 0);
177
179 assert(got_fh == 1);
182
183 printf("Test completed successfully.\n");
184 return 0;
185}
186
187
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
fuse_ino_t ino
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2test__setattr_8c_source.html0000644000175000017500000012116615156613443024266 0ustar berndbernd libfuse: fuse-3.18.1/test/test_setattr.c Source File
libfuse
test_setattr.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
9
10#define FUSE_USE_VERSION 30
11
12/* Not really needed - just to test build with FUSE_USE_VERSION == 30 */
13#include <fuse.h>
14
15#include <fuse_config.h>
16#include <fuse_lowlevel.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <assert.h>
23#include <stddef.h>
24#include <unistd.h>
25#include <pthread.h>
26
27#ifndef __linux__
28#include <limits.h>
29#else
30#include <linux/limits.h>
31#endif
32
33#define FILE_INO 2
34#define FILE_NAME "truncate_me"
35
36static int got_fh;
37static mode_t file_mode = S_IFREG | 0644;
38
39static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
40 stbuf->st_ino = ino;
41 if (ino == FUSE_ROOT_ID) {
42 stbuf->st_mode = S_IFDIR | 0755;
43 stbuf->st_nlink = 1;
44 }
45
46 else if (ino == FILE_INO) {
47 stbuf->st_mode = file_mode;
48 stbuf->st_nlink = 1;
49 stbuf->st_size = 0;
50 }
51
52 else
53 return -1;
54
55 return 0;
56}
57
58static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
59 const char *name) {
60 struct fuse_entry_param e;
61 memset(&e, 0, sizeof(e));
62
63 if (parent != FUSE_ROOT_ID)
64 goto err_out;
65 else if (strcmp(name, FILE_NAME) == 0)
66 e.ino = FILE_INO;
67 else
68 goto err_out;
69
70 if (tfs_stat(e.ino, &e.attr) != 0)
71 goto err_out;
72 fuse_reply_entry(req, &e);
73 return;
74
75err_out:
76 fuse_reply_err(req, ENOENT);
77}
78
79static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
80 struct fuse_file_info *fi) {
81 struct stat stbuf;
82
83 (void) fi;
84
85 memset(&stbuf, 0, sizeof(stbuf));
86 if (tfs_stat(ino, &stbuf) != 0)
87 fuse_reply_err(req, ENOENT);
88 else
89 fuse_reply_attr(req, &stbuf, 5);
90}
91
92static void tfs_open(fuse_req_t req, fuse_ino_t ino,
93 struct fuse_file_info *fi) {
94 if (ino == FUSE_ROOT_ID)
95 fuse_reply_err(req, EISDIR);
96 else {
97 assert(ino == FILE_INO);
98 fi->fh = FILE_INO;
99 fuse_reply_open(req, fi);
100 }
101}
102
103static void tfs_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
104 int to_set, struct fuse_file_info *fi) {
105 if(ino != FILE_INO ||
106 !(to_set & FUSE_SET_ATTR_MODE)) {
107 fuse_reply_err(req, EINVAL);
108 return;
109 }
110
111 if(fi == NULL)
112 fprintf(stderr, "setattr with fi == NULL\n");
113 else if (fi->fh != FILE_INO)
114 fprintf(stderr, "setattr with wrong fi->fh\n");
115 else {
116 fprintf(stderr, "setattr ok\n");
117 got_fh = 1;
118 file_mode = attr->st_mode;
119 }
120
121 tfs_getattr(req, ino, fi);
122}
123
124static struct fuse_lowlevel_ops tfs_oper = {
125 .lookup = tfs_lookup,
126 .getattr = tfs_getattr,
127 .open = tfs_open,
128 .setattr = tfs_setattr,
129};
130
131static void* run_fs(void *data) {
132 struct fuse_session *se = (struct fuse_session*) data;
133 assert(fuse_session_loop(se) == 0);
134 return NULL;
135}
136
137static void test_fs(char *mountpoint) {
138 char fname[PATH_MAX];
139 int fd;
140
141 assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME,
142 mountpoint) > 0);
143 fd = open(fname, O_WRONLY);
144 if (fd == -1) {
145 perror(fname);
146 assert(0);
147 }
148
149 assert(fchmod(fd, 0600) == 0);
150 close(fd);
151}
152
153int main(int argc, char *argv[]) {
154 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
155 struct fuse_session *se;
156 struct fuse_cmdline_opts fuse_opts;
157 pthread_t fs_thread;
158
159 assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
160#ifndef __FreeBSD__
161 assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
162#endif
163 se = fuse_session_new(&args, &tfs_oper,
164 sizeof(tfs_oper), NULL);
165 assert (se != NULL);
166 assert(fuse_set_signal_handlers(se) == 0);
167 assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
168
169 /* Start file-system thread */
170 assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
171
172 /* Do test */
173 test_fs(fuse_opts.mountpoint);
174
175 /* Stop file system */
176 assert(pthread_cancel(fs_thread) == 0);
177
179 assert(got_fh == 1);
182
183 printf("Test completed successfully.\n");
184 return 0;
185}
186
187
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
fuse_ino_t ino
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/test_2test__setattr_8c_source.html0000644000175000017500000012100315156613443022100 0ustar berndbernd libfuse: test/test_setattr.c Source File
libfuse
test_setattr.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
9
10#define FUSE_USE_VERSION 30
11
12/* Not really needed - just to test build with FUSE_USE_VERSION == 30 */
13#include <fuse.h>
14
15#include <fuse_config.h>
16#include <fuse_lowlevel.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <assert.h>
23#include <stddef.h>
24#include <unistd.h>
25#include <pthread.h>
26
27#ifndef __linux__
28#include <limits.h>
29#else
30#include <linux/limits.h>
31#endif
32
33#define FILE_INO 2
34#define FILE_NAME "truncate_me"
35
36static int got_fh;
37static mode_t file_mode = S_IFREG | 0644;
38
39static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
40 stbuf->st_ino = ino;
41 if (ino == FUSE_ROOT_ID) {
42 stbuf->st_mode = S_IFDIR | 0755;
43 stbuf->st_nlink = 1;
44 }
45
46 else if (ino == FILE_INO) {
47 stbuf->st_mode = file_mode;
48 stbuf->st_nlink = 1;
49 stbuf->st_size = 0;
50 }
51
52 else
53 return -1;
54
55 return 0;
56}
57
58static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
59 const char *name) {
60 struct fuse_entry_param e;
61 memset(&e, 0, sizeof(e));
62
63 if (parent != FUSE_ROOT_ID)
64 goto err_out;
65 else if (strcmp(name, FILE_NAME) == 0)
66 e.ino = FILE_INO;
67 else
68 goto err_out;
69
70 if (tfs_stat(e.ino, &e.attr) != 0)
71 goto err_out;
72 fuse_reply_entry(req, &e);
73 return;
74
75err_out:
76 fuse_reply_err(req, ENOENT);
77}
78
79static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
80 struct fuse_file_info *fi) {
81 struct stat stbuf;
82
83 (void) fi;
84
85 memset(&stbuf, 0, sizeof(stbuf));
86 if (tfs_stat(ino, &stbuf) != 0)
87 fuse_reply_err(req, ENOENT);
88 else
89 fuse_reply_attr(req, &stbuf, 5);
90}
91
92static void tfs_open(fuse_req_t req, fuse_ino_t ino,
93 struct fuse_file_info *fi) {
94 if (ino == FUSE_ROOT_ID)
95 fuse_reply_err(req, EISDIR);
96 else {
97 assert(ino == FILE_INO);
98 fi->fh = FILE_INO;
99 fuse_reply_open(req, fi);
100 }
101}
102
103static void tfs_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
104 int to_set, struct fuse_file_info *fi) {
105 if(ino != FILE_INO ||
106 !(to_set & FUSE_SET_ATTR_MODE)) {
107 fuse_reply_err(req, EINVAL);
108 return;
109 }
110
111 if(fi == NULL)
112 fprintf(stderr, "setattr with fi == NULL\n");
113 else if (fi->fh != FILE_INO)
114 fprintf(stderr, "setattr with wrong fi->fh\n");
115 else {
116 fprintf(stderr, "setattr ok\n");
117 got_fh = 1;
118 file_mode = attr->st_mode;
119 }
120
121 tfs_getattr(req, ino, fi);
122}
123
124static struct fuse_lowlevel_ops tfs_oper = {
125 .lookup = tfs_lookup,
126 .getattr = tfs_getattr,
127 .open = tfs_open,
128 .setattr = tfs_setattr,
129};
130
131static void* run_fs(void *data) {
132 struct fuse_session *se = (struct fuse_session*) data;
133 assert(fuse_session_loop(se) == 0);
134 return NULL;
135}
136
137static void test_fs(char *mountpoint) {
138 char fname[PATH_MAX];
139 int fd;
140
141 assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME,
142 mountpoint) > 0);
143 fd = open(fname, O_WRONLY);
144 if (fd == -1) {
145 perror(fname);
146 assert(0);
147 }
148
149 assert(fchmod(fd, 0600) == 0);
150 close(fd);
151}
152
153int main(int argc, char *argv[]) {
154 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
155 struct fuse_session *se;
156 struct fuse_cmdline_opts fuse_opts;
157 pthread_t fs_thread;
158
159 assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
160#ifndef __FreeBSD__
161 assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
162#endif
163 se = fuse_session_new(&args, &tfs_oper,
164 sizeof(tfs_oper), NULL);
165 assert (se != NULL);
166 assert(fuse_set_signal_handlers(se) == 0);
167 assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
168
169 /* Start file-system thread */
170 assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
171
172 /* Do test */
173 test_fs(fuse_opts.mountpoint);
174
175 /* Stop file system */
176 assert(pthread_cancel(fs_thread) == 0);
177
179 assert(got_fh == 1);
182
183 printf("Test completed successfully.\n");
184 return 0;
185}
186
187
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
fuse_ino_t ino
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2test__signals_8c_source.html0000644000175000017500000011602015156613443024231 0ustar berndbernd libfuse: fuse-3.18.1/test/test_signals.c Source File
libfuse
test_signals.c
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2025 Bernd Schubert <bernd@bsbernd.com>
4 *
5 * Test for signal handling in libfuse.
6 *
7 * This program can be distributed under the terms of the GNU LGPLv2.
8 * See the file GPL2.txt
9 */
10
11#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
12
13#include "fuse_config.h"
14#include "fuse_lowlevel.h"
15#include "fuse_i.h"
16
17#include <pthread.h>
18#include <stdatomic.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <signal.h>
24#include <errno.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27
28static void test_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
29{
30 (void)parent;
31 (void)name;
32 /* Simulate slow lookup to test signal interruption */
33 sleep(2);
34 fuse_reply_err(req, ENOENT);
35}
36
37static void test_ll_getattr(fuse_req_t req, fuse_ino_t ino,
38 struct fuse_file_info *fi)
39{
40 (void)ino;
41 (void)fi;
42 /* Simulate slow getattr to test signal interruption */
43 sleep(2);
44 fuse_reply_err(req, ENOENT);
45}
46
47static const struct fuse_lowlevel_ops test_ll_ops = {
48 .lookup = test_ll_lookup,
49 .getattr = test_ll_getattr,
50};
51
52static void *signal_sender_thread(void *arg)
53{
54 (void)arg;
55
56 usleep(2 * 1000 * 1000);
57
58 /* Send SIGTERM to the process */
59 kill(getpid(), SIGTERM);
60 return NULL;
61}
62
63static void fork_child(void)
64{
65 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
66 struct fuse_session *se;
67 struct fuse_loop_config *loop_config;
68 pthread_t sig_thread;
69 char *mountpoint = NULL;
70 int ret = -1;
71
72 /* Add the program name to arg[0] */
73 if (fuse_opt_add_arg(&args, "test_signals")) {
74 fprintf(stderr, "Failed to add argument\n");
75 goto out_free_mountpoint;
76 }
77
78 /* Add debug flag to see more output */
79 fuse_opt_add_arg(&args, "-d");
80
81 /* Create temporary mount point */
82 mountpoint = strdup("/tmp/fuse_test_XXXXXX");
83 if (!mountpoint || !mkdtemp(mountpoint)) {
84 fprintf(stderr, "Failed to create temp dir\n");
85 goto out_free_args;
86 }
87
88 /* Create session */
89 se = fuse_session_new(&args, &test_ll_ops, sizeof(test_ll_ops), NULL);
90 if (!se) {
91 fprintf(stderr, "Failed to create FUSE session\n");
92 goto out_free_mountpoint;
93 }
94
95 /* Mount filesystem */
96 if (fuse_session_mount(se, mountpoint)) {
97 fprintf(stderr, "Failed to mount FUSE filesystem\n");
98 goto out_destroy_session;
99 }
100
101 /* Create loop config */
102 loop_config = fuse_loop_cfg_create();
103 if (!loop_config) {
104 fprintf(stderr, "Failed to create loop config\n");
105 goto out_unmount;
106 }
107 fuse_loop_cfg_set_clone_fd(loop_config, 0);
108 fuse_loop_cfg_set_max_threads(loop_config, 2);
109
110 /* Set up signal handlers */
111 if (fuse_set_signal_handlers(se)) {
112 fprintf(stderr, "Failed to set up signal handlers\n");
113 goto out_destroy_config;
114 }
115
116 /* Create thread that will send signals */
117 if (pthread_create(&sig_thread, NULL, signal_sender_thread, NULL)) {
118 fprintf(stderr, "Failed to create signal sender thread\n");
119 goto out_remove_handlers;
120 }
121
122 /* Enter FUSE loop */
123 ret = fuse_session_loop_mt_312(se, loop_config);
124
125 printf("Debug: fuse_session_loop_mt_312 returned %d\n", ret);
126 printf("Debug: session exited state: %d\n", fuse_session_exited(se));
127 printf("Debug: session status: %d\n", se->error);
128
129 /* Check exit status before cleanup */
130 int clean_exit = (fuse_session_exited(se) && se->error == SIGTERM);
131
132 /* Clean up */
133 pthread_join(sig_thread, NULL);
137 fuse_loop_cfg_destroy(loop_config);
138 rmdir(mountpoint);
139 free(mountpoint);
140 fuse_opt_free_args(&args);
141
142 /* Use saved exit status */
143 if (clean_exit) {
144 printf("Debug: Clean shutdown via SIGTERM\n");
145 exit(0);
146 }
147 printf("Debug: Exiting with status %d\n", ret != 0);
148 exit(ret != 0);
149
150out_remove_handlers:
152out_destroy_config:
153 fuse_loop_cfg_destroy(loop_config);
154out_unmount:
156out_destroy_session:
158out_free_mountpoint:
159 rmdir(mountpoint);
160 free(mountpoint);
161out_free_args:
162 fuse_opt_free_args(&args);
163 exit(1);
164}
165
166static void run_test_in_child(void)
167{
168 pid_t child;
169 int status;
170
171 child = fork();
172 if (child == -1) {
173 perror("fork");
174 exit(1);
175 }
176
177 if (child == 0)
178 fork_child();
179
180 /* In parent process */
181 if (waitpid(child, &status, 0) == -1) {
182 perror("waitpid");
183 exit(1);
184 }
185
186 /* Check if child exited due to SIGTERM - this is expected */
187 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM) {
188 printf("Child process terminated by SIGTERM as expected\n");
189 exit(0);
190 }
191
192 /* For any other type of exit, maintain existing behavior */
193 exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
194}
195
196int main(void)
197{
198 printf("Testing SIGTERM handling in libfuse\n");
199 run_test_in_child();
200 printf("SIGTERM handling test passed\n");
201 return 0;
202}
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_exited(struct fuse_session *se)
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/test_2test__signals_8c_source.html0000644000175000017500000011563515156613443022070 0ustar berndbernd libfuse: test/test_signals.c Source File
libfuse
test_signals.c
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2025 Bernd Schubert <bernd@bsbernd.com>
4 *
5 * Test for signal handling in libfuse.
6 *
7 * This program can be distributed under the terms of the GNU LGPLv2.
8 * See the file GPL2.txt
9 */
10
11#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
12
13#include "fuse_config.h"
14#include "fuse_lowlevel.h"
15#include "fuse_i.h"
16
17#include <pthread.h>
18#include <stdatomic.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <signal.h>
24#include <errno.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27
28static void test_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
29{
30 (void)parent;
31 (void)name;
32 /* Simulate slow lookup to test signal interruption */
33 sleep(2);
34 fuse_reply_err(req, ENOENT);
35}
36
37static void test_ll_getattr(fuse_req_t req, fuse_ino_t ino,
38 struct fuse_file_info *fi)
39{
40 (void)ino;
41 (void)fi;
42 /* Simulate slow getattr to test signal interruption */
43 sleep(2);
44 fuse_reply_err(req, ENOENT);
45}
46
47static const struct fuse_lowlevel_ops test_ll_ops = {
48 .lookup = test_ll_lookup,
49 .getattr = test_ll_getattr,
50};
51
52static void *signal_sender_thread(void *arg)
53{
54 (void)arg;
55
56 usleep(2 * 1000 * 1000);
57
58 /* Send SIGTERM to the process */
59 kill(getpid(), SIGTERM);
60 return NULL;
61}
62
63static void fork_child(void)
64{
65 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
66 struct fuse_session *se;
67 struct fuse_loop_config *loop_config;
68 pthread_t sig_thread;
69 char *mountpoint = NULL;
70 int ret = -1;
71
72 /* Add the program name to arg[0] */
73 if (fuse_opt_add_arg(&args, "test_signals")) {
74 fprintf(stderr, "Failed to add argument\n");
75 goto out_free_mountpoint;
76 }
77
78 /* Add debug flag to see more output */
79 fuse_opt_add_arg(&args, "-d");
80
81 /* Create temporary mount point */
82 mountpoint = strdup("/tmp/fuse_test_XXXXXX");
83 if (!mountpoint || !mkdtemp(mountpoint)) {
84 fprintf(stderr, "Failed to create temp dir\n");
85 goto out_free_args;
86 }
87
88 /* Create session */
89 se = fuse_session_new(&args, &test_ll_ops, sizeof(test_ll_ops), NULL);
90 if (!se) {
91 fprintf(stderr, "Failed to create FUSE session\n");
92 goto out_free_mountpoint;
93 }
94
95 /* Mount filesystem */
96 if (fuse_session_mount(se, mountpoint)) {
97 fprintf(stderr, "Failed to mount FUSE filesystem\n");
98 goto out_destroy_session;
99 }
100
101 /* Create loop config */
102 loop_config = fuse_loop_cfg_create();
103 if (!loop_config) {
104 fprintf(stderr, "Failed to create loop config\n");
105 goto out_unmount;
106 }
107 fuse_loop_cfg_set_clone_fd(loop_config, 0);
108 fuse_loop_cfg_set_max_threads(loop_config, 2);
109
110 /* Set up signal handlers */
111 if (fuse_set_signal_handlers(se)) {
112 fprintf(stderr, "Failed to set up signal handlers\n");
113 goto out_destroy_config;
114 }
115
116 /* Create thread that will send signals */
117 if (pthread_create(&sig_thread, NULL, signal_sender_thread, NULL)) {
118 fprintf(stderr, "Failed to create signal sender thread\n");
119 goto out_remove_handlers;
120 }
121
122 /* Enter FUSE loop */
123 ret = fuse_session_loop_mt_312(se, loop_config);
124
125 printf("Debug: fuse_session_loop_mt_312 returned %d\n", ret);
126 printf("Debug: session exited state: %d\n", fuse_session_exited(se));
127 printf("Debug: session status: %d\n", se->error);
128
129 /* Check exit status before cleanup */
130 int clean_exit = (fuse_session_exited(se) && se->error == SIGTERM);
131
132 /* Clean up */
133 pthread_join(sig_thread, NULL);
137 fuse_loop_cfg_destroy(loop_config);
138 rmdir(mountpoint);
139 free(mountpoint);
140 fuse_opt_free_args(&args);
141
142 /* Use saved exit status */
143 if (clean_exit) {
144 printf("Debug: Clean shutdown via SIGTERM\n");
145 exit(0);
146 }
147 printf("Debug: Exiting with status %d\n", ret != 0);
148 exit(ret != 0);
149
150out_remove_handlers:
152out_destroy_config:
153 fuse_loop_cfg_destroy(loop_config);
154out_unmount:
156out_destroy_session:
158out_free_mountpoint:
159 rmdir(mountpoint);
160 free(mountpoint);
161out_free_args:
162 fuse_opt_free_args(&args);
163 exit(1);
164}
165
166static void run_test_in_child(void)
167{
168 pid_t child;
169 int status;
170
171 child = fork();
172 if (child == -1) {
173 perror("fork");
174 exit(1);
175 }
176
177 if (child == 0)
178 fork_child();
179
180 /* In parent process */
181 if (waitpid(child, &status, 0) == -1) {
182 perror("waitpid");
183 exit(1);
184 }
185
186 /* Check if child exited due to SIGTERM - this is expected */
187 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM) {
188 printf("Child process terminated by SIGTERM as expected\n");
189 exit(0);
190 }
191
192 /* For any other type of exit, maintain existing behavior */
193 exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
194}
195
196int main(void)
197{
198 printf("Testing SIGTERM handling in libfuse\n");
199 run_test_in_child();
200 printf("SIGTERM handling test passed\n");
201 return 0;
202}
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_exited(struct fuse_session *se)
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2test__syscalls_8c_source.html0000644000175000017500000114273215156613443024442 0ustar berndbernd libfuse: fuse-3.17.4/test/test_syscalls.c Source File
libfuse
test_syscalls.c
1#define _GNU_SOURCE
2#include "fuse_config.h"
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <stdarg.h>
7#include <string.h>
8#include <unistd.h>
9#include <fcntl.h>
10#include <dirent.h>
11#include <utime.h>
12#include <errno.h>
13#include <assert.h>
14#include <sys/socket.h>
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <sys/un.h>
18
19#ifndef ALLPERMS
20# define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)/* 07777 */
21#endif
22
23
24static const char *basepath;
25static const char *basepath_r;
26static char testfile[1024];
27static char testfile2[1024];
28static char testdir[1024];
29static char testdir2[1024];
30static char testsock[1024];
31static char subfile[1280];
32
33static char testfile_r[1024];
34static char testfile2_r[1024];
35static char testdir_r[1024];
36static char testdir2_r[1024];
37static char subfile_r[1280];
38
39static char testname[256];
40static char testdata[] = "abcdefghijklmnopqrstuvwxyz";
41static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./";
42static const char *testdir_files[] = { "f1", "f2", NULL};
43static long seekdir_offsets[4];
44static char zerodata[4096];
45static int testdatalen = sizeof(testdata) - 1;
46static int testdata2len = sizeof(testdata2) - 1;
47static unsigned int testnum = 0;
48static unsigned int select_test = 0;
49static unsigned int skip_test = 0;
50static unsigned int unlinked_test = 0;
51
52#define MAX_ENTRIES 1024
53#define MAX_TESTS 100
54
55static struct test {
56 int fd;
57 struct stat stat;
58} tests[MAX_TESTS];
59
60static void test_perror(const char *func, const char *msg)
61{
62 fprintf(stderr, "%s %s() - %s: %s\n", testname, func, msg,
63 strerror(errno));
64}
65
66static void test_error(const char *func, const char *msg, ...)
67 __attribute__ ((format (printf, 2, 3)));
68
69static void __start_test(const char *fmt, ...)
70 __attribute__ ((format (printf, 1, 2)));
71
72static void test_error(const char *func, const char *msg, ...)
73{
74 va_list ap;
75 fprintf(stderr, "%s %s() - ", testname, func);
76 va_start(ap, msg);
77 vfprintf(stderr, msg, ap);
78 va_end(ap);
79 fprintf(stderr, "\n");
80}
81
82static int is_dot_or_dotdot(const char *name) {
83 return name[0] == '.' &&
84 (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
85}
86
87static void success(void)
88{
89 fprintf(stderr, "%s OK\n", testname);
90}
91
92#define this_test (&tests[testnum-1])
93#define next_test (&tests[testnum])
94
95static void __start_test(const char *fmt, ...)
96{
97 unsigned int n;
98 va_list ap;
99 n = sprintf(testname, "%3i [", testnum);
100 va_start(ap, fmt);
101 n += vsprintf(testname + n, fmt, ap);
102 va_end(ap);
103 sprintf(testname + n, "]");
104 // Use dedicated testfile per test
105 sprintf(testfile, "%s/testfile.%d", basepath, testnum);
106 sprintf(testfile_r, "%s/testfile.%d", basepath_r, testnum);
107 if (testnum > MAX_TESTS) {
108 fprintf(stderr, "%s - too many tests\n", testname);
109 exit(1);
110 }
111 this_test->fd = -1;
112}
113
114#define start_test(msg, args...) { \
115 testnum++; \
116 if ((select_test && testnum != select_test) || \
117 (testnum == skip_test)) { \
118 return 0; \
119 } \
120 __start_test(msg, ##args); \
121}
122
123#define PERROR(msg) test_perror(__FUNCTION__, msg)
124#define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args)
125
126#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
127
128static int st_check_size(struct stat *st, int len)
129{
130 if (st->st_size != len) {
131 ERROR("length %u instead of %u", (int) st->st_size,
132 (int) len);
133 return -1;
134 }
135 return 0;
136}
137
138static int check_size(const char *path, int len)
139{
140 struct stat stbuf;
141 int res = stat(path, &stbuf);
142 if (res == -1) {
143 PERROR("stat");
144 return -1;
145 }
146 return st_check_size(&stbuf, len);
147}
148
149static int check_testfile_size(const char *path, int len)
150{
151 this_test->stat.st_size = len;
152 return check_size(path, len);
153}
154
155static int st_check_type(struct stat *st, mode_t type)
156{
157 if ((st->st_mode & S_IFMT) != type) {
158 ERROR("type 0%o instead of 0%o", st->st_mode & S_IFMT, type);
159 return -1;
160 }
161 return 0;
162}
163
164static int check_type(const char *path, mode_t type)
165{
166 struct stat stbuf;
167 int res = lstat(path, &stbuf);
168 if (res == -1) {
169 PERROR("lstat");
170 return -1;
171 }
172 return st_check_type(&stbuf, type);
173}
174
175static int st_check_mode(struct stat *st, mode_t mode)
176{
177 if ((st->st_mode & ALLPERMS) != mode) {
178 ERROR("mode 0%o instead of 0%o", st->st_mode & ALLPERMS,
179 mode);
180 return -1;
181 }
182 return 0;
183}
184
185static int check_mode(const char *path, mode_t mode)
186{
187 struct stat stbuf;
188 int res = lstat(path, &stbuf);
189 if (res == -1) {
190 PERROR("lstat");
191 return -1;
192 }
193 return st_check_mode(&stbuf, mode);
194}
195
196static int check_testfile_mode(const char *path, mode_t mode)
197{
198 this_test->stat.st_mode &= ~ALLPERMS;
199 this_test->stat.st_mode |= mode;
200 return check_mode(path, mode);
201}
202
203static int check_times(const char *path, time_t atime, time_t mtime)
204{
205 int err = 0;
206 struct stat stbuf;
207 int res = lstat(path, &stbuf);
208 if (res == -1) {
209 PERROR("lstat");
210 return -1;
211 }
212 if (stbuf.st_atime != atime) {
213 ERROR("atime %li instead of %li", stbuf.st_atime, atime);
214 err--;
215 }
216 if (stbuf.st_mtime != mtime) {
217 ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
218 err--;
219 }
220 if (err)
221 return -1;
222
223 return 0;
224}
225
226#if 0
227static int fcheck_times(int fd, time_t atime, time_t mtime)
228{
229 int err = 0;
230 struct stat stbuf;
231 int res = fstat(fd, &stbuf);
232 if (res == -1) {
233 PERROR("fstat");
234 return -1;
235 }
236 if (stbuf.st_atime != atime) {
237 ERROR("atime %li instead of %li", stbuf.st_atime, atime);
238 err--;
239 }
240 if (stbuf.st_mtime != mtime) {
241 ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
242 err--;
243 }
244 if (err)
245 return -1;
246
247 return 0;
248}
249#endif
250
251static int st_check_nlink(struct stat *st, nlink_t nlink)
252{
253 if (st->st_nlink != nlink) {
254 ERROR("nlink %li instead of %li", (long) st->st_nlink,
255 (long) nlink);
256 return -1;
257 }
258 return 0;
259}
260
261static int check_nlink(const char *path, nlink_t nlink)
262{
263 struct stat stbuf;
264 int res = lstat(path, &stbuf);
265 if (res == -1) {
266 PERROR("lstat");
267 return -1;
268 }
269 return st_check_nlink(&stbuf, nlink);
270}
271
272static int fcheck_stat(int fd, int flags, struct stat *st)
273{
274 struct stat stbuf;
275 int res = fstat(fd, &stbuf);
276 if (res == -1) {
277 if (flags & O_PATH) {
278 // With O_PATH fd, the server does not have to keep
279 // the inode alive so FUSE inode may be stale or bad
280 if (errno == ESTALE || errno == EIO ||
281 errno == ENOENT || errno == EBADF)
282 return 0;
283 }
284 PERROR("fstat");
285 return -1;
286 }
287
288 int err = 0;
289 err += st_check_type(&stbuf, st->st_mode & S_IFMT);
290 err += st_check_mode(&stbuf, st->st_mode & ALLPERMS);
291 err += st_check_size(&stbuf, st->st_size);
292 err += st_check_nlink(&stbuf, st->st_nlink);
293
294 return err;
295}
296
297static int check_nonexist(const char *path)
298{
299 struct stat stbuf;
300 int res = lstat(path, &stbuf);
301 if (res == 0) {
302 ERROR("file should not exist");
303 return -1;
304 }
305 if (errno != ENOENT) {
306 ERROR("file should not exist: %s", strerror(errno));
307 return -1;
308 }
309 return 0;
310}
311
312static int check_buffer(const char *buf, const char *data, unsigned len)
313{
314 if (memcmp(buf, data, len) != 0) {
315 ERROR("data mismatch");
316 return -1;
317 }
318 return 0;
319}
320
321static int check_data(const char *path, const char *data, int offset,
322 unsigned len)
323{
324 char buf[4096];
325 int res;
326 int fd = open(path, O_RDONLY);
327 if (fd == -1) {
328 PERROR("open");
329 return -1;
330 }
331 if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
332 PERROR("lseek");
333 close(fd);
334 return -1;
335 }
336 while (len) {
337 int rdlen = len < sizeof(buf) ? len : sizeof(buf);
338 res = read(fd, buf, rdlen);
339 if (res == -1) {
340 PERROR("read");
341 close(fd);
342 return -1;
343 }
344 if (res != rdlen) {
345 ERROR("short read: %u instead of %u", res, rdlen);
346 close(fd);
347 return -1;
348 }
349 if (check_buffer(buf, data, rdlen) != 0) {
350 close(fd);
351 return -1;
352 }
353 data += rdlen;
354 len -= rdlen;
355 }
356 res = close(fd);
357 if (res == -1) {
358 PERROR("close");
359 return -1;
360 }
361 return 0;
362}
363
364static int fcheck_data(int fd, const char *data, int offset,
365 unsigned len)
366{
367 char buf[4096];
368 int res;
369 if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
370 PERROR("lseek");
371 return -1;
372 }
373 while (len) {
374 int rdlen = len < sizeof(buf) ? len : sizeof(buf);
375 res = read(fd, buf, rdlen);
376 if (res == -1) {
377 PERROR("read");
378 return -1;
379 }
380 if (res != rdlen) {
381 ERROR("short read: %u instead of %u", res, rdlen);
382 return -1;
383 }
384 if (check_buffer(buf, data, rdlen) != 0) {
385 return -1;
386 }
387 data += rdlen;
388 len -= rdlen;
389 }
390 return 0;
391}
392
393static int check_dir_contents(const char *path, const char **contents)
394{
395 int i;
396 int res;
397 int err = 0;
398 int found[MAX_ENTRIES];
399 const char *cont[MAX_ENTRIES];
400 DIR *dp;
401
402 for (i = 0; contents[i]; i++) {
403 assert(i < MAX_ENTRIES - 3);
404 found[i] = 0;
405 cont[i] = contents[i];
406 }
407 cont[i] = NULL;
408
409 dp = opendir(path);
410 if (dp == NULL) {
411 PERROR("opendir");
412 return -1;
413 }
414 memset(found, 0, sizeof(found));
415 while(1) {
416 struct dirent *de;
417 errno = 0;
418 de = readdir(dp);
419 if (de == NULL) {
420 if (errno) {
421 PERROR("readdir");
422 closedir(dp);
423 return -1;
424 }
425 break;
426 }
427 if (is_dot_or_dotdot(de->d_name))
428 continue;
429 for (i = 0; cont[i] != NULL; i++) {
430 assert(i < MAX_ENTRIES);
431 if (strcmp(cont[i], de->d_name) == 0) {
432 if (found[i]) {
433 ERROR("duplicate entry <%s>",
434 de->d_name);
435 err--;
436 } else
437 found[i] = 1;
438 break;
439 }
440 }
441 if (!cont[i]) {
442 ERROR("unexpected entry <%s>", de->d_name);
443 err --;
444 }
445 }
446 for (i = 0; cont[i] != NULL; i++) {
447 if (!found[i]) {
448 ERROR("missing entry <%s>", cont[i]);
449 err--;
450 }
451 }
452 res = closedir(dp);
453 if (res == -1) {
454 PERROR("closedir");
455 return -1;
456 }
457 if (err)
458 return -1;
459
460 return 0;
461}
462
463static int create_file(const char *path, const char *data, int len)
464{
465 int res;
466 int fd;
467
468 unlink(path);
469 fd = creat(path, 0644);
470 if (fd == -1) {
471 PERROR("creat");
472 return -1;
473 }
474 if (len) {
475 res = write(fd, data, len);
476 if (res == -1) {
477 PERROR("write");
478 close(fd);
479 return -1;
480 }
481 if (res != len) {
482 ERROR("write is short: %u instead of %u", res, len);
483 close(fd);
484 return -1;
485 }
486 }
487 res = close(fd);
488 if (res == -1) {
489 PERROR("close");
490 return -1;
491 }
492 res = check_type(path, S_IFREG);
493 if (res == -1)
494 return -1;
495 res = check_mode(path, 0644);
496 if (res == -1)
497 return -1;
498 res = check_nlink(path, 1);
499 if (res == -1)
500 return -1;
501 res = check_size(path, len);
502 if (res == -1)
503 return -1;
504
505 if (len) {
506 res = check_data(path, data, 0, len);
507 if (res == -1)
508 return -1;
509 }
510
511 return 0;
512}
513
514static int create_path_fd(const char *path, const char *data, int len)
515{
516 int path_fd;
517 int res;
518
519 res = create_file(path, data, len);
520 if (res == -1)
521 return -1;
522
523 path_fd = open(path, O_PATH);
524 if (path_fd == -1)
525 PERROR("open(O_PATH)");
526
527 return path_fd;
528}
529
530// Can be called once per test
531static int create_testfile(const char *path, const char *data, int len)
532{
533 struct test *t = this_test;
534 struct stat *st = &t->stat;
535 int res, fd;
536
537 if (t->fd > 0) {
538 ERROR("testfile already created");
539 return -1;
540 }
541
542 fd = create_path_fd(path, data, len);
543 if (fd == -1)
544 return -1;
545
546 t->fd = fd;
547
548 res = fstat(fd, st);
549 if (res == -1) {
550 PERROR("fstat");
551 return -1;
552 }
553
554 return 0;
555}
556
557static int check_unlinked_testfile(int fd)
558{
559 struct stat *st = &this_test->stat;
560
561 st->st_nlink = 0;
562 return fcheck_stat(fd, O_PATH, st);
563}
564
565// Check recorded testfiles after all tests completed
566static int check_unlinked_testfiles(void)
567{
568 int fd;
569 int res, err = 0;
570 int num = testnum;
571
572 if (!unlinked_test)
573 return 0;
574
575 testnum = 0;
576 while (testnum < num) {
577 fd = next_test->fd;
578 start_test("check_unlinked_testfile");
579 if (fd == -1)
580 continue;
581
582 err += check_unlinked_testfile(fd);
583 res = close(fd);
584 if (res == -1) {
585 PERROR("close(test_fd)");
586 err--;
587 }
588 }
589
590 if (err) {
591 fprintf(stderr, "%i unlinked testfile checks failed\n", -err);
592 return 1;
593 }
594
595 return err;
596}
597
598static int cleanup_dir(const char *path, const char **dir_files, int quiet)
599{
600 int i;
601 int err = 0;
602
603 for (i = 0; dir_files[i]; i++) {
604 int res;
605 char fpath[1280];
606 sprintf(fpath, "%s/%s", path, dir_files[i]);
607 res = unlink(fpath);
608 if (res == -1 && !quiet) {
609 PERROR("unlink");
610 err --;
611 }
612 }
613 if (err)
614 return -1;
615
616 return 0;
617}
618
619static int create_dir(const char *path, const char **dir_files)
620{
621 int res;
622 int i;
623
624 rmdir(path);
625 res = mkdir(path, 0755);
626 if (res == -1) {
627 PERROR("mkdir");
628 return -1;
629 }
630 res = check_type(path, S_IFDIR);
631 if (res == -1)
632 return -1;
633 res = check_mode(path, 0755);
634 if (res == -1)
635 return -1;
636
637 for (i = 0; dir_files[i]; i++) {
638 char fpath[1280];
639 sprintf(fpath, "%s/%s", path, dir_files[i]);
640 res = create_file(fpath, "", 0);
641 if (res == -1) {
642 cleanup_dir(path, dir_files, 1);
643 return -1;
644 }
645 }
646 res = check_dir_contents(path, dir_files);
647 if (res == -1) {
648 cleanup_dir(path, dir_files, 1);
649 return -1;
650 }
651
652 return 0;
653}
654
655static int test_truncate(int len)
656{
657 const char *data = testdata;
658 int datalen = testdatalen;
659 int res;
660
661 start_test("truncate(%u)", (int) len);
662 res = create_testfile(testfile, data, datalen);
663 if (res == -1)
664 return -1;
665
666 res = truncate(testfile, len);
667 if (res == -1) {
668 PERROR("truncate");
669 return -1;
670 }
671 res = check_testfile_size(testfile, len);
672 if (res == -1)
673 return -1;
674
675 if (len > 0) {
676 if (len <= datalen) {
677 res = check_data(testfile, data, 0, len);
678 if (res == -1)
679 return -1;
680 } else {
681 res = check_data(testfile, data, 0, datalen);
682 if (res == -1)
683 return -1;
684 res = check_data(testfile, zerodata, datalen,
685 len - datalen);
686 if (res == -1)
687 return -1;
688 }
689 }
690 res = unlink(testfile);
691 if (res == -1) {
692 PERROR("unlink");
693 return -1;
694 }
695 res = check_nonexist(testfile);
696 if (res == -1)
697 return -1;
698
699 success();
700 return 0;
701}
702
703static int test_ftruncate(int len, int mode)
704{
705 const char *data = testdata;
706 int datalen = testdatalen;
707 int res;
708 int fd;
709
710 start_test("ftruncate(%u) mode: 0%03o", len, mode);
711 res = create_testfile(testfile, data, datalen);
712 if (res == -1)
713 return -1;
714
715 fd = open(testfile, O_WRONLY);
716 if (fd == -1) {
717 PERROR("open");
718 return -1;
719 }
720
721 res = fchmod(fd, mode);
722 if (res == -1) {
723 PERROR("fchmod");
724 close(fd);
725 return -1;
726 }
727 res = check_testfile_mode(testfile, mode);
728 if (res == -1) {
729 close(fd);
730 return -1;
731 }
732 res = ftruncate(fd, len);
733 if (res == -1) {
734 PERROR("ftruncate");
735 close(fd);
736 return -1;
737 }
738 close(fd);
739 res = check_testfile_size(testfile, len);
740 if (res == -1)
741 return -1;
742
743 if (len > 0) {
744 if (len <= datalen) {
745 res = check_data(testfile, data, 0, len);
746 if (res == -1)
747 return -1;
748 } else {
749 res = check_data(testfile, data, 0, datalen);
750 if (res == -1)
751 return -1;
752 res = check_data(testfile, zerodata, datalen,
753 len - datalen);
754 if (res == -1)
755 return -1;
756 }
757 }
758 res = unlink(testfile);
759 if (res == -1) {
760 PERROR("unlink");
761 return -1;
762 }
763 res = check_nonexist(testfile);
764 if (res == -1)
765 return -1;
766
767 success();
768 return 0;
769}
770
771static int test_seekdir(void)
772{
773 int i;
774 int res;
775 DIR *dp;
776 struct dirent *de = NULL;
777
778 start_test("seekdir");
779 res = create_dir(testdir, testdir_files);
780 if (res == -1)
781 return res;
782
783 dp = opendir(testdir);
784 if (dp == NULL) {
785 PERROR("opendir");
786 return -1;
787 }
788
789 /* Remember dir offsets */
790 for (i = 0; i < ARRAY_SIZE(seekdir_offsets); i++) {
791 seekdir_offsets[i] = telldir(dp);
792 errno = 0;
793 de = readdir(dp);
794 if (de == NULL) {
795 if (errno) {
796 PERROR("readdir");
797 goto fail;
798 }
799 break;
800 }
801 }
802
803 /* Walk until the end of directory */
804 while (de)
805 de = readdir(dp);
806
807 /* Start from the last valid dir offset and seek backwards */
808 for (i--; i >= 0; i--) {
809 seekdir(dp, seekdir_offsets[i]);
810 de = readdir(dp);
811 if (de == NULL) {
812 ERROR("Unexpected end of directory after seekdir()");
813 goto fail;
814 }
815 }
816
817 closedir(dp);
818 res = cleanup_dir(testdir, testdir_files, 0);
819 if (!res)
820 success();
821 return res;
822fail:
823 closedir(dp);
824 cleanup_dir(testdir, testdir_files, 1);
825 return -1;
826}
827
828#ifdef HAVE_COPY_FILE_RANGE
829static int test_copy_file_range(void)
830{
831 const char *data = testdata;
832 int datalen = testdatalen;
833 int err = 0;
834 int res;
835 int fd_in, fd_out;
836 off_t pos_in = 0, pos_out = 0;
837
838 start_test("copy_file_range");
839 unlink(testfile);
840 fd_in = open(testfile, O_CREAT | O_RDWR, 0644);
841 if (fd_in == -1) {
842 PERROR("creat");
843 return -1;
844 }
845 res = write(fd_in, data, datalen);
846 if (res == -1) {
847 PERROR("write");
848 close(fd_in);
849 return -1;
850 }
851 if (res != datalen) {
852 ERROR("write is short: %u instead of %u", res, datalen);
853 close(fd_in);
854 return -1;
855 }
856
857 unlink(testfile2);
858 fd_out = creat(testfile2, 0644);
859 if (fd_out == -1) {
860 PERROR("creat");
861 close(fd_in);
862 return -1;
863 }
864 res = copy_file_range(fd_in, &pos_in, fd_out, &pos_out, datalen, 0);
865 if (res == -1) {
866 PERROR("copy_file_range");
867 close(fd_in);
868 close(fd_out);
869 return -1;
870 }
871 if (res != datalen) {
872 ERROR("copy is short: %u instead of %u", res, datalen);
873 close(fd_in);
874 close(fd_out);
875 return -1;
876 }
877
878 res = close(fd_in);
879 if (res == -1) {
880 PERROR("close");
881 close(fd_out);
882 return -1;
883 }
884 res = close(fd_out);
885 if (res == -1) {
886 PERROR("close");
887 return -1;
888 }
889
890 err = check_data(testfile2, data, 0, datalen);
891
892 res = unlink(testfile);
893 if (res == -1) {
894 PERROR("unlink");
895 return -1;
896 }
897 res = check_nonexist(testfile);
898 if (res == -1)
899 return -1;
900 if (err)
901 return -1;
902
903 res = unlink(testfile2);
904 if (res == -1) {
905 PERROR("unlink");
906 return -1;
907 }
908 res = check_nonexist(testfile2);
909 if (res == -1)
910 return -1;
911 if (err)
912 return -1;
913
914 success();
915 return 0;
916}
917#else
918static int test_copy_file_range(void)
919{
920 return 0;
921}
922#endif
923
924static int test_utime(void)
925{
926 struct utimbuf utm;
927 time_t atime = 987631200;
928 time_t mtime = 123116400;
929 int res;
930
931 start_test("utime");
932 res = create_testfile(testfile, NULL, 0);
933 if (res == -1)
934 return -1;
935
936 utm.actime = atime;
937 utm.modtime = mtime;
938 res = utime(testfile, &utm);
939 if (res == -1) {
940 PERROR("utime");
941 return -1;
942 }
943 res = check_times(testfile, atime, mtime);
944 if (res == -1) {
945 return -1;
946 }
947 res = unlink(testfile);
948 if (res == -1) {
949 PERROR("unlink");
950 return -1;
951 }
952 res = check_nonexist(testfile);
953 if (res == -1)
954 return -1;
955
956 success();
957 return 0;
958}
959
960static int test_create(void)
961{
962 const char *data = testdata;
963 int datalen = testdatalen;
964 int err = 0;
965 int res;
966 int fd;
967
968 start_test("create");
969 unlink(testfile);
970 fd = creat(testfile, 0644);
971 if (fd == -1) {
972 PERROR("creat");
973 return -1;
974 }
975 res = write(fd, data, datalen);
976 if (res == -1) {
977 PERROR("write");
978 close(fd);
979 return -1;
980 }
981 if (res != datalen) {
982 ERROR("write is short: %u instead of %u", res, datalen);
983 close(fd);
984 return -1;
985 }
986 res = close(fd);
987 if (res == -1) {
988 PERROR("close");
989 return -1;
990 }
991 res = check_type(testfile, S_IFREG);
992 if (res == -1)
993 return -1;
994 err += check_mode(testfile, 0644);
995 err += check_nlink(testfile, 1);
996 err += check_size(testfile, datalen);
997 err += check_data(testfile, data, 0, datalen);
998 res = unlink(testfile);
999 if (res == -1) {
1000 PERROR("unlink");
1001 return -1;
1002 }
1003 res = check_nonexist(testfile);
1004 if (res == -1)
1005 return -1;
1006 if (err)
1007 return -1;
1008
1009 success();
1010 return 0;
1011}
1012
1013static int test_create_unlink(void)
1014{
1015 const char *data = testdata;
1016 int datalen = testdatalen;
1017 int err = 0;
1018 int res;
1019 int fd;
1020
1021 start_test("create+unlink");
1022 unlink(testfile);
1023 fd = open(testfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
1024 if (fd == -1) {
1025 PERROR("creat");
1026 return -1;
1027 }
1028 res = unlink(testfile);
1029 if (res == -1) {
1030 PERROR("unlink");
1031 close(fd);
1032 return -1;
1033 }
1034 res = check_nonexist(testfile);
1035 if (res == -1) {
1036 close(fd);
1037 return -1;
1038 }
1039 res = write(fd, data, datalen);
1040 if (res == -1) {
1041 PERROR("write");
1042 close(fd);
1043 return -1;
1044 }
1045 if (res != datalen) {
1046 ERROR("write is short: %u instead of %u", res, datalen);
1047 close(fd);
1048 return -1;
1049 }
1050 struct stat st = {
1051 .st_mode = S_IFREG | 0644,
1052 .st_size = datalen,
1053 };
1054 err = fcheck_stat(fd, O_RDWR, &st);
1055 err += fcheck_data(fd, data, 0, datalen);
1056 res = close(fd);
1057 if (res == -1) {
1058 PERROR("close");
1059 err--;
1060 }
1061 if (err)
1062 return -1;
1063
1064 success();
1065 return 0;
1066}
1067
1068static int test_mknod(void)
1069{
1070 int err = 0;
1071 int res;
1072
1073 start_test("mknod");
1074 unlink(testfile);
1075 res = mknod(testfile, 0644, 0);
1076 if (res == -1) {
1077 PERROR("mknod");
1078 return -1;
1079 }
1080 res = check_type(testfile, S_IFREG);
1081 if (res == -1)
1082 return -1;
1083 err += check_mode(testfile, 0644);
1084 err += check_nlink(testfile, 1);
1085 err += check_size(testfile, 0);
1086 res = unlink(testfile);
1087 if (res == -1) {
1088 PERROR("unlink");
1089 return -1;
1090 }
1091 res = check_nonexist(testfile);
1092 if (res == -1)
1093 return -1;
1094 if (err)
1095 return -1;
1096
1097 success();
1098 return 0;
1099}
1100
1101#define test_open(exist, flags, mode) do_test_open(exist, flags, #flags, mode)
1102
1103static int do_test_open(int exist, int flags, const char *flags_str, int mode)
1104{
1105 char buf[4096];
1106 const char *data = testdata;
1107 int datalen = testdatalen;
1108 unsigned currlen = 0;
1109 int err = 0;
1110 int res;
1111 int fd;
1112 off_t off;
1113
1114 start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode);
1115 unlink(testfile);
1116 if (exist) {
1117 res = create_file(testfile_r, testdata2, testdata2len);
1118 if (res == -1)
1119 return -1;
1120
1121 currlen = testdata2len;
1122 }
1123
1124 fd = open(testfile, flags, mode);
1125 if ((flags & O_CREAT) && (flags & O_EXCL) && exist) {
1126 if (fd != -1) {
1127 ERROR("open should have failed");
1128 close(fd);
1129 return -1;
1130 } else if (errno == EEXIST)
1131 goto succ;
1132 }
1133 if (!(flags & O_CREAT) && !exist) {
1134 if (fd != -1) {
1135 ERROR("open should have failed");
1136 close(fd);
1137 return -1;
1138 } else if (errno == ENOENT)
1139 goto succ;
1140 }
1141 if (fd == -1) {
1142 PERROR("open");
1143 return -1;
1144 }
1145
1146 if (flags & O_TRUNC)
1147 currlen = 0;
1148
1149 err += check_type(testfile, S_IFREG);
1150 if (exist)
1151 err += check_mode(testfile, 0644);
1152 else
1153 err += check_mode(testfile, mode);
1154 err += check_nlink(testfile, 1);
1155 err += check_size(testfile, currlen);
1156 if (exist && !(flags & O_TRUNC) && (mode & S_IRUSR))
1157 err += check_data(testfile, testdata2, 0, testdata2len);
1158
1159 res = write(fd, data, datalen);
1160 if ((flags & O_ACCMODE) != O_RDONLY) {
1161 if (res == -1) {
1162 PERROR("write");
1163 err --;
1164 } else if (res != datalen) {
1165 ERROR("write is short: %u instead of %u", res, datalen);
1166 err --;
1167 } else {
1168 if (datalen > (int) currlen)
1169 currlen = datalen;
1170
1171 err += check_size(testfile, currlen);
1172
1173 if (mode & S_IRUSR) {
1174 err += check_data(testfile, data, 0, datalen);
1175 if (exist && !(flags & O_TRUNC) &&
1176 testdata2len > datalen)
1177 err += check_data(testfile,
1178 testdata2 + datalen,
1179 datalen,
1180 testdata2len - datalen);
1181 }
1182 }
1183 } else {
1184 if (res != -1) {
1185 ERROR("write should have failed");
1186 err --;
1187 } else if (errno != EBADF) {
1188 PERROR("write");
1189 err --;
1190 }
1191 }
1192 off = lseek(fd, SEEK_SET, 0);
1193 if (off == (off_t) -1) {
1194 PERROR("lseek");
1195 err--;
1196 } else if (off != 0) {
1197 ERROR("offset should have returned 0");
1198 err --;
1199 }
1200 res = read(fd, buf, sizeof(buf));
1201 if ((flags & O_ACCMODE) != O_WRONLY) {
1202 if (res == -1) {
1203 PERROR("read");
1204 err--;
1205 } else {
1206 int readsize =
1207 currlen < sizeof(buf) ? currlen : sizeof(buf);
1208 if (res != readsize) {
1209 ERROR("read is short: %i instead of %u",
1210 res, readsize);
1211 err--;
1212 } else {
1213 if ((flags & O_ACCMODE) != O_RDONLY) {
1214 err += check_buffer(buf, data, datalen);
1215 if (exist && !(flags & O_TRUNC) &&
1216 testdata2len > datalen)
1217 err += check_buffer(buf + datalen,
1218 testdata2 + datalen,
1219 testdata2len - datalen);
1220 } else if (exist)
1221 err += check_buffer(buf, testdata2,
1222 testdata2len);
1223 }
1224 }
1225 } else {
1226 if (res != -1) {
1227 ERROR("read should have failed");
1228 err --;
1229 } else if (errno != EBADF) {
1230 PERROR("read");
1231 err --;
1232 }
1233 }
1234
1235 res = close(fd);
1236 if (res == -1) {
1237 PERROR("close");
1238 return -1;
1239 }
1240 res = unlink(testfile);
1241 if (res == -1) {
1242 PERROR("unlink");
1243 return -1;
1244 }
1245 res = check_nonexist(testfile);
1246 if (res == -1)
1247 return -1;
1248 res = check_nonexist(testfile_r);
1249 if (res == -1)
1250 return -1;
1251 if (err)
1252 return -1;
1253
1254succ:
1255 success();
1256 return 0;
1257}
1258
1259#define test_open_acc(flags, mode, err) \
1260 do_test_open_acc(flags, #flags, mode, err)
1261
1262static int do_test_open_acc(int flags, const char *flags_str, int mode, int err)
1263{
1264 const char *data = testdata;
1265 int datalen = testdatalen;
1266 int res;
1267 int fd;
1268
1269 start_test("open_acc(%s) mode: 0%03o message: '%s'", flags_str, mode,
1270 strerror(err));
1271 unlink(testfile);
1272 res = create_testfile(testfile, data, datalen);
1273 if (res == -1)
1274 return -1;
1275
1276 res = chmod(testfile, mode);
1277 if (res == -1) {
1278 PERROR("chmod");
1279 return -1;
1280 }
1281
1282 res = check_testfile_mode(testfile, mode);
1283 if (res == -1)
1284 return -1;
1285
1286 fd = open(testfile, flags);
1287 if (fd == -1) {
1288 if (err != errno) {
1289 PERROR("open");
1290 return -1;
1291 }
1292 } else {
1293 if (err) {
1294 ERROR("open should have failed");
1295 close(fd);
1296 return -1;
1297 }
1298 close(fd);
1299 }
1300
1301 res = unlink(testfile);
1302 if (res == -1) {
1303 PERROR("unlink");
1304 return -1;
1305 }
1306 res = check_nonexist(testfile);
1307 if (res == -1)
1308 return -1;
1309 res = check_nonexist(testfile_r);
1310 if (res == -1)
1311 return -1;
1312
1313 success();
1314 return 0;
1315}
1316
1317static int test_symlink(void)
1318{
1319 char buf[1024];
1320 const char *data = testdata;
1321 int datalen = testdatalen;
1322 int linklen = strlen(testfile);
1323 int err = 0;
1324 int res;
1325
1326 start_test("symlink");
1327 res = create_testfile(testfile, data, datalen);
1328 if (res == -1)
1329 return -1;
1330
1331 unlink(testfile2);
1332 res = symlink(testfile, testfile2);
1333 if (res == -1) {
1334 PERROR("symlink");
1335 return -1;
1336 }
1337 res = check_type(testfile2, S_IFLNK);
1338 if (res == -1)
1339 return -1;
1340 err += check_mode(testfile2, 0777);
1341 err += check_nlink(testfile2, 1);
1342 res = readlink(testfile2, buf, sizeof(buf));
1343 if (res == -1) {
1344 PERROR("readlink");
1345 err--;
1346 }
1347 if (res != linklen) {
1348 ERROR("short readlink: %u instead of %u", res, linklen);
1349 err--;
1350 }
1351 if (memcmp(buf, testfile, linklen) != 0) {
1352 ERROR("link mismatch");
1353 err--;
1354 }
1355 err += check_size(testfile2, datalen);
1356 err += check_data(testfile2, data, 0, datalen);
1357 res = unlink(testfile2);
1358 if (res == -1) {
1359 PERROR("unlink");
1360 return -1;
1361 }
1362 res = check_nonexist(testfile2);
1363 if (res == -1)
1364 return -1;
1365 if (err)
1366 return -1;
1367
1368 res = unlink(testfile);
1369 if (res == -1) {
1370 PERROR("unlink");
1371 return -1;
1372 }
1373 res = check_nonexist(testfile);
1374 if (res == -1)
1375 return -1;
1376
1377 success();
1378 return 0;
1379}
1380
1381static int test_link(void)
1382{
1383 const char *data = testdata;
1384 int datalen = testdatalen;
1385 int err = 0;
1386 int res;
1387
1388 start_test("link");
1389 res = create_testfile(testfile, data, datalen);
1390 if (res == -1)
1391 return -1;
1392
1393 unlink(testfile2);
1394 res = link(testfile, testfile2);
1395 if (res == -1) {
1396 PERROR("link");
1397 return -1;
1398 }
1399 res = check_type(testfile2, S_IFREG);
1400 if (res == -1)
1401 return -1;
1402 err += check_mode(testfile2, 0644);
1403 err += check_nlink(testfile2, 2);
1404 err += check_size(testfile2, datalen);
1405 err += check_data(testfile2, data, 0, datalen);
1406 res = unlink(testfile);
1407 if (res == -1) {
1408 PERROR("unlink");
1409 return -1;
1410 }
1411 res = check_nonexist(testfile);
1412 if (res == -1)
1413 return -1;
1414
1415 err += check_nlink(testfile2, 1);
1416 res = unlink(testfile2);
1417 if (res == -1) {
1418 PERROR("unlink");
1419 return -1;
1420 }
1421 res = check_nonexist(testfile2);
1422 if (res == -1)
1423 return -1;
1424 if (err)
1425 return -1;
1426
1427 success();
1428 return 0;
1429}
1430
1431static int test_link2(void)
1432{
1433 const char *data = testdata;
1434 int datalen = testdatalen;
1435 int err = 0;
1436 int res;
1437
1438 start_test("link-unlink-link");
1439 res = create_testfile(testfile, data, datalen);
1440 if (res == -1)
1441 return -1;
1442
1443 unlink(testfile2);
1444 res = link(testfile, testfile2);
1445 if (res == -1) {
1446 PERROR("link");
1447 return -1;
1448 }
1449 res = unlink(testfile);
1450 if (res == -1) {
1451 PERROR("unlink");
1452 return -1;
1453 }
1454 res = check_nonexist(testfile);
1455 if (res == -1)
1456 return -1;
1457 res = link(testfile2, testfile);
1458 if (res == -1) {
1459 PERROR("link");
1460 }
1461 res = check_type(testfile, S_IFREG);
1462 if (res == -1)
1463 return -1;
1464 err += check_mode(testfile, 0644);
1465 err += check_nlink(testfile, 2);
1466 err += check_size(testfile, datalen);
1467 err += check_data(testfile, data, 0, datalen);
1468
1469 res = unlink(testfile2);
1470 if (res == -1) {
1471 PERROR("unlink");
1472 return -1;
1473 }
1474 err += check_nlink(testfile, 1);
1475 res = unlink(testfile);
1476 if (res == -1) {
1477 PERROR("unlink");
1478 return -1;
1479 }
1480 res = check_nonexist(testfile);
1481 if (res == -1)
1482 return -1;
1483 if (err)
1484 return -1;
1485
1486 success();
1487 return 0;
1488}
1489
1490static int test_rename_file(void)
1491{
1492 const char *data = testdata;
1493 int datalen = testdatalen;
1494 int err = 0;
1495 int res;
1496
1497 start_test("rename file");
1498 res = create_testfile(testfile, data, datalen);
1499 if (res == -1)
1500 return -1;
1501
1502 unlink(testfile2);
1503 res = rename(testfile, testfile2);
1504 if (res == -1) {
1505 PERROR("rename");
1506 return -1;
1507 }
1508 res = check_nonexist(testfile);
1509 if (res == -1)
1510 return -1;
1511 res = check_type(testfile2, S_IFREG);
1512 if (res == -1)
1513 return -1;
1514 err += check_mode(testfile2, 0644);
1515 err += check_nlink(testfile2, 1);
1516 err += check_size(testfile2, datalen);
1517 err += check_data(testfile2, data, 0, datalen);
1518 res = unlink(testfile2);
1519 if (res == -1) {
1520 PERROR("unlink");
1521 return -1;
1522 }
1523 res = check_nonexist(testfile2);
1524 if (res == -1)
1525 return -1;
1526 if (err)
1527 return -1;
1528
1529 success();
1530 return 0;
1531}
1532
1533static int test_rename_dir(void)
1534{
1535 int err = 0;
1536 int res;
1537
1538 start_test("rename dir");
1539 res = create_dir(testdir, testdir_files);
1540 if (res == -1)
1541 return -1;
1542
1543 rmdir(testdir2);
1544 res = rename(testdir, testdir2);
1545 if (res == -1) {
1546 PERROR("rename");
1547 cleanup_dir(testdir, testdir_files, 1);
1548 return -1;
1549 }
1550 res = check_nonexist(testdir);
1551 if (res == -1) {
1552 cleanup_dir(testdir, testdir_files, 1);
1553 return -1;
1554 }
1555 res = check_type(testdir2, S_IFDIR);
1556 if (res == -1) {
1557 cleanup_dir(testdir2, testdir_files, 1);
1558 return -1;
1559 }
1560 err += check_mode(testdir2, 0755);
1561 err += check_dir_contents(testdir2, testdir_files);
1562 err += cleanup_dir(testdir2, testdir_files, 0);
1563 res = rmdir(testdir2);
1564 if (res == -1) {
1565 PERROR("rmdir");
1566 return -1;
1567 }
1568 res = check_nonexist(testdir2);
1569 if (res == -1)
1570 return -1;
1571 if (err)
1572 return -1;
1573
1574 success();
1575 return 0;
1576}
1577
1578static int test_rename_dir_loop(void)
1579{
1580#define PATH(p) (snprintf(path, sizeof path, "%s/%s", testdir, p), path)
1581#define PATH2(p) (snprintf(path2, sizeof path2, "%s/%s", testdir, p), path2)
1582
1583 char path[1280], path2[1280];
1584 int err = 0;
1585 int res;
1586
1587 start_test("rename dir loop");
1588
1589 res = create_dir(testdir, testdir_files);
1590 if (res == -1)
1591 return -1;
1592
1593 res = mkdir(PATH("a"), 0755);
1594 if (res == -1) {
1595 PERROR("mkdir");
1596 goto fail;
1597 }
1598
1599 res = rename(PATH("a"), PATH2("a"));
1600 if (res == -1) {
1601 PERROR("rename");
1602 goto fail;
1603 }
1604
1605 errno = 0;
1606 res = rename(PATH("a"), PATH2("a/b"));
1607 if (res == 0 || errno != EINVAL) {
1608 PERROR("rename");
1609 goto fail;
1610 }
1611
1612 res = mkdir(PATH("a/b"), 0755);
1613 if (res == -1) {
1614 PERROR("mkdir");
1615 goto fail;
1616 }
1617
1618 res = mkdir(PATH("a/b/c"), 0755);
1619 if (res == -1) {
1620 PERROR("mkdir");
1621 goto fail;
1622 }
1623
1624 errno = 0;
1625 res = rename(PATH("a"), PATH2("a/b/c"));
1626 if (res == 0 || errno != EINVAL) {
1627 PERROR("rename");
1628 goto fail;
1629 }
1630
1631 errno = 0;
1632 res = rename(PATH("a"), PATH2("a/b/c/a"));
1633 if (res == 0 || errno != EINVAL) {
1634 PERROR("rename");
1635 goto fail;
1636 }
1637
1638 errno = 0;
1639 res = rename(PATH("a/b/c"), PATH2("a"));
1640 if (res == 0 || errno != ENOTEMPTY) {
1641 PERROR("rename");
1642 goto fail;
1643 }
1644
1645 res = open(PATH("a/foo"), O_CREAT, 0644);
1646 if (res == -1) {
1647 PERROR("open");
1648 goto fail;
1649 }
1650 close(res);
1651
1652 res = rename(PATH("a/foo"), PATH2("a/bar"));
1653 if (res == -1) {
1654 PERROR("rename");
1655 goto fail;
1656 }
1657
1658 res = rename(PATH("a/bar"), PATH2("a/foo"));
1659 if (res == -1) {
1660 PERROR("rename");
1661 goto fail;
1662 }
1663
1664 res = rename(PATH("a/foo"), PATH2("a/b/bar"));
1665 if (res == -1) {
1666 PERROR("rename");
1667 goto fail;
1668 }
1669
1670 res = rename(PATH("a/b/bar"), PATH2("a/foo"));
1671 if (res == -1) {
1672 PERROR("rename");
1673 goto fail;
1674 }
1675
1676 res = rename(PATH("a/foo"), PATH2("a/b/c/bar"));
1677 if (res == -1) {
1678 PERROR("rename");
1679 goto fail;
1680 }
1681
1682 res = rename(PATH("a/b/c/bar"), PATH2("a/foo"));
1683 if (res == -1) {
1684 PERROR("rename");
1685 goto fail;
1686 }
1687
1688 res = open(PATH("a/bar"), O_CREAT, 0644);
1689 if (res == -1) {
1690 PERROR("open");
1691 goto fail;
1692 }
1693 close(res);
1694
1695 res = rename(PATH("a/foo"), PATH2("a/bar"));
1696 if (res == -1) {
1697 PERROR("rename");
1698 goto fail;
1699 }
1700
1701 unlink(PATH("a/bar"));
1702
1703 res = rename(PATH("a/b"), PATH2("a/d"));
1704 if (res == -1) {
1705 PERROR("rename");
1706 goto fail;
1707 }
1708
1709 res = rename(PATH("a/d"), PATH2("a/b"));
1710 if (res == -1) {
1711 PERROR("rename");
1712 goto fail;
1713 }
1714
1715 res = mkdir(PATH("a/d"), 0755);
1716 if (res == -1) {
1717 PERROR("mkdir");
1718 goto fail;
1719 }
1720
1721 res = rename(PATH("a/b"), PATH2("a/d"));
1722 if (res == -1) {
1723 PERROR("rename");
1724 goto fail;
1725 }
1726
1727 res = rename(PATH("a/d"), PATH2("a/b"));
1728 if (res == -1) {
1729 PERROR("rename");
1730 goto fail;
1731 }
1732
1733 res = mkdir(PATH("a/d"), 0755);
1734 if (res == -1) {
1735 PERROR("mkdir");
1736 goto fail;
1737 }
1738
1739 res = mkdir(PATH("a/d/e"), 0755);
1740 if (res == -1) {
1741 PERROR("mkdir");
1742 goto fail;
1743 }
1744
1745 errno = 0;
1746 res = rename(PATH("a/b"), PATH2("a/d"));
1747 if (res == 0 || (errno != ENOTEMPTY && errno != EEXIST)) {
1748 PERROR("rename");
1749 goto fail;
1750 }
1751
1752 rmdir(PATH("a/d/e"));
1753 rmdir(PATH("a/d"));
1754
1755 rmdir(PATH("a/b/c"));
1756 rmdir(PATH("a/b"));
1757 rmdir(PATH("a"));
1758
1759 err += cleanup_dir(testdir, testdir_files, 0);
1760 res = rmdir(testdir);
1761 if (res == -1) {
1762 PERROR("rmdir");
1763 goto fail;
1764 }
1765 res = check_nonexist(testdir);
1766 if (res == -1)
1767 return -1;
1768 if (err)
1769 return -1;
1770
1771 success();
1772 return 0;
1773
1774fail:
1775 unlink(PATH("a/bar"));
1776
1777 rmdir(PATH("a/d/e"));
1778 rmdir(PATH("a/d"));
1779
1780 rmdir(PATH("a/b/c"));
1781 rmdir(PATH("a/b"));
1782 rmdir(PATH("a"));
1783
1784 cleanup_dir(testdir, testdir_files, 1);
1785 rmdir(testdir);
1786
1787 return -1;
1788
1789#undef PATH2
1790#undef PATH
1791}
1792
1793static int test_mkfifo(void)
1794{
1795 int res;
1796 int err = 0;
1797
1798 start_test("mkfifo");
1799 unlink(testfile);
1800 res = mkfifo(testfile, 0644);
1801 if (res == -1) {
1802 PERROR("mkfifo");
1803 return -1;
1804 }
1805 res = check_type(testfile, S_IFIFO);
1806 if (res == -1)
1807 return -1;
1808 err += check_mode(testfile, 0644);
1809 err += check_nlink(testfile, 1);
1810 res = unlink(testfile);
1811 if (res == -1) {
1812 PERROR("unlink");
1813 return -1;
1814 }
1815 res = check_nonexist(testfile);
1816 if (res == -1)
1817 return -1;
1818 if (err)
1819 return -1;
1820
1821 success();
1822 return 0;
1823}
1824
1825static int test_mkdir(void)
1826{
1827 int res;
1828 int err = 0;
1829 const char *dir_contents[] = {NULL};
1830
1831 start_test("mkdir");
1832 rmdir(testdir);
1833 res = mkdir(testdir, 0755);
1834 if (res == -1) {
1835 PERROR("mkdir");
1836 return -1;
1837 }
1838 res = check_type(testdir, S_IFDIR);
1839 if (res == -1)
1840 return -1;
1841 err += check_mode(testdir, 0755);
1842 /* Some file systems (like btrfs) don't track link
1843 count for directories */
1844 //err += check_nlink(testdir, 2);
1845 err += check_dir_contents(testdir, dir_contents);
1846 res = rmdir(testdir);
1847 if (res == -1) {
1848 PERROR("rmdir");
1849 return -1;
1850 }
1851 res = check_nonexist(testdir);
1852 if (res == -1)
1853 return -1;
1854 if (err)
1855 return -1;
1856
1857 success();
1858 return 0;
1859}
1860
1861static int test_socket(void)
1862{
1863 struct sockaddr_un su;
1864 int fd;
1865 int res;
1866 int err = 0;
1867 const size_t test_sock_len = strlen(testsock) + 1;
1868
1869 start_test("socket");
1870 if (test_sock_len > sizeof(su.sun_path)) {
1871 fprintf(stderr, "Need to shorten mount point by %zu chars\n",
1872 strlen(testsock) + 1 - sizeof(su.sun_path));
1873 return -1;
1874 }
1875 unlink(testsock);
1876 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1877 if (fd < 0) {
1878 PERROR("socket");
1879 return -1;
1880 }
1881 su.sun_family = AF_UNIX;
1882
1883 strncpy(su.sun_path, testsock, test_sock_len);
1884 su.sun_path[sizeof(su.sun_path) - 1] = '\0';
1885 res = bind(fd, (struct sockaddr*)&su, sizeof(su));
1886 if (res == -1) {
1887 PERROR("bind");
1888 return -1;
1889 }
1890
1891 res = check_type(testsock, S_IFSOCK);
1892 if (res == -1) {
1893 close(fd);
1894 return -1;
1895 }
1896 err += check_nlink(testsock, 1);
1897 close(fd);
1898 res = unlink(testsock);
1899 if (res == -1) {
1900 PERROR("unlink");
1901 return -1;
1902 }
1903 res = check_nonexist(testsock);
1904 if (res == -1)
1905 return -1;
1906 if (err)
1907 return -1;
1908
1909 success();
1910 return 0;
1911}
1912
1913#define test_create_ro_dir(flags) \
1914 do_test_create_ro_dir(flags, #flags)
1915
1916static int do_test_create_ro_dir(int flags, const char *flags_str)
1917{
1918 int res;
1919 int err = 0;
1920 int fd;
1921
1922 start_test("open(%s) in read-only directory", flags_str);
1923 rmdir(testdir);
1924 res = mkdir(testdir, 0555);
1925 if (res == -1) {
1926 PERROR("mkdir");
1927 return -1;
1928 }
1929 fd = open(subfile, flags, 0644);
1930 if (fd != -1) {
1931 close(fd);
1932 unlink(subfile);
1933 ERROR("open should have failed");
1934 err--;
1935 } else {
1936 res = check_nonexist(subfile);
1937 if (res == -1)
1938 err--;
1939 }
1940 unlink(subfile);
1941 res = rmdir(testdir);
1942 if (res == -1) {
1943 PERROR("rmdir");
1944 return -1;
1945 }
1946 res = check_nonexist(testdir);
1947 if (res == -1)
1948 return -1;
1949 if (err)
1950 return -1;
1951
1952 success();
1953 return 0;
1954}
1955
1956#ifndef __FreeBSD__
1957/* this tests open with O_TMPFILE
1958 note that this will only work with the fuse low level api
1959 you will get ENOTSUP with the high level api */
1960static int test_create_tmpfile(void)
1961{
1962 rmdir(testdir);
1963 int res = mkdir(testdir, 0777);
1964 if (res)
1965 return -1;
1966
1967 start_test("create tmpfile");
1968
1969 int fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
1970 if(fd == -1) {
1971 if (errno == ENOTSUP) {
1972 /* don't bother if we're working on an old kernel
1973 or on the high level API */
1974 return 0;
1975 }
1976
1977 PERROR("open O_TMPFILE | O_RDWR");
1978 return -1;
1979 }
1980 close(fd);
1981
1982 fd = open(testdir, O_TMPFILE | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
1983 if(fd == -1){
1984 PERROR("open with O_TMPFILE | O_WRONLY | O_EXCL");
1985 return -1;
1986 };
1987 close(fd);
1988
1989 fd = open(testdir, O_TMPFILE | O_RDONLY, S_IRUSR);
1990 if (fd != -1) {
1991 ERROR("open with O_TMPFILE | O_RDONLY succeeded");
1992 return -1;
1993 }
1994
1995 success();
1996 return 0;
1997}
1998
1999static int test_create_and_link_tmpfile(void)
2000{
2001 /* skip this test for now since the github runner will fail in the linkat call below */
2002 return 0;
2003
2004 rmdir(testdir);
2005 unlink(testfile);
2006
2007 int res = mkdir(testdir, 0777);
2008 if (res)
2009 return -1;
2010
2011 start_test("create and link tmpfile");
2012
2013 int fd = open(testdir, O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
2014 if(fd == -1) {
2015 if (errno == ENOTSUP) {
2016 /* don't bother if we're working on an old kernel
2017 or on the high level API */
2018 return 0;
2019 }
2020 PERROR("open with O_TMPFILE | O_RDWR | O_EXCL");
2021 return -1;
2022 }
2023
2024 if (!linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
2025 ERROR("linkat succeeded on a tmpfile opened with O_EXCL");
2026 return -1;
2027 }
2028 close(fd);
2029
2030 fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
2031 if(fd == -1) {
2032 PERROR("open O_TMPFILE");
2033 return -1;
2034 }
2035
2036 if (check_nonexist(testfile)) {
2037 return -1;
2038 }
2039
2040 if (linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
2041 PERROR("linkat tempfile");
2042 return -1;
2043 }
2044 close(fd);
2045
2046 if (check_nlink(testfile, 1)) {
2047 return -1;
2048 }
2049 unlink(testfile);
2050
2051 success();
2052 return 0;
2053}
2054#endif
2055
2056int main(int argc, char *argv[])
2057{
2058 int err = 0;
2059 int a;
2060 int is_root;
2061
2062 umask(0);
2063 if (argc < 2 || argc > 4) {
2064 fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#] [-u]\n", argv[0]);
2065 return 1;
2066 }
2067 basepath = argv[1];
2068 basepath_r = basepath;
2069 for (a = 2; a < argc; a++) {
2070 char *endptr;
2071 char *arg = argv[a];
2072 if (arg[0] == ':') {
2073 basepath_r = arg + 1;
2074 } else {
2075 if (arg[0] == '-') {
2076 arg++;
2077 if (arg[0] == 'u') {
2078 unlinked_test = 1;
2079 endptr = arg + 1;
2080 } else {
2081 skip_test = strtoul(arg, &endptr, 10);
2082 }
2083 } else {
2084 select_test = strtoul(arg, &endptr, 10);
2085 }
2086 if (arg[0] == '\0' || *endptr != '\0') {
2087 fprintf(stderr, "invalid option: '%s'\n", argv[a]);
2088 return 1;
2089 }
2090 }
2091 }
2092 assert(strlen(basepath) < 512);
2093 assert(strlen(basepath_r) < 512);
2094 if (basepath[0] != '/') {
2095 fprintf(stderr, "testdir must be an absolute path\n");
2096 return 1;
2097 }
2098
2099 sprintf(testfile, "%s/testfile", basepath);
2100 sprintf(testfile2, "%s/testfile2", basepath);
2101 sprintf(testdir, "%s/testdir", basepath);
2102 sprintf(testdir2, "%s/testdir2", basepath);
2103 sprintf(subfile, "%s/subfile", testdir2);
2104 sprintf(testsock, "%s/testsock", basepath);
2105
2106 sprintf(testfile_r, "%s/testfile", basepath_r);
2107 sprintf(testfile2_r, "%s/testfile2", basepath_r);
2108 sprintf(testdir_r, "%s/testdir", basepath_r);
2109 sprintf(testdir2_r, "%s/testdir2", basepath_r);
2110 sprintf(subfile_r, "%s/subfile", testdir2_r);
2111
2112 is_root = (geteuid() == 0);
2113
2114 err += test_create();
2115 err += test_create_unlink();
2116 err += test_symlink();
2117 err += test_link();
2118 err += test_link2();
2119 err += test_mknod();
2120 err += test_mkfifo();
2121 err += test_mkdir();
2122 err += test_rename_file();
2123 err += test_rename_dir();
2124 err += test_rename_dir_loop();
2125 err += test_seekdir();
2126 err += test_socket();
2127 err += test_utime();
2128 err += test_truncate(0);
2129 err += test_truncate(testdatalen / 2);
2130 err += test_truncate(testdatalen);
2131 err += test_truncate(testdatalen + 100);
2132 err += test_ftruncate(0, 0600);
2133 err += test_ftruncate(testdatalen / 2, 0600);
2134 err += test_ftruncate(testdatalen, 0600);
2135 err += test_ftruncate(testdatalen + 100, 0600);
2136 err += test_ftruncate(0, 0400);
2137 err += test_ftruncate(0, 0200);
2138 err += test_ftruncate(0, 0000);
2139 err += test_open(0, O_RDONLY, 0);
2140 err += test_open(1, O_RDONLY, 0);
2141 err += test_open(1, O_RDWR, 0);
2142 err += test_open(1, O_WRONLY, 0);
2143 err += test_open(0, O_RDWR | O_CREAT, 0600);
2144 err += test_open(1, O_RDWR | O_CREAT, 0600);
2145 err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600);
2146 err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600);
2147 err += test_open(0, O_RDONLY | O_CREAT, 0600);
2148 err += test_open(0, O_RDONLY | O_CREAT, 0400);
2149 err += test_open(0, O_RDONLY | O_CREAT, 0200);
2150 err += test_open(0, O_RDONLY | O_CREAT, 0000);
2151 err += test_open(0, O_WRONLY | O_CREAT, 0600);
2152 err += test_open(0, O_WRONLY | O_CREAT, 0400);
2153 err += test_open(0, O_WRONLY | O_CREAT, 0200);
2154 err += test_open(0, O_WRONLY | O_CREAT, 0000);
2155 err += test_open(0, O_RDWR | O_CREAT, 0400);
2156 err += test_open(0, O_RDWR | O_CREAT, 0200);
2157 err += test_open(0, O_RDWR | O_CREAT, 0000);
2158 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600);
2159 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600);
2160 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000);
2161 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000);
2162 err += test_open_acc(O_RDONLY, 0600, 0);
2163 err += test_open_acc(O_WRONLY, 0600, 0);
2164 err += test_open_acc(O_RDWR, 0600, 0);
2165 err += test_open_acc(O_RDONLY, 0400, 0);
2166 err += test_open_acc(O_WRONLY, 0200, 0);
2167 if(!is_root) {
2168 err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES);
2169 err += test_open_acc(O_WRONLY, 0400, EACCES);
2170 err += test_open_acc(O_RDWR, 0400, EACCES);
2171 err += test_open_acc(O_RDONLY, 0200, EACCES);
2172 err += test_open_acc(O_RDWR, 0200, EACCES);
2173 err += test_open_acc(O_RDONLY, 0000, EACCES);
2174 err += test_open_acc(O_WRONLY, 0000, EACCES);
2175 err += test_open_acc(O_RDWR, 0000, EACCES);
2176 }
2177 err += test_create_ro_dir(O_CREAT);
2178 err += test_create_ro_dir(O_CREAT | O_EXCL);
2179 err += test_create_ro_dir(O_CREAT | O_WRONLY);
2180 err += test_create_ro_dir(O_CREAT | O_TRUNC);
2181 err += test_copy_file_range();
2182#ifndef __FreeBSD__
2183 err += test_create_tmpfile();
2184 err += test_create_and_link_tmpfile();
2185#endif
2186
2187 unlink(testfile2);
2188 unlink(testsock);
2189 rmdir(testdir);
2190 rmdir(testdir2);
2191
2192 if (err) {
2193 fprintf(stderr, "%i tests failed\n", -err);
2194 return 1;
2195 }
2196
2197 return check_unlinked_testfiles();
2198}
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2test__syscalls_8c_source.html0000644000175000017500000115750615156613443024445 0ustar berndbernd libfuse: fuse-3.18.1/test/test_syscalls.c Source File
libfuse
test_syscalls.c
1#define _GNU_SOURCE
2#include "fuse_config.h"
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <stdarg.h>
7#include <string.h>
8#include <unistd.h>
9#include <fcntl.h>
10#include <dirent.h>
11#include <utime.h>
12#include <errno.h>
13#include <assert.h>
14#include <time.h>
15#include <sys/socket.h>
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <sys/un.h>
19
20#ifndef ALLPERMS
21# define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)/* 07777 */
22#endif
23
24
25static const char *basepath;
26static const char *basepath_r;
27static char testfile[1024];
28static char testfile2[1024];
29static char testdir[1024];
30static char testdir2[1024];
31static char testsock[1024];
32static char subfile[1280];
33
34static char testfile_r[1024];
35static char testfile2_r[1024];
36static char testdir_r[1024];
37static char testdir2_r[1024];
38static char subfile_r[1280];
39
40static char testname[256];
41static char testdata[] = "abcdefghijklmnopqrstuvwxyz";
42static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./";
43static const char *testdir_files[] = { "f1", "f2", NULL};
44static long seekdir_offsets[4];
45static char zerodata[4096];
46static int testdatalen = sizeof(testdata) - 1;
47static int testdata2len = sizeof(testdata2) - 1;
48static unsigned int testnum = 0;
49static unsigned int select_test = 0;
50static unsigned int skip_test = 0;
51static unsigned int unlinked_test = 0;
52
53#define MAX_ENTRIES 1024
54#define MAX_TESTS 100
55
56static struct test {
57 int fd;
58 struct stat stat;
59} tests[MAX_TESTS];
60
61static void test_perror(const char *func, const char *msg)
62{
63 fprintf(stderr, "%s %s() - %s: %s\n", testname, func, msg,
64 strerror(errno));
65}
66
67static void test_error(const char *func, const char *msg, ...)
68 __attribute__ ((format (printf, 2, 3)));
69
70static void __start_test(const char *fmt, ...)
71 __attribute__ ((format (printf, 1, 2)));
72
73static void test_error(const char *func, const char *msg, ...)
74{
75 va_list ap;
76 fprintf(stderr, "%s %s() - ", testname, func);
77 va_start(ap, msg);
78 vfprintf(stderr, msg, ap);
79 va_end(ap);
80 fprintf(stderr, "\n");
81}
82
83static int is_dot_or_dotdot(const char *name) {
84 return name[0] == '.' &&
85 (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
86}
87
88static void success(void)
89{
90 fprintf(stderr, "%s OK\n", testname);
91}
92
93#define this_test (&tests[testnum-1])
94#define next_test (&tests[testnum])
95
96static void __start_test(const char *fmt, ...)
97{
98 unsigned int n;
99 va_list ap;
100 n = sprintf(testname, "%3i [", testnum);
101 va_start(ap, fmt);
102 n += vsprintf(testname + n, fmt, ap);
103 va_end(ap);
104 sprintf(testname + n, "]");
105 // Use dedicated testfile per test
106 sprintf(testfile, "%s/testfile.%d", basepath, testnum);
107 sprintf(testfile_r, "%s/testfile.%d", basepath_r, testnum);
108 if (testnum > MAX_TESTS) {
109 fprintf(stderr, "%s - too many tests\n", testname);
110 exit(1);
111 }
112 this_test->fd = -1;
113}
114
115#define start_test(msg, args...) { \
116 testnum++; \
117 if ((select_test && testnum != select_test) || \
118 (testnum == skip_test)) { \
119 return 0; \
120 } \
121 __start_test(msg, ##args); \
122}
123
124#define PERROR(msg) test_perror(__FUNCTION__, msg)
125#define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args)
126
127#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
128
129static int st_check_size(struct stat *st, int len)
130{
131 if (st->st_size != len) {
132 ERROR("length %u instead of %u", (int) st->st_size,
133 (int) len);
134 return -1;
135 }
136 return 0;
137}
138
139static int check_size(const char *path, int len)
140{
141 struct stat stbuf;
142 int res = stat(path, &stbuf);
143 if (res == -1) {
144 PERROR("stat");
145 return -1;
146 }
147 return st_check_size(&stbuf, len);
148}
149
150static int check_testfile_size(const char *path, int len)
151{
152 this_test->stat.st_size = len;
153 return check_size(path, len);
154}
155
156static int st_check_type(struct stat *st, mode_t type)
157{
158 if ((st->st_mode & S_IFMT) != type) {
159 ERROR("type 0%o instead of 0%o", st->st_mode & S_IFMT, type);
160 return -1;
161 }
162 return 0;
163}
164
165static int check_type(const char *path, mode_t type)
166{
167 struct stat stbuf;
168 int res = lstat(path, &stbuf);
169 if (res == -1) {
170 PERROR("lstat");
171 return -1;
172 }
173 return st_check_type(&stbuf, type);
174}
175
176static int st_check_mode(struct stat *st, mode_t mode)
177{
178 if ((st->st_mode & ALLPERMS) != mode) {
179 ERROR("mode 0%o instead of 0%o", st->st_mode & ALLPERMS,
180 mode);
181 return -1;
182 }
183 return 0;
184}
185
186static int check_mode(const char *path, mode_t mode)
187{
188 struct stat stbuf;
189 int res = lstat(path, &stbuf);
190 if (res == -1) {
191 PERROR("lstat");
192 return -1;
193 }
194 return st_check_mode(&stbuf, mode);
195}
196
197static int check_testfile_mode(const char *path, mode_t mode)
198{
199 this_test->stat.st_mode &= ~ALLPERMS;
200 this_test->stat.st_mode |= mode;
201 return check_mode(path, mode);
202}
203
204static int check_times(const char *path, time_t atime, time_t mtime)
205{
206 int err = 0;
207 struct stat stbuf;
208 int res = lstat(path, &stbuf);
209 if (res == -1) {
210 PERROR("lstat");
211 return -1;
212 }
213 if (stbuf.st_atime != atime) {
214 ERROR("atime %li instead of %li", stbuf.st_atime, atime);
215 err--;
216 }
217 if (stbuf.st_mtime != mtime) {
218 ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
219 err--;
220 }
221 if (err)
222 return -1;
223
224 return 0;
225}
226
227#if 0
228static int fcheck_times(int fd, time_t atime, time_t mtime)
229{
230 int err = 0;
231 struct stat stbuf;
232 int res = fstat(fd, &stbuf);
233 if (res == -1) {
234 PERROR("fstat");
235 return -1;
236 }
237 if (stbuf.st_atime != atime) {
238 ERROR("atime %li instead of %li", stbuf.st_atime, atime);
239 err--;
240 }
241 if (stbuf.st_mtime != mtime) {
242 ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
243 err--;
244 }
245 if (err)
246 return -1;
247
248 return 0;
249}
250#endif
251
252static int st_check_nlink(struct stat *st, nlink_t nlink)
253{
254 if (st->st_nlink != nlink) {
255 ERROR("nlink %li instead of %li", (long) st->st_nlink,
256 (long) nlink);
257 return -1;
258 }
259 return 0;
260}
261
262static int check_nlink(const char *path, nlink_t nlink)
263{
264 struct stat stbuf;
265 int res = lstat(path, &stbuf);
266 if (res == -1) {
267 PERROR("lstat");
268 return -1;
269 }
270 return st_check_nlink(&stbuf, nlink);
271}
272
273static int fcheck_stat(int fd, int flags, struct stat *st)
274{
275 struct stat stbuf;
276 int res = fstat(fd, &stbuf);
277 if (res == -1) {
278 if (flags & O_PATH) {
279 // With O_PATH fd, the server does not have to keep
280 // the inode alive so FUSE inode may be stale or bad
281 if (errno == ESTALE || errno == EIO ||
282 errno == ENOENT || errno == EBADF)
283 return 0;
284 }
285 PERROR("fstat");
286 return -1;
287 }
288
289 int err = 0;
290 err += st_check_type(&stbuf, st->st_mode & S_IFMT);
291 err += st_check_mode(&stbuf, st->st_mode & ALLPERMS);
292 err += st_check_size(&stbuf, st->st_size);
293 err += st_check_nlink(&stbuf, st->st_nlink);
294
295 return err;
296}
297
298static int check_nonexist(const char *path)
299{
300 struct stat stbuf;
301 int res = lstat(path, &stbuf);
302 if (res == 0) {
303 ERROR("file should not exist");
304 return -1;
305 }
306 if (errno != ENOENT) {
307 ERROR("file should not exist: %s", strerror(errno));
308 return -1;
309 }
310 return 0;
311}
312
313static int check_buffer(const char *buf, const char *data, unsigned len)
314{
315 if (memcmp(buf, data, len) != 0) {
316 ERROR("data mismatch");
317 return -1;
318 }
319 return 0;
320}
321
322static int check_data(const char *path, const char *data, int offset,
323 unsigned len)
324{
325 char buf[4096];
326 int res;
327 int fd = open(path, O_RDONLY);
328 if (fd == -1) {
329 PERROR("open");
330 return -1;
331 }
332 if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
333 PERROR("lseek");
334 close(fd);
335 return -1;
336 }
337 while (len) {
338 int rdlen = len < sizeof(buf) ? len : sizeof(buf);
339 res = read(fd, buf, rdlen);
340 if (res == -1) {
341 PERROR("read");
342 close(fd);
343 return -1;
344 }
345 if (res != rdlen) {
346 ERROR("short read: %u instead of %u", res, rdlen);
347 close(fd);
348 return -1;
349 }
350 if (check_buffer(buf, data, rdlen) != 0) {
351 close(fd);
352 return -1;
353 }
354 data += rdlen;
355 len -= rdlen;
356 }
357 res = close(fd);
358 if (res == -1) {
359 PERROR("close");
360 return -1;
361 }
362 return 0;
363}
364
365static int fcheck_data(int fd, const char *data, int offset,
366 unsigned len)
367{
368 char buf[4096];
369 int res;
370 if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
371 PERROR("lseek");
372 return -1;
373 }
374 while (len) {
375 int rdlen = len < sizeof(buf) ? len : sizeof(buf);
376 res = read(fd, buf, rdlen);
377 if (res == -1) {
378 PERROR("read");
379 return -1;
380 }
381 if (res != rdlen) {
382 ERROR("short read: %u instead of %u", res, rdlen);
383 return -1;
384 }
385 if (check_buffer(buf, data, rdlen) != 0) {
386 return -1;
387 }
388 data += rdlen;
389 len -= rdlen;
390 }
391 return 0;
392}
393
394static int check_dir_contents(const char *path, const char **contents)
395{
396 int i;
397 int res;
398 int err = 0;
399 int found[MAX_ENTRIES];
400 const char *cont[MAX_ENTRIES];
401 DIR *dp;
402
403 for (i = 0; contents[i]; i++) {
404 assert(i < MAX_ENTRIES - 3);
405 found[i] = 0;
406 cont[i] = contents[i];
407 }
408 cont[i] = NULL;
409
410 dp = opendir(path);
411 if (dp == NULL) {
412 PERROR("opendir");
413 return -1;
414 }
415 memset(found, 0, sizeof(found));
416 while(1) {
417 struct dirent *de;
418 errno = 0;
419 de = readdir(dp);
420 if (de == NULL) {
421 if (errno) {
422 PERROR("readdir");
423 closedir(dp);
424 return -1;
425 }
426 break;
427 }
428 if (is_dot_or_dotdot(de->d_name))
429 continue;
430 for (i = 0; cont[i] != NULL; i++) {
431 assert(i < MAX_ENTRIES);
432 if (strcmp(cont[i], de->d_name) == 0) {
433 if (found[i]) {
434 ERROR("duplicate entry <%s>",
435 de->d_name);
436 err--;
437 } else
438 found[i] = 1;
439 break;
440 }
441 }
442 if (!cont[i]) {
443 ERROR("unexpected entry <%s>", de->d_name);
444 err --;
445 }
446 }
447 for (i = 0; cont[i] != NULL; i++) {
448 if (!found[i]) {
449 ERROR("missing entry <%s>", cont[i]);
450 err--;
451 }
452 }
453 res = closedir(dp);
454 if (res == -1) {
455 PERROR("closedir");
456 return -1;
457 }
458 if (err)
459 return -1;
460
461 return 0;
462}
463
464static int create_file(const char *path, const char *data, int len)
465{
466 int res;
467 int fd;
468
469 unlink(path);
470 fd = creat(path, 0644);
471 if (fd == -1) {
472 PERROR("creat");
473 return -1;
474 }
475 if (len) {
476 res = write(fd, data, len);
477 if (res == -1) {
478 PERROR("write");
479 close(fd);
480 return -1;
481 }
482 if (res != len) {
483 ERROR("write is short: %u instead of %u", res, len);
484 close(fd);
485 return -1;
486 }
487 }
488 res = close(fd);
489 if (res == -1) {
490 PERROR("close");
491 return -1;
492 }
493 res = check_type(path, S_IFREG);
494 if (res == -1)
495 return -1;
496 res = check_mode(path, 0644);
497 if (res == -1)
498 return -1;
499 res = check_nlink(path, 1);
500 if (res == -1)
501 return -1;
502 res = check_size(path, len);
503 if (res == -1)
504 return -1;
505
506 if (len) {
507 res = check_data(path, data, 0, len);
508 if (res == -1)
509 return -1;
510 }
511
512 return 0;
513}
514
515static int create_path_fd(const char *path, const char *data, int len)
516{
517 int path_fd;
518 int res;
519
520 res = create_file(path, data, len);
521 if (res == -1)
522 return -1;
523
524 path_fd = open(path, O_PATH);
525 if (path_fd == -1)
526 PERROR("open(O_PATH)");
527
528 return path_fd;
529}
530
531// Can be called once per test
532static int create_testfile(const char *path, const char *data, int len)
533{
534 struct test *t = this_test;
535 struct stat *st = &t->stat;
536 int res, fd;
537
538 if (t->fd > 0) {
539 ERROR("testfile already created");
540 return -1;
541 }
542
543 fd = create_path_fd(path, data, len);
544 if (fd == -1)
545 return -1;
546
547 t->fd = fd;
548
549 res = fstat(fd, st);
550 if (res == -1) {
551 PERROR("fstat");
552 return -1;
553 }
554
555 return 0;
556}
557
558static int check_unlinked_testfile(int fd)
559{
560 struct stat *st = &this_test->stat;
561
562 st->st_nlink = 0;
563 return fcheck_stat(fd, O_PATH, st);
564}
565
566// Check recorded testfiles after all tests completed
567static int check_unlinked_testfiles(void)
568{
569 int fd;
570 int res, err = 0;
571 int num = testnum;
572
573 if (!unlinked_test)
574 return 0;
575
576 testnum = 0;
577 while (testnum < num) {
578 fd = next_test->fd;
579 start_test("check_unlinked_testfile");
580 if (fd == -1)
581 continue;
582
583 err += check_unlinked_testfile(fd);
584 res = close(fd);
585 if (res == -1) {
586 PERROR("close(test_fd)");
587 err--;
588 }
589 }
590
591 if (err) {
592 fprintf(stderr, "%i unlinked testfile checks failed\n", -err);
593 return 1;
594 }
595
596 return err;
597}
598
599static int cleanup_dir(const char *path, const char **dir_files, int quiet)
600{
601 int i;
602 int err = 0;
603
604 for (i = 0; dir_files[i]; i++) {
605 int res;
606 char fpath[1280];
607 sprintf(fpath, "%s/%s", path, dir_files[i]);
608 res = unlink(fpath);
609 if (res == -1 && !quiet) {
610 PERROR("unlink");
611 err --;
612 }
613 }
614 if (err)
615 return -1;
616
617 return 0;
618}
619
620static int create_dir(const char *path, const char **dir_files)
621{
622 int res;
623 int i;
624
625 rmdir(path);
626 res = mkdir(path, 0755);
627 if (res == -1) {
628 PERROR("mkdir");
629 return -1;
630 }
631 res = check_type(path, S_IFDIR);
632 if (res == -1)
633 return -1;
634 res = check_mode(path, 0755);
635 if (res == -1)
636 return -1;
637
638 for (i = 0; dir_files[i]; i++) {
639 char fpath[1280];
640 sprintf(fpath, "%s/%s", path, dir_files[i]);
641 res = create_file(fpath, "", 0);
642 if (res == -1) {
643 cleanup_dir(path, dir_files, 1);
644 return -1;
645 }
646 }
647 res = check_dir_contents(path, dir_files);
648 if (res == -1) {
649 cleanup_dir(path, dir_files, 1);
650 return -1;
651 }
652
653 return 0;
654}
655
656static int test_truncate(int len)
657{
658 const char *data = testdata;
659 int datalen = testdatalen;
660 int res;
661
662 start_test("truncate(%u)", (int) len);
663 res = create_testfile(testfile, data, datalen);
664 if (res == -1)
665 return -1;
666
667 res = truncate(testfile, len);
668 if (res == -1) {
669 PERROR("truncate");
670 return -1;
671 }
672 res = check_testfile_size(testfile, len);
673 if (res == -1)
674 return -1;
675
676 if (len > 0) {
677 if (len <= datalen) {
678 res = check_data(testfile, data, 0, len);
679 if (res == -1)
680 return -1;
681 } else {
682 res = check_data(testfile, data, 0, datalen);
683 if (res == -1)
684 return -1;
685 res = check_data(testfile, zerodata, datalen,
686 len - datalen);
687 if (res == -1)
688 return -1;
689 }
690 }
691 res = unlink(testfile);
692 if (res == -1) {
693 PERROR("unlink");
694 return -1;
695 }
696 res = check_nonexist(testfile);
697 if (res == -1)
698 return -1;
699
700 success();
701 return 0;
702}
703
704static int test_ftruncate(int len, int mode)
705{
706 const char *data = testdata;
707 int datalen = testdatalen;
708 int res;
709 int fd;
710
711 start_test("ftruncate(%u) mode: 0%03o", len, mode);
712 res = create_testfile(testfile, data, datalen);
713 if (res == -1)
714 return -1;
715
716 fd = open(testfile, O_WRONLY);
717 if (fd == -1) {
718 PERROR("open");
719 return -1;
720 }
721
722 res = fchmod(fd, mode);
723 if (res == -1) {
724 PERROR("fchmod");
725 close(fd);
726 return -1;
727 }
728 res = check_testfile_mode(testfile, mode);
729 if (res == -1) {
730 close(fd);
731 return -1;
732 }
733 res = ftruncate(fd, len);
734 if (res == -1) {
735 PERROR("ftruncate");
736 close(fd);
737 return -1;
738 }
739 close(fd);
740 res = check_testfile_size(testfile, len);
741 if (res == -1)
742 return -1;
743
744 if (len > 0) {
745 if (len <= datalen) {
746 res = check_data(testfile, data, 0, len);
747 if (res == -1)
748 return -1;
749 } else {
750 res = check_data(testfile, data, 0, datalen);
751 if (res == -1)
752 return -1;
753 res = check_data(testfile, zerodata, datalen,
754 len - datalen);
755 if (res == -1)
756 return -1;
757 }
758 }
759 res = unlink(testfile);
760 if (res == -1) {
761 PERROR("unlink");
762 return -1;
763 }
764 res = check_nonexist(testfile);
765 if (res == -1)
766 return -1;
767
768 success();
769 return 0;
770}
771
772static int test_seekdir(void)
773{
774 int i;
775 int res;
776 DIR *dp;
777 struct dirent *de = NULL;
778
779 start_test("seekdir");
780 res = create_dir(testdir, testdir_files);
781 if (res == -1)
782 return res;
783
784 dp = opendir(testdir);
785 if (dp == NULL) {
786 PERROR("opendir");
787 return -1;
788 }
789
790 /* Remember dir offsets */
791 for (i = 0; i < ARRAY_SIZE(seekdir_offsets); i++) {
792 seekdir_offsets[i] = telldir(dp);
793 errno = 0;
794 de = readdir(dp);
795 if (de == NULL) {
796 if (errno) {
797 PERROR("readdir");
798 goto fail;
799 }
800 break;
801 }
802 }
803
804 /* Walk until the end of directory */
805 while (de)
806 de = readdir(dp);
807
808 /* Start from the last valid dir offset and seek backwards */
809 for (i--; i >= 0; i--) {
810 seekdir(dp, seekdir_offsets[i]);
811 de = readdir(dp);
812 if (de == NULL) {
813 ERROR("Unexpected end of directory after seekdir()");
814 goto fail;
815 }
816 }
817
818 closedir(dp);
819 res = cleanup_dir(testdir, testdir_files, 0);
820 if (!res)
821 success();
822 return res;
823fail:
824 closedir(dp);
825 cleanup_dir(testdir, testdir_files, 1);
826 return -1;
827}
828
829#ifdef HAVE_COPY_FILE_RANGE
830static int test_copy_file_range(void)
831{
832 const char *data = testdata;
833 int datalen = testdatalen;
834 int err = 0;
835 int res;
836 int fd_in, fd_out;
837 off_t pos_in = 0, pos_out = 0;
838
839 start_test("copy_file_range");
840 unlink(testfile);
841 fd_in = open(testfile, O_CREAT | O_RDWR, 0644);
842 if (fd_in == -1) {
843 PERROR("creat");
844 return -1;
845 }
846 res = write(fd_in, data, datalen);
847 if (res == -1) {
848 PERROR("write");
849 close(fd_in);
850 return -1;
851 }
852 if (res != datalen) {
853 ERROR("write is short: %u instead of %u", res, datalen);
854 close(fd_in);
855 return -1;
856 }
857
858 unlink(testfile2);
859 fd_out = creat(testfile2, 0644);
860 if (fd_out == -1) {
861 PERROR("creat");
862 close(fd_in);
863 return -1;
864 }
865 res = copy_file_range(fd_in, &pos_in, fd_out, &pos_out, datalen, 0);
866 if (res == -1) {
867 PERROR("copy_file_range");
868 close(fd_in);
869 close(fd_out);
870 return -1;
871 }
872 if (res != datalen) {
873 ERROR("copy is short: %u instead of %u", res, datalen);
874 close(fd_in);
875 close(fd_out);
876 return -1;
877 }
878
879 res = close(fd_in);
880 if (res == -1) {
881 PERROR("close");
882 close(fd_out);
883 return -1;
884 }
885 res = close(fd_out);
886 if (res == -1) {
887 PERROR("close");
888 return -1;
889 }
890
891 err = check_data(testfile2, data, 0, datalen);
892
893 res = unlink(testfile);
894 if (res == -1) {
895 PERROR("unlink");
896 return -1;
897 }
898 res = check_nonexist(testfile);
899 if (res == -1)
900 return -1;
901 if (err)
902 return -1;
903
904 res = unlink(testfile2);
905 if (res == -1) {
906 PERROR("unlink");
907 return -1;
908 }
909 res = check_nonexist(testfile2);
910 if (res == -1)
911 return -1;
912 if (err)
913 return -1;
914
915 success();
916 return 0;
917}
918#else
919static int test_copy_file_range(void)
920{
921 return 0;
922}
923#endif
924
925#ifdef HAVE_STATX
926static int test_statx(void)
927{
928 struct statx sb;
929 char msg[] = "hi";
930 size_t msg_size = sizeof(msg);
931 struct timespec tp;
932 int res;
933
934 memset(&sb, 0, sizeof(sb));
935 unlink(testfile);
936
937 start_test("statx");
938
939 res = create_testfile(testfile, msg, msg_size);
940 if (res == -1)
941 return -1;
942
943 res = statx(-1, testfile, AT_EMPTY_PATH,
944 STATX_BASIC_STATS | STATX_BTIME, &sb);
945 if (res == -1)
946 return -1;
947
948 if (sb.stx_size != msg_size)
949 return -1;
950
951 clock_gettime(CLOCK_REALTIME, &tp);
952
953 if (sb.stx_btime.tv_sec > tp.tv_sec)
954 return -1;
955
956 if (sb.stx_btime.tv_sec == tp.tv_sec &&
957 sb.stx_btime.tv_nsec >= tp.tv_nsec)
958 return -1;
959
960 unlink(testfile);
961
962 success();
963 return 0;
964}
965#else
966static int test_statx(void)
967{
968 return 0;
969}
970#endif
971
972static int test_utime(void)
973{
974 struct utimbuf utm;
975 time_t atime = 987631200;
976 time_t mtime = 123116400;
977 int res;
978
979 start_test("utime");
980 res = create_testfile(testfile, NULL, 0);
981 if (res == -1)
982 return -1;
983
984 utm.actime = atime;
985 utm.modtime = mtime;
986 res = utime(testfile, &utm);
987 if (res == -1) {
988 PERROR("utime");
989 return -1;
990 }
991 res = check_times(testfile, atime, mtime);
992 if (res == -1) {
993 return -1;
994 }
995 res = unlink(testfile);
996 if (res == -1) {
997 PERROR("unlink");
998 return -1;
999 }
1000 res = check_nonexist(testfile);
1001 if (res == -1)
1002 return -1;
1003
1004 success();
1005 return 0;
1006}
1007
1008static int test_create(void)
1009{
1010 const char *data = testdata;
1011 int datalen = testdatalen;
1012 int err = 0;
1013 int res;
1014 int fd;
1015
1016 start_test("create");
1017 unlink(testfile);
1018 fd = creat(testfile, 0644);
1019 if (fd == -1) {
1020 PERROR("creat");
1021 return -1;
1022 }
1023 res = write(fd, data, datalen);
1024 if (res == -1) {
1025 PERROR("write");
1026 close(fd);
1027 return -1;
1028 }
1029 if (res != datalen) {
1030 ERROR("write is short: %u instead of %u", res, datalen);
1031 close(fd);
1032 return -1;
1033 }
1034 res = close(fd);
1035 if (res == -1) {
1036 PERROR("close");
1037 return -1;
1038 }
1039 res = check_type(testfile, S_IFREG);
1040 if (res == -1)
1041 return -1;
1042 err += check_mode(testfile, 0644);
1043 err += check_nlink(testfile, 1);
1044 err += check_size(testfile, datalen);
1045 err += check_data(testfile, data, 0, datalen);
1046 res = unlink(testfile);
1047 if (res == -1) {
1048 PERROR("unlink");
1049 return -1;
1050 }
1051 res = check_nonexist(testfile);
1052 if (res == -1)
1053 return -1;
1054 if (err)
1055 return -1;
1056
1057 success();
1058 return 0;
1059}
1060
1061static int test_create_unlink(void)
1062{
1063 const char *data = testdata;
1064 int datalen = testdatalen;
1065 int err = 0;
1066 int res;
1067 int fd;
1068
1069 start_test("create+unlink");
1070 unlink(testfile);
1071 fd = open(testfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
1072 if (fd == -1) {
1073 PERROR("creat");
1074 return -1;
1075 }
1076 res = unlink(testfile);
1077 if (res == -1) {
1078 PERROR("unlink");
1079 close(fd);
1080 return -1;
1081 }
1082 res = check_nonexist(testfile);
1083 if (res == -1) {
1084 close(fd);
1085 return -1;
1086 }
1087 res = write(fd, data, datalen);
1088 if (res == -1) {
1089 PERROR("write");
1090 close(fd);
1091 return -1;
1092 }
1093 if (res != datalen) {
1094 ERROR("write is short: %u instead of %u", res, datalen);
1095 close(fd);
1096 return -1;
1097 }
1098 struct stat st = {
1099 .st_mode = S_IFREG | 0644,
1100 .st_size = datalen,
1101 };
1102 err = fcheck_stat(fd, O_RDWR, &st);
1103 err += fcheck_data(fd, data, 0, datalen);
1104 res = close(fd);
1105 if (res == -1) {
1106 PERROR("close");
1107 err--;
1108 }
1109 if (err)
1110 return -1;
1111
1112 success();
1113 return 0;
1114}
1115
1116static int test_mknod(void)
1117{
1118 int err = 0;
1119 int res;
1120
1121 start_test("mknod");
1122 unlink(testfile);
1123 res = mknod(testfile, 0644, 0);
1124 if (res == -1) {
1125 PERROR("mknod");
1126 return -1;
1127 }
1128 res = check_type(testfile, S_IFREG);
1129 if (res == -1)
1130 return -1;
1131 err += check_mode(testfile, 0644);
1132 err += check_nlink(testfile, 1);
1133 err += check_size(testfile, 0);
1134 res = unlink(testfile);
1135 if (res == -1) {
1136 PERROR("unlink");
1137 return -1;
1138 }
1139 res = check_nonexist(testfile);
1140 if (res == -1)
1141 return -1;
1142 if (err)
1143 return -1;
1144
1145 success();
1146 return 0;
1147}
1148
1149#define test_open(exist, flags, mode) do_test_open(exist, flags, #flags, mode)
1150
1151static int do_test_open(int exist, int flags, const char *flags_str, int mode)
1152{
1153 char buf[4096];
1154 const char *data = testdata;
1155 int datalen = testdatalen;
1156 unsigned currlen = 0;
1157 int err = 0;
1158 int res;
1159 int fd;
1160 off_t off;
1161
1162 start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode);
1163 unlink(testfile);
1164 if (exist) {
1165 res = create_file(testfile_r, testdata2, testdata2len);
1166 if (res == -1)
1167 return -1;
1168
1169 currlen = testdata2len;
1170 }
1171
1172 fd = open(testfile, flags, mode);
1173 if ((flags & O_CREAT) && (flags & O_EXCL) && exist) {
1174 if (fd != -1) {
1175 ERROR("open should have failed");
1176 close(fd);
1177 return -1;
1178 } else if (errno == EEXIST)
1179 goto succ;
1180 }
1181 if (!(flags & O_CREAT) && !exist) {
1182 if (fd != -1) {
1183 ERROR("open should have failed");
1184 close(fd);
1185 return -1;
1186 } else if (errno == ENOENT)
1187 goto succ;
1188 }
1189 if (fd == -1) {
1190 PERROR("open");
1191 return -1;
1192 }
1193
1194 if (flags & O_TRUNC)
1195 currlen = 0;
1196
1197 err += check_type(testfile, S_IFREG);
1198 if (exist)
1199 err += check_mode(testfile, 0644);
1200 else
1201 err += check_mode(testfile, mode);
1202 err += check_nlink(testfile, 1);
1203 err += check_size(testfile, currlen);
1204 if (exist && !(flags & O_TRUNC) && (mode & S_IRUSR))
1205 err += check_data(testfile, testdata2, 0, testdata2len);
1206
1207 res = write(fd, data, datalen);
1208 if ((flags & O_ACCMODE) != O_RDONLY) {
1209 if (res == -1) {
1210 PERROR("write");
1211 err --;
1212 } else if (res != datalen) {
1213 ERROR("write is short: %u instead of %u", res, datalen);
1214 err --;
1215 } else {
1216 if (datalen > (int) currlen)
1217 currlen = datalen;
1218
1219 err += check_size(testfile, currlen);
1220
1221 if (mode & S_IRUSR) {
1222 err += check_data(testfile, data, 0, datalen);
1223 if (exist && !(flags & O_TRUNC) &&
1224 testdata2len > datalen)
1225 err += check_data(testfile,
1226 testdata2 + datalen,
1227 datalen,
1228 testdata2len - datalen);
1229 }
1230 }
1231 } else {
1232 if (res != -1) {
1233 ERROR("write should have failed");
1234 err --;
1235 } else if (errno != EBADF) {
1236 PERROR("write");
1237 err --;
1238 }
1239 }
1240 off = lseek(fd, SEEK_SET, 0);
1241 if (off == (off_t) -1) {
1242 PERROR("lseek");
1243 err--;
1244 } else if (off != 0) {
1245 ERROR("offset should have returned 0");
1246 err --;
1247 }
1248 res = read(fd, buf, sizeof(buf));
1249 if ((flags & O_ACCMODE) != O_WRONLY) {
1250 if (res == -1) {
1251 PERROR("read");
1252 err--;
1253 } else {
1254 int readsize =
1255 currlen < sizeof(buf) ? currlen : sizeof(buf);
1256 if (res != readsize) {
1257 ERROR("read is short: %i instead of %u",
1258 res, readsize);
1259 err--;
1260 } else {
1261 if ((flags & O_ACCMODE) != O_RDONLY) {
1262 err += check_buffer(buf, data, datalen);
1263 if (exist && !(flags & O_TRUNC) &&
1264 testdata2len > datalen)
1265 err += check_buffer(buf + datalen,
1266 testdata2 + datalen,
1267 testdata2len - datalen);
1268 } else if (exist)
1269 err += check_buffer(buf, testdata2,
1270 testdata2len);
1271 }
1272 }
1273 } else {
1274 if (res != -1) {
1275 ERROR("read should have failed");
1276 err --;
1277 } else if (errno != EBADF) {
1278 PERROR("read");
1279 err --;
1280 }
1281 }
1282
1283 res = close(fd);
1284 if (res == -1) {
1285 PERROR("close");
1286 return -1;
1287 }
1288 res = unlink(testfile);
1289 if (res == -1) {
1290 PERROR("unlink");
1291 return -1;
1292 }
1293 res = check_nonexist(testfile);
1294 if (res == -1)
1295 return -1;
1296 res = check_nonexist(testfile_r);
1297 if (res == -1)
1298 return -1;
1299 if (err)
1300 return -1;
1301
1302succ:
1303 success();
1304 return 0;
1305}
1306
1307#define test_open_acc(flags, mode, err) \
1308 do_test_open_acc(flags, #flags, mode, err)
1309
1310static int do_test_open_acc(int flags, const char *flags_str, int mode, int err)
1311{
1312 const char *data = testdata;
1313 int datalen = testdatalen;
1314 int res;
1315 int fd;
1316
1317 start_test("open_acc(%s) mode: 0%03o message: '%s'", flags_str, mode,
1318 strerror(err));
1319 unlink(testfile);
1320 res = create_testfile(testfile, data, datalen);
1321 if (res == -1)
1322 return -1;
1323
1324 res = chmod(testfile, mode);
1325 if (res == -1) {
1326 PERROR("chmod");
1327 return -1;
1328 }
1329
1330 res = check_testfile_mode(testfile, mode);
1331 if (res == -1)
1332 return -1;
1333
1334 fd = open(testfile, flags);
1335 if (fd == -1) {
1336 if (err != errno) {
1337 PERROR("open");
1338 return -1;
1339 }
1340 } else {
1341 if (err) {
1342 ERROR("open should have failed");
1343 close(fd);
1344 return -1;
1345 }
1346 close(fd);
1347 }
1348
1349 res = unlink(testfile);
1350 if (res == -1) {
1351 PERROR("unlink");
1352 return -1;
1353 }
1354 res = check_nonexist(testfile);
1355 if (res == -1)
1356 return -1;
1357 res = check_nonexist(testfile_r);
1358 if (res == -1)
1359 return -1;
1360
1361 success();
1362 return 0;
1363}
1364
1365static int test_symlink(void)
1366{
1367 char buf[1024];
1368 const char *data = testdata;
1369 int datalen = testdatalen;
1370 int linklen = strlen(testfile);
1371 int err = 0;
1372 int res;
1373
1374 start_test("symlink");
1375 res = create_testfile(testfile, data, datalen);
1376 if (res == -1)
1377 return -1;
1378
1379 unlink(testfile2);
1380 res = symlink(testfile, testfile2);
1381 if (res == -1) {
1382 PERROR("symlink");
1383 return -1;
1384 }
1385 res = check_type(testfile2, S_IFLNK);
1386 if (res == -1)
1387 return -1;
1388 err += check_mode(testfile2, 0777);
1389 err += check_nlink(testfile2, 1);
1390 res = readlink(testfile2, buf, sizeof(buf));
1391 if (res == -1) {
1392 PERROR("readlink");
1393 err--;
1394 }
1395 if (res != linklen) {
1396 ERROR("short readlink: %u instead of %u", res, linklen);
1397 err--;
1398 }
1399 if (memcmp(buf, testfile, linklen) != 0) {
1400 ERROR("link mismatch");
1401 err--;
1402 }
1403 err += check_size(testfile2, datalen);
1404 err += check_data(testfile2, data, 0, datalen);
1405 res = unlink(testfile2);
1406 if (res == -1) {
1407 PERROR("unlink");
1408 return -1;
1409 }
1410 res = check_nonexist(testfile2);
1411 if (res == -1)
1412 return -1;
1413 if (err)
1414 return -1;
1415
1416 res = unlink(testfile);
1417 if (res == -1) {
1418 PERROR("unlink");
1419 return -1;
1420 }
1421 res = check_nonexist(testfile);
1422 if (res == -1)
1423 return -1;
1424
1425 success();
1426 return 0;
1427}
1428
1429static int test_link(void)
1430{
1431 const char *data = testdata;
1432 int datalen = testdatalen;
1433 int err = 0;
1434 int res;
1435
1436 start_test("link");
1437 res = create_testfile(testfile, data, datalen);
1438 if (res == -1)
1439 return -1;
1440
1441 unlink(testfile2);
1442 res = link(testfile, testfile2);
1443 if (res == -1) {
1444 PERROR("link");
1445 return -1;
1446 }
1447 res = check_type(testfile2, S_IFREG);
1448 if (res == -1)
1449 return -1;
1450 err += check_mode(testfile2, 0644);
1451 err += check_nlink(testfile2, 2);
1452 err += check_size(testfile2, datalen);
1453 err += check_data(testfile2, data, 0, datalen);
1454 res = unlink(testfile);
1455 if (res == -1) {
1456 PERROR("unlink");
1457 return -1;
1458 }
1459 res = check_nonexist(testfile);
1460 if (res == -1)
1461 return -1;
1462
1463 err += check_nlink(testfile2, 1);
1464 res = unlink(testfile2);
1465 if (res == -1) {
1466 PERROR("unlink");
1467 return -1;
1468 }
1469 res = check_nonexist(testfile2);
1470 if (res == -1)
1471 return -1;
1472 if (err)
1473 return -1;
1474
1475 success();
1476 return 0;
1477}
1478
1479static int test_link2(void)
1480{
1481 const char *data = testdata;
1482 int datalen = testdatalen;
1483 int err = 0;
1484 int res;
1485
1486 start_test("link-unlink-link");
1487 res = create_testfile(testfile, data, datalen);
1488 if (res == -1)
1489 return -1;
1490
1491 unlink(testfile2);
1492 res = link(testfile, testfile2);
1493 if (res == -1) {
1494 PERROR("link");
1495 return -1;
1496 }
1497 res = unlink(testfile);
1498 if (res == -1) {
1499 PERROR("unlink");
1500 return -1;
1501 }
1502 res = check_nonexist(testfile);
1503 if (res == -1)
1504 return -1;
1505 res = link(testfile2, testfile);
1506 if (res == -1) {
1507 PERROR("link");
1508 }
1509 res = check_type(testfile, S_IFREG);
1510 if (res == -1)
1511 return -1;
1512 err += check_mode(testfile, 0644);
1513 err += check_nlink(testfile, 2);
1514 err += check_size(testfile, datalen);
1515 err += check_data(testfile, data, 0, datalen);
1516
1517 res = unlink(testfile2);
1518 if (res == -1) {
1519 PERROR("unlink");
1520 return -1;
1521 }
1522 err += check_nlink(testfile, 1);
1523 res = unlink(testfile);
1524 if (res == -1) {
1525 PERROR("unlink");
1526 return -1;
1527 }
1528 res = check_nonexist(testfile);
1529 if (res == -1)
1530 return -1;
1531 if (err)
1532 return -1;
1533
1534 success();
1535 return 0;
1536}
1537
1538static int test_rename_file(void)
1539{
1540 const char *data = testdata;
1541 int datalen = testdatalen;
1542 int err = 0;
1543 int res;
1544
1545 start_test("rename file");
1546 res = create_testfile(testfile, data, datalen);
1547 if (res == -1)
1548 return -1;
1549
1550 unlink(testfile2);
1551 res = rename(testfile, testfile2);
1552 if (res == -1) {
1553 PERROR("rename");
1554 return -1;
1555 }
1556 res = check_nonexist(testfile);
1557 if (res == -1)
1558 return -1;
1559 res = check_type(testfile2, S_IFREG);
1560 if (res == -1)
1561 return -1;
1562 err += check_mode(testfile2, 0644);
1563 err += check_nlink(testfile2, 1);
1564 err += check_size(testfile2, datalen);
1565 err += check_data(testfile2, data, 0, datalen);
1566 res = unlink(testfile2);
1567 if (res == -1) {
1568 PERROR("unlink");
1569 return -1;
1570 }
1571 res = check_nonexist(testfile2);
1572 if (res == -1)
1573 return -1;
1574 if (err)
1575 return -1;
1576
1577 success();
1578 return 0;
1579}
1580
1581static int test_rename_dir(void)
1582{
1583 int err = 0;
1584 int res;
1585
1586 start_test("rename dir");
1587 res = create_dir(testdir, testdir_files);
1588 if (res == -1)
1589 return -1;
1590
1591 rmdir(testdir2);
1592 res = rename(testdir, testdir2);
1593 if (res == -1) {
1594 PERROR("rename");
1595 cleanup_dir(testdir, testdir_files, 1);
1596 return -1;
1597 }
1598 res = check_nonexist(testdir);
1599 if (res == -1) {
1600 cleanup_dir(testdir, testdir_files, 1);
1601 return -1;
1602 }
1603 res = check_type(testdir2, S_IFDIR);
1604 if (res == -1) {
1605 cleanup_dir(testdir2, testdir_files, 1);
1606 return -1;
1607 }
1608 err += check_mode(testdir2, 0755);
1609 err += check_dir_contents(testdir2, testdir_files);
1610 err += cleanup_dir(testdir2, testdir_files, 0);
1611 res = rmdir(testdir2);
1612 if (res == -1) {
1613 PERROR("rmdir");
1614 return -1;
1615 }
1616 res = check_nonexist(testdir2);
1617 if (res == -1)
1618 return -1;
1619 if (err)
1620 return -1;
1621
1622 success();
1623 return 0;
1624}
1625
1626static int test_rename_dir_loop(void)
1627{
1628#define PATH(p) (snprintf(path, sizeof path, "%s/%s", testdir, p), path)
1629#define PATH2(p) (snprintf(path2, sizeof path2, "%s/%s", testdir, p), path2)
1630
1631 char path[1280], path2[1280];
1632 int err = 0;
1633 int res;
1634
1635 start_test("rename dir loop");
1636
1637 res = create_dir(testdir, testdir_files);
1638 if (res == -1)
1639 return -1;
1640
1641 res = mkdir(PATH("a"), 0755);
1642 if (res == -1) {
1643 PERROR("mkdir");
1644 goto fail;
1645 }
1646
1647 res = rename(PATH("a"), PATH2("a"));
1648 if (res == -1) {
1649 PERROR("rename");
1650 goto fail;
1651 }
1652
1653 errno = 0;
1654 res = rename(PATH("a"), PATH2("a/b"));
1655 if (res == 0 || errno != EINVAL) {
1656 PERROR("rename");
1657 goto fail;
1658 }
1659
1660 res = mkdir(PATH("a/b"), 0755);
1661 if (res == -1) {
1662 PERROR("mkdir");
1663 goto fail;
1664 }
1665
1666 res = mkdir(PATH("a/b/c"), 0755);
1667 if (res == -1) {
1668 PERROR("mkdir");
1669 goto fail;
1670 }
1671
1672 errno = 0;
1673 res = rename(PATH("a"), PATH2("a/b/c"));
1674 if (res == 0 || errno != EINVAL) {
1675 PERROR("rename");
1676 goto fail;
1677 }
1678
1679 errno = 0;
1680 res = rename(PATH("a"), PATH2("a/b/c/a"));
1681 if (res == 0 || errno != EINVAL) {
1682 PERROR("rename");
1683 goto fail;
1684 }
1685
1686 errno = 0;
1687 res = rename(PATH("a/b/c"), PATH2("a"));
1688 if (res == 0 || errno != ENOTEMPTY) {
1689 PERROR("rename");
1690 goto fail;
1691 }
1692
1693 res = open(PATH("a/foo"), O_CREAT, 0644);
1694 if (res == -1) {
1695 PERROR("open");
1696 goto fail;
1697 }
1698 close(res);
1699
1700 res = rename(PATH("a/foo"), PATH2("a/bar"));
1701 if (res == -1) {
1702 PERROR("rename");
1703 goto fail;
1704 }
1705
1706 res = rename(PATH("a/bar"), PATH2("a/foo"));
1707 if (res == -1) {
1708 PERROR("rename");
1709 goto fail;
1710 }
1711
1712 res = rename(PATH("a/foo"), PATH2("a/b/bar"));
1713 if (res == -1) {
1714 PERROR("rename");
1715 goto fail;
1716 }
1717
1718 res = rename(PATH("a/b/bar"), PATH2("a/foo"));
1719 if (res == -1) {
1720 PERROR("rename");
1721 goto fail;
1722 }
1723
1724 res = rename(PATH("a/foo"), PATH2("a/b/c/bar"));
1725 if (res == -1) {
1726 PERROR("rename");
1727 goto fail;
1728 }
1729
1730 res = rename(PATH("a/b/c/bar"), PATH2("a/foo"));
1731 if (res == -1) {
1732 PERROR("rename");
1733 goto fail;
1734 }
1735
1736 res = open(PATH("a/bar"), O_CREAT, 0644);
1737 if (res == -1) {
1738 PERROR("open");
1739 goto fail;
1740 }
1741 close(res);
1742
1743 res = rename(PATH("a/foo"), PATH2("a/bar"));
1744 if (res == -1) {
1745 PERROR("rename");
1746 goto fail;
1747 }
1748
1749 unlink(PATH("a/bar"));
1750
1751 res = rename(PATH("a/b"), PATH2("a/d"));
1752 if (res == -1) {
1753 PERROR("rename");
1754 goto fail;
1755 }
1756
1757 res = rename(PATH("a/d"), PATH2("a/b"));
1758 if (res == -1) {
1759 PERROR("rename");
1760 goto fail;
1761 }
1762
1763 res = mkdir(PATH("a/d"), 0755);
1764 if (res == -1) {
1765 PERROR("mkdir");
1766 goto fail;
1767 }
1768
1769 res = rename(PATH("a/b"), PATH2("a/d"));
1770 if (res == -1) {
1771 PERROR("rename");
1772 goto fail;
1773 }
1774
1775 res = rename(PATH("a/d"), PATH2("a/b"));
1776 if (res == -1) {
1777 PERROR("rename");
1778 goto fail;
1779 }
1780
1781 res = mkdir(PATH("a/d"), 0755);
1782 if (res == -1) {
1783 PERROR("mkdir");
1784 goto fail;
1785 }
1786
1787 res = mkdir(PATH("a/d/e"), 0755);
1788 if (res == -1) {
1789 PERROR("mkdir");
1790 goto fail;
1791 }
1792
1793 errno = 0;
1794 res = rename(PATH("a/b"), PATH2("a/d"));
1795 if (res == 0 || (errno != ENOTEMPTY && errno != EEXIST)) {
1796 PERROR("rename");
1797 goto fail;
1798 }
1799
1800 rmdir(PATH("a/d/e"));
1801 rmdir(PATH("a/d"));
1802
1803 rmdir(PATH("a/b/c"));
1804 rmdir(PATH("a/b"));
1805 rmdir(PATH("a"));
1806
1807 err += cleanup_dir(testdir, testdir_files, 0);
1808 res = rmdir(testdir);
1809 if (res == -1) {
1810 PERROR("rmdir");
1811 goto fail;
1812 }
1813 res = check_nonexist(testdir);
1814 if (res == -1)
1815 return -1;
1816 if (err)
1817 return -1;
1818
1819 success();
1820 return 0;
1821
1822fail:
1823 unlink(PATH("a/bar"));
1824
1825 rmdir(PATH("a/d/e"));
1826 rmdir(PATH("a/d"));
1827
1828 rmdir(PATH("a/b/c"));
1829 rmdir(PATH("a/b"));
1830 rmdir(PATH("a"));
1831
1832 cleanup_dir(testdir, testdir_files, 1);
1833 rmdir(testdir);
1834
1835 return -1;
1836
1837#undef PATH2
1838#undef PATH
1839}
1840
1841static int test_mkfifo(void)
1842{
1843 int res;
1844 int err = 0;
1845
1846 start_test("mkfifo");
1847 unlink(testfile);
1848 res = mkfifo(testfile, 0644);
1849 if (res == -1) {
1850 PERROR("mkfifo");
1851 return -1;
1852 }
1853 res = check_type(testfile, S_IFIFO);
1854 if (res == -1)
1855 return -1;
1856 err += check_mode(testfile, 0644);
1857 err += check_nlink(testfile, 1);
1858 res = unlink(testfile);
1859 if (res == -1) {
1860 PERROR("unlink");
1861 return -1;
1862 }
1863 res = check_nonexist(testfile);
1864 if (res == -1)
1865 return -1;
1866 if (err)
1867 return -1;
1868
1869 success();
1870 return 0;
1871}
1872
1873static int test_mkdir(void)
1874{
1875 int res;
1876 int err = 0;
1877 const char *dir_contents[] = {NULL};
1878
1879 start_test("mkdir");
1880 rmdir(testdir);
1881 res = mkdir(testdir, 0755);
1882 if (res == -1) {
1883 PERROR("mkdir");
1884 return -1;
1885 }
1886 res = check_type(testdir, S_IFDIR);
1887 if (res == -1)
1888 return -1;
1889 err += check_mode(testdir, 0755);
1890 /* Some file systems (like btrfs) don't track link
1891 count for directories */
1892 //err += check_nlink(testdir, 2);
1893 err += check_dir_contents(testdir, dir_contents);
1894 res = rmdir(testdir);
1895 if (res == -1) {
1896 PERROR("rmdir");
1897 return -1;
1898 }
1899 res = check_nonexist(testdir);
1900 if (res == -1)
1901 return -1;
1902 if (err)
1903 return -1;
1904
1905 success();
1906 return 0;
1907}
1908
1909static int test_socket(void)
1910{
1911 struct sockaddr_un su;
1912 int fd;
1913 int res;
1914 int err = 0;
1915 const size_t test_sock_len = strlen(testsock) + 1;
1916
1917 start_test("socket");
1918 if (test_sock_len > sizeof(su.sun_path)) {
1919 fprintf(stderr, "Need to shorten mount point by %zu chars\n",
1920 strlen(testsock) + 1 - sizeof(su.sun_path));
1921 return -1;
1922 }
1923 unlink(testsock);
1924 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1925 if (fd < 0) {
1926 PERROR("socket");
1927 return -1;
1928 }
1929 su.sun_family = AF_UNIX;
1930
1931 strncpy(su.sun_path, testsock, test_sock_len);
1932 su.sun_path[sizeof(su.sun_path) - 1] = '\0';
1933 res = bind(fd, (struct sockaddr*)&su, sizeof(su));
1934 if (res == -1) {
1935 PERROR("bind");
1936 return -1;
1937 }
1938
1939 res = check_type(testsock, S_IFSOCK);
1940 if (res == -1) {
1941 close(fd);
1942 return -1;
1943 }
1944 err += check_nlink(testsock, 1);
1945 close(fd);
1946 res = unlink(testsock);
1947 if (res == -1) {
1948 PERROR("unlink");
1949 return -1;
1950 }
1951 res = check_nonexist(testsock);
1952 if (res == -1)
1953 return -1;
1954 if (err)
1955 return -1;
1956
1957 success();
1958 return 0;
1959}
1960
1961#define test_create_ro_dir(flags) \
1962 do_test_create_ro_dir(flags, #flags)
1963
1964static int do_test_create_ro_dir(int flags, const char *flags_str)
1965{
1966 int res;
1967 int err = 0;
1968 int fd;
1969
1970 start_test("open(%s) in read-only directory", flags_str);
1971 rmdir(testdir);
1972 res = mkdir(testdir, 0555);
1973 if (res == -1) {
1974 PERROR("mkdir");
1975 return -1;
1976 }
1977 fd = open(subfile, flags, 0644);
1978 if (fd != -1) {
1979 close(fd);
1980 unlink(subfile);
1981 ERROR("open should have failed");
1982 err--;
1983 } else {
1984 res = check_nonexist(subfile);
1985 if (res == -1)
1986 err--;
1987 }
1988 unlink(subfile);
1989 res = rmdir(testdir);
1990 if (res == -1) {
1991 PERROR("rmdir");
1992 return -1;
1993 }
1994 res = check_nonexist(testdir);
1995 if (res == -1)
1996 return -1;
1997 if (err)
1998 return -1;
1999
2000 success();
2001 return 0;
2002}
2003
2004#ifndef __FreeBSD__
2005/* this tests open with O_TMPFILE
2006 note that this will only work with the fuse low level api
2007 you will get ENOTSUP with the high level api */
2008static int test_create_tmpfile(void)
2009{
2010 rmdir(testdir);
2011 int res = mkdir(testdir, 0777);
2012 if (res)
2013 return -1;
2014
2015 start_test("create tmpfile");
2016
2017 int fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
2018 if(fd == -1) {
2019 if (errno == ENOTSUP) {
2020 /* don't bother if we're working on an old kernel
2021 or on the high level API */
2022 return 0;
2023 }
2024
2025 PERROR("open O_TMPFILE | O_RDWR");
2026 return -1;
2027 }
2028 close(fd);
2029
2030 fd = open(testdir, O_TMPFILE | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
2031 if(fd == -1){
2032 PERROR("open with O_TMPFILE | O_WRONLY | O_EXCL");
2033 return -1;
2034 };
2035 close(fd);
2036
2037 fd = open(testdir, O_TMPFILE | O_RDONLY, S_IRUSR);
2038 if (fd != -1) {
2039 ERROR("open with O_TMPFILE | O_RDONLY succeeded");
2040 return -1;
2041 }
2042
2043 success();
2044 return 0;
2045}
2046
2047static int test_create_and_link_tmpfile(void)
2048{
2049 /* skip this test for now since the github runner will fail in the linkat call below */
2050 return 0;
2051
2052 rmdir(testdir);
2053 unlink(testfile);
2054
2055 int res = mkdir(testdir, 0777);
2056 if (res)
2057 return -1;
2058
2059 start_test("create and link tmpfile");
2060
2061 int fd = open(testdir, O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
2062 if(fd == -1) {
2063 if (errno == ENOTSUP) {
2064 /* don't bother if we're working on an old kernel
2065 or on the high level API */
2066 return 0;
2067 }
2068 PERROR("open with O_TMPFILE | O_RDWR | O_EXCL");
2069 return -1;
2070 }
2071
2072 if (!linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
2073 ERROR("linkat succeeded on a tmpfile opened with O_EXCL");
2074 return -1;
2075 }
2076 close(fd);
2077
2078 fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
2079 if(fd == -1) {
2080 PERROR("open O_TMPFILE");
2081 return -1;
2082 }
2083
2084 if (check_nonexist(testfile)) {
2085 return -1;
2086 }
2087
2088 if (linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
2089 PERROR("linkat tempfile");
2090 return -1;
2091 }
2092 close(fd);
2093
2094 if (check_nlink(testfile, 1)) {
2095 return -1;
2096 }
2097 unlink(testfile);
2098
2099 success();
2100 return 0;
2101}
2102#endif
2103
2104int main(int argc, char *argv[])
2105{
2106 int err = 0;
2107 int a;
2108 int is_root;
2109
2110 umask(0);
2111 if (argc < 2 || argc > 4) {
2112 fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#] [-u]\n", argv[0]);
2113 return 1;
2114 }
2115 basepath = argv[1];
2116 basepath_r = basepath;
2117 for (a = 2; a < argc; a++) {
2118 char *endptr;
2119 char *arg = argv[a];
2120 if (arg[0] == ':') {
2121 basepath_r = arg + 1;
2122 } else {
2123 if (arg[0] == '-') {
2124 arg++;
2125 if (arg[0] == 'u') {
2126 unlinked_test = 1;
2127 endptr = arg + 1;
2128 } else {
2129 skip_test = strtoul(arg, &endptr, 10);
2130 }
2131 } else {
2132 select_test = strtoul(arg, &endptr, 10);
2133 }
2134 if (arg[0] == '\0' || *endptr != '\0') {
2135 fprintf(stderr, "invalid option: '%s'\n", argv[a]);
2136 return 1;
2137 }
2138 }
2139 }
2140 assert(strlen(basepath) < 512);
2141 assert(strlen(basepath_r) < 512);
2142 if (basepath[0] != '/') {
2143 fprintf(stderr, "testdir must be an absolute path\n");
2144 return 1;
2145 }
2146
2147 sprintf(testfile, "%s/testfile", basepath);
2148 sprintf(testfile2, "%s/testfile2", basepath);
2149 sprintf(testdir, "%s/testdir", basepath);
2150 sprintf(testdir2, "%s/testdir2", basepath);
2151 sprintf(subfile, "%s/subfile", testdir2);
2152 sprintf(testsock, "%s/testsock", basepath);
2153
2154 sprintf(testfile_r, "%s/testfile", basepath_r);
2155 sprintf(testfile2_r, "%s/testfile2", basepath_r);
2156 sprintf(testdir_r, "%s/testdir", basepath_r);
2157 sprintf(testdir2_r, "%s/testdir2", basepath_r);
2158 sprintf(subfile_r, "%s/subfile", testdir2_r);
2159
2160 is_root = (geteuid() == 0);
2161
2162 err += test_create();
2163 err += test_create_unlink();
2164 err += test_symlink();
2165 err += test_link();
2166 err += test_link2();
2167 err += test_mknod();
2168 err += test_mkfifo();
2169 err += test_mkdir();
2170 err += test_rename_file();
2171 err += test_rename_dir();
2172 err += test_rename_dir_loop();
2173 err += test_seekdir();
2174 err += test_socket();
2175 err += test_utime();
2176 err += test_truncate(0);
2177 err += test_truncate(testdatalen / 2);
2178 err += test_truncate(testdatalen);
2179 err += test_truncate(testdatalen + 100);
2180 err += test_ftruncate(0, 0600);
2181 err += test_ftruncate(testdatalen / 2, 0600);
2182 err += test_ftruncate(testdatalen, 0600);
2183 err += test_ftruncate(testdatalen + 100, 0600);
2184 err += test_ftruncate(0, 0400);
2185 err += test_ftruncate(0, 0200);
2186 err += test_ftruncate(0, 0000);
2187 err += test_open(0, O_RDONLY, 0);
2188 err += test_open(1, O_RDONLY, 0);
2189 err += test_open(1, O_RDWR, 0);
2190 err += test_open(1, O_WRONLY, 0);
2191 err += test_open(0, O_RDWR | O_CREAT, 0600);
2192 err += test_open(1, O_RDWR | O_CREAT, 0600);
2193 err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600);
2194 err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600);
2195 err += test_open(0, O_RDONLY | O_CREAT, 0600);
2196 err += test_open(0, O_RDONLY | O_CREAT, 0400);
2197 err += test_open(0, O_RDONLY | O_CREAT, 0200);
2198 err += test_open(0, O_RDONLY | O_CREAT, 0000);
2199 err += test_open(0, O_WRONLY | O_CREAT, 0600);
2200 err += test_open(0, O_WRONLY | O_CREAT, 0400);
2201 err += test_open(0, O_WRONLY | O_CREAT, 0200);
2202 err += test_open(0, O_WRONLY | O_CREAT, 0000);
2203 err += test_open(0, O_RDWR | O_CREAT, 0400);
2204 err += test_open(0, O_RDWR | O_CREAT, 0200);
2205 err += test_open(0, O_RDWR | O_CREAT, 0000);
2206 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600);
2207 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600);
2208 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000);
2209 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000);
2210 err += test_open_acc(O_RDONLY, 0600, 0);
2211 err += test_open_acc(O_WRONLY, 0600, 0);
2212 err += test_open_acc(O_RDWR, 0600, 0);
2213 err += test_open_acc(O_RDONLY, 0400, 0);
2214 err += test_open_acc(O_WRONLY, 0200, 0);
2215 if(!is_root) {
2216 err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES);
2217 err += test_open_acc(O_WRONLY, 0400, EACCES);
2218 err += test_open_acc(O_RDWR, 0400, EACCES);
2219 err += test_open_acc(O_RDONLY, 0200, EACCES);
2220 err += test_open_acc(O_RDWR, 0200, EACCES);
2221 err += test_open_acc(O_RDONLY, 0000, EACCES);
2222 err += test_open_acc(O_WRONLY, 0000, EACCES);
2223 err += test_open_acc(O_RDWR, 0000, EACCES);
2224 }
2225 err += test_create_ro_dir(O_CREAT);
2226 err += test_create_ro_dir(O_CREAT | O_EXCL);
2227 err += test_create_ro_dir(O_CREAT | O_WRONLY);
2228 err += test_create_ro_dir(O_CREAT | O_TRUNC);
2229 err += test_copy_file_range();
2230 err += test_statx();
2231#ifndef __FreeBSD__
2232 err += test_create_tmpfile();
2233 err += test_create_and_link_tmpfile();
2234#endif
2235
2236 unlink(testfile2);
2237 unlink(testsock);
2238 rmdir(testdir);
2239 rmdir(testdir2);
2240
2241 if (err) {
2242 fprintf(stderr, "%i tests failed\n", -err);
2243 return 1;
2244 }
2245
2246 return check_unlinked_testfiles();
2247}
fuse-3.18.2/doc/html/test_2test__syscalls_8c_source.html0000644000175000017500000115732315156613443022266 0ustar berndbernd libfuse: test/test_syscalls.c Source File
libfuse
test_syscalls.c
1#define _GNU_SOURCE
2#include "fuse_config.h"
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <stdarg.h>
7#include <string.h>
8#include <unistd.h>
9#include <fcntl.h>
10#include <dirent.h>
11#include <utime.h>
12#include <errno.h>
13#include <assert.h>
14#include <time.h>
15#include <sys/socket.h>
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <sys/un.h>
19
20#ifndef ALLPERMS
21# define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)/* 07777 */
22#endif
23
24
25static const char *basepath;
26static const char *basepath_r;
27static char testfile[1024];
28static char testfile2[1024];
29static char testdir[1024];
30static char testdir2[1024];
31static char testsock[1024];
32static char subfile[1280];
33
34static char testfile_r[1024];
35static char testfile2_r[1024];
36static char testdir_r[1024];
37static char testdir2_r[1024];
38static char subfile_r[1280];
39
40static char testname[256];
41static char testdata[] = "abcdefghijklmnopqrstuvwxyz";
42static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./";
43static const char *testdir_files[] = { "f1", "f2", NULL};
44static long seekdir_offsets[4];
45static char zerodata[4096];
46static int testdatalen = sizeof(testdata) - 1;
47static int testdata2len = sizeof(testdata2) - 1;
48static unsigned int testnum = 0;
49static unsigned int select_test = 0;
50static unsigned int skip_test = 0;
51static unsigned int unlinked_test = 0;
52
53#define MAX_ENTRIES 1024
54#define MAX_TESTS 100
55
56static struct test {
57 int fd;
58 struct stat stat;
59} tests[MAX_TESTS];
60
61static void test_perror(const char *func, const char *msg)
62{
63 fprintf(stderr, "%s %s() - %s: %s\n", testname, func, msg,
64 strerror(errno));
65}
66
67static void test_error(const char *func, const char *msg, ...)
68 __attribute__ ((format (printf, 2, 3)));
69
70static void __start_test(const char *fmt, ...)
71 __attribute__ ((format (printf, 1, 2)));
72
73static void test_error(const char *func, const char *msg, ...)
74{
75 va_list ap;
76 fprintf(stderr, "%s %s() - ", testname, func);
77 va_start(ap, msg);
78 vfprintf(stderr, msg, ap);
79 va_end(ap);
80 fprintf(stderr, "\n");
81}
82
83static int is_dot_or_dotdot(const char *name) {
84 return name[0] == '.' &&
85 (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
86}
87
88static void success(void)
89{
90 fprintf(stderr, "%s OK\n", testname);
91}
92
93#define this_test (&tests[testnum-1])
94#define next_test (&tests[testnum])
95
96static void __start_test(const char *fmt, ...)
97{
98 unsigned int n;
99 va_list ap;
100 n = sprintf(testname, "%3i [", testnum);
101 va_start(ap, fmt);
102 n += vsprintf(testname + n, fmt, ap);
103 va_end(ap);
104 sprintf(testname + n, "]");
105 // Use dedicated testfile per test
106 sprintf(testfile, "%s/testfile.%d", basepath, testnum);
107 sprintf(testfile_r, "%s/testfile.%d", basepath_r, testnum);
108 if (testnum > MAX_TESTS) {
109 fprintf(stderr, "%s - too many tests\n", testname);
110 exit(1);
111 }
112 this_test->fd = -1;
113}
114
115#define start_test(msg, args...) { \
116 testnum++; \
117 if ((select_test && testnum != select_test) || \
118 (testnum == skip_test)) { \
119 return 0; \
120 } \
121 __start_test(msg, ##args); \
122}
123
124#define PERROR(msg) test_perror(__FUNCTION__, msg)
125#define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args)
126
127#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
128
129static int st_check_size(struct stat *st, int len)
130{
131 if (st->st_size != len) {
132 ERROR("length %u instead of %u", (int) st->st_size,
133 (int) len);
134 return -1;
135 }
136 return 0;
137}
138
139static int check_size(const char *path, int len)
140{
141 struct stat stbuf;
142 int res = stat(path, &stbuf);
143 if (res == -1) {
144 PERROR("stat");
145 return -1;
146 }
147 return st_check_size(&stbuf, len);
148}
149
150static int check_testfile_size(const char *path, int len)
151{
152 this_test->stat.st_size = len;
153 return check_size(path, len);
154}
155
156static int st_check_type(struct stat *st, mode_t type)
157{
158 if ((st->st_mode & S_IFMT) != type) {
159 ERROR("type 0%o instead of 0%o", st->st_mode & S_IFMT, type);
160 return -1;
161 }
162 return 0;
163}
164
165static int check_type(const char *path, mode_t type)
166{
167 struct stat stbuf;
168 int res = lstat(path, &stbuf);
169 if (res == -1) {
170 PERROR("lstat");
171 return -1;
172 }
173 return st_check_type(&stbuf, type);
174}
175
176static int st_check_mode(struct stat *st, mode_t mode)
177{
178 if ((st->st_mode & ALLPERMS) != mode) {
179 ERROR("mode 0%o instead of 0%o", st->st_mode & ALLPERMS,
180 mode);
181 return -1;
182 }
183 return 0;
184}
185
186static int check_mode(const char *path, mode_t mode)
187{
188 struct stat stbuf;
189 int res = lstat(path, &stbuf);
190 if (res == -1) {
191 PERROR("lstat");
192 return -1;
193 }
194 return st_check_mode(&stbuf, mode);
195}
196
197static int check_testfile_mode(const char *path, mode_t mode)
198{
199 this_test->stat.st_mode &= ~ALLPERMS;
200 this_test->stat.st_mode |= mode;
201 return check_mode(path, mode);
202}
203
204static int check_times(const char *path, time_t atime, time_t mtime)
205{
206 int err = 0;
207 struct stat stbuf;
208 int res = lstat(path, &stbuf);
209 if (res == -1) {
210 PERROR("lstat");
211 return -1;
212 }
213 if (stbuf.st_atime != atime) {
214 ERROR("atime %li instead of %li", stbuf.st_atime, atime);
215 err--;
216 }
217 if (stbuf.st_mtime != mtime) {
218 ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
219 err--;
220 }
221 if (err)
222 return -1;
223
224 return 0;
225}
226
227#if 0
228static int fcheck_times(int fd, time_t atime, time_t mtime)
229{
230 int err = 0;
231 struct stat stbuf;
232 int res = fstat(fd, &stbuf);
233 if (res == -1) {
234 PERROR("fstat");
235 return -1;
236 }
237 if (stbuf.st_atime != atime) {
238 ERROR("atime %li instead of %li", stbuf.st_atime, atime);
239 err--;
240 }
241 if (stbuf.st_mtime != mtime) {
242 ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
243 err--;
244 }
245 if (err)
246 return -1;
247
248 return 0;
249}
250#endif
251
252static int st_check_nlink(struct stat *st, nlink_t nlink)
253{
254 if (st->st_nlink != nlink) {
255 ERROR("nlink %li instead of %li", (long) st->st_nlink,
256 (long) nlink);
257 return -1;
258 }
259 return 0;
260}
261
262static int check_nlink(const char *path, nlink_t nlink)
263{
264 struct stat stbuf;
265 int res = lstat(path, &stbuf);
266 if (res == -1) {
267 PERROR("lstat");
268 return -1;
269 }
270 return st_check_nlink(&stbuf, nlink);
271}
272
273static int fcheck_stat(int fd, int flags, struct stat *st)
274{
275 struct stat stbuf;
276 int res = fstat(fd, &stbuf);
277 if (res == -1) {
278 if (flags & O_PATH) {
279 // With O_PATH fd, the server does not have to keep
280 // the inode alive so FUSE inode may be stale or bad
281 if (errno == ESTALE || errno == EIO ||
282 errno == ENOENT || errno == EBADF)
283 return 0;
284 }
285 PERROR("fstat");
286 return -1;
287 }
288
289 int err = 0;
290 err += st_check_type(&stbuf, st->st_mode & S_IFMT);
291 err += st_check_mode(&stbuf, st->st_mode & ALLPERMS);
292 err += st_check_size(&stbuf, st->st_size);
293 err += st_check_nlink(&stbuf, st->st_nlink);
294
295 return err;
296}
297
298static int check_nonexist(const char *path)
299{
300 struct stat stbuf;
301 int res = lstat(path, &stbuf);
302 if (res == 0) {
303 ERROR("file should not exist");
304 return -1;
305 }
306 if (errno != ENOENT) {
307 ERROR("file should not exist: %s", strerror(errno));
308 return -1;
309 }
310 return 0;
311}
312
313static int check_buffer(const char *buf, const char *data, unsigned len)
314{
315 if (memcmp(buf, data, len) != 0) {
316 ERROR("data mismatch");
317 return -1;
318 }
319 return 0;
320}
321
322static int check_data(const char *path, const char *data, int offset,
323 unsigned len)
324{
325 char buf[4096];
326 int res;
327 int fd = open(path, O_RDONLY);
328 if (fd == -1) {
329 PERROR("open");
330 return -1;
331 }
332 if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
333 PERROR("lseek");
334 close(fd);
335 return -1;
336 }
337 while (len) {
338 int rdlen = len < sizeof(buf) ? len : sizeof(buf);
339 res = read(fd, buf, rdlen);
340 if (res == -1) {
341 PERROR("read");
342 close(fd);
343 return -1;
344 }
345 if (res != rdlen) {
346 ERROR("short read: %u instead of %u", res, rdlen);
347 close(fd);
348 return -1;
349 }
350 if (check_buffer(buf, data, rdlen) != 0) {
351 close(fd);
352 return -1;
353 }
354 data += rdlen;
355 len -= rdlen;
356 }
357 res = close(fd);
358 if (res == -1) {
359 PERROR("close");
360 return -1;
361 }
362 return 0;
363}
364
365static int fcheck_data(int fd, const char *data, int offset,
366 unsigned len)
367{
368 char buf[4096];
369 int res;
370 if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
371 PERROR("lseek");
372 return -1;
373 }
374 while (len) {
375 int rdlen = len < sizeof(buf) ? len : sizeof(buf);
376 res = read(fd, buf, rdlen);
377 if (res == -1) {
378 PERROR("read");
379 return -1;
380 }
381 if (res != rdlen) {
382 ERROR("short read: %u instead of %u", res, rdlen);
383 return -1;
384 }
385 if (check_buffer(buf, data, rdlen) != 0) {
386 return -1;
387 }
388 data += rdlen;
389 len -= rdlen;
390 }
391 return 0;
392}
393
394static int check_dir_contents(const char *path, const char **contents)
395{
396 int i;
397 int res;
398 int err = 0;
399 int found[MAX_ENTRIES];
400 const char *cont[MAX_ENTRIES];
401 DIR *dp;
402
403 for (i = 0; contents[i]; i++) {
404 assert(i < MAX_ENTRIES - 3);
405 found[i] = 0;
406 cont[i] = contents[i];
407 }
408 cont[i] = NULL;
409
410 dp = opendir(path);
411 if (dp == NULL) {
412 PERROR("opendir");
413 return -1;
414 }
415 memset(found, 0, sizeof(found));
416 while(1) {
417 struct dirent *de;
418 errno = 0;
419 de = readdir(dp);
420 if (de == NULL) {
421 if (errno) {
422 PERROR("readdir");
423 closedir(dp);
424 return -1;
425 }
426 break;
427 }
428 if (is_dot_or_dotdot(de->d_name))
429 continue;
430 for (i = 0; cont[i] != NULL; i++) {
431 assert(i < MAX_ENTRIES);
432 if (strcmp(cont[i], de->d_name) == 0) {
433 if (found[i]) {
434 ERROR("duplicate entry <%s>",
435 de->d_name);
436 err--;
437 } else
438 found[i] = 1;
439 break;
440 }
441 }
442 if (!cont[i]) {
443 ERROR("unexpected entry <%s>", de->d_name);
444 err --;
445 }
446 }
447 for (i = 0; cont[i] != NULL; i++) {
448 if (!found[i]) {
449 ERROR("missing entry <%s>", cont[i]);
450 err--;
451 }
452 }
453 res = closedir(dp);
454 if (res == -1) {
455 PERROR("closedir");
456 return -1;
457 }
458 if (err)
459 return -1;
460
461 return 0;
462}
463
464static int create_file(const char *path, const char *data, int len)
465{
466 int res;
467 int fd;
468
469 unlink(path);
470 fd = creat(path, 0644);
471 if (fd == -1) {
472 PERROR("creat");
473 return -1;
474 }
475 if (len) {
476 res = write(fd, data, len);
477 if (res == -1) {
478 PERROR("write");
479 close(fd);
480 return -1;
481 }
482 if (res != len) {
483 ERROR("write is short: %u instead of %u", res, len);
484 close(fd);
485 return -1;
486 }
487 }
488 res = close(fd);
489 if (res == -1) {
490 PERROR("close");
491 return -1;
492 }
493 res = check_type(path, S_IFREG);
494 if (res == -1)
495 return -1;
496 res = check_mode(path, 0644);
497 if (res == -1)
498 return -1;
499 res = check_nlink(path, 1);
500 if (res == -1)
501 return -1;
502 res = check_size(path, len);
503 if (res == -1)
504 return -1;
505
506 if (len) {
507 res = check_data(path, data, 0, len);
508 if (res == -1)
509 return -1;
510 }
511
512 return 0;
513}
514
515static int create_path_fd(const char *path, const char *data, int len)
516{
517 int path_fd;
518 int res;
519
520 res = create_file(path, data, len);
521 if (res == -1)
522 return -1;
523
524 path_fd = open(path, O_PATH);
525 if (path_fd == -1)
526 PERROR("open(O_PATH)");
527
528 return path_fd;
529}
530
531// Can be called once per test
532static int create_testfile(const char *path, const char *data, int len)
533{
534 struct test *t = this_test;
535 struct stat *st = &t->stat;
536 int res, fd;
537
538 if (t->fd > 0) {
539 ERROR("testfile already created");
540 return -1;
541 }
542
543 fd = create_path_fd(path, data, len);
544 if (fd == -1)
545 return -1;
546
547 t->fd = fd;
548
549 res = fstat(fd, st);
550 if (res == -1) {
551 PERROR("fstat");
552 return -1;
553 }
554
555 return 0;
556}
557
558static int check_unlinked_testfile(int fd)
559{
560 struct stat *st = &this_test->stat;
561
562 st->st_nlink = 0;
563 return fcheck_stat(fd, O_PATH, st);
564}
565
566// Check recorded testfiles after all tests completed
567static int check_unlinked_testfiles(void)
568{
569 int fd;
570 int res, err = 0;
571 int num = testnum;
572
573 if (!unlinked_test)
574 return 0;
575
576 testnum = 0;
577 while (testnum < num) {
578 fd = next_test->fd;
579 start_test("check_unlinked_testfile");
580 if (fd == -1)
581 continue;
582
583 err += check_unlinked_testfile(fd);
584 res = close(fd);
585 if (res == -1) {
586 PERROR("close(test_fd)");
587 err--;
588 }
589 }
590
591 if (err) {
592 fprintf(stderr, "%i unlinked testfile checks failed\n", -err);
593 return 1;
594 }
595
596 return err;
597}
598
599static int cleanup_dir(const char *path, const char **dir_files, int quiet)
600{
601 int i;
602 int err = 0;
603
604 for (i = 0; dir_files[i]; i++) {
605 int res;
606 char fpath[1280];
607 sprintf(fpath, "%s/%s", path, dir_files[i]);
608 res = unlink(fpath);
609 if (res == -1 && !quiet) {
610 PERROR("unlink");
611 err --;
612 }
613 }
614 if (err)
615 return -1;
616
617 return 0;
618}
619
620static int create_dir(const char *path, const char **dir_files)
621{
622 int res;
623 int i;
624
625 rmdir(path);
626 res = mkdir(path, 0755);
627 if (res == -1) {
628 PERROR("mkdir");
629 return -1;
630 }
631 res = check_type(path, S_IFDIR);
632 if (res == -1)
633 return -1;
634 res = check_mode(path, 0755);
635 if (res == -1)
636 return -1;
637
638 for (i = 0; dir_files[i]; i++) {
639 char fpath[1280];
640 sprintf(fpath, "%s/%s", path, dir_files[i]);
641 res = create_file(fpath, "", 0);
642 if (res == -1) {
643 cleanup_dir(path, dir_files, 1);
644 return -1;
645 }
646 }
647 res = check_dir_contents(path, dir_files);
648 if (res == -1) {
649 cleanup_dir(path, dir_files, 1);
650 return -1;
651 }
652
653 return 0;
654}
655
656static int test_truncate(int len)
657{
658 const char *data = testdata;
659 int datalen = testdatalen;
660 int res;
661
662 start_test("truncate(%u)", (int) len);
663 res = create_testfile(testfile, data, datalen);
664 if (res == -1)
665 return -1;
666
667 res = truncate(testfile, len);
668 if (res == -1) {
669 PERROR("truncate");
670 return -1;
671 }
672 res = check_testfile_size(testfile, len);
673 if (res == -1)
674 return -1;
675
676 if (len > 0) {
677 if (len <= datalen) {
678 res = check_data(testfile, data, 0, len);
679 if (res == -1)
680 return -1;
681 } else {
682 res = check_data(testfile, data, 0, datalen);
683 if (res == -1)
684 return -1;
685 res = check_data(testfile, zerodata, datalen,
686 len - datalen);
687 if (res == -1)
688 return -1;
689 }
690 }
691 res = unlink(testfile);
692 if (res == -1) {
693 PERROR("unlink");
694 return -1;
695 }
696 res = check_nonexist(testfile);
697 if (res == -1)
698 return -1;
699
700 success();
701 return 0;
702}
703
704static int test_ftruncate(int len, int mode)
705{
706 const char *data = testdata;
707 int datalen = testdatalen;
708 int res;
709 int fd;
710
711 start_test("ftruncate(%u) mode: 0%03o", len, mode);
712 res = create_testfile(testfile, data, datalen);
713 if (res == -1)
714 return -1;
715
716 fd = open(testfile, O_WRONLY);
717 if (fd == -1) {
718 PERROR("open");
719 return -1;
720 }
721
722 res = fchmod(fd, mode);
723 if (res == -1) {
724 PERROR("fchmod");
725 close(fd);
726 return -1;
727 }
728 res = check_testfile_mode(testfile, mode);
729 if (res == -1) {
730 close(fd);
731 return -1;
732 }
733 res = ftruncate(fd, len);
734 if (res == -1) {
735 PERROR("ftruncate");
736 close(fd);
737 return -1;
738 }
739 close(fd);
740 res = check_testfile_size(testfile, len);
741 if (res == -1)
742 return -1;
743
744 if (len > 0) {
745 if (len <= datalen) {
746 res = check_data(testfile, data, 0, len);
747 if (res == -1)
748 return -1;
749 } else {
750 res = check_data(testfile, data, 0, datalen);
751 if (res == -1)
752 return -1;
753 res = check_data(testfile, zerodata, datalen,
754 len - datalen);
755 if (res == -1)
756 return -1;
757 }
758 }
759 res = unlink(testfile);
760 if (res == -1) {
761 PERROR("unlink");
762 return -1;
763 }
764 res = check_nonexist(testfile);
765 if (res == -1)
766 return -1;
767
768 success();
769 return 0;
770}
771
772static int test_seekdir(void)
773{
774 int i;
775 int res;
776 DIR *dp;
777 struct dirent *de = NULL;
778
779 start_test("seekdir");
780 res = create_dir(testdir, testdir_files);
781 if (res == -1)
782 return res;
783
784 dp = opendir(testdir);
785 if (dp == NULL) {
786 PERROR("opendir");
787 return -1;
788 }
789
790 /* Remember dir offsets */
791 for (i = 0; i < ARRAY_SIZE(seekdir_offsets); i++) {
792 seekdir_offsets[i] = telldir(dp);
793 errno = 0;
794 de = readdir(dp);
795 if (de == NULL) {
796 if (errno) {
797 PERROR("readdir");
798 goto fail;
799 }
800 break;
801 }
802 }
803
804 /* Walk until the end of directory */
805 while (de)
806 de = readdir(dp);
807
808 /* Start from the last valid dir offset and seek backwards */
809 for (i--; i >= 0; i--) {
810 seekdir(dp, seekdir_offsets[i]);
811 de = readdir(dp);
812 if (de == NULL) {
813 ERROR("Unexpected end of directory after seekdir()");
814 goto fail;
815 }
816 }
817
818 closedir(dp);
819 res = cleanup_dir(testdir, testdir_files, 0);
820 if (!res)
821 success();
822 return res;
823fail:
824 closedir(dp);
825 cleanup_dir(testdir, testdir_files, 1);
826 return -1;
827}
828
829#ifdef HAVE_COPY_FILE_RANGE
830static int test_copy_file_range(void)
831{
832 const char *data = testdata;
833 int datalen = testdatalen;
834 int err = 0;
835 int res;
836 int fd_in, fd_out;
837 off_t pos_in = 0, pos_out = 0;
838
839 start_test("copy_file_range");
840 unlink(testfile);
841 fd_in = open(testfile, O_CREAT | O_RDWR, 0644);
842 if (fd_in == -1) {
843 PERROR("creat");
844 return -1;
845 }
846 res = write(fd_in, data, datalen);
847 if (res == -1) {
848 PERROR("write");
849 close(fd_in);
850 return -1;
851 }
852 if (res != datalen) {
853 ERROR("write is short: %u instead of %u", res, datalen);
854 close(fd_in);
855 return -1;
856 }
857
858 unlink(testfile2);
859 fd_out = creat(testfile2, 0644);
860 if (fd_out == -1) {
861 PERROR("creat");
862 close(fd_in);
863 return -1;
864 }
865 res = copy_file_range(fd_in, &pos_in, fd_out, &pos_out, datalen, 0);
866 if (res == -1) {
867 PERROR("copy_file_range");
868 close(fd_in);
869 close(fd_out);
870 return -1;
871 }
872 if (res != datalen) {
873 ERROR("copy is short: %u instead of %u", res, datalen);
874 close(fd_in);
875 close(fd_out);
876 return -1;
877 }
878
879 res = close(fd_in);
880 if (res == -1) {
881 PERROR("close");
882 close(fd_out);
883 return -1;
884 }
885 res = close(fd_out);
886 if (res == -1) {
887 PERROR("close");
888 return -1;
889 }
890
891 err = check_data(testfile2, data, 0, datalen);
892
893 res = unlink(testfile);
894 if (res == -1) {
895 PERROR("unlink");
896 return -1;
897 }
898 res = check_nonexist(testfile);
899 if (res == -1)
900 return -1;
901 if (err)
902 return -1;
903
904 res = unlink(testfile2);
905 if (res == -1) {
906 PERROR("unlink");
907 return -1;
908 }
909 res = check_nonexist(testfile2);
910 if (res == -1)
911 return -1;
912 if (err)
913 return -1;
914
915 success();
916 return 0;
917}
918#else
919static int test_copy_file_range(void)
920{
921 return 0;
922}
923#endif
924
925#ifdef HAVE_STATX
926static int test_statx(void)
927{
928 struct statx sb;
929 char msg[] = "hi";
930 size_t msg_size = sizeof(msg);
931 struct timespec tp;
932 int res;
933
934 memset(&sb, 0, sizeof(sb));
935 unlink(testfile);
936
937 start_test("statx");
938
939 res = create_testfile(testfile, msg, msg_size);
940 if (res == -1)
941 return -1;
942
943 res = statx(-1, testfile, AT_EMPTY_PATH,
944 STATX_BASIC_STATS | STATX_BTIME, &sb);
945 if (res == -1)
946 return -1;
947
948 if (sb.stx_size != msg_size)
949 return -1;
950
951 clock_gettime(CLOCK_REALTIME, &tp);
952
953 if (sb.stx_btime.tv_sec > tp.tv_sec)
954 return -1;
955
956 if (sb.stx_btime.tv_sec == tp.tv_sec &&
957 sb.stx_btime.tv_nsec >= tp.tv_nsec)
958 return -1;
959
960 unlink(testfile);
961
962 success();
963 return 0;
964}
965#else
966static int test_statx(void)
967{
968 return 0;
969}
970#endif
971
972static int test_utime(void)
973{
974 struct utimbuf utm;
975 time_t atime = 987631200;
976 time_t mtime = 123116400;
977 int res;
978
979 start_test("utime");
980 res = create_testfile(testfile, NULL, 0);
981 if (res == -1)
982 return -1;
983
984 utm.actime = atime;
985 utm.modtime = mtime;
986 res = utime(testfile, &utm);
987 if (res == -1) {
988 PERROR("utime");
989 return -1;
990 }
991 res = check_times(testfile, atime, mtime);
992 if (res == -1) {
993 return -1;
994 }
995 res = unlink(testfile);
996 if (res == -1) {
997 PERROR("unlink");
998 return -1;
999 }
1000 res = check_nonexist(testfile);
1001 if (res == -1)
1002 return -1;
1003
1004 success();
1005 return 0;
1006}
1007
1008static int test_create(void)
1009{
1010 const char *data = testdata;
1011 int datalen = testdatalen;
1012 int err = 0;
1013 int res;
1014 int fd;
1015
1016 start_test("create");
1017 unlink(testfile);
1018 fd = creat(testfile, 0644);
1019 if (fd == -1) {
1020 PERROR("creat");
1021 return -1;
1022 }
1023 res = write(fd, data, datalen);
1024 if (res == -1) {
1025 PERROR("write");
1026 close(fd);
1027 return -1;
1028 }
1029 if (res != datalen) {
1030 ERROR("write is short: %u instead of %u", res, datalen);
1031 close(fd);
1032 return -1;
1033 }
1034 res = close(fd);
1035 if (res == -1) {
1036 PERROR("close");
1037 return -1;
1038 }
1039 res = check_type(testfile, S_IFREG);
1040 if (res == -1)
1041 return -1;
1042 err += check_mode(testfile, 0644);
1043 err += check_nlink(testfile, 1);
1044 err += check_size(testfile, datalen);
1045 err += check_data(testfile, data, 0, datalen);
1046 res = unlink(testfile);
1047 if (res == -1) {
1048 PERROR("unlink");
1049 return -1;
1050 }
1051 res = check_nonexist(testfile);
1052 if (res == -1)
1053 return -1;
1054 if (err)
1055 return -1;
1056
1057 success();
1058 return 0;
1059}
1060
1061static int test_create_unlink(void)
1062{
1063 const char *data = testdata;
1064 int datalen = testdatalen;
1065 int err = 0;
1066 int res;
1067 int fd;
1068
1069 start_test("create+unlink");
1070 unlink(testfile);
1071 fd = open(testfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
1072 if (fd == -1) {
1073 PERROR("creat");
1074 return -1;
1075 }
1076 res = unlink(testfile);
1077 if (res == -1) {
1078 PERROR("unlink");
1079 close(fd);
1080 return -1;
1081 }
1082 res = check_nonexist(testfile);
1083 if (res == -1) {
1084 close(fd);
1085 return -1;
1086 }
1087 res = write(fd, data, datalen);
1088 if (res == -1) {
1089 PERROR("write");
1090 close(fd);
1091 return -1;
1092 }
1093 if (res != datalen) {
1094 ERROR("write is short: %u instead of %u", res, datalen);
1095 close(fd);
1096 return -1;
1097 }
1098 struct stat st = {
1099 .st_mode = S_IFREG | 0644,
1100 .st_size = datalen,
1101 };
1102 err = fcheck_stat(fd, O_RDWR, &st);
1103 err += fcheck_data(fd, data, 0, datalen);
1104 res = close(fd);
1105 if (res == -1) {
1106 PERROR("close");
1107 err--;
1108 }
1109 if (err)
1110 return -1;
1111
1112 success();
1113 return 0;
1114}
1115
1116static int test_mknod(void)
1117{
1118 int err = 0;
1119 int res;
1120
1121 start_test("mknod");
1122 unlink(testfile);
1123 res = mknod(testfile, 0644, 0);
1124 if (res == -1) {
1125 PERROR("mknod");
1126 return -1;
1127 }
1128 res = check_type(testfile, S_IFREG);
1129 if (res == -1)
1130 return -1;
1131 err += check_mode(testfile, 0644);
1132 err += check_nlink(testfile, 1);
1133 err += check_size(testfile, 0);
1134 res = unlink(testfile);
1135 if (res == -1) {
1136 PERROR("unlink");
1137 return -1;
1138 }
1139 res = check_nonexist(testfile);
1140 if (res == -1)
1141 return -1;
1142 if (err)
1143 return -1;
1144
1145 success();
1146 return 0;
1147}
1148
1149#define test_open(exist, flags, mode) do_test_open(exist, flags, #flags, mode)
1150
1151static int do_test_open(int exist, int flags, const char *flags_str, int mode)
1152{
1153 char buf[4096];
1154 const char *data = testdata;
1155 int datalen = testdatalen;
1156 unsigned currlen = 0;
1157 int err = 0;
1158 int res;
1159 int fd;
1160 off_t off;
1161
1162 start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode);
1163 unlink(testfile);
1164 if (exist) {
1165 res = create_file(testfile_r, testdata2, testdata2len);
1166 if (res == -1)
1167 return -1;
1168
1169 currlen = testdata2len;
1170 }
1171
1172 fd = open(testfile, flags, mode);
1173 if ((flags & O_CREAT) && (flags & O_EXCL) && exist) {
1174 if (fd != -1) {
1175 ERROR("open should have failed");
1176 close(fd);
1177 return -1;
1178 } else if (errno == EEXIST)
1179 goto succ;
1180 }
1181 if (!(flags & O_CREAT) && !exist) {
1182 if (fd != -1) {
1183 ERROR("open should have failed");
1184 close(fd);
1185 return -1;
1186 } else if (errno == ENOENT)
1187 goto succ;
1188 }
1189 if (fd == -1) {
1190 PERROR("open");
1191 return -1;
1192 }
1193
1194 if (flags & O_TRUNC)
1195 currlen = 0;
1196
1197 err += check_type(testfile, S_IFREG);
1198 if (exist)
1199 err += check_mode(testfile, 0644);
1200 else
1201 err += check_mode(testfile, mode);
1202 err += check_nlink(testfile, 1);
1203 err += check_size(testfile, currlen);
1204 if (exist && !(flags & O_TRUNC) && (mode & S_IRUSR))
1205 err += check_data(testfile, testdata2, 0, testdata2len);
1206
1207 res = write(fd, data, datalen);
1208 if ((flags & O_ACCMODE) != O_RDONLY) {
1209 if (res == -1) {
1210 PERROR("write");
1211 err --;
1212 } else if (res != datalen) {
1213 ERROR("write is short: %u instead of %u", res, datalen);
1214 err --;
1215 } else {
1216 if (datalen > (int) currlen)
1217 currlen = datalen;
1218
1219 err += check_size(testfile, currlen);
1220
1221 if (mode & S_IRUSR) {
1222 err += check_data(testfile, data, 0, datalen);
1223 if (exist && !(flags & O_TRUNC) &&
1224 testdata2len > datalen)
1225 err += check_data(testfile,
1226 testdata2 + datalen,
1227 datalen,
1228 testdata2len - datalen);
1229 }
1230 }
1231 } else {
1232 if (res != -1) {
1233 ERROR("write should have failed");
1234 err --;
1235 } else if (errno != EBADF) {
1236 PERROR("write");
1237 err --;
1238 }
1239 }
1240 off = lseek(fd, SEEK_SET, 0);
1241 if (off == (off_t) -1) {
1242 PERROR("lseek");
1243 err--;
1244 } else if (off != 0) {
1245 ERROR("offset should have returned 0");
1246 err --;
1247 }
1248 res = read(fd, buf, sizeof(buf));
1249 if ((flags & O_ACCMODE) != O_WRONLY) {
1250 if (res == -1) {
1251 PERROR("read");
1252 err--;
1253 } else {
1254 int readsize =
1255 currlen < sizeof(buf) ? currlen : sizeof(buf);
1256 if (res != readsize) {
1257 ERROR("read is short: %i instead of %u",
1258 res, readsize);
1259 err--;
1260 } else {
1261 if ((flags & O_ACCMODE) != O_RDONLY) {
1262 err += check_buffer(buf, data, datalen);
1263 if (exist && !(flags & O_TRUNC) &&
1264 testdata2len > datalen)
1265 err += check_buffer(buf + datalen,
1266 testdata2 + datalen,
1267 testdata2len - datalen);
1268 } else if (exist)
1269 err += check_buffer(buf, testdata2,
1270 testdata2len);
1271 }
1272 }
1273 } else {
1274 if (res != -1) {
1275 ERROR("read should have failed");
1276 err --;
1277 } else if (errno != EBADF) {
1278 PERROR("read");
1279 err --;
1280 }
1281 }
1282
1283 res = close(fd);
1284 if (res == -1) {
1285 PERROR("close");
1286 return -1;
1287 }
1288 res = unlink(testfile);
1289 if (res == -1) {
1290 PERROR("unlink");
1291 return -1;
1292 }
1293 res = check_nonexist(testfile);
1294 if (res == -1)
1295 return -1;
1296 res = check_nonexist(testfile_r);
1297 if (res == -1)
1298 return -1;
1299 if (err)
1300 return -1;
1301
1302succ:
1303 success();
1304 return 0;
1305}
1306
1307#define test_open_acc(flags, mode, err) \
1308 do_test_open_acc(flags, #flags, mode, err)
1309
1310static int do_test_open_acc(int flags, const char *flags_str, int mode, int err)
1311{
1312 const char *data = testdata;
1313 int datalen = testdatalen;
1314 int res;
1315 int fd;
1316
1317 start_test("open_acc(%s) mode: 0%03o message: '%s'", flags_str, mode,
1318 strerror(err));
1319 unlink(testfile);
1320 res = create_testfile(testfile, data, datalen);
1321 if (res == -1)
1322 return -1;
1323
1324 res = chmod(testfile, mode);
1325 if (res == -1) {
1326 PERROR("chmod");
1327 return -1;
1328 }
1329
1330 res = check_testfile_mode(testfile, mode);
1331 if (res == -1)
1332 return -1;
1333
1334 fd = open(testfile, flags);
1335 if (fd == -1) {
1336 if (err != errno) {
1337 PERROR("open");
1338 return -1;
1339 }
1340 } else {
1341 if (err) {
1342 ERROR("open should have failed");
1343 close(fd);
1344 return -1;
1345 }
1346 close(fd);
1347 }
1348
1349 res = unlink(testfile);
1350 if (res == -1) {
1351 PERROR("unlink");
1352 return -1;
1353 }
1354 res = check_nonexist(testfile);
1355 if (res == -1)
1356 return -1;
1357 res = check_nonexist(testfile_r);
1358 if (res == -1)
1359 return -1;
1360
1361 success();
1362 return 0;
1363}
1364
1365static int test_symlink(void)
1366{
1367 char buf[1024];
1368 const char *data = testdata;
1369 int datalen = testdatalen;
1370 int linklen = strlen(testfile);
1371 int err = 0;
1372 int res;
1373
1374 start_test("symlink");
1375 res = create_testfile(testfile, data, datalen);
1376 if (res == -1)
1377 return -1;
1378
1379 unlink(testfile2);
1380 res = symlink(testfile, testfile2);
1381 if (res == -1) {
1382 PERROR("symlink");
1383 return -1;
1384 }
1385 res = check_type(testfile2, S_IFLNK);
1386 if (res == -1)
1387 return -1;
1388 err += check_mode(testfile2, 0777);
1389 err += check_nlink(testfile2, 1);
1390 res = readlink(testfile2, buf, sizeof(buf));
1391 if (res == -1) {
1392 PERROR("readlink");
1393 err--;
1394 }
1395 if (res != linklen) {
1396 ERROR("short readlink: %u instead of %u", res, linklen);
1397 err--;
1398 }
1399 if (memcmp(buf, testfile, linklen) != 0) {
1400 ERROR("link mismatch");
1401 err--;
1402 }
1403 err += check_size(testfile2, datalen);
1404 err += check_data(testfile2, data, 0, datalen);
1405 res = unlink(testfile2);
1406 if (res == -1) {
1407 PERROR("unlink");
1408 return -1;
1409 }
1410 res = check_nonexist(testfile2);
1411 if (res == -1)
1412 return -1;
1413 if (err)
1414 return -1;
1415
1416 res = unlink(testfile);
1417 if (res == -1) {
1418 PERROR("unlink");
1419 return -1;
1420 }
1421 res = check_nonexist(testfile);
1422 if (res == -1)
1423 return -1;
1424
1425 success();
1426 return 0;
1427}
1428
1429static int test_link(void)
1430{
1431 const char *data = testdata;
1432 int datalen = testdatalen;
1433 int err = 0;
1434 int res;
1435
1436 start_test("link");
1437 res = create_testfile(testfile, data, datalen);
1438 if (res == -1)
1439 return -1;
1440
1441 unlink(testfile2);
1442 res = link(testfile, testfile2);
1443 if (res == -1) {
1444 PERROR("link");
1445 return -1;
1446 }
1447 res = check_type(testfile2, S_IFREG);
1448 if (res == -1)
1449 return -1;
1450 err += check_mode(testfile2, 0644);
1451 err += check_nlink(testfile2, 2);
1452 err += check_size(testfile2, datalen);
1453 err += check_data(testfile2, data, 0, datalen);
1454 res = unlink(testfile);
1455 if (res == -1) {
1456 PERROR("unlink");
1457 return -1;
1458 }
1459 res = check_nonexist(testfile);
1460 if (res == -1)
1461 return -1;
1462
1463 err += check_nlink(testfile2, 1);
1464 res = unlink(testfile2);
1465 if (res == -1) {
1466 PERROR("unlink");
1467 return -1;
1468 }
1469 res = check_nonexist(testfile2);
1470 if (res == -1)
1471 return -1;
1472 if (err)
1473 return -1;
1474
1475 success();
1476 return 0;
1477}
1478
1479static int test_link2(void)
1480{
1481 const char *data = testdata;
1482 int datalen = testdatalen;
1483 int err = 0;
1484 int res;
1485
1486 start_test("link-unlink-link");
1487 res = create_testfile(testfile, data, datalen);
1488 if (res == -1)
1489 return -1;
1490
1491 unlink(testfile2);
1492 res = link(testfile, testfile2);
1493 if (res == -1) {
1494 PERROR("link");
1495 return -1;
1496 }
1497 res = unlink(testfile);
1498 if (res == -1) {
1499 PERROR("unlink");
1500 return -1;
1501 }
1502 res = check_nonexist(testfile);
1503 if (res == -1)
1504 return -1;
1505 res = link(testfile2, testfile);
1506 if (res == -1) {
1507 PERROR("link");
1508 }
1509 res = check_type(testfile, S_IFREG);
1510 if (res == -1)
1511 return -1;
1512 err += check_mode(testfile, 0644);
1513 err += check_nlink(testfile, 2);
1514 err += check_size(testfile, datalen);
1515 err += check_data(testfile, data, 0, datalen);
1516
1517 res = unlink(testfile2);
1518 if (res == -1) {
1519 PERROR("unlink");
1520 return -1;
1521 }
1522 err += check_nlink(testfile, 1);
1523 res = unlink(testfile);
1524 if (res == -1) {
1525 PERROR("unlink");
1526 return -1;
1527 }
1528 res = check_nonexist(testfile);
1529 if (res == -1)
1530 return -1;
1531 if (err)
1532 return -1;
1533
1534 success();
1535 return 0;
1536}
1537
1538static int test_rename_file(void)
1539{
1540 const char *data = testdata;
1541 int datalen = testdatalen;
1542 int err = 0;
1543 int res;
1544
1545 start_test("rename file");
1546 res = create_testfile(testfile, data, datalen);
1547 if (res == -1)
1548 return -1;
1549
1550 unlink(testfile2);
1551 res = rename(testfile, testfile2);
1552 if (res == -1) {
1553 PERROR("rename");
1554 return -1;
1555 }
1556 res = check_nonexist(testfile);
1557 if (res == -1)
1558 return -1;
1559 res = check_type(testfile2, S_IFREG);
1560 if (res == -1)
1561 return -1;
1562 err += check_mode(testfile2, 0644);
1563 err += check_nlink(testfile2, 1);
1564 err += check_size(testfile2, datalen);
1565 err += check_data(testfile2, data, 0, datalen);
1566 res = unlink(testfile2);
1567 if (res == -1) {
1568 PERROR("unlink");
1569 return -1;
1570 }
1571 res = check_nonexist(testfile2);
1572 if (res == -1)
1573 return -1;
1574 if (err)
1575 return -1;
1576
1577 success();
1578 return 0;
1579}
1580
1581static int test_rename_dir(void)
1582{
1583 int err = 0;
1584 int res;
1585
1586 start_test("rename dir");
1587 res = create_dir(testdir, testdir_files);
1588 if (res == -1)
1589 return -1;
1590
1591 rmdir(testdir2);
1592 res = rename(testdir, testdir2);
1593 if (res == -1) {
1594 PERROR("rename");
1595 cleanup_dir(testdir, testdir_files, 1);
1596 return -1;
1597 }
1598 res = check_nonexist(testdir);
1599 if (res == -1) {
1600 cleanup_dir(testdir, testdir_files, 1);
1601 return -1;
1602 }
1603 res = check_type(testdir2, S_IFDIR);
1604 if (res == -1) {
1605 cleanup_dir(testdir2, testdir_files, 1);
1606 return -1;
1607 }
1608 err += check_mode(testdir2, 0755);
1609 err += check_dir_contents(testdir2, testdir_files);
1610 err += cleanup_dir(testdir2, testdir_files, 0);
1611 res = rmdir(testdir2);
1612 if (res == -1) {
1613 PERROR("rmdir");
1614 return -1;
1615 }
1616 res = check_nonexist(testdir2);
1617 if (res == -1)
1618 return -1;
1619 if (err)
1620 return -1;
1621
1622 success();
1623 return 0;
1624}
1625
1626static int test_rename_dir_loop(void)
1627{
1628#define PATH(p) (snprintf(path, sizeof path, "%s/%s", testdir, p), path)
1629#define PATH2(p) (snprintf(path2, sizeof path2, "%s/%s", testdir, p), path2)
1630
1631 char path[1280], path2[1280];
1632 int err = 0;
1633 int res;
1634
1635 start_test("rename dir loop");
1636
1637 res = create_dir(testdir, testdir_files);
1638 if (res == -1)
1639 return -1;
1640
1641 res = mkdir(PATH("a"), 0755);
1642 if (res == -1) {
1643 PERROR("mkdir");
1644 goto fail;
1645 }
1646
1647 res = rename(PATH("a"), PATH2("a"));
1648 if (res == -1) {
1649 PERROR("rename");
1650 goto fail;
1651 }
1652
1653 errno = 0;
1654 res = rename(PATH("a"), PATH2("a/b"));
1655 if (res == 0 || errno != EINVAL) {
1656 PERROR("rename");
1657 goto fail;
1658 }
1659
1660 res = mkdir(PATH("a/b"), 0755);
1661 if (res == -1) {
1662 PERROR("mkdir");
1663 goto fail;
1664 }
1665
1666 res = mkdir(PATH("a/b/c"), 0755);
1667 if (res == -1) {
1668 PERROR("mkdir");
1669 goto fail;
1670 }
1671
1672 errno = 0;
1673 res = rename(PATH("a"), PATH2("a/b/c"));
1674 if (res == 0 || errno != EINVAL) {
1675 PERROR("rename");
1676 goto fail;
1677 }
1678
1679 errno = 0;
1680 res = rename(PATH("a"), PATH2("a/b/c/a"));
1681 if (res == 0 || errno != EINVAL) {
1682 PERROR("rename");
1683 goto fail;
1684 }
1685
1686 errno = 0;
1687 res = rename(PATH("a/b/c"), PATH2("a"));
1688 if (res == 0 || errno != ENOTEMPTY) {
1689 PERROR("rename");
1690 goto fail;
1691 }
1692
1693 res = open(PATH("a/foo"), O_CREAT, 0644);
1694 if (res == -1) {
1695 PERROR("open");
1696 goto fail;
1697 }
1698 close(res);
1699
1700 res = rename(PATH("a/foo"), PATH2("a/bar"));
1701 if (res == -1) {
1702 PERROR("rename");
1703 goto fail;
1704 }
1705
1706 res = rename(PATH("a/bar"), PATH2("a/foo"));
1707 if (res == -1) {
1708 PERROR("rename");
1709 goto fail;
1710 }
1711
1712 res = rename(PATH("a/foo"), PATH2("a/b/bar"));
1713 if (res == -1) {
1714 PERROR("rename");
1715 goto fail;
1716 }
1717
1718 res = rename(PATH("a/b/bar"), PATH2("a/foo"));
1719 if (res == -1) {
1720 PERROR("rename");
1721 goto fail;
1722 }
1723
1724 res = rename(PATH("a/foo"), PATH2("a/b/c/bar"));
1725 if (res == -1) {
1726 PERROR("rename");
1727 goto fail;
1728 }
1729
1730 res = rename(PATH("a/b/c/bar"), PATH2("a/foo"));
1731 if (res == -1) {
1732 PERROR("rename");
1733 goto fail;
1734 }
1735
1736 res = open(PATH("a/bar"), O_CREAT, 0644);
1737 if (res == -1) {
1738 PERROR("open");
1739 goto fail;
1740 }
1741 close(res);
1742
1743 res = rename(PATH("a/foo"), PATH2("a/bar"));
1744 if (res == -1) {
1745 PERROR("rename");
1746 goto fail;
1747 }
1748
1749 unlink(PATH("a/bar"));
1750
1751 res = rename(PATH("a/b"), PATH2("a/d"));
1752 if (res == -1) {
1753 PERROR("rename");
1754 goto fail;
1755 }
1756
1757 res = rename(PATH("a/d"), PATH2("a/b"));
1758 if (res == -1) {
1759 PERROR("rename");
1760 goto fail;
1761 }
1762
1763 res = mkdir(PATH("a/d"), 0755);
1764 if (res == -1) {
1765 PERROR("mkdir");
1766 goto fail;
1767 }
1768
1769 res = rename(PATH("a/b"), PATH2("a/d"));
1770 if (res == -1) {
1771 PERROR("rename");
1772 goto fail;
1773 }
1774
1775 res = rename(PATH("a/d"), PATH2("a/b"));
1776 if (res == -1) {
1777 PERROR("rename");
1778 goto fail;
1779 }
1780
1781 res = mkdir(PATH("a/d"), 0755);
1782 if (res == -1) {
1783 PERROR("mkdir");
1784 goto fail;
1785 }
1786
1787 res = mkdir(PATH("a/d/e"), 0755);
1788 if (res == -1) {
1789 PERROR("mkdir");
1790 goto fail;
1791 }
1792
1793 errno = 0;
1794 res = rename(PATH("a/b"), PATH2("a/d"));
1795 if (res == 0 || (errno != ENOTEMPTY && errno != EEXIST)) {
1796 PERROR("rename");
1797 goto fail;
1798 }
1799
1800 rmdir(PATH("a/d/e"));
1801 rmdir(PATH("a/d"));
1802
1803 rmdir(PATH("a/b/c"));
1804 rmdir(PATH("a/b"));
1805 rmdir(PATH("a"));
1806
1807 err += cleanup_dir(testdir, testdir_files, 0);
1808 res = rmdir(testdir);
1809 if (res == -1) {
1810 PERROR("rmdir");
1811 goto fail;
1812 }
1813 res = check_nonexist(testdir);
1814 if (res == -1)
1815 return -1;
1816 if (err)
1817 return -1;
1818
1819 success();
1820 return 0;
1821
1822fail:
1823 unlink(PATH("a/bar"));
1824
1825 rmdir(PATH("a/d/e"));
1826 rmdir(PATH("a/d"));
1827
1828 rmdir(PATH("a/b/c"));
1829 rmdir(PATH("a/b"));
1830 rmdir(PATH("a"));
1831
1832 cleanup_dir(testdir, testdir_files, 1);
1833 rmdir(testdir);
1834
1835 return -1;
1836
1837#undef PATH2
1838#undef PATH
1839}
1840
1841static int test_mkfifo(void)
1842{
1843 int res;
1844 int err = 0;
1845
1846 start_test("mkfifo");
1847 unlink(testfile);
1848 res = mkfifo(testfile, 0644);
1849 if (res == -1) {
1850 PERROR("mkfifo");
1851 return -1;
1852 }
1853 res = check_type(testfile, S_IFIFO);
1854 if (res == -1)
1855 return -1;
1856 err += check_mode(testfile, 0644);
1857 err += check_nlink(testfile, 1);
1858 res = unlink(testfile);
1859 if (res == -1) {
1860 PERROR("unlink");
1861 return -1;
1862 }
1863 res = check_nonexist(testfile);
1864 if (res == -1)
1865 return -1;
1866 if (err)
1867 return -1;
1868
1869 success();
1870 return 0;
1871}
1872
1873static int test_mkdir(void)
1874{
1875 int res;
1876 int err = 0;
1877 const char *dir_contents[] = {NULL};
1878
1879 start_test("mkdir");
1880 rmdir(testdir);
1881 res = mkdir(testdir, 0755);
1882 if (res == -1) {
1883 PERROR("mkdir");
1884 return -1;
1885 }
1886 res = check_type(testdir, S_IFDIR);
1887 if (res == -1)
1888 return -1;
1889 err += check_mode(testdir, 0755);
1890 /* Some file systems (like btrfs) don't track link
1891 count for directories */
1892 //err += check_nlink(testdir, 2);
1893 err += check_dir_contents(testdir, dir_contents);
1894 res = rmdir(testdir);
1895 if (res == -1) {
1896 PERROR("rmdir");
1897 return -1;
1898 }
1899 res = check_nonexist(testdir);
1900 if (res == -1)
1901 return -1;
1902 if (err)
1903 return -1;
1904
1905 success();
1906 return 0;
1907}
1908
1909static int test_socket(void)
1910{
1911 struct sockaddr_un su;
1912 int fd;
1913 int res;
1914 int err = 0;
1915 const size_t test_sock_len = strlen(testsock) + 1;
1916
1917 start_test("socket");
1918 if (test_sock_len > sizeof(su.sun_path)) {
1919 fprintf(stderr, "Need to shorten mount point by %zu chars\n",
1920 strlen(testsock) + 1 - sizeof(su.sun_path));
1921 return -1;
1922 }
1923 unlink(testsock);
1924 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1925 if (fd < 0) {
1926 PERROR("socket");
1927 return -1;
1928 }
1929 su.sun_family = AF_UNIX;
1930
1931 strncpy(su.sun_path, testsock, test_sock_len);
1932 su.sun_path[sizeof(su.sun_path) - 1] = '\0';
1933 res = bind(fd, (struct sockaddr*)&su, sizeof(su));
1934 if (res == -1) {
1935 PERROR("bind");
1936 return -1;
1937 }
1938
1939 res = check_type(testsock, S_IFSOCK);
1940 if (res == -1) {
1941 close(fd);
1942 return -1;
1943 }
1944 err += check_nlink(testsock, 1);
1945 close(fd);
1946 res = unlink(testsock);
1947 if (res == -1) {
1948 PERROR("unlink");
1949 return -1;
1950 }
1951 res = check_nonexist(testsock);
1952 if (res == -1)
1953 return -1;
1954 if (err)
1955 return -1;
1956
1957 success();
1958 return 0;
1959}
1960
1961#define test_create_ro_dir(flags) \
1962 do_test_create_ro_dir(flags, #flags)
1963
1964static int do_test_create_ro_dir(int flags, const char *flags_str)
1965{
1966 int res;
1967 int err = 0;
1968 int fd;
1969
1970 start_test("open(%s) in read-only directory", flags_str);
1971 rmdir(testdir);
1972 res = mkdir(testdir, 0555);
1973 if (res == -1) {
1974 PERROR("mkdir");
1975 return -1;
1976 }
1977 fd = open(subfile, flags, 0644);
1978 if (fd != -1) {
1979 close(fd);
1980 unlink(subfile);
1981 ERROR("open should have failed");
1982 err--;
1983 } else {
1984 res = check_nonexist(subfile);
1985 if (res == -1)
1986 err--;
1987 }
1988 unlink(subfile);
1989 res = rmdir(testdir);
1990 if (res == -1) {
1991 PERROR("rmdir");
1992 return -1;
1993 }
1994 res = check_nonexist(testdir);
1995 if (res == -1)
1996 return -1;
1997 if (err)
1998 return -1;
1999
2000 success();
2001 return 0;
2002}
2003
2004#ifndef __FreeBSD__
2005/* this tests open with O_TMPFILE
2006 note that this will only work with the fuse low level api
2007 you will get ENOTSUP with the high level api */
2008static int test_create_tmpfile(void)
2009{
2010 rmdir(testdir);
2011 int res = mkdir(testdir, 0777);
2012 if (res)
2013 return -1;
2014
2015 start_test("create tmpfile");
2016
2017 int fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
2018 if(fd == -1) {
2019 if (errno == ENOTSUP) {
2020 /* don't bother if we're working on an old kernel
2021 or on the high level API */
2022 return 0;
2023 }
2024
2025 PERROR("open O_TMPFILE | O_RDWR");
2026 return -1;
2027 }
2028 close(fd);
2029
2030 fd = open(testdir, O_TMPFILE | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
2031 if(fd == -1){
2032 PERROR("open with O_TMPFILE | O_WRONLY | O_EXCL");
2033 return -1;
2034 };
2035 close(fd);
2036
2037 fd = open(testdir, O_TMPFILE | O_RDONLY, S_IRUSR);
2038 if (fd != -1) {
2039 ERROR("open with O_TMPFILE | O_RDONLY succeeded");
2040 return -1;
2041 }
2042
2043 success();
2044 return 0;
2045}
2046
2047static int test_create_and_link_tmpfile(void)
2048{
2049 /* skip this test for now since the github runner will fail in the linkat call below */
2050 return 0;
2051
2052 rmdir(testdir);
2053 unlink(testfile);
2054
2055 int res = mkdir(testdir, 0777);
2056 if (res)
2057 return -1;
2058
2059 start_test("create and link tmpfile");
2060
2061 int fd = open(testdir, O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
2062 if(fd == -1) {
2063 if (errno == ENOTSUP) {
2064 /* don't bother if we're working on an old kernel
2065 or on the high level API */
2066 return 0;
2067 }
2068 PERROR("open with O_TMPFILE | O_RDWR | O_EXCL");
2069 return -1;
2070 }
2071
2072 if (!linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
2073 ERROR("linkat succeeded on a tmpfile opened with O_EXCL");
2074 return -1;
2075 }
2076 close(fd);
2077
2078 fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
2079 if(fd == -1) {
2080 PERROR("open O_TMPFILE");
2081 return -1;
2082 }
2083
2084 if (check_nonexist(testfile)) {
2085 return -1;
2086 }
2087
2088 if (linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
2089 PERROR("linkat tempfile");
2090 return -1;
2091 }
2092 close(fd);
2093
2094 if (check_nlink(testfile, 1)) {
2095 return -1;
2096 }
2097 unlink(testfile);
2098
2099 success();
2100 return 0;
2101}
2102#endif
2103
2104int main(int argc, char *argv[])
2105{
2106 int err = 0;
2107 int a;
2108 int is_root;
2109
2110 umask(0);
2111 if (argc < 2 || argc > 4) {
2112 fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#] [-u]\n", argv[0]);
2113 return 1;
2114 }
2115 basepath = argv[1];
2116 basepath_r = basepath;
2117 for (a = 2; a < argc; a++) {
2118 char *endptr;
2119 char *arg = argv[a];
2120 if (arg[0] == ':') {
2121 basepath_r = arg + 1;
2122 } else {
2123 if (arg[0] == '-') {
2124 arg++;
2125 if (arg[0] == 'u') {
2126 unlinked_test = 1;
2127 endptr = arg + 1;
2128 } else {
2129 skip_test = strtoul(arg, &endptr, 10);
2130 }
2131 } else {
2132 select_test = strtoul(arg, &endptr, 10);
2133 }
2134 if (arg[0] == '\0' || *endptr != '\0') {
2135 fprintf(stderr, "invalid option: '%s'\n", argv[a]);
2136 return 1;
2137 }
2138 }
2139 }
2140 assert(strlen(basepath) < 512);
2141 assert(strlen(basepath_r) < 512);
2142 if (basepath[0] != '/') {
2143 fprintf(stderr, "testdir must be an absolute path\n");
2144 return 1;
2145 }
2146
2147 sprintf(testfile, "%s/testfile", basepath);
2148 sprintf(testfile2, "%s/testfile2", basepath);
2149 sprintf(testdir, "%s/testdir", basepath);
2150 sprintf(testdir2, "%s/testdir2", basepath);
2151 sprintf(subfile, "%s/subfile", testdir2);
2152 sprintf(testsock, "%s/testsock", basepath);
2153
2154 sprintf(testfile_r, "%s/testfile", basepath_r);
2155 sprintf(testfile2_r, "%s/testfile2", basepath_r);
2156 sprintf(testdir_r, "%s/testdir", basepath_r);
2157 sprintf(testdir2_r, "%s/testdir2", basepath_r);
2158 sprintf(subfile_r, "%s/subfile", testdir2_r);
2159
2160 is_root = (geteuid() == 0);
2161
2162 err += test_create();
2163 err += test_create_unlink();
2164 err += test_symlink();
2165 err += test_link();
2166 err += test_link2();
2167 err += test_mknod();
2168 err += test_mkfifo();
2169 err += test_mkdir();
2170 err += test_rename_file();
2171 err += test_rename_dir();
2172 err += test_rename_dir_loop();
2173 err += test_seekdir();
2174 err += test_socket();
2175 err += test_utime();
2176 err += test_truncate(0);
2177 err += test_truncate(testdatalen / 2);
2178 err += test_truncate(testdatalen);
2179 err += test_truncate(testdatalen + 100);
2180 err += test_ftruncate(0, 0600);
2181 err += test_ftruncate(testdatalen / 2, 0600);
2182 err += test_ftruncate(testdatalen, 0600);
2183 err += test_ftruncate(testdatalen + 100, 0600);
2184 err += test_ftruncate(0, 0400);
2185 err += test_ftruncate(0, 0200);
2186 err += test_ftruncate(0, 0000);
2187 err += test_open(0, O_RDONLY, 0);
2188 err += test_open(1, O_RDONLY, 0);
2189 err += test_open(1, O_RDWR, 0);
2190 err += test_open(1, O_WRONLY, 0);
2191 err += test_open(0, O_RDWR | O_CREAT, 0600);
2192 err += test_open(1, O_RDWR | O_CREAT, 0600);
2193 err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600);
2194 err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600);
2195 err += test_open(0, O_RDONLY | O_CREAT, 0600);
2196 err += test_open(0, O_RDONLY | O_CREAT, 0400);
2197 err += test_open(0, O_RDONLY | O_CREAT, 0200);
2198 err += test_open(0, O_RDONLY | O_CREAT, 0000);
2199 err += test_open(0, O_WRONLY | O_CREAT, 0600);
2200 err += test_open(0, O_WRONLY | O_CREAT, 0400);
2201 err += test_open(0, O_WRONLY | O_CREAT, 0200);
2202 err += test_open(0, O_WRONLY | O_CREAT, 0000);
2203 err += test_open(0, O_RDWR | O_CREAT, 0400);
2204 err += test_open(0, O_RDWR | O_CREAT, 0200);
2205 err += test_open(0, O_RDWR | O_CREAT, 0000);
2206 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600);
2207 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600);
2208 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000);
2209 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000);
2210 err += test_open_acc(O_RDONLY, 0600, 0);
2211 err += test_open_acc(O_WRONLY, 0600, 0);
2212 err += test_open_acc(O_RDWR, 0600, 0);
2213 err += test_open_acc(O_RDONLY, 0400, 0);
2214 err += test_open_acc(O_WRONLY, 0200, 0);
2215 if(!is_root) {
2216 err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES);
2217 err += test_open_acc(O_WRONLY, 0400, EACCES);
2218 err += test_open_acc(O_RDWR, 0400, EACCES);
2219 err += test_open_acc(O_RDONLY, 0200, EACCES);
2220 err += test_open_acc(O_RDWR, 0200, EACCES);
2221 err += test_open_acc(O_RDONLY, 0000, EACCES);
2222 err += test_open_acc(O_WRONLY, 0000, EACCES);
2223 err += test_open_acc(O_RDWR, 0000, EACCES);
2224 }
2225 err += test_create_ro_dir(O_CREAT);
2226 err += test_create_ro_dir(O_CREAT | O_EXCL);
2227 err += test_create_ro_dir(O_CREAT | O_WRONLY);
2228 err += test_create_ro_dir(O_CREAT | O_TRUNC);
2229 err += test_copy_file_range();
2230 err += test_statx();
2231#ifndef __FreeBSD__
2232 err += test_create_tmpfile();
2233 err += test_create_and_link_tmpfile();
2234#endif
2235
2236 unlink(testfile2);
2237 unlink(testsock);
2238 rmdir(testdir);
2239 rmdir(testdir2);
2240
2241 if (err) {
2242 fprintf(stderr, "%i tests failed\n", -err);
2243 return 1;
2244 }
2245
2246 return check_unlinked_testfiles();
2247}
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2test__want__conversion_8c_source.html0000644000175000017500000013400015156613443026146 0ustar berndbernd libfuse: fuse-3.17.4/test/test_want_conversion.c Source File
libfuse
test_want_conversion.c
1#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
2
3#include "util.h"
4#include "fuse_i.h"
5#include "fuse_lowlevel.h"
6#include <stdio.h>
7#include <assert.h>
8#include <inttypes.h>
9#include <stdbool.h>
10#include <err.h>
11
12static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
13{
14 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
15
16 printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64
17 " want_default=0x%" PRIx32 " want_ext_default=0x%" PRIx64 "\n",
18 prefix, conn->want, conn->want_ext, se->conn_want,
19 se->conn_want_ext);
20}
21
22static void application_init_old_style(struct fuse_conn_info *conn)
23{
24 /* Simulate application init the old style */
26 conn->want &= ~FUSE_CAP_SPLICE_READ;
27
28 /*
29 * Also use new style API, as that might happen through
30 * fuse_apply_conn_info_opts()
31 */
33}
34
35static void application_init_new_style(struct fuse_conn_info *conn)
36{
37 /* Simulate application init the new style */
41}
42
43static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
44{
45 /* High-level init */
47
48 if (new_style)
49 application_init_new_style(conn);
50 else
51 application_init_old_style(conn);
52}
53
54static void test_do_init(struct fuse_conn_info *conn, bool new_style)
55{
56 /* Initial setup */
61 conn->capable = fuse_lower_32_bits(conn->capable_ext);
62
66
67 print_conn_info("Initial state", conn);
68
69 int rc;
70
71 test_fuse_fs_init(conn, new_style);
72 print_conn_info("After init", conn);
73
75 assert(rc == 0);
76
77 /* Verify all expected flags are set */
78 assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
79 assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
80 assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
81 assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
82 assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
83 assert(conn->want_ext & FUSE_CAP_IOCTL_DIR);
84
85 /* Verify no other flags are set */
86 assert(conn->want_ext ==
90
91 print_conn_info("After init", conn);
92}
93
94static void test_want_conversion_basic(void)
95{
96 const struct fuse_lowlevel_ops ops = { 0 };
97 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
98 struct fuse_session *se;
99 struct fuse_conn_info *conn;
100
101 /* Add the program name to arg[0] */
102 if (fuse_opt_add_arg(&args, "test_signals")) {
103 fprintf(stderr, "Failed to add argument\n");
104 errx(1, "Failed to add argument");
105 }
106
107
108 se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
109 assert(se);
110 conn = &se->conn;
111 printf("\nTesting basic want conversion, old style:\n");
112 test_do_init(conn, false);
114
115 se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
116 assert(se);
117 conn = &se->conn;
118 printf("\nTesting basic want conversion, new style:\n");
119 test_do_init(conn, true);
120 print_conn_info("After init", conn);
122
123 fuse_opt_free_args(&args);
124
125}
126
127static void test_want_conversion_conflict(void)
128{
129 struct fuse_conn_info conn = { 0 };
130 int rc;
131
132 printf("\nTesting want conversion conflict:\n");
133
134 /* Test conflicting values */
135 /* Initialize like fuse_lowlevel.c does */
139 conn.capable = fuse_lower_32_bits(conn.capable_ext);
140 conn.want_ext = conn.capable_ext;
141 conn.want = fuse_lower_32_bits(conn.want_ext);
142 print_conn_info("Test conflict initial", &conn);
143
144 /* Simulate application init modifying capabilities */
145 conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
146 conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */
147
149 assert(rc == -EINVAL);
150 print_conn_info("Test conflict after", &conn);
151
152 printf("Want conversion conflict test passed\n");
153}
154
155static void test_want_conversion_high_bits(void)
156{
157 struct fuse_conn_info conn = { 0 };
158 int rc;
159
160 printf("\nTesting want conversion high bits preservation:\n");
161
162 /* Test high bits preservation */
163 conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ;
164 conn.want = fuse_lower_32_bits(conn.want_ext);
165 print_conn_info("Test high bits initial", &conn);
166
168 assert(rc == 0);
169 assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
170 print_conn_info("Test high bits after", &conn);
171
172 printf("Want conversion high bits test passed\n");
173}
174
175int main(void)
176{
177 test_want_conversion_basic();
178 test_want_conversion_conflict();
179 test_want_conversion_high_bits();
180 return 0;
181}
#define FUSE_CAP_IOCTL_DIR
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
uint64_t capable_ext
uint32_t capable
uint64_t want_ext
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2test__want__conversion_8c_source.html0000644000175000017500000013400015156613443026144 0ustar berndbernd libfuse: fuse-3.18.1/test/test_want_conversion.c Source File
libfuse
test_want_conversion.c
1#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
2
3#include "util.h"
4#include "fuse_i.h"
5#include "fuse_lowlevel.h"
6#include <stdio.h>
7#include <assert.h>
8#include <inttypes.h>
9#include <stdbool.h>
10#include <err.h>
11
12static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
13{
14 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
15
16 printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64
17 " want_default=0x%" PRIx32 " want_ext_default=0x%" PRIx64 "\n",
18 prefix, conn->want, conn->want_ext, se->conn_want,
19 se->conn_want_ext);
20}
21
22static void application_init_old_style(struct fuse_conn_info *conn)
23{
24 /* Simulate application init the old style */
26 conn->want &= ~FUSE_CAP_SPLICE_READ;
27
28 /*
29 * Also use new style API, as that might happen through
30 * fuse_apply_conn_info_opts()
31 */
33}
34
35static void application_init_new_style(struct fuse_conn_info *conn)
36{
37 /* Simulate application init the new style */
41}
42
43static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
44{
45 /* High-level init */
47
48 if (new_style)
49 application_init_new_style(conn);
50 else
51 application_init_old_style(conn);
52}
53
54static void test_do_init(struct fuse_conn_info *conn, bool new_style)
55{
56 /* Initial setup */
61 conn->capable = fuse_lower_32_bits(conn->capable_ext);
62
66
67 print_conn_info("Initial state", conn);
68
69 int rc;
70
71 test_fuse_fs_init(conn, new_style);
72 print_conn_info("After init", conn);
73
75 assert(rc == 0);
76
77 /* Verify all expected flags are set */
78 assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
79 assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
80 assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
81 assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
82 assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
83 assert(conn->want_ext & FUSE_CAP_IOCTL_DIR);
84
85 /* Verify no other flags are set */
86 assert(conn->want_ext ==
90
91 print_conn_info("After init", conn);
92}
93
94static void test_want_conversion_basic(void)
95{
96 const struct fuse_lowlevel_ops ops = { 0 };
97 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
98 struct fuse_session *se;
99 struct fuse_conn_info *conn;
100
101 /* Add the program name to arg[0] */
102 if (fuse_opt_add_arg(&args, "test_signals")) {
103 fprintf(stderr, "Failed to add argument\n");
104 errx(1, "Failed to add argument");
105 }
106
107
108 se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
109 assert(se);
110 conn = &se->conn;
111 printf("\nTesting basic want conversion, old style:\n");
112 test_do_init(conn, false);
114
115 se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
116 assert(se);
117 conn = &se->conn;
118 printf("\nTesting basic want conversion, new style:\n");
119 test_do_init(conn, true);
120 print_conn_info("After init", conn);
122
123 fuse_opt_free_args(&args);
124
125}
126
127static void test_want_conversion_conflict(void)
128{
129 struct fuse_conn_info conn = { 0 };
130 int rc;
131
132 printf("\nTesting want conversion conflict:\n");
133
134 /* Test conflicting values */
135 /* Initialize like fuse_lowlevel.c does */
139 conn.capable = fuse_lower_32_bits(conn.capable_ext);
140 conn.want_ext = conn.capable_ext;
141 conn.want = fuse_lower_32_bits(conn.want_ext);
142 print_conn_info("Test conflict initial", &conn);
143
144 /* Simulate application init modifying capabilities */
145 conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
146 conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */
147
149 assert(rc == -EINVAL);
150 print_conn_info("Test conflict after", &conn);
151
152 printf("Want conversion conflict test passed\n");
153}
154
155static void test_want_conversion_high_bits(void)
156{
157 struct fuse_conn_info conn = { 0 };
158 int rc;
159
160 printf("\nTesting want conversion high bits preservation:\n");
161
162 /* Test high bits preservation */
163 conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ;
164 conn.want = fuse_lower_32_bits(conn.want_ext);
165 print_conn_info("Test high bits initial", &conn);
166
168 assert(rc == 0);
169 assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
170 print_conn_info("Test high bits after", &conn);
171
172 printf("Want conversion high bits test passed\n");
173}
174
175int main(void)
176{
177 test_want_conversion_basic();
178 test_want_conversion_conflict();
179 test_want_conversion_high_bits();
180 return 0;
181}
#define FUSE_CAP_IOCTL_DIR
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
uint64_t capable_ext
uint32_t capable
uint64_t want_ext
fuse-3.18.2/doc/html/test_2test__want__conversion_8c_source.html0000644000175000017500000013361515156613443024003 0ustar berndbernd libfuse: test/test_want_conversion.c Source File
libfuse
test_want_conversion.c
1#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
2
3#include "util.h"
4#include "fuse_i.h"
5#include "fuse_lowlevel.h"
6#include <stdio.h>
7#include <assert.h>
8#include <inttypes.h>
9#include <stdbool.h>
10#include <err.h>
11
12static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
13{
14 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
15
16 printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64
17 " want_default=0x%" PRIx32 " want_ext_default=0x%" PRIx64 "\n",
18 prefix, conn->want, conn->want_ext, se->conn_want,
19 se->conn_want_ext);
20}
21
22static void application_init_old_style(struct fuse_conn_info *conn)
23{
24 /* Simulate application init the old style */
26 conn->want &= ~FUSE_CAP_SPLICE_READ;
27
28 /*
29 * Also use new style API, as that might happen through
30 * fuse_apply_conn_info_opts()
31 */
33}
34
35static void application_init_new_style(struct fuse_conn_info *conn)
36{
37 /* Simulate application init the new style */
41}
42
43static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
44{
45 /* High-level init */
47
48 if (new_style)
49 application_init_new_style(conn);
50 else
51 application_init_old_style(conn);
52}
53
54static void test_do_init(struct fuse_conn_info *conn, bool new_style)
55{
56 /* Initial setup */
61 conn->capable = fuse_lower_32_bits(conn->capable_ext);
62
66
67 print_conn_info("Initial state", conn);
68
69 int rc;
70
71 test_fuse_fs_init(conn, new_style);
72 print_conn_info("After init", conn);
73
75 assert(rc == 0);
76
77 /* Verify all expected flags are set */
78 assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
79 assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
80 assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
81 assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
82 assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
83 assert(conn->want_ext & FUSE_CAP_IOCTL_DIR);
84
85 /* Verify no other flags are set */
86 assert(conn->want_ext ==
90
91 print_conn_info("After init", conn);
92}
93
94static void test_want_conversion_basic(void)
95{
96 const struct fuse_lowlevel_ops ops = { 0 };
97 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
98 struct fuse_session *se;
99 struct fuse_conn_info *conn;
100
101 /* Add the program name to arg[0] */
102 if (fuse_opt_add_arg(&args, "test_signals")) {
103 fprintf(stderr, "Failed to add argument\n");
104 errx(1, "Failed to add argument");
105 }
106
107
108 se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
109 assert(se);
110 conn = &se->conn;
111 printf("\nTesting basic want conversion, old style:\n");
112 test_do_init(conn, false);
114
115 se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
116 assert(se);
117 conn = &se->conn;
118 printf("\nTesting basic want conversion, new style:\n");
119 test_do_init(conn, true);
120 print_conn_info("After init", conn);
122
123 fuse_opt_free_args(&args);
124
125}
126
127static void test_want_conversion_conflict(void)
128{
129 struct fuse_conn_info conn = { 0 };
130 int rc;
131
132 printf("\nTesting want conversion conflict:\n");
133
134 /* Test conflicting values */
135 /* Initialize like fuse_lowlevel.c does */
139 conn.capable = fuse_lower_32_bits(conn.capable_ext);
140 conn.want_ext = conn.capable_ext;
141 conn.want = fuse_lower_32_bits(conn.want_ext);
142 print_conn_info("Test conflict initial", &conn);
143
144 /* Simulate application init modifying capabilities */
145 conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
146 conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */
147
149 assert(rc == -EINVAL);
150 print_conn_info("Test conflict after", &conn);
151
152 printf("Want conversion conflict test passed\n");
153}
154
155static void test_want_conversion_high_bits(void)
156{
157 struct fuse_conn_info conn = { 0 };
158 int rc;
159
160 printf("\nTesting want conversion high bits preservation:\n");
161
162 /* Test high bits preservation */
163 conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ;
164 conn.want = fuse_lower_32_bits(conn.want_ext);
165 print_conn_info("Test high bits initial", &conn);
166
168 assert(rc == 0);
169 assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
170 print_conn_info("Test high bits after", &conn);
171
172 printf("Want conversion high bits test passed\n");
173}
174
175int main(void)
176{
177 test_want_conversion_basic();
178 test_want_conversion_conflict();
179 test_want_conversion_high_bits();
180 return 0;
181}
#define FUSE_CAP_IOCTL_DIR
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
uint64_t capable_ext
uint32_t capable
uint64_t want_ext
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2test__write__cache_8c_source.html0000644000175000017500000017442415156613443025223 0ustar berndbernd libfuse: fuse-3.17.4/test/test_write_cache.c Source File
libfuse
test_write_cache.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file COPYING.
7*/
8
9
10#define FUSE_USE_VERSION 30
11
12/* Not really needed - just to test build with FUSE_USE_VERSION == 30 */
13#include <fuse.h>
14
15#include <fuse_config.h>
16#include <fuse_lowlevel.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <assert.h>
23#include <stddef.h>
24#include <unistd.h>
25#include <sys/stat.h>
26#include <pthread.h>
27#include <stdatomic.h>
28
29#ifndef __linux__
30#include <limits.h>
31#else
32#include <linux/limits.h>
33#endif
34
35#define FILE_INO 2
36#define FILE_NAME "write_me"
37
38/* Command line parsing */
39struct options {
40 int writeback;
41 int data_size;
42 int delay_ms;
43} options = {
44 .writeback = 0,
45 .data_size = 2048,
46 .delay_ms = 0,
47};
48
49#define WRITE_SYSCALLS 64
50
51#define OPTION(t, p) \
52 { t, offsetof(struct options, p), 1 }
53static const struct fuse_opt option_spec[] = {
54 OPTION("writeback_cache", writeback),
55 OPTION("--data-size=%d", data_size),
56 OPTION("--delay_ms=%d", delay_ms),
58};
59static int got_write;
60static atomic_int write_cnt;
61
62pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
63pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
64static int write_start, write_done;
65
66static void tfs_init (void *userdata, struct fuse_conn_info *conn)
67{
68 (void) userdata;
69
70 if(options.writeback) {
73 }
74}
75
76static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
77 stbuf->st_ino = ino;
78 if (ino == FUSE_ROOT_ID) {
79 stbuf->st_mode = S_IFDIR | 0755;
80 stbuf->st_nlink = 1;
81 }
82
83 else if (ino == FILE_INO) {
84 stbuf->st_mode = S_IFREG | 0222;
85 stbuf->st_nlink = 1;
86 stbuf->st_size = 0;
87 }
88
89 else
90 return -1;
91
92 return 0;
93}
94
95static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
96 const char *name) {
97 struct fuse_entry_param e;
98 memset(&e, 0, sizeof(e));
99
100 if (parent != FUSE_ROOT_ID)
101 goto err_out;
102 else if (strcmp(name, FILE_NAME) == 0)
103 e.ino = FILE_INO;
104 else
105 goto err_out;
106
107 if (tfs_stat(e.ino, &e.attr) != 0)
108 goto err_out;
109 fuse_reply_entry(req, &e);
110 return;
111
112err_out:
113 fuse_reply_err(req, ENOENT);
114}
115
116static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
117 struct fuse_file_info *fi) {
118 struct stat stbuf;
119
120 (void) fi;
121
122 memset(&stbuf, 0, sizeof(stbuf));
123 if (tfs_stat(ino, &stbuf) != 0)
124 fuse_reply_err(req, ENOENT);
125 else
126 fuse_reply_attr(req, &stbuf, 5);
127}
128
129static void tfs_open(fuse_req_t req, fuse_ino_t ino,
130 struct fuse_file_info *fi) {
131 if (ino == FUSE_ROOT_ID)
132 fuse_reply_err(req, EISDIR);
133 else {
134 assert(ino == FILE_INO);
135 /* Test close(rofd) does not block waiting for pending writes */
136 fi->noflush = !options.writeback && options.delay_ms &&
137 (fi->flags & O_ACCMODE) == O_RDONLY;
138 fuse_reply_open(req, fi);
139 }
140}
141
142static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
143 size_t size, off_t off, struct fuse_file_info *fi) {
144 (void) fi; (void) buf; (void) off;
145 size_t expected;
146
147 assert(ino == FILE_INO);
148 expected = options.data_size;
149 if(options.writeback)
150 expected *= 2;
151
152 write_cnt++;
153
154 if(size != expected && !options.writeback)
155 fprintf(stderr, "ERROR: Expected %zd bytes, got %zd\n!",
156 expected, size);
157 else
158 got_write = 1;
159
160 /* Simulate waiting for pending writes */
161 if (options.delay_ms) {
162 pthread_mutex_lock(&lock);
163 write_start = 1;
164 pthread_cond_signal(&cond);
165 pthread_mutex_unlock(&lock);
166
167 usleep(options.delay_ms * 1000);
168
169 pthread_mutex_lock(&lock);
170 write_done = 1;
171 pthread_cond_signal(&cond);
172 pthread_mutex_unlock(&lock);
173 }
174
175 fuse_reply_write(req, size);
176}
177
178static struct fuse_lowlevel_ops tfs_oper = {
179 .init = tfs_init,
180 .lookup = tfs_lookup,
181 .getattr = tfs_getattr,
182 .open = tfs_open,
183 .write = tfs_write,
184};
185
186static void* close_rofd(void *data) {
187 int rofd = (int)(long) data;
188
189 /* Wait for first write to start */
190 pthread_mutex_lock(&lock);
191 while (!write_start && !write_done)
192 pthread_cond_wait(&cond, &lock);
193 pthread_mutex_unlock(&lock);
194
195 close(rofd);
196 printf("rofd closed. write_start: %d write_done: %d\n", write_start, write_done);
197
198 /* First write should not have been completed */
199 if (write_done)
200 fprintf(stderr, "ERROR: close(rofd) blocked on write!\n");
201
202 return NULL;
203}
204
205static void* run_fs(void *data) {
206 struct fuse_session *se = (struct fuse_session*) data;
207 assert(fuse_session_loop(se) == 0);
208 return NULL;
209}
210
211static void test_fs(char *mountpoint) {
212 char fname[PATH_MAX];
213 char *buf;
214 const size_t iosize = options.data_size;
215 const size_t dsize = options.data_size * WRITE_SYSCALLS;
216 int fd, rofd;
217 pthread_t rofd_thread;
218 off_t off = 0;
219
220 buf = malloc(dsize);
221 assert(buf != NULL);
222 assert((fd = open("/dev/urandom", O_RDONLY)) != -1);
223 assert(read(fd, buf, dsize) == dsize);
224 close(fd);
225
226 assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME,
227 mountpoint) > 0);
228 fd = open(fname, O_WRONLY);
229 if (fd == -1) {
230 perror(fname);
231 assert(0);
232 }
233
234 if (options.delay_ms) {
235 /* Verify that close(rofd) does not block waiting for pending writes */
236 rofd = open(fname, O_RDONLY);
237 assert(pthread_create(&rofd_thread, NULL, close_rofd, (void *)(long)rofd) == 0);
238 /* Give close_rofd time to start */
239 usleep(options.delay_ms * 1000);
240 }
241
242 for (int cnt = 0; cnt < WRITE_SYSCALLS; cnt++) {
243 assert(pwrite(fd, buf + off, iosize, off) == iosize);
244 off += iosize;
245 assert(off <= dsize);
246 }
247 free(buf);
248 close(fd);
249
250 if (options.delay_ms) {
251 printf("rwfd closed. write_start: %d write_done: %d\n", write_start, write_done);
252 assert(pthread_join(rofd_thread, NULL) == 0);
253 }
254}
255
256int main(int argc, char *argv[]) {
257 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
258 struct fuse_session *se;
259 struct fuse_cmdline_opts fuse_opts;
260 pthread_t fs_thread;
261
262 assert(fuse_opt_parse(&args, &options, option_spec, NULL) == 0);
263 assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
264#ifndef __FreeBSD__
265 assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
266#endif
267 se = fuse_session_new(&args, &tfs_oper,
268 sizeof(tfs_oper), NULL);
269 fuse_opt_free_args(&args);
270 assert (se != NULL);
271 assert(fuse_set_signal_handlers(se) == 0);
272 assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
273
274 /* Start file-system thread */
275 assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
276
277 /* Write test data */
278 test_fs(fuse_opts.mountpoint);
279 free(fuse_opts.mountpoint);
280
281 /* Stop file system */
284 assert(pthread_join(fs_thread, NULL) == 0);
285
286 assert(got_write == 1);
287
288 /*
289 * when writeback cache is enabled, kernel side can merge requests, but
290 * memory pressure, system 'sync' might trigger data flushes before - flush
291 * might happen in between write syscalls - merging subpage writes into
292 * a single page and pages into large fuse requests might or might not work.
293 * Though we can expect that that at least some (but maybe all) write
294 * system calls can be merged.
295 */
296 if (options.writeback)
297 assert(write_cnt < WRITE_SYSCALLS);
298 else
299 assert(write_cnt == WRITE_SYSCALLS);
300
303
304 printf("Test completed successfully.\n");
305 return 0;
306}
307
308
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_WRITEBACK_CACHE
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
fuse_ino_t ino
uint32_t noflush
Definition fuse_common.h:93
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2test__write__cache_8c_source.html0000644000175000017500000017735115156613443025223 0ustar berndbernd libfuse: fuse-3.18.1/test/test_write_cache.c Source File
libfuse
test_write_cache.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
9#define FUSE_USE_VERSION 30
10
11/* Not really needed - just to test build with FUSE_USE_VERSION == 30 */
12#include <fuse.h>
13
14#include <fuse_config.h>
15#include <fuse_lowlevel.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <assert.h>
22#include <stddef.h>
23#include <unistd.h>
24#include <sys/stat.h>
25#include <pthread.h>
26#include <stdatomic.h>
27
28#ifndef __linux__
29#include <limits.h>
30#else
31#include <linux/limits.h>
32#endif
33
34#define FILE_INO 2
35#define FILE_NAME "write_me"
36
37/* Command line parsing */
38struct options {
39 int writeback;
40 int data_size;
41 int delay_ms;
42} options = {
43 .writeback = 0,
44 .data_size = 2048,
45 .delay_ms = 0,
46};
47
48#define WRITE_SYSCALLS 64
49
50#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
51static const struct fuse_opt option_spec[] = {
52 OPTION("writeback_cache", writeback),
53 OPTION("--data-size=%d", data_size), OPTION("--delay_ms=%d", delay_ms),
55};
56static int got_write;
57static atomic_int write_cnt;
58
59pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
60pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
61static int write_start, write_done;
62
63static void tfs_init(void *userdata, struct fuse_conn_info *conn)
64{
65 (void)userdata;
66
67 if (options.writeback) {
70 }
71}
72
73static int tfs_stat(fuse_ino_t ino, struct stat *stbuf)
74{
75 stbuf->st_ino = ino;
76 if (ino == FUSE_ROOT_ID) {
77 stbuf->st_mode = S_IFDIR | 0755;
78 stbuf->st_nlink = 1;
79 }
80
81 else if (ino == FILE_INO) {
82 stbuf->st_mode = S_IFREG | 0222;
83 stbuf->st_nlink = 1;
84 stbuf->st_size = 0;
85 }
86
87 else
88 return -1;
89
90 return 0;
91}
92
93static void tfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
94{
95 struct fuse_entry_param e;
96
97 memset(&e, 0, sizeof(e));
98
99 if (parent != FUSE_ROOT_ID)
100 goto err_out;
101 else if (strcmp(name, FILE_NAME) == 0)
102 e.ino = FILE_INO;
103 else
104 goto err_out;
105
106 if (tfs_stat(e.ino, &e.attr) != 0)
107 goto err_out;
108 fuse_reply_entry(req, &e);
109 return;
110
111err_out:
112 fuse_reply_err(req, ENOENT);
113}
114
115static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
116 struct fuse_file_info *fi)
117{
118 struct stat stbuf;
119
120 (void)fi;
121
122 memset(&stbuf, 0, sizeof(stbuf));
123 if (tfs_stat(ino, &stbuf) != 0)
124 fuse_reply_err(req, ENOENT);
125 else
126 fuse_reply_attr(req, &stbuf, 5);
127}
128
129static void tfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
130{
131 if (ino == FUSE_ROOT_ID)
132 fuse_reply_err(req, EISDIR);
133 else {
134 assert(ino == FILE_INO);
135 /* Test close(rofd) does not block waiting for pending writes */
136 fi->noflush = !options.writeback && options.delay_ms &&
137 (fi->flags & O_ACCMODE) == O_RDONLY;
138 fuse_reply_open(req, fi);
139 }
140}
141
142static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
143 size_t size, off_t off, struct fuse_file_info *fi)
144{
145 (void)fi;
146 (void)buf;
147 (void)off;
148 size_t expected;
149
150 assert(ino == FILE_INO);
151 expected = options.data_size;
152 if (options.writeback)
153 expected *= 2;
154
155 write_cnt++;
156
157 if (size != expected && !options.writeback)
158 fprintf(stderr, "ERROR: Expected %zd bytes, got %zd\n!",
159 expected, size);
160 else
161 got_write = 1;
162
163 /* Simulate waiting for pending writes */
164 if (options.delay_ms) {
165 pthread_mutex_lock(&lock);
166 write_start = 1;
167 pthread_cond_signal(&cond);
168 pthread_mutex_unlock(&lock);
169
170 usleep(options.delay_ms * 1000);
171
172 pthread_mutex_lock(&lock);
173 write_done = 1;
174 pthread_cond_signal(&cond);
175 pthread_mutex_unlock(&lock);
176 }
177
178 fuse_reply_write(req, size);
179}
180
181static struct fuse_lowlevel_ops tfs_oper = {
182 .init = tfs_init,
183 .lookup = tfs_lookup,
184 .getattr = tfs_getattr,
185 .open = tfs_open,
186 .write = tfs_write,
187};
188
189static void *close_rofd(void *data)
190{
191 int rofd = (int)(long)data;
192
193 /* Wait for first write to start */
194 pthread_mutex_lock(&lock);
195 while (!write_start && !write_done)
196 pthread_cond_wait(&cond, &lock);
197 pthread_mutex_unlock(&lock);
198
199 close(rofd);
200 printf("rofd closed. write_start: %d write_done: %d\n", write_start,
201 write_done);
202
203 /* First write should not have been completed */
204 if (write_done)
205 fprintf(stderr, "ERROR: close(rofd) blocked on write!\n");
206
207 return NULL;
208}
209
210static void *run_fs(void *data)
211{
212 struct fuse_session *se = (struct fuse_session *)data;
213
214 assert(fuse_session_loop(se) == 0);
215 return NULL;
216}
217
218static void test_fs(char *mountpoint)
219{
220 char fname[PATH_MAX];
221 char *buf;
222 const size_t iosize = options.data_size;
223 const size_t dsize = options.data_size * WRITE_SYSCALLS;
224 int fd, rofd;
225 pthread_t rofd_thread;
226 off_t off = 0;
227
228 buf = malloc(dsize);
229 assert(buf != NULL);
230 assert((fd = open("/dev/urandom", O_RDONLY)) != -1);
231 assert(read(fd, buf, dsize) == dsize);
232 close(fd);
233
234 assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME, mountpoint) > 0);
235 fd = open(fname, O_WRONLY);
236 if (fd == -1) {
237 perror(fname);
238 assert(0);
239 }
240
241 if (options.delay_ms) {
242 /* Verify that close(rofd) does not block waiting for pending writes */
243 rofd = open(fname, O_RDONLY);
244 assert(pthread_create(&rofd_thread, NULL, close_rofd,
245 (void *)(long)rofd) == 0);
246 /* Give close_rofd time to start */
247 usleep(options.delay_ms * 1000);
248 }
249
250 for (int cnt = 0; cnt < WRITE_SYSCALLS; cnt++) {
251 assert(pwrite(fd, buf + off, iosize, off) == iosize);
252 off += iosize;
253 assert(off <= dsize);
254 }
255 free(buf);
256 close(fd);
257
258 if (options.delay_ms) {
259 printf("rwfd closed. write_start: %d write_done: %d\n",
260 write_start, write_done);
261 assert(pthread_join(rofd_thread, NULL) == 0);
262 }
263}
264
265int main(int argc, char *argv[])
266{
267 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
268 struct fuse_session *se;
269 struct fuse_cmdline_opts fuse_opts;
270 pthread_t fs_thread;
271
272 assert(fuse_opt_parse(&args, &options, option_spec, NULL) == 0);
273 assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
274#ifndef __FreeBSD__
275 assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
276#endif
277 se = fuse_session_new(&args, &tfs_oper, sizeof(tfs_oper), NULL);
278 fuse_opt_free_args(&args);
279 assert(se != NULL);
280 assert(fuse_set_signal_handlers(se) == 0);
281 assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
282
283 /* Start file-system thread */
284 assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
285
286 /* Write test data */
287 test_fs(fuse_opts.mountpoint);
288 free(fuse_opts.mountpoint);
289
290 /* Stop file system */
293 assert(pthread_join(fs_thread, NULL) == 0);
294
295 assert(got_write == 1);
296
297 /*
298 * when writeback cache is enabled, kernel side can merge requests, but
299 * memory pressure, system 'sync' might trigger data flushes before - flush
300 * might happen in between write syscalls - merging subpage writes into
301 * a single page and pages into large fuse requests might or might not work.
302 * Though we can expect that that at least some (but maybe all) write
303 * system calls can be merged.
304 */
305 if (options.writeback)
306 assert(write_cnt < WRITE_SYSCALLS);
307 else
308 assert(write_cnt == WRITE_SYSCALLS);
309
312
313 printf("Test completed successfully.\n");
314 return 0;
315}
316
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_WRITEBACK_CACHE
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
fuse_ino_t ino
uint32_t noflush
Definition fuse_common.h:93
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/test_2test__write__cache_8c_source.html0000644000175000017500000017716615156613443023053 0ustar berndbernd libfuse: test/test_write_cache.c Source File
libfuse
test_write_cache.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
9#define FUSE_USE_VERSION 30
10
11/* Not really needed - just to test build with FUSE_USE_VERSION == 30 */
12#include <fuse.h>
13
14#include <fuse_config.h>
15#include <fuse_lowlevel.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <assert.h>
22#include <stddef.h>
23#include <unistd.h>
24#include <sys/stat.h>
25#include <pthread.h>
26#include <stdatomic.h>
27
28#ifndef __linux__
29#include <limits.h>
30#else
31#include <linux/limits.h>
32#endif
33
34#define FILE_INO 2
35#define FILE_NAME "write_me"
36
37/* Command line parsing */
38struct options {
39 int writeback;
40 int data_size;
41 int delay_ms;
42} options = {
43 .writeback = 0,
44 .data_size = 2048,
45 .delay_ms = 0,
46};
47
48#define WRITE_SYSCALLS 64
49
50#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
51static const struct fuse_opt option_spec[] = {
52 OPTION("writeback_cache", writeback),
53 OPTION("--data-size=%d", data_size), OPTION("--delay_ms=%d", delay_ms),
55};
56static int got_write;
57static atomic_int write_cnt;
58
59pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
60pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
61static int write_start, write_done;
62
63static void tfs_init(void *userdata, struct fuse_conn_info *conn)
64{
65 (void)userdata;
66
67 if (options.writeback) {
70 }
71}
72
73static int tfs_stat(fuse_ino_t ino, struct stat *stbuf)
74{
75 stbuf->st_ino = ino;
76 if (ino == FUSE_ROOT_ID) {
77 stbuf->st_mode = S_IFDIR | 0755;
78 stbuf->st_nlink = 1;
79 }
80
81 else if (ino == FILE_INO) {
82 stbuf->st_mode = S_IFREG | 0222;
83 stbuf->st_nlink = 1;
84 stbuf->st_size = 0;
85 }
86
87 else
88 return -1;
89
90 return 0;
91}
92
93static void tfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
94{
95 struct fuse_entry_param e;
96
97 memset(&e, 0, sizeof(e));
98
99 if (parent != FUSE_ROOT_ID)
100 goto err_out;
101 else if (strcmp(name, FILE_NAME) == 0)
102 e.ino = FILE_INO;
103 else
104 goto err_out;
105
106 if (tfs_stat(e.ino, &e.attr) != 0)
107 goto err_out;
108 fuse_reply_entry(req, &e);
109 return;
110
111err_out:
112 fuse_reply_err(req, ENOENT);
113}
114
115static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
116 struct fuse_file_info *fi)
117{
118 struct stat stbuf;
119
120 (void)fi;
121
122 memset(&stbuf, 0, sizeof(stbuf));
123 if (tfs_stat(ino, &stbuf) != 0)
124 fuse_reply_err(req, ENOENT);
125 else
126 fuse_reply_attr(req, &stbuf, 5);
127}
128
129static void tfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
130{
131 if (ino == FUSE_ROOT_ID)
132 fuse_reply_err(req, EISDIR);
133 else {
134 assert(ino == FILE_INO);
135 /* Test close(rofd) does not block waiting for pending writes */
136 fi->noflush = !options.writeback && options.delay_ms &&
137 (fi->flags & O_ACCMODE) == O_RDONLY;
138 fuse_reply_open(req, fi);
139 }
140}
141
142static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
143 size_t size, off_t off, struct fuse_file_info *fi)
144{
145 (void)fi;
146 (void)buf;
147 (void)off;
148 size_t expected;
149
150 assert(ino == FILE_INO);
151 expected = options.data_size;
152 if (options.writeback)
153 expected *= 2;
154
155 write_cnt++;
156
157 if (size != expected && !options.writeback)
158 fprintf(stderr, "ERROR: Expected %zd bytes, got %zd\n!",
159 expected, size);
160 else
161 got_write = 1;
162
163 /* Simulate waiting for pending writes */
164 if (options.delay_ms) {
165 pthread_mutex_lock(&lock);
166 write_start = 1;
167 pthread_cond_signal(&cond);
168 pthread_mutex_unlock(&lock);
169
170 usleep(options.delay_ms * 1000);
171
172 pthread_mutex_lock(&lock);
173 write_done = 1;
174 pthread_cond_signal(&cond);
175 pthread_mutex_unlock(&lock);
176 }
177
178 fuse_reply_write(req, size);
179}
180
181static struct fuse_lowlevel_ops tfs_oper = {
182 .init = tfs_init,
183 .lookup = tfs_lookup,
184 .getattr = tfs_getattr,
185 .open = tfs_open,
186 .write = tfs_write,
187};
188
189static void *close_rofd(void *data)
190{
191 int rofd = (int)(long)data;
192
193 /* Wait for first write to start */
194 pthread_mutex_lock(&lock);
195 while (!write_start && !write_done)
196 pthread_cond_wait(&cond, &lock);
197 pthread_mutex_unlock(&lock);
198
199 close(rofd);
200 printf("rofd closed. write_start: %d write_done: %d\n", write_start,
201 write_done);
202
203 /* First write should not have been completed */
204 if (write_done)
205 fprintf(stderr, "ERROR: close(rofd) blocked on write!\n");
206
207 return NULL;
208}
209
210static void *run_fs(void *data)
211{
212 struct fuse_session *se = (struct fuse_session *)data;
213
214 assert(fuse_session_loop(se) == 0);
215 return NULL;
216}
217
218static void test_fs(char *mountpoint)
219{
220 char fname[PATH_MAX];
221 char *buf;
222 const size_t iosize = options.data_size;
223 const size_t dsize = options.data_size * WRITE_SYSCALLS;
224 int fd, rofd;
225 pthread_t rofd_thread;
226 off_t off = 0;
227
228 buf = malloc(dsize);
229 assert(buf != NULL);
230 assert((fd = open("/dev/urandom", O_RDONLY)) != -1);
231 assert(read(fd, buf, dsize) == dsize);
232 close(fd);
233
234 assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME, mountpoint) > 0);
235 fd = open(fname, O_WRONLY);
236 if (fd == -1) {
237 perror(fname);
238 assert(0);
239 }
240
241 if (options.delay_ms) {
242 /* Verify that close(rofd) does not block waiting for pending writes */
243 rofd = open(fname, O_RDONLY);
244 assert(pthread_create(&rofd_thread, NULL, close_rofd,
245 (void *)(long)rofd) == 0);
246 /* Give close_rofd time to start */
247 usleep(options.delay_ms * 1000);
248 }
249
250 for (int cnt = 0; cnt < WRITE_SYSCALLS; cnt++) {
251 assert(pwrite(fd, buf + off, iosize, off) == iosize);
252 off += iosize;
253 assert(off <= dsize);
254 }
255 free(buf);
256 close(fd);
257
258 if (options.delay_ms) {
259 printf("rwfd closed. write_start: %d write_done: %d\n",
260 write_start, write_done);
261 assert(pthread_join(rofd_thread, NULL) == 0);
262 }
263}
264
265int main(int argc, char *argv[])
266{
267 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
268 struct fuse_session *se;
269 struct fuse_cmdline_opts fuse_opts;
270 pthread_t fs_thread;
271
272 assert(fuse_opt_parse(&args, &options, option_spec, NULL) == 0);
273 assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
274#ifndef __FreeBSD__
275 assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
276#endif
277 se = fuse_session_new(&args, &tfs_oper, sizeof(tfs_oper), NULL);
278 fuse_opt_free_args(&args);
279 assert(se != NULL);
280 assert(fuse_set_signal_handlers(se) == 0);
281 assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
282
283 /* Start file-system thread */
284 assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
285
286 /* Write test data */
287 test_fs(fuse_opts.mountpoint);
288 free(fuse_opts.mountpoint);
289
290 /* Stop file system */
293 assert(pthread_join(fs_thread, NULL) == 0);
294
295 assert(got_write == 1);
296
297 /*
298 * when writeback cache is enabled, kernel side can merge requests, but
299 * memory pressure, system 'sync' might trigger data flushes before - flush
300 * might happen in between write syscalls - merging subpage writes into
301 * a single page and pages into large fuse requests might or might not work.
302 * Though we can expect that that at least some (but maybe all) write
303 * system calls can be merged.
304 */
305 if (options.writeback)
306 assert(write_cnt < WRITE_SYSCALLS);
307 else
308 assert(write_cnt == WRITE_SYSCALLS);
309
312
313 printf("Test completed successfully.\n");
314 return 0;
315}
316
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_WRITEBACK_CACHE
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
fuse_ino_t ino
uint32_t noflush
Definition fuse_common.h:93
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_817_84_2test_2wrong__command_8c_source.html0000644000175000017500000001203215156613443024364 0ustar berndbernd libfuse: fuse-3.17.4/test/wrong_command.c Source File
libfuse
wrong_command.c
1#include <stdio.h>
2
3int main(void) {
4#ifdef MESON_IS_SUBPROJECT
5 fprintf(stderr, "libfuse tests were skipped because it's a meson subproject.\n"
6 "If you wish to run them try:\n"
7 "'cd <srcdir>/subprojects/libfuse && meson . build && cd build && python3 -m pytest test/' instead");
8 return 77; /* report as a skipped test */
9#else
10 fprintf(stderr, "\x1B[31m\e[1m"
11 "This is not the command you are looking for.\n"
12 "You probably want to run 'python3 -m pytest test/' instead"
13 "\e[0m\n");
14 return 1;
15#endif
16}
fuse-3.18.2/doc/html/fuse-3_818_81_2test_2wrong__command_8c_source.html0000644000175000017500000001203215156613443024362 0ustar berndbernd libfuse: fuse-3.18.1/test/wrong_command.c Source File
libfuse
wrong_command.c
1#include <stdio.h>
2
3int main(void) {
4#ifdef MESON_IS_SUBPROJECT
5 fprintf(stderr, "libfuse tests were skipped because it's a meson subproject.\n"
6 "If you wish to run them try:\n"
7 "'cd <srcdir>/subprojects/libfuse && meson . build && cd build && python3 -m pytest test/' instead");
8 return 77; /* report as a skipped test */
9#else
10 fprintf(stderr, "\x1B[31m\e[1m"
11 "This is not the command you are looking for.\n"
12 "You probably want to run 'python3 -m pytest test/' instead"
13 "\e[0m\n");
14 return 1;
15#endif
16}
fuse-3.18.2/doc/html/test_2wrong__command_8c_source.html0000644000175000017500000001164715156613443022221 0ustar berndbernd libfuse: test/wrong_command.c Source File
libfuse
wrong_command.c
1#include <stdio.h>
2
3int main(void) {
4#ifdef MESON_IS_SUBPROJECT
5 fprintf(stderr, "libfuse tests were skipped because it's a meson subproject.\n"
6 "If you wish to run them try:\n"
7 "'cd <srcdir>/subprojects/libfuse && meson . build && cd build && python3 -m pytest test/' instead");
8 return 77; /* report as a skipped test */
9#else
10 fprintf(stderr, "\x1B[31m\e[1m"
11 "This is not the command you are looking for.\n"
12 "You probably want to run 'python3 -m pytest test/' instead"
13 "\e[0m\n");
14 return 1;
15#endif
16}
fuse-3.18.2/doc/html/fuse-3_817_84_2util_2fusermount_8c_source.html0000644000175000017500000077625015156613443023623 0ustar berndbernd libfuse: fuse-3.17.4/util/fusermount.c Source File
libfuse
fusermount.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file COPYING.
7*/
8/* This program does the mounting and unmounting of FUSE filesystems */
9
10#define _GNU_SOURCE /* for clone,strchrnul and close_range */
11#include "fuse_config.h"
12#include "mount_util.h"
13#include "util.h"
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <ctype.h>
19#include <unistd.h>
20#include <getopt.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <pwd.h>
24#include <paths.h>
25#include <mntent.h>
26#include <sys/wait.h>
27#include <sys/stat.h>
28#include <sys/param.h>
29
30#include "fuse_mount_compat.h"
31
32#include <sys/fsuid.h>
33#include <sys/socket.h>
34#include <sys/utsname.h>
35#include <sched.h>
36#include <stdbool.h>
37#include <sys/vfs.h>
38
39#ifdef HAVE_CLOSE_RANGE
40#include <linux/close_range.h>
41#endif
42
43#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
44
45#define FUSE_DEV "/dev/fuse"
46
47static const char *progname;
48
49static int user_allow_other = 0;
50static int mount_max = 1000;
51
52static int auto_unmount = 0;
53
54#ifdef GETMNTENT_NEEDS_UNESCAPING
55// Older versions of musl libc don't unescape entries in /etc/mtab
56
57// unescapes octal sequences like \040 in-place
58// That's ok, because unescaping can not extend the length of the string.
59static void unescape(char *buf) {
60 char *src = buf;
61 char *dest = buf;
62 while (1) {
63 char *next_src = strchrnul(src, '\\');
64 int offset = next_src - src;
65 memmove(dest, src, offset);
66 src = next_src;
67 dest += offset;
68
69 if(*src == '\0') {
70 *dest = *src;
71 return;
72 }
73 src++;
74
75 if('0' <= src[0] && src[0] < '2' &&
76 '0' <= src[1] && src[1] < '8' &&
77 '0' <= src[2] && src[2] < '8') {
78 *dest++ = (src[0] - '0') << 6
79 | (src[1] - '0') << 3
80 | (src[2] - '0') << 0;
81 src += 3;
82 } else if (src[0] == '\\') {
83 *dest++ = '\\';
84 src += 1;
85 } else {
86 *dest++ = '\\';
87 }
88 }
89}
90
91static struct mntent *GETMNTENT(FILE *stream)
92{
93 struct mntent *entp = getmntent(stream);
94 if(entp != NULL) {
95 unescape(entp->mnt_fsname);
96 unescape(entp->mnt_dir);
97 unescape(entp->mnt_type);
98 unescape(entp->mnt_opts);
99 }
100 return entp;
101}
102#else
103#define GETMNTENT getmntent
104#endif // GETMNTENT_NEEDS_UNESCAPING
105
106/*
107 * Take a ',' separated option string and extract "x-" options
108 */
109static int extract_x_options(const char *original, char **non_x_opts,
110 char **x_opts)
111{
112 size_t orig_len;
113 const char *opt, *opt_end;
114
115 orig_len = strlen(original) + 1;
116
117 *non_x_opts = calloc(1, orig_len);
118 *x_opts = calloc(1, orig_len);
119
120 size_t non_x_opts_len = orig_len;
121 size_t x_opts_len = orig_len;
122
123 if (*non_x_opts == NULL || *x_opts == NULL) {
124 fprintf(stderr, "%s: Failed to allocate %zuB.\n",
125 __func__, orig_len);
126 return -ENOMEM;
127 }
128
129 for (opt = original; opt < original + orig_len; opt = opt_end + 1) {
130 char *opt_buf;
131
132 opt_end = strchr(opt, ',');
133 if (opt_end == NULL)
134 opt_end = original + orig_len;
135
136 size_t opt_len = opt_end - opt;
137 size_t opt_len_left = orig_len - (opt - original);
138 size_t buf_len;
139 bool is_x_opts;
140
141 if (strncmp(opt, "x-", MIN(2, opt_len_left)) == 0) {
142 buf_len = x_opts_len;
143 is_x_opts = true;
144 opt_buf = *x_opts;
145 } else {
146 buf_len = non_x_opts_len;
147 is_x_opts = false;
148 opt_buf = *non_x_opts;
149 }
150
151 if (buf_len < orig_len) {
152 strncat(opt_buf, ",", 2);
153 buf_len -= 1;
154 }
155
156 /* omits ',' */
157 if ((ssize_t)(buf_len - opt_len) < 0) {
158 /* This would be a bug */
159 fprintf(stderr, "%s: no buf space left in copy, orig='%s'\n",
160 __func__, original);
161 return -EIO;
162 }
163
164 strncat(opt_buf, opt, opt_end - opt);
165 buf_len -= opt_len;
166
167 if (is_x_opts)
168 x_opts_len = buf_len;
169 else
170 non_x_opts_len = buf_len;
171 }
172
173 return 0;
174}
175
176static const char *get_user_name(void)
177{
178 struct passwd *pw = getpwuid(getuid());
179 if (pw != NULL && pw->pw_name != NULL)
180 return pw->pw_name;
181 else {
182 fprintf(stderr, "%s: could not determine username\n", progname);
183 return NULL;
184 }
185}
186
187static uid_t oldfsuid;
188static gid_t oldfsgid;
189
190static void drop_privs(void)
191{
192 if (getuid() != 0) {
193 oldfsuid = setfsuid(getuid());
194 oldfsgid = setfsgid(getgid());
195 }
196}
197
198static void restore_privs(void)
199{
200 if (getuid() != 0) {
201 setfsuid(oldfsuid);
202 setfsgid(oldfsgid);
203 }
204}
205
206#ifndef IGNORE_MTAB
207/*
208 * Make sure that /etc/mtab is checked and updated atomically
209 */
210static int lock_umount(void)
211{
212 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
213 int mtablock;
214 int res;
215 struct stat mtab_stat;
216
217 /* /etc/mtab could be a symlink to /proc/mounts */
218 if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
219 return -1;
220
221 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
222 if (mtablock == -1) {
223 fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
224 progname, strerror(errno));
225 return -1;
226 }
227 res = lockf(mtablock, F_LOCK, 0);
228 if (res < 0) {
229 fprintf(stderr, "%s: error getting lock: %s\n", progname,
230 strerror(errno));
231 close(mtablock);
232 return -1;
233 }
234
235 return mtablock;
236}
237
238static void unlock_umount(int mtablock)
239{
240 if (mtablock >= 0) {
241 int res;
242
243 res = lockf(mtablock, F_ULOCK, 0);
244 if (res < 0) {
245 fprintf(stderr, "%s: error releasing lock: %s\n",
246 progname, strerror(errno));
247 }
248 close(mtablock);
249 }
250}
251
252static int add_mount(const char *source, const char *mnt, const char *type,
253 const char *opts)
254{
255 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
256}
257
258static int may_unmount(const char *mnt, int quiet)
259{
260 struct mntent *entp;
261 FILE *fp;
262 const char *user = NULL;
263 char uidstr[32];
264 unsigned uidlen = 0;
265 int found;
266 const char *mtab = _PATH_MOUNTED;
267
268 user = get_user_name();
269 if (user == NULL)
270 return -1;
271
272 fp = setmntent(mtab, "r");
273 if (fp == NULL) {
274 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
275 strerror(errno));
276 return -1;
277 }
278
279 uidlen = sprintf(uidstr, "%u", getuid());
280
281 found = 0;
282 while ((entp = GETMNTENT(fp)) != NULL) {
283 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
284 (strcmp(entp->mnt_type, "fuse") == 0 ||
285 strcmp(entp->mnt_type, "fuseblk") == 0 ||
286 strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
287 strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
288 char *p = strstr(entp->mnt_opts, "user=");
289 if (p &&
290 (p == entp->mnt_opts || *(p-1) == ',') &&
291 strcmp(p + 5, user) == 0) {
292 found = 1;
293 break;
294 }
295 /* /etc/mtab is a link pointing to
296 /proc/mounts: */
297 else if ((p =
298 strstr(entp->mnt_opts, "user_id=")) &&
299 (p == entp->mnt_opts ||
300 *(p-1) == ',') &&
301 strncmp(p + 8, uidstr, uidlen) == 0 &&
302 (*(p+8+uidlen) == ',' ||
303 *(p+8+uidlen) == '\0')) {
304 found = 1;
305 break;
306 }
307 }
308 }
309 endmntent(fp);
310
311 if (!found) {
312 if (!quiet)
313 fprintf(stderr,
314 "%s: entry for %s not found in %s\n",
315 progname, mnt, mtab);
316 return -1;
317 }
318
319 return 0;
320}
321#endif
322
323/*
324 * Check whether the file specified in "fusermount3 -u" is really a
325 * mountpoint and not a symlink. This is necessary otherwise the user
326 * could move the mountpoint away and replace it with a symlink
327 * pointing to an arbitrary mount, thereby tricking fusermount3 into
328 * unmounting that (umount(2) will follow symlinks).
329 *
330 * This is the child process running in a separate mount namespace, so
331 * we don't mess with the global namespace and if the process is
332 * killed for any reason, mounts are automatically cleaned up.
333 *
334 * First make sure nothing is propagated back into the parent
335 * namespace by marking all mounts "private".
336 *
337 * Then bind mount parent onto a stable base where the user can't move
338 * it around.
339 *
340 * Finally check /proc/mounts for an entry matching the requested
341 * mountpoint. If it's found then we are OK, and the user can't move
342 * it around within the parent directory as rename() will return
343 * EBUSY. Be careful to ignore any mounts that existed before the
344 * bind.
345 */
346static int check_is_mount_child(void *p)
347{
348 const char **a = p;
349 const char *last = a[0];
350 const char *mnt = a[1];
351 const char *type = a[2];
352 int res;
353 const char *procmounts = "/proc/mounts";
354 int found;
355 FILE *fp;
356 struct mntent *entp;
357 int count;
358
359 res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
360 if (res == -1) {
361 fprintf(stderr, "%s: failed to mark mounts private: %s\n",
362 progname, strerror(errno));
363 return 1;
364 }
365
366 fp = setmntent(procmounts, "r");
367 if (fp == NULL) {
368 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
369 procmounts, strerror(errno));
370 return 1;
371 }
372
373 count = 0;
374 while (GETMNTENT(fp) != NULL)
375 count++;
376 endmntent(fp);
377
378 fp = setmntent(procmounts, "r");
379 if (fp == NULL) {
380 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
381 procmounts, strerror(errno));
382 return 1;
383 }
384
385 res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
386 if (res == -1) {
387 fprintf(stderr, "%s: failed to bind parent to /: %s\n",
388 progname, strerror(errno));
389 return 1;
390 }
391
392 found = 0;
393 while ((entp = GETMNTENT(fp)) != NULL) {
394 if (count > 0) {
395 count--;
396 continue;
397 }
398 if (entp->mnt_dir[0] == '/' &&
399 strcmp(entp->mnt_dir + 1, last) == 0 &&
400 (!type || strcmp(entp->mnt_type, type) == 0)) {
401 found = 1;
402 break;
403 }
404 }
405 endmntent(fp);
406
407 if (!found) {
408 fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
409 return 1;
410 }
411
412 return 0;
413}
414
415static pid_t clone_newns(void *a)
416{
417 char buf[131072];
418 char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
419
420#ifdef __ia64__
421 extern int __clone2(int (*fn)(void *),
422 void *child_stack_base, size_t stack_size,
423 int flags, void *arg, pid_t *ptid,
424 void *tls, pid_t *ctid);
425
426 return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
427 CLONE_NEWNS, a, NULL, NULL, NULL);
428#else
429 return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
430#endif
431}
432
433static int check_is_mount(const char *last, const char *mnt, const char *type)
434{
435 pid_t pid, p;
436 int status;
437 const char *a[3] = { last, mnt, type };
438
439 pid = clone_newns((void *) a);
440 if (pid == (pid_t) -1) {
441 fprintf(stderr, "%s: failed to clone namespace: %s\n",
442 progname, strerror(errno));
443 return -1;
444 }
445 p = waitpid(pid, &status, __WCLONE);
446 if (p == (pid_t) -1) {
447 fprintf(stderr, "%s: waitpid failed: %s\n",
448 progname, strerror(errno));
449 return -1;
450 }
451 if (!WIFEXITED(status)) {
452 fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
453 progname, status);
454 return -1;
455 }
456 if (WEXITSTATUS(status) != 0)
457 return -1;
458
459 return 0;
460}
461
462static int chdir_to_parent(char *copy, const char **lastp)
463{
464 char *tmp;
465 const char *parent;
466 char buf[65536];
467 int res;
468
469 tmp = strrchr(copy, '/');
470 if (tmp == NULL || tmp[1] == '\0') {
471 fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
472 progname, copy);
473 return -1;
474 }
475 if (tmp != copy) {
476 *tmp = '\0';
477 parent = copy;
478 *lastp = tmp + 1;
479 } else if (tmp[1] != '\0') {
480 *lastp = tmp + 1;
481 parent = "/";
482 } else {
483 *lastp = ".";
484 parent = "/";
485 }
486
487 res = chdir(parent);
488 if (res == -1) {
489 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
490 progname, parent, strerror(errno));
491 return -1;
492 }
493
494 if (getcwd(buf, sizeof(buf)) == NULL) {
495 fprintf(stderr, "%s: failed to obtain current directory: %s\n",
496 progname, strerror(errno));
497 return -1;
498 }
499 if (strcmp(buf, parent) != 0) {
500 fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
501 parent, buf);
502 return -1;
503
504 }
505
506 return 0;
507}
508
509#ifndef IGNORE_MTAB
510static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
511{
512 int res;
513 char *copy;
514 const char *last;
515 int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW;
516
517 if (getuid() != 0) {
518 res = may_unmount(mnt, quiet);
519 if (res == -1)
520 return -1;
521 }
522
523 copy = strdup(mnt);
524 if (copy == NULL) {
525 fprintf(stderr, "%s: failed to allocate memory\n", progname);
526 return -1;
527 }
528
529 drop_privs();
530 res = chdir_to_parent(copy, &last);
531 if (res == -1) {
532 restore_privs();
533 goto out;
534 }
535
536 res = umount2(last, umount_flags);
537 restore_privs();
538 if (res == -1 && !quiet) {
539 fprintf(stderr, "%s: failed to unmount %s: %s\n",
540 progname, mnt, strerror(errno));
541 }
542
543out:
544 free(copy);
545 if (res == -1)
546 return -1;
547
548 res = chdir("/");
549 if (res == -1) {
550 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
551 return -1;
552 }
553
554 return fuse_mnt_remove_mount(progname, mnt);
555}
556
557static int unmount_fuse(const char *mnt, int quiet, int lazy)
558{
559 int res;
560 int mtablock = lock_umount();
561
562 res = unmount_fuse_locked(mnt, quiet, lazy);
563 unlock_umount(mtablock);
564
565 return res;
566}
567
568static int count_fuse_fs(void)
569{
570 struct mntent *entp;
571 int count = 0;
572 const char *mtab = _PATH_MOUNTED;
573 FILE *fp = setmntent(mtab, "r");
574 if (fp == NULL) {
575 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
576 strerror(errno));
577 return -1;
578 }
579 while ((entp = GETMNTENT(fp)) != NULL) {
580 if (strcmp(entp->mnt_type, "fuse") == 0 ||
581 strncmp(entp->mnt_type, "fuse.", 5) == 0)
582 count ++;
583 }
584 endmntent(fp);
585 return count;
586}
587
588
589#else /* IGNORE_MTAB */
590static int count_fuse_fs(void)
591{
592 return 0;
593}
594
595static int add_mount(const char *source, const char *mnt, const char *type,
596 const char *opts)
597{
598 (void) source;
599 (void) mnt;
600 (void) type;
601 (void) opts;
602 return 0;
603}
604
605static int unmount_fuse(const char *mnt, int quiet, int lazy)
606{
607 (void) quiet;
608 return fuse_mnt_umount(progname, mnt, mnt, lazy);
609}
610#endif /* IGNORE_MTAB */
611
612static void strip_line(char *line)
613{
614 char *s = strchr(line, '#');
615 if (s != NULL)
616 s[0] = '\0';
617 for (s = line + strlen(line) - 1;
618 s >= line && isspace((unsigned char) *s); s--);
619 s[1] = '\0';
620 for (s = line; isspace((unsigned char) *s); s++);
621 if (s != line)
622 memmove(line, s, strlen(s)+1);
623}
624
625static void parse_line(char *line, int linenum)
626{
627 int tmp;
628 if (strcmp(line, "user_allow_other") == 0)
629 user_allow_other = 1;
630 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
631 mount_max = tmp;
632 else if(line[0])
633 fprintf(stderr,
634 "%s: unknown parameter in %s at line %i: '%s'\n",
635 progname, FUSE_CONF, linenum, line);
636}
637
638static void read_conf(void)
639{
640 FILE *fp = fopen(FUSE_CONF, "r");
641 if (fp != NULL) {
642 int linenum = 1;
643 char line[256];
644 int isnewline = 1;
645 while (fgets(line, sizeof(line), fp) != NULL) {
646 if (isnewline) {
647 if (line[strlen(line)-1] == '\n') {
648 strip_line(line);
649 parse_line(line, linenum);
650 } else {
651 isnewline = 0;
652 }
653 } else if(line[strlen(line)-1] == '\n') {
654 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
655
656 isnewline = 1;
657 }
658 if (isnewline)
659 linenum ++;
660 }
661 if (!isnewline) {
662 fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
663
664 }
665 if (ferror(fp)) {
666 fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
667 exit(1);
668 }
669 fclose(fp);
670 } else if (errno != ENOENT) {
671 bool fatal = (errno != EACCES && errno != ELOOP &&
672 errno != ENAMETOOLONG && errno != ENOTDIR &&
673 errno != EOVERFLOW);
674 fprintf(stderr, "%s: failed to open %s: %s\n",
675 progname, FUSE_CONF, strerror(errno));
676 if (fatal)
677 exit(1);
678 }
679}
680
681static int begins_with(const char *s, const char *beg)
682{
683 if (strncmp(s, beg, strlen(beg)) == 0)
684 return 1;
685 else
686 return 0;
687}
688
689struct mount_flags {
690 const char *opt;
691 unsigned long flag;
692 int on;
693 int safe;
694};
695
696static struct mount_flags mount_flags[] = {
697 {"rw", MS_RDONLY, 0, 1},
698 {"ro", MS_RDONLY, 1, 1},
699 {"suid", MS_NOSUID, 0, 0},
700 {"nosuid", MS_NOSUID, 1, 1},
701 {"dev", MS_NODEV, 0, 0},
702 {"nodev", MS_NODEV, 1, 1},
703 {"exec", MS_NOEXEC, 0, 1},
704 {"noexec", MS_NOEXEC, 1, 1},
705 {"async", MS_SYNCHRONOUS, 0, 1},
706 {"sync", MS_SYNCHRONOUS, 1, 1},
707 {"atime", MS_NOATIME, 0, 1},
708 {"noatime", MS_NOATIME, 1, 1},
709 {"diratime", MS_NODIRATIME, 0, 1},
710 {"nodiratime", MS_NODIRATIME, 1, 1},
711 {"lazytime", MS_LAZYTIME, 1, 1},
712 {"nolazytime", MS_LAZYTIME, 0, 1},
713 {"relatime", MS_RELATIME, 1, 1},
714 {"norelatime", MS_RELATIME, 0, 1},
715 {"strictatime", MS_STRICTATIME, 1, 1},
716 {"nostrictatime", MS_STRICTATIME, 0, 1},
717 {"dirsync", MS_DIRSYNC, 1, 1},
718 {"symfollow", MS_NOSYMFOLLOW, 0, 1},
719 {"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
720 {NULL, 0, 0, 0}
721};
722
723static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
724{
725 int i;
726
727 for (i = 0; mount_flags[i].opt != NULL; i++) {
728 const char *opt = mount_flags[i].opt;
729 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
730 *on = mount_flags[i].on;
731 *flag = mount_flags[i].flag;
732 if (!mount_flags[i].safe && getuid() != 0) {
733 *flag = 0;
734 fprintf(stderr,
735 "%s: unsafe option %s ignored\n",
736 progname, opt);
737 }
738 return 1;
739 }
740 }
741 return 0;
742}
743
744static int add_option(char **optsp, const char *opt, unsigned expand)
745{
746 char *newopts;
747 if (*optsp == NULL)
748 newopts = strdup(opt);
749 else {
750 unsigned oldsize = strlen(*optsp);
751 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
752 newopts = (char *) realloc(*optsp, newsize);
753 if (newopts)
754 sprintf(newopts + oldsize, ",%s", opt);
755 }
756 if (newopts == NULL) {
757 fprintf(stderr, "%s: failed to allocate memory\n", progname);
758 return -1;
759 }
760 *optsp = newopts;
761 return 0;
762}
763
764static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
765{
766 int i;
767 int l;
768
769 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
770 return -1;
771
772 for (i = 0; mount_flags[i].opt != NULL; i++) {
773 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
774 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
775 return -1;
776 }
777
778 if (add_option(mnt_optsp, opts, 0) == -1)
779 return -1;
780 /* remove comma from end of opts*/
781 l = strlen(*mnt_optsp);
782 if ((*mnt_optsp)[l-1] == ',')
783 (*mnt_optsp)[l-1] = '\0';
784 if (getuid() != 0) {
785 const char *user = get_user_name();
786 if (user == NULL)
787 return -1;
788
789 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
790 return -1;
791 strcat(*mnt_optsp, user);
792 }
793 return 0;
794}
795
796static int opt_eq(const char *s, unsigned len, const char *opt)
797{
798 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
799 return 1;
800 else
801 return 0;
802}
803
804static int get_string_opt(const char *s, unsigned len, const char *opt,
805 char **val)
806{
807 int i;
808 unsigned opt_len = strlen(opt);
809 char *d;
810
811 if (*val)
812 free(*val);
813 *val = (char *) malloc(len - opt_len + 1);
814 if (!*val) {
815 fprintf(stderr, "%s: failed to allocate memory\n", progname);
816 return 0;
817 }
818
819 d = *val;
820 s += opt_len;
821 len -= opt_len;
822 for (i = 0; i < len; i++) {
823 if (s[i] == '\\' && i + 1 < len)
824 i++;
825 *d++ = s[i];
826 }
827 *d = '\0';
828 return 1;
829}
830
831/* The kernel silently truncates the "data" argument to PAGE_SIZE-1 characters.
832 * This can be dangerous if it e.g. truncates the option "group_id=1000" to
833 * "group_id=1".
834 * This wrapper detects this case and bails out with an error.
835 */
836static int mount_notrunc(const char *source, const char *target,
837 const char *filesystemtype, unsigned long mountflags,
838 const char *data) {
839 if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
840 fprintf(stderr, "%s: mount options too long\n", progname);
841 errno = EINVAL;
842 return -1;
843 }
844 return mount(source, target, filesystemtype, mountflags, data);
845}
846
847
848static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
849 int fd, const char *opts, const char *dev, char **sourcep,
850 char **mnt_optsp)
851{
852 int res;
853 int flags = MS_NOSUID | MS_NODEV;
854 char *optbuf;
855 char *mnt_opts = NULL;
856 const char *s;
857 char *d;
858 char *fsname = NULL;
859 char *subtype = NULL;
860 char *source = NULL;
861 char *type = NULL;
862 int blkdev = 0;
863
864 optbuf = (char *) malloc(strlen(opts) + 128);
865 if (!optbuf) {
866 fprintf(stderr, "%s: failed to allocate memory\n", progname);
867 return -1;
868 }
869
870 for (s = opts, d = optbuf; *s;) {
871 unsigned len;
872 const char *fsname_str = "fsname=";
873 const char *subtype_str = "subtype=";
874 bool escape_ok = begins_with(s, fsname_str) ||
875 begins_with(s, subtype_str);
876 for (len = 0; s[len]; len++) {
877 if (escape_ok && s[len] == '\\' && s[len + 1])
878 len++;
879 else if (s[len] == ',')
880 break;
881 }
882 if (begins_with(s, fsname_str)) {
883 if (!get_string_opt(s, len, fsname_str, &fsname))
884 goto err;
885 } else if (begins_with(s, subtype_str)) {
886 if (!get_string_opt(s, len, subtype_str, &subtype))
887 goto err;
888 } else if (opt_eq(s, len, "blkdev")) {
889 if (getuid() != 0) {
890 fprintf(stderr,
891 "%s: option blkdev is privileged\n",
892 progname);
893 goto err;
894 }
895 blkdev = 1;
896 } else if (opt_eq(s, len, "auto_unmount")) {
897 auto_unmount = 1;
898 } else if (!opt_eq(s, len, "nonempty") &&
899 !begins_with(s, "fd=") &&
900 !begins_with(s, "rootmode=") &&
901 !begins_with(s, "user_id=") &&
902 !begins_with(s, "group_id=")) {
903 int on;
904 int flag;
905 int skip_option = 0;
906 if (opt_eq(s, len, "large_read")) {
907 struct utsname utsname;
908 unsigned kmaj, kmin;
909 res = uname(&utsname);
910 if (res == 0 &&
911 sscanf(utsname.release, "%u.%u",
912 &kmaj, &kmin) == 2 &&
913 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
914 fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
915 skip_option = 1;
916 }
917 }
918 if (getuid() != 0 && !user_allow_other &&
919 (opt_eq(s, len, "allow_other") ||
920 opt_eq(s, len, "allow_root"))) {
921 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
922 goto err;
923 }
924 if (!skip_option) {
925 if (find_mount_flag(s, len, &on, &flag)) {
926 if (on)
927 flags |= flag;
928 else
929 flags &= ~flag;
930 } else if (opt_eq(s, len, "default_permissions") ||
931 opt_eq(s, len, "allow_other") ||
932 begins_with(s, "max_read=") ||
933 begins_with(s, "blksize=")) {
934 memcpy(d, s, len);
935 d += len;
936 *d++ = ',';
937 } else {
938 fprintf(stderr, "%s: unknown option '%.*s'\n", progname, len, s);
939 exit(1);
940 }
941 }
942 }
943 s += len;
944 if (*s)
945 s++;
946 }
947 *d = '\0';
948 res = get_mnt_opts(flags, optbuf, &mnt_opts);
949 if (res == -1)
950 goto err;
951
952 sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
953 fd, rootmode, getuid(), getgid());
954
955 source = malloc((fsname ? strlen(fsname) : 0) +
956 (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
957
958 type = malloc((subtype ? strlen(subtype) : 0) + 32);
959 if (!type || !source) {
960 fprintf(stderr, "%s: failed to allocate memory\n", progname);
961 goto err;
962 }
963
964 if (subtype)
965 sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
966 else
967 strcpy(type, blkdev ? "fuseblk" : "fuse");
968
969 if (fsname)
970 strcpy(source, fsname);
971 else
972 strcpy(source, subtype ? subtype : dev);
973
974 res = mount_notrunc(source, mnt, type, flags, optbuf);
975 if (res == -1 && errno == ENODEV && subtype) {
976 /* Probably missing subtype support */
977 strcpy(type, blkdev ? "fuseblk" : "fuse");
978 if (fsname) {
979 if (!blkdev)
980 sprintf(source, "%s#%s", subtype, fsname);
981 } else {
982 strcpy(source, type);
983 }
984
985 res = mount_notrunc(source, mnt, type, flags, optbuf);
986 }
987 if (res == -1 && errno == EINVAL) {
988 /* It could be an old version not supporting group_id */
989 sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
990 fd, rootmode, getuid());
991 res = mount_notrunc(source, mnt, type, flags, optbuf);
992 }
993 if (res == -1) {
994 int errno_save = errno;
995 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
996 fprintf(stderr, "%s: 'fuseblk' support missing\n",
997 progname);
998 else
999 fprintf(stderr, "%s: mount failed: %s\n", progname,
1000 strerror(errno_save));
1001 goto err;
1002 }
1003 *sourcep = source;
1004 *typep = type;
1005 *mnt_optsp = mnt_opts;
1006 free(fsname);
1007 free(optbuf);
1008
1009 return 0;
1010
1011err:
1012 free(fsname);
1013 free(subtype);
1014 free(source);
1015 free(type);
1016 free(mnt_opts);
1017 free(optbuf);
1018 return -1;
1019}
1020
1021static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
1022{
1023 int res;
1024 const char *mnt = *mntp;
1025 const char *origmnt = mnt;
1026 struct statfs fs_buf;
1027 size_t i;
1028
1029 res = lstat(mnt, stbuf);
1030 if (res == -1) {
1031 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1032 progname, mnt, strerror(errno));
1033 return -1;
1034 }
1035
1036 /* No permission checking is done for root */
1037 if (getuid() == 0)
1038 return 0;
1039
1040 if (S_ISDIR(stbuf->st_mode)) {
1041 res = chdir(mnt);
1042 if (res == -1) {
1043 fprintf(stderr,
1044 "%s: failed to chdir to mountpoint: %s\n",
1045 progname, strerror(errno));
1046 return -1;
1047 }
1048 mnt = *mntp = ".";
1049 res = lstat(mnt, stbuf);
1050 if (res == -1) {
1051 fprintf(stderr,
1052 "%s: failed to access mountpoint %s: %s\n",
1053 progname, origmnt, strerror(errno));
1054 return -1;
1055 }
1056
1057 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
1058 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
1059 progname, origmnt);
1060 return -1;
1061 }
1062
1063 res = access(mnt, W_OK);
1064 if (res == -1) {
1065 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
1066 progname, origmnt);
1067 return -1;
1068 }
1069 } else if (S_ISREG(stbuf->st_mode)) {
1070 static char procfile[256];
1071 *mountpoint_fd = open(mnt, O_WRONLY);
1072 if (*mountpoint_fd == -1) {
1073 fprintf(stderr, "%s: failed to open %s: %s\n",
1074 progname, mnt, strerror(errno));
1075 return -1;
1076 }
1077 res = fstat(*mountpoint_fd, stbuf);
1078 if (res == -1) {
1079 fprintf(stderr,
1080 "%s: failed to access mountpoint %s: %s\n",
1081 progname, mnt, strerror(errno));
1082 return -1;
1083 }
1084 if (!S_ISREG(stbuf->st_mode)) {
1085 fprintf(stderr,
1086 "%s: mountpoint %s is no longer a regular file\n",
1087 progname, mnt);
1088 return -1;
1089 }
1090
1091 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
1092 *mntp = procfile;
1093 } else {
1094 fprintf(stderr,
1095 "%s: mountpoint %s is not a directory or a regular file\n",
1096 progname, mnt);
1097 return -1;
1098 }
1099
1100 /* Do not permit mounting over anything in procfs - it has a couple
1101 * places to which we have "write access" without being supposed to be
1102 * able to just put anything we want there.
1103 * Luckily, without allow_other, we can't get other users to actually
1104 * use any fake information we try to put there anyway.
1105 * Use a whitelist to be safe. */
1106 if (statfs(*mntp, &fs_buf)) {
1107 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1108 progname, mnt, strerror(errno));
1109 return -1;
1110 }
1111
1112 /* Define permitted filesystems for the mount target. This was
1113 * originally the same list as used by the ecryptfs mount helper
1114 * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
1115 * but got expanded as we found more filesystems that needed to be
1116 * overlaid. */
1117 typeof(fs_buf.f_type) f_type_whitelist[] = {
1118 0x61756673 /* AUFS_SUPER_MAGIC */,
1119 0x00000187 /* AUTOFS_SUPER_MAGIC */,
1120 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
1121 0x9123683E /* BTRFS_SUPER_MAGIC */,
1122 0x00C36400 /* CEPH_SUPER_MAGIC */,
1123 0xFF534D42 /* CIFS_MAGIC_NUMBER */,
1124 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
1125 0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
1126 0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
1127 0xF2F52010 /* F2FS_SUPER_MAGIC */,
1128 0x65735546 /* FUSE_SUPER_MAGIC */,
1129 0x01161970 /* GFS2_MAGIC */,
1130 0x47504653 /* GPFS_SUPER_MAGIC */,
1131 0x0000482b /* HFSPLUS_SUPER_MAGIC */,
1132 0x000072B6 /* JFFS2_SUPER_MAGIC */,
1133 0x3153464A /* JFS_SUPER_MAGIC */,
1134 0x0BD00BD0 /* LL_SUPER_MAGIC */,
1135 0X00004D44 /* MSDOS_SUPER_MAGIC */,
1136 0x0000564C /* NCP_SUPER_MAGIC */,
1137 0x00006969 /* NFS_SUPER_MAGIC */,
1138 0x00003434 /* NILFS_SUPER_MAGIC */,
1139 0x5346544E /* NTFS_SB_MAGIC */,
1140 0x7366746E /* NTFS3_SUPER_MAGIC */,
1141 0x5346414f /* OPENAFS_SUPER_MAGIC */,
1142 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
1143 0xAAD7AAEA /* PANFS_SUPER_MAGIC */,
1144 0x52654973 /* REISERFS_SUPER_MAGIC */,
1145 0xFE534D42 /* SMB2_SUPER_MAGIC */,
1146 0x73717368 /* SQUASHFS_MAGIC */,
1147 0x01021994 /* TMPFS_MAGIC */,
1148 0x24051905 /* UBIFS_SUPER_MAGIC */,
1149#if __SIZEOF_LONG__ > 4
1150 0x736675005346544e /* UFSD */,
1151#endif
1152 0x58465342 /* XFS_SB_MAGIC */,
1153 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
1154 0x858458f6 /* RAMFS_MAGIC */,
1155 };
1156 for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
1157 if (f_type_whitelist[i] == fs_buf.f_type)
1158 return 0;
1159 }
1160
1161 fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
1162 progname, (unsigned long)fs_buf.f_type);
1163 return -1;
1164}
1165
1166static int try_open(const char *dev, char **devp, int silent)
1167{
1168 int fd = open(dev, O_RDWR);
1169 if (fd != -1) {
1170 *devp = strdup(dev);
1171 if (*devp == NULL) {
1172 fprintf(stderr, "%s: failed to allocate memory\n",
1173 progname);
1174 close(fd);
1175 fd = -1;
1176 }
1177 } else if (errno == ENODEV ||
1178 errno == ENOENT)/* check for ENOENT too, for the udev case */
1179 return -2;
1180 else if (!silent) {
1181 fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
1182 strerror(errno));
1183 }
1184 return fd;
1185}
1186
1187static int try_open_fuse_device(char **devp)
1188{
1189 int fd;
1190
1191 drop_privs();
1192 fd = try_open(FUSE_DEV, devp, 0);
1193 restore_privs();
1194 return fd;
1195}
1196
1197static int open_fuse_device(char **devp)
1198{
1199 int fd = try_open_fuse_device(devp);
1200 if (fd >= -1)
1201 return fd;
1202
1203 fprintf(stderr,
1204 "%s: fuse device not found, try 'modprobe fuse' first\n",
1205 progname);
1206
1207 return -1;
1208}
1209
1210
1211static int mount_fuse(const char *mnt, const char *opts, const char **type)
1212{
1213 int res;
1214 int fd;
1215 char *dev;
1216 struct stat stbuf;
1217 char *source = NULL;
1218 char *mnt_opts = NULL;
1219 const char *real_mnt = mnt;
1220 int mountpoint_fd = -1;
1221 char *do_mount_opts = NULL;
1222 char *x_opts = NULL;
1223
1224 fd = open_fuse_device(&dev);
1225 if (fd == -1)
1226 return -1;
1227
1228 drop_privs();
1229 read_conf();
1230
1231 if (getuid() != 0 && mount_max != -1) {
1232 int mount_count = count_fuse_fs();
1233 if (mount_count >= mount_max) {
1234 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
1235 goto fail_close_fd;
1236 }
1237 }
1238
1239 // Extract any options starting with "x-"
1240 res= extract_x_options(opts, &do_mount_opts, &x_opts);
1241 if (res)
1242 goto fail_close_fd;
1243
1244 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1245 restore_privs();
1246 if (res != -1)
1247 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
1248 fd, do_mount_opts, dev, &source, &mnt_opts);
1249
1250 if (mountpoint_fd != -1)
1251 close(mountpoint_fd);
1252
1253 if (res == -1)
1254 goto fail_close_fd;
1255
1256 res = chdir("/");
1257 if (res == -1) {
1258 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1259 goto fail_close_fd;
1260 }
1261
1262 if (geteuid() == 0) {
1263 if (x_opts && strlen(x_opts) > 0) {
1264 /*
1265 * Add back the options starting with "x-" to opts from
1266 * do_mount. +2 for ',' and '\0'
1267 */
1268 size_t mnt_opts_len = strlen(mnt_opts);
1269 size_t x_mnt_opts_len = mnt_opts_len+
1270 strlen(x_opts) + 2;
1271 char *x_mnt_opts = calloc(1, x_mnt_opts_len);
1272
1273 if (mnt_opts_len) {
1274 strcpy(x_mnt_opts, mnt_opts);
1275 strncat(x_mnt_opts, ",", 2);
1276 }
1277
1278 strncat(x_mnt_opts, x_opts,
1279 x_mnt_opts_len - mnt_opts_len - 2);
1280
1281 free(mnt_opts);
1282 mnt_opts = x_mnt_opts;
1283 }
1284
1285 res = add_mount(source, mnt, *type, mnt_opts);
1286 if (res == -1) {
1287 /* Can't clean up mount in a non-racy way */
1288 goto fail_close_fd;
1289 }
1290 }
1291
1292out_free:
1293 free(source);
1294 free(mnt_opts);
1295 free(dev);
1296 free(x_opts);
1297 free(do_mount_opts);
1298
1299 return fd;
1300
1301fail_close_fd:
1302 close(fd);
1303 fd = -1;
1304 goto out_free;
1305}
1306
1307static int send_fd(int sock_fd, int fd)
1308{
1309 int retval;
1310 struct msghdr msg;
1311 struct cmsghdr *p_cmsg;
1312 struct iovec vec;
1313 size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
1314 int *p_fds;
1315 char sendchar = 0;
1316
1317 msg.msg_control = cmsgbuf;
1318 msg.msg_controllen = sizeof(cmsgbuf);
1319 p_cmsg = CMSG_FIRSTHDR(&msg);
1320 p_cmsg->cmsg_level = SOL_SOCKET;
1321 p_cmsg->cmsg_type = SCM_RIGHTS;
1322 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1323 p_fds = (int *) CMSG_DATA(p_cmsg);
1324 *p_fds = fd;
1325 msg.msg_controllen = p_cmsg->cmsg_len;
1326 msg.msg_name = NULL;
1327 msg.msg_namelen = 0;
1328 msg.msg_iov = &vec;
1329 msg.msg_iovlen = 1;
1330 msg.msg_flags = 0;
1331 /* "To pass file descriptors or credentials you need to send/read at
1332 * least one byte" (man 7 unix) */
1333 vec.iov_base = &sendchar;
1334 vec.iov_len = sizeof(sendchar);
1335 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1336 if (retval != 1) {
1337 perror("sending file descriptor");
1338 return -1;
1339 }
1340 return 0;
1341}
1342
1343/* Helper for should_auto_unmount
1344 *
1345 * fusermount typically has the s-bit set - initial open of `mnt` was as root
1346 * and got EACCESS as 'allow_other' was not specified.
1347 * Try opening `mnt` again with uid and guid of the calling process.
1348 */
1349static int recheck_ENOTCONN_as_owner(const char *mnt)
1350{
1351 int pid = fork();
1352 if(pid == -1) {
1353 perror("fuse: recheck_ENOTCONN_as_owner can't fork");
1354 _exit(EXIT_FAILURE);
1355 } else if(pid == 0) {
1356 uid_t uid = getuid();
1357 gid_t gid = getgid();
1358 if(setresgid(gid, gid, gid) == -1) {
1359 perror("fuse: can't set resgid");
1360 _exit(EXIT_FAILURE);
1361 }
1362 if(setresuid(uid, uid, uid) == -1) {
1363 perror("fuse: can't set resuid");
1364 _exit(EXIT_FAILURE);
1365 }
1366
1367 int fd = open(mnt, O_RDONLY);
1368 if(fd == -1 && errno == ENOTCONN)
1369 _exit(EXIT_SUCCESS);
1370 else
1371 _exit(EXIT_FAILURE);
1372 } else {
1373 int status;
1374 int res = waitpid(pid, &status, 0);
1375 if (res == -1) {
1376 perror("fuse: waiting for child failed");
1377 _exit(EXIT_FAILURE);
1378 }
1379 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
1380 }
1381}
1382
1383/* The parent fuse process has died: decide whether to auto_unmount.
1384 *
1385 * In the normal case (umount or fusermount -u), the filesystem
1386 * has already been unmounted. If we simply unmount again we can
1387 * cause problems with stacked mounts (e.g. autofs).
1388 *
1389 * So we unmount here only in abnormal case where fuse process has
1390 * died without unmount happening. To detect this, we first look in
1391 * the mount table to make sure the mountpoint is still mounted and
1392 * has proper type. If so, we then see if opening the mount dir is
1393 * returning 'Transport endpoint is not connected'.
1394 *
1395 * The order of these is important, because if autofs is in use,
1396 * opening the dir to check for ENOTCONN will cause a new mount
1397 * in the normal case where filesystem has been unmounted cleanly.
1398 */
1399static int should_auto_unmount(const char *mnt, const char *type)
1400{
1401 char *copy;
1402 const char *last;
1403 int result = 0;
1404 int fd;
1405
1406 copy = strdup(mnt);
1407 if (copy == NULL) {
1408 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1409 return 0;
1410 }
1411
1412 if (chdir_to_parent(copy, &last) == -1)
1413 goto out;
1414 if (check_is_mount(last, mnt, type) == -1)
1415 goto out;
1416
1417 fd = open(mnt, O_RDONLY);
1418
1419 if (fd != -1) {
1420 close(fd);
1421 } else {
1422 switch(errno) {
1423 case ENOTCONN:
1424 result = 1;
1425 break;
1426 case EACCES:
1427 result = recheck_ENOTCONN_as_owner(mnt);
1428 break;
1429 default:
1430 result = 0;
1431 break;
1432 }
1433 }
1434out:
1435 free(copy);
1436 return result;
1437}
1438
1439static void usage(void)
1440{
1441 printf("%s: [options] mountpoint\n"
1442 "Options:\n"
1443 " -h print help\n"
1444 " -V print version\n"
1445 " -o opt[,opt...] mount options\n"
1446 " -u unmount\n"
1447 " -q quiet\n"
1448 " -z lazy unmount\n",
1449 progname);
1450 exit(1);
1451}
1452
1453static void show_version(void)
1454{
1455 printf("fusermount3 version: %s\n", PACKAGE_VERSION);
1456 exit(0);
1457}
1458
1459static void close_range_loop(int min_fd, int max_fd, int cfd)
1460{
1461 for (int fd = min_fd; fd <= max_fd; fd++)
1462 if (fd != cfd)
1463 close(fd);
1464}
1465
1466/*
1467 * Close all inherited fds that are not needed
1468 * Ideally these wouldn't come up at all, applications should better
1469 * use FD_CLOEXEC / O_CLOEXEC
1470 */
1471static int close_inherited_fds(int cfd)
1472{
1473 int rc = -1;
1474 int nullfd;
1475
1476 /* We can't even report an error */
1477 if (cfd <= STDERR_FILENO)
1478 return -EINVAL;
1479
1480#ifdef HAVE_CLOSE_RANGE
1481 if (cfd < STDERR_FILENO + 2) {
1482 close_range_loop(STDERR_FILENO + 1, cfd - 1, cfd);
1483 } else {
1484 rc = close_range(STDERR_FILENO + 1, cfd - 1, 0);
1485 if (rc < 0)
1486 goto fallback;
1487 }
1488
1489 /* Close high range */
1490 rc = close_range(cfd + 1, ~0U, 0);
1491#else
1492 goto fallback; /* make use of fallback to avoid compiler warnings */
1493#endif
1494
1495fallback:
1496 if (rc < 0) {
1497 int max_fd = sysconf(_SC_OPEN_MAX) - 1;
1498
1499 close_range_loop(STDERR_FILENO + 1, max_fd, cfd);
1500 }
1501
1502 nullfd = open("/dev/null", O_RDWR);
1503 if (nullfd < 0) {
1504 perror("fusermount: cannot open /dev/null");
1505 return -errno;
1506 }
1507
1508 /* Redirect stdin, stdout, stderr to /dev/null */
1509 dup2(nullfd, STDIN_FILENO);
1510 dup2(nullfd, STDOUT_FILENO);
1511 dup2(nullfd, STDERR_FILENO);
1512 if (nullfd > STDERR_FILENO)
1513 close(nullfd);
1514
1515 return 0;
1516}
1517
1518int main(int argc, char *argv[])
1519{
1520 sigset_t sigset;
1521 int ch;
1522 int fd;
1523 int res;
1524 char *origmnt;
1525 char *mnt;
1526 static int unmount = 0;
1527 static int lazy = 0;
1528 static int quiet = 0;
1529 char *commfd = NULL;
1530 long cfd;
1531 const char *opts = "";
1532 const char *type = NULL;
1533 int setup_auto_unmount_only = 0;
1534
1535 static const struct option long_opts[] = {
1536 {"unmount", no_argument, NULL, 'u'},
1537 {"lazy", no_argument, NULL, 'z'},
1538 {"quiet", no_argument, NULL, 'q'},
1539 {"help", no_argument, NULL, 'h'},
1540 {"version", no_argument, NULL, 'V'},
1541 {"options", required_argument, NULL, 'o'},
1542 // Note: auto-unmount and comm-fd don't have short versions.
1543 // They'ne meant for internal use by mount.c
1544 {"auto-unmount", no_argument, NULL, 'U'},
1545 {"comm-fd", required_argument, NULL, 'c'},
1546 {0, 0, 0, 0}};
1547
1548 progname = strdup(argc > 0 ? argv[0] : "fusermount");
1549 if (progname == NULL) {
1550 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1551 exit(1);
1552 }
1553
1554 while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
1555 NULL)) != -1) {
1556 switch (ch) {
1557 case 'h':
1558 usage();
1559 break;
1560
1561 case 'V':
1562 show_version();
1563 break;
1564
1565 case 'o':
1566 opts = optarg;
1567 break;
1568
1569 case 'u':
1570 unmount = 1;
1571 break;
1572 case 'U':
1573 unmount = 1;
1574 auto_unmount = 1;
1575 setup_auto_unmount_only = 1;
1576 break;
1577 case 'c':
1578 commfd = optarg;
1579 break;
1580 case 'z':
1581 lazy = 1;
1582 break;
1583
1584 case 'q':
1585 quiet = 1;
1586 break;
1587
1588 default:
1589 exit(1);
1590 }
1591 }
1592
1593 if (lazy && !unmount) {
1594 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1595 exit(1);
1596 }
1597
1598 if (optind >= argc) {
1599 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1600 exit(1);
1601 } else if (argc > optind + 1) {
1602 fprintf(stderr, "%s: extra arguments after the mountpoint\n",
1603 progname);
1604 exit(1);
1605 }
1606
1607 origmnt = argv[optind];
1608
1609 drop_privs();
1610 mnt = fuse_mnt_resolve_path(progname, origmnt);
1611 if (mnt != NULL) {
1612 res = chdir("/");
1613 if (res == -1) {
1614 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1615 goto err_out;
1616 }
1617 }
1618 restore_privs();
1619 if (mnt == NULL)
1620 exit(1);
1621
1622 umask(033);
1623 if (!setup_auto_unmount_only && unmount)
1624 goto do_unmount;
1625
1626 if(commfd == NULL)
1627 commfd = getenv(FUSE_COMMFD_ENV);
1628 if (commfd == NULL) {
1629 fprintf(stderr, "%s: old style mounting not supported\n",
1630 progname);
1631 goto err_out;
1632 }
1633
1634 res = libfuse_strtol(commfd, &cfd);
1635 if (res) {
1636 fprintf(stderr,
1637 "%s: invalid _FUSE_COMMFD: %s\n",
1638 progname, commfd);
1639 goto err_out;
1640
1641 }
1642
1643 {
1644 struct stat statbuf;
1645 fstat(cfd, &statbuf);
1646 if(!S_ISSOCK(statbuf.st_mode)) {
1647 fprintf(stderr,
1648 "%s: file descriptor %li is not a socket, can't send fuse fd\n",
1649 progname, cfd);
1650 goto err_out;
1651 }
1652 }
1653
1654 if (setup_auto_unmount_only)
1655 goto wait_for_auto_unmount;
1656
1657 fd = mount_fuse(mnt, opts, &type);
1658 if (fd == -1)
1659 goto err_out;
1660
1661 res = send_fd(cfd, fd);
1662 if (res != 0) {
1663 umount2(mnt, MNT_DETACH); /* lazy umount */
1664 goto err_out;
1665 }
1666 close(fd);
1667
1668 if (!auto_unmount) {
1669 free(mnt);
1670 free((void*) type);
1671 return 0;
1672 }
1673
1674wait_for_auto_unmount:
1675 /* Become a daemon and wait for the parent to exit or die.
1676 ie For the control socket to get closed.
1677 Btw, we don't want to use daemon() function here because
1678 it forks and messes with the file descriptors. */
1679
1680 res = close_inherited_fds(cfd);
1681 if (res < 0)
1682 exit(EXIT_FAILURE);
1683
1684 setsid();
1685 res = chdir("/");
1686 if (res == -1) {
1687 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1688 goto err_out;
1689 }
1690
1691 sigfillset(&sigset);
1692 sigprocmask(SIG_BLOCK, &sigset, NULL);
1693
1694 lazy = 1;
1695 quiet = 1;
1696
1697 while (1) {
1698 unsigned char buf[16];
1699 int n = recv(cfd, buf, sizeof(buf), 0);
1700 if (!n)
1701 break;
1702
1703 if (n < 0) {
1704 if (errno == EINTR)
1705 continue;
1706 break;
1707 }
1708 }
1709
1710 if (!should_auto_unmount(mnt, type)) {
1711 goto success_out;
1712 }
1713
1714do_unmount:
1715 if (geteuid() == 0)
1716 res = unmount_fuse(mnt, quiet, lazy);
1717 else {
1718 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
1719 if (res == -1 && !quiet)
1720 fprintf(stderr,
1721 "%s: failed to unmount %s: %s\n",
1722 progname, mnt, strerror(errno));
1723 }
1724 if (res == -1)
1725 goto err_out;
1726
1727success_out:
1728 free((void*) type);
1729 free(mnt);
1730 return 0;
1731
1732err_out:
1733 free((void*) type);
1734 free(mnt);
1735 exit(1);
1736}
fuse-3.18.2/doc/html/fuse-3_818_81_2util_2fusermount_8c_source.html0000644000175000017500000101435015156613443023604 0ustar berndbernd libfuse: fuse-3.18.1/util/fusermount.c Source File
libfuse
fusermount.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8/* This program does the mounting and unmounting of FUSE filesystems */
9
10#define _GNU_SOURCE /* for clone,strchrnul and close_range */
11#include "fuse_config.h"
12#include "mount_util.h"
13#include "util.h"
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <ctype.h>
19#include <unistd.h>
20#include <getopt.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <pwd.h>
24#include <paths.h>
25#include <mntent.h>
26#include <sys/wait.h>
27#include <sys/stat.h>
28#include <sys/param.h>
29
30#include "fuse_mount_compat.h"
31
32#include <sys/fsuid.h>
33#include <sys/socket.h>
34#include <sys/utsname.h>
35#include <sched.h>
36#include <stdbool.h>
37#include <sys/vfs.h>
38
39#if defined HAVE_CLOSE_RANGE && defined linux
40#include <linux/close_range.h>
41#endif
42
43#if defined HAVE_LISTMOUNT
44#include <linux/mount.h>
45#include <syscall.h>
46#include <stdint.h>
47#endif
48
49#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
50#define FUSE_KERN_DEVICE_ENV "FUSE_KERN_DEVICE"
51
52#define FUSE_DEV "/dev/fuse"
53
54static const char *progname;
55
56static int user_allow_other = 0;
57static int mount_max = 1000;
58
59static int auto_unmount = 0;
60
61#ifdef GETMNTENT_NEEDS_UNESCAPING
62// Older versions of musl libc don't unescape entries in /etc/mtab
63
64// unescapes octal sequences like \040 in-place
65// That's ok, because unescaping can not extend the length of the string.
66static void unescape(char *buf) {
67 char *src = buf;
68 char *dest = buf;
69 while (1) {
70 char *next_src = strchrnul(src, '\\');
71 int offset = next_src - src;
72 memmove(dest, src, offset);
73 src = next_src;
74 dest += offset;
75
76 if(*src == '\0') {
77 *dest = *src;
78 return;
79 }
80 src++;
81
82 if('0' <= src[0] && src[0] < '2' &&
83 '0' <= src[1] && src[1] < '8' &&
84 '0' <= src[2] && src[2] < '8') {
85 *dest++ = (src[0] - '0') << 6
86 | (src[1] - '0') << 3
87 | (src[2] - '0') << 0;
88 src += 3;
89 } else if (src[0] == '\\') {
90 *dest++ = '\\';
91 src += 1;
92 } else {
93 *dest++ = '\\';
94 }
95 }
96}
97
98static struct mntent *GETMNTENT(FILE *stream)
99{
100 struct mntent *entp = getmntent(stream);
101 if(entp != NULL) {
102 unescape(entp->mnt_fsname);
103 unescape(entp->mnt_dir);
104 unescape(entp->mnt_type);
105 unescape(entp->mnt_opts);
106 }
107 return entp;
108}
109#else
110#define GETMNTENT getmntent
111#endif // GETMNTENT_NEEDS_UNESCAPING
112
113/*
114 * Take a ',' separated option string and extract "x-" options
115 */
116static int extract_x_options(const char *original, char **non_x_opts,
117 char **x_opts)
118{
119 size_t orig_len;
120 const char *opt, *opt_end;
121
122 orig_len = strlen(original) + 1;
123
124 *non_x_opts = calloc(1, orig_len);
125 *x_opts = calloc(1, orig_len);
126
127 size_t non_x_opts_len = orig_len;
128 size_t x_opts_len = orig_len;
129
130 if (*non_x_opts == NULL || *x_opts == NULL) {
131 fprintf(stderr, "%s: Failed to allocate %zuB.\n",
132 __func__, orig_len);
133 return -ENOMEM;
134 }
135
136 for (opt = original; opt < original + orig_len; opt = opt_end + 1) {
137 char *opt_buf;
138
139 opt_end = strchr(opt, ',');
140 if (opt_end == NULL)
141 opt_end = original + orig_len;
142
143 size_t opt_len = opt_end - opt;
144 size_t opt_len_left = orig_len - (opt - original);
145 size_t buf_len;
146 bool is_x_opts;
147
148 if (strncmp(opt, "x-", MIN(2, opt_len_left)) == 0) {
149 buf_len = x_opts_len;
150 is_x_opts = true;
151 opt_buf = *x_opts;
152 } else {
153 buf_len = non_x_opts_len;
154 is_x_opts = false;
155 opt_buf = *non_x_opts;
156 }
157
158 if (buf_len < orig_len) {
159 strncat(opt_buf, ",", 2);
160 buf_len -= 1;
161 }
162
163 /* omits ',' */
164 if ((ssize_t)(buf_len - opt_len) < 0) {
165 /* This would be a bug */
166 fprintf(stderr, "%s: no buf space left in copy, orig='%s'\n",
167 __func__, original);
168 return -EIO;
169 }
170
171 strncat(opt_buf, opt, opt_end - opt);
172 buf_len -= opt_len;
173
174 if (is_x_opts)
175 x_opts_len = buf_len;
176 else
177 non_x_opts_len = buf_len;
178 }
179
180 return 0;
181}
182
183static const char *get_user_name(void)
184{
185 struct passwd *pw = getpwuid(getuid());
186 if (pw != NULL && pw->pw_name != NULL)
187 return pw->pw_name;
188 else {
189 fprintf(stderr, "%s: could not determine username\n", progname);
190 return NULL;
191 }
192}
193
194static uid_t oldfsuid;
195static gid_t oldfsgid;
196
197static void drop_privs(void)
198{
199 if (getuid() != 0) {
200 oldfsuid = setfsuid(getuid());
201 oldfsgid = setfsgid(getgid());
202 }
203}
204
205static void restore_privs(void)
206{
207 if (getuid() != 0) {
208 setfsuid(oldfsuid);
209 setfsgid(oldfsgid);
210 }
211}
212
213#ifndef IGNORE_MTAB
214/*
215 * Make sure that /etc/mtab is checked and updated atomically
216 */
217static int lock_umount(void)
218{
219 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
220 int mtablock;
221 int res;
222 struct stat mtab_stat;
223
224 /* /etc/mtab could be a symlink to /proc/mounts */
225 if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
226 return -1;
227
228 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
229 if (mtablock == -1) {
230 fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
231 progname, strerror(errno));
232 return -1;
233 }
234 res = lockf(mtablock, F_LOCK, 0);
235 if (res < 0) {
236 fprintf(stderr, "%s: error getting lock: %s\n", progname,
237 strerror(errno));
238 close(mtablock);
239 return -1;
240 }
241
242 return mtablock;
243}
244
245static void unlock_umount(int mtablock)
246{
247 if (mtablock >= 0) {
248 int res;
249
250 res = lockf(mtablock, F_ULOCK, 0);
251 if (res < 0) {
252 fprintf(stderr, "%s: error releasing lock: %s\n",
253 progname, strerror(errno));
254 }
255 close(mtablock);
256 }
257}
258
259static int add_mount(const char *source, const char *mnt, const char *type,
260 const char *opts)
261{
262 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
263}
264
265static int may_unmount(const char *mnt, int quiet)
266{
267 struct mntent *entp;
268 FILE *fp;
269 const char *user = NULL;
270 char uidstr[32];
271 unsigned uidlen = 0;
272 int found;
273 const char *mtab = _PATH_MOUNTED;
274
275 user = get_user_name();
276 if (user == NULL)
277 return -1;
278
279 fp = setmntent(mtab, "r");
280 if (fp == NULL) {
281 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
282 strerror(errno));
283 return -1;
284 }
285
286 uidlen = sprintf(uidstr, "%u", getuid());
287
288 found = 0;
289 while ((entp = GETMNTENT(fp)) != NULL) {
290 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
291 (strcmp(entp->mnt_type, "fuse") == 0 ||
292 strcmp(entp->mnt_type, "fuseblk") == 0 ||
293 strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
294 strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
295 char *p = strstr(entp->mnt_opts, "user=");
296 if (p &&
297 (p == entp->mnt_opts || *(p-1) == ',') &&
298 strcmp(p + 5, user) == 0) {
299 found = 1;
300 break;
301 }
302 /* /etc/mtab is a link pointing to
303 /proc/mounts: */
304 else if ((p =
305 strstr(entp->mnt_opts, "user_id=")) &&
306 (p == entp->mnt_opts ||
307 *(p-1) == ',') &&
308 strncmp(p + 8, uidstr, uidlen) == 0 &&
309 (*(p+8+uidlen) == ',' ||
310 *(p+8+uidlen) == '\0')) {
311 found = 1;
312 break;
313 }
314 }
315 }
316 endmntent(fp);
317
318 if (!found) {
319 if (!quiet)
320 fprintf(stderr,
321 "%s: entry for %s not found in %s\n",
322 progname, mnt, mtab);
323 return -1;
324 }
325
326 return 0;
327}
328#endif
329
330/*
331 * Check whether the file specified in "fusermount3 -u" is really a
332 * mountpoint and not a symlink. This is necessary otherwise the user
333 * could move the mountpoint away and replace it with a symlink
334 * pointing to an arbitrary mount, thereby tricking fusermount3 into
335 * unmounting that (umount(2) will follow symlinks).
336 *
337 * This is the child process running in a separate mount namespace, so
338 * we don't mess with the global namespace and if the process is
339 * killed for any reason, mounts are automatically cleaned up.
340 *
341 * First make sure nothing is propagated back into the parent
342 * namespace by marking all mounts "private".
343 *
344 * Then bind mount parent onto a stable base where the user can't move
345 * it around.
346 *
347 * Finally check /proc/mounts for an entry matching the requested
348 * mountpoint. If it's found then we are OK, and the user can't move
349 * it around within the parent directory as rename() will return
350 * EBUSY. Be careful to ignore any mounts that existed before the
351 * bind.
352 */
353static int check_is_mount_child(void *p)
354{
355 const char **a = p;
356 const char *last = a[0];
357 const char *mnt = a[1];
358 const char *type = a[2];
359 int res;
360 const char *procmounts = "/proc/mounts";
361 int found;
362 FILE *fp;
363 struct mntent *entp;
364 int count;
365
366 res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
367 if (res == -1) {
368 fprintf(stderr, "%s: failed to mark mounts private: %s\n",
369 progname, strerror(errno));
370 return 1;
371 }
372
373 fp = setmntent(procmounts, "r");
374 if (fp == NULL) {
375 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
376 procmounts, strerror(errno));
377 return 1;
378 }
379
380 count = 0;
381 while (GETMNTENT(fp) != NULL)
382 count++;
383 endmntent(fp);
384
385 fp = setmntent(procmounts, "r");
386 if (fp == NULL) {
387 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
388 procmounts, strerror(errno));
389 return 1;
390 }
391
392 res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
393 if (res == -1) {
394 fprintf(stderr, "%s: failed to bind parent to /: %s\n",
395 progname, strerror(errno));
396 return 1;
397 }
398
399 found = 0;
400 while ((entp = GETMNTENT(fp)) != NULL) {
401 if (count > 0) {
402 count--;
403 continue;
404 }
405 if (entp->mnt_dir[0] == '/' &&
406 strcmp(entp->mnt_dir + 1, last) == 0 &&
407 (!type || strcmp(entp->mnt_type, type) == 0)) {
408 found = 1;
409 break;
410 }
411 }
412 endmntent(fp);
413
414 if (!found) {
415 fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
416 return 1;
417 }
418
419 return 0;
420}
421
422static pid_t clone_newns(void *a)
423{
424 char buf[131072];
425 char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
426
427#ifdef __ia64__
428 extern int __clone2(int (*fn)(void *),
429 void *child_stack_base, size_t stack_size,
430 int flags, void *arg, pid_t *ptid,
431 void *tls, pid_t *ctid);
432
433 return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
434 CLONE_NEWNS, a, NULL, NULL, NULL);
435#else
436 return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
437#endif
438}
439
440static int check_is_mount(const char *last, const char *mnt, const char *type)
441{
442 pid_t pid, p;
443 int status;
444 const char *a[3] = { last, mnt, type };
445
446 pid = clone_newns((void *) a);
447 if (pid == (pid_t) -1) {
448 fprintf(stderr, "%s: failed to clone namespace: %s\n",
449 progname, strerror(errno));
450 return -1;
451 }
452 p = waitpid(pid, &status, __WCLONE);
453 if (p == (pid_t) -1) {
454 fprintf(stderr, "%s: waitpid failed: %s\n",
455 progname, strerror(errno));
456 return -1;
457 }
458 if (!WIFEXITED(status)) {
459 fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
460 progname, status);
461 return -1;
462 }
463 if (WEXITSTATUS(status) != 0)
464 return -1;
465
466 return 0;
467}
468
469static int chdir_to_parent(char *copy, const char **lastp)
470{
471 char *tmp;
472 const char *parent;
473 char buf[65536];
474 int res;
475
476 tmp = strrchr(copy, '/');
477 if (tmp == NULL || tmp[1] == '\0') {
478 fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
479 progname, copy);
480 return -1;
481 }
482 if (tmp != copy) {
483 *tmp = '\0';
484 parent = copy;
485 *lastp = tmp + 1;
486 } else if (tmp[1] != '\0') {
487 *lastp = tmp + 1;
488 parent = "/";
489 } else {
490 *lastp = ".";
491 parent = "/";
492 }
493
494 res = chdir(parent);
495 if (res == -1) {
496 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
497 progname, parent, strerror(errno));
498 return -1;
499 }
500
501 if (getcwd(buf, sizeof(buf)) == NULL) {
502 fprintf(stderr, "%s: failed to obtain current directory: %s\n",
503 progname, strerror(errno));
504 return -1;
505 }
506 if (strcmp(buf, parent) != 0) {
507 fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
508 parent, buf);
509 return -1;
510
511 }
512
513 return 0;
514}
515
516#ifndef IGNORE_MTAB
517static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
518{
519 int res;
520 char *copy;
521 const char *last;
522 int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW;
523
524 if (getuid() != 0) {
525 res = may_unmount(mnt, quiet);
526 if (res == -1)
527 return -1;
528 }
529
530 copy = strdup(mnt);
531 if (copy == NULL) {
532 fprintf(stderr, "%s: failed to allocate memory\n", progname);
533 return -1;
534 }
535
536 drop_privs();
537 res = chdir_to_parent(copy, &last);
538 if (res == -1) {
539 restore_privs();
540 goto out;
541 }
542
543 res = umount2(last, umount_flags);
544 restore_privs();
545 if (res == -1 && !quiet) {
546 fprintf(stderr, "%s: failed to unmount %s: %s\n",
547 progname, mnt, strerror(errno));
548 }
549
550out:
551 free(copy);
552 if (res == -1)
553 return -1;
554
555 res = chdir("/");
556 if (res == -1) {
557 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
558 return -1;
559 }
560
561 return fuse_mnt_remove_mount(progname, mnt);
562}
563
564static int unmount_fuse(const char *mnt, int quiet, int lazy)
565{
566 int res;
567 int mtablock = lock_umount();
568
569 res = unmount_fuse_locked(mnt, quiet, lazy);
570 unlock_umount(mtablock);
571
572 return res;
573}
574
575static int count_fuse_fs_mtab(void)
576{
577 struct mntent *entp;
578 int count = 0;
579 const char *mtab = _PATH_MOUNTED;
580 FILE *fp = setmntent(mtab, "r");
581 if (fp == NULL) {
582 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
583 strerror(errno));
584 return -1;
585 }
586 while ((entp = GETMNTENT(fp)) != NULL) {
587 if (strcmp(entp->mnt_type, "fuse") == 0 ||
588 strncmp(entp->mnt_type, "fuse.", 5) == 0)
589 count ++;
590 }
591 endmntent(fp);
592 return count;
593}
594
595#ifdef HAVE_LISTMOUNT
596static int count_fuse_fs_ls_mnt(void)
597{
598 #define SMBUF_SIZE 1024
599 #define MNT_ID_LEN 128
600
601 int fuse_count = 0;
602 int n_mounts = 0;
603 int ret = 0;
604 uint64_t mnt_ids[MNT_ID_LEN];
605 unsigned char smbuf[SMBUF_SIZE];
606 struct mnt_id_req req = {
607 .size = sizeof(struct mnt_id_req),
608 };
609 struct statmount *sm;
610
611 for (;;) {
612 req.mnt_id = LSMT_ROOT;
613
614 n_mounts = syscall(SYS_listmount, &req, &mnt_ids, MNT_ID_LEN, 0);
615 if (n_mounts == -1) {
616 if (errno != ENOSYS) {
617 fprintf(stderr, "%s: failed to list mounts: %s\n", progname,
618 strerror(errno));
619 }
620 return -1;
621 }
622
623 for (int i = 0; i < n_mounts; i++) {
624 req.mnt_id = mnt_ids[i];
625 req.param = STATMOUNT_FS_TYPE;
626 ret = syscall(SYS_statmount, &req, &smbuf, SMBUF_SIZE, 0);
627 if (ret) {
628 if (errno == ENOENT)
629 continue;
630
631 fprintf(stderr, "%s: failed to stat mount %lld: %s\n", progname,
632 req.mnt_id, strerror(errno));
633 return -1;
634 }
635
636 sm = (struct statmount *)smbuf;
637 if (sm->mask & STATMOUNT_FS_TYPE &&
638 strcmp(&sm->str[sm->fs_type], "fuse") == 0)
639 fuse_count++;
640 }
641
642 if (n_mounts < MNT_ID_LEN)
643 break;
644 req.param = mnt_ids[MNT_ID_LEN - 1];
645 }
646 return fuse_count;
647}
648
649static int count_fuse_fs(void)
650{
651 int count = count_fuse_fs_ls_mnt();
652
653 return count >= 0 ? count : count_fuse_fs_mtab();
654}
655#else
656static int count_fuse_fs(void)
657{
658 return count_fuse_fs_mtab();
659}
660#endif
661
662#else /* IGNORE_MTAB */
663static int count_fuse_fs(void)
664{
665 return 0;
666}
667
668static int add_mount(const char *source, const char *mnt, const char *type,
669 const char *opts)
670{
671 (void) source;
672 (void) mnt;
673 (void) type;
674 (void) opts;
675 return 0;
676}
677
678static int unmount_fuse(const char *mnt, int quiet, int lazy)
679{
680 (void) quiet;
681 return fuse_mnt_umount(progname, mnt, mnt, lazy);
682}
683#endif /* IGNORE_MTAB */
684
685static void strip_line(char *line)
686{
687 char *s = strchr(line, '#');
688 if (s != NULL)
689 s[0] = '\0';
690 for (s = line + strlen(line) - 1;
691 s >= line && isspace((unsigned char) *s); s--);
692 s[1] = '\0';
693 for (s = line; isspace((unsigned char) *s); s++);
694 if (s != line)
695 memmove(line, s, strlen(s)+1);
696}
697
698static void parse_line(char *line, int linenum)
699{
700 int tmp;
701 if (strcmp(line, "user_allow_other") == 0)
702 user_allow_other = 1;
703 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
704 mount_max = tmp;
705 else if(line[0])
706 fprintf(stderr,
707 "%s: unknown parameter in %s at line %i: '%s'\n",
708 progname, FUSE_CONF, linenum, line);
709}
710
711static void read_conf(void)
712{
713 FILE *fp = fopen(FUSE_CONF, "r");
714 if (fp != NULL) {
715 int linenum = 1;
716 char line[256];
717 int isnewline = 1;
718 while (fgets(line, sizeof(line), fp) != NULL) {
719 if (isnewline) {
720 if (line[strlen(line)-1] == '\n') {
721 strip_line(line);
722 parse_line(line, linenum);
723 } else {
724 isnewline = 0;
725 }
726 } else if(line[strlen(line)-1] == '\n') {
727 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
728
729 isnewline = 1;
730 }
731 if (isnewline)
732 linenum ++;
733 }
734 if (!isnewline) {
735 fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
736
737 }
738 if (ferror(fp)) {
739 fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
740 exit(1);
741 }
742 fclose(fp);
743 } else if (errno != ENOENT) {
744 bool fatal = (errno != EACCES && errno != ELOOP &&
745 errno != ENAMETOOLONG && errno != ENOTDIR &&
746 errno != EOVERFLOW);
747 fprintf(stderr, "%s: failed to open %s: %s\n",
748 progname, FUSE_CONF, strerror(errno));
749 if (fatal)
750 exit(1);
751 }
752}
753
754static int begins_with(const char *s, const char *beg)
755{
756 if (strncmp(s, beg, strlen(beg)) == 0)
757 return 1;
758 else
759 return 0;
760}
761
762struct mount_flags {
763 const char *opt;
764 unsigned long flag;
765 int on;
766 int safe;
767};
768
769static struct mount_flags mount_flags[] = {
770 {"rw", MS_RDONLY, 0, 1},
771 {"ro", MS_RDONLY, 1, 1},
772 {"suid", MS_NOSUID, 0, 0},
773 {"nosuid", MS_NOSUID, 1, 1},
774 {"dev", MS_NODEV, 0, 0},
775 {"nodev", MS_NODEV, 1, 1},
776 {"exec", MS_NOEXEC, 0, 1},
777 {"noexec", MS_NOEXEC, 1, 1},
778 {"async", MS_SYNCHRONOUS, 0, 1},
779 {"sync", MS_SYNCHRONOUS, 1, 1},
780 {"atime", MS_NOATIME, 0, 1},
781 {"noatime", MS_NOATIME, 1, 1},
782 {"diratime", MS_NODIRATIME, 0, 1},
783 {"nodiratime", MS_NODIRATIME, 1, 1},
784 {"lazytime", MS_LAZYTIME, 1, 1},
785 {"nolazytime", MS_LAZYTIME, 0, 1},
786 {"relatime", MS_RELATIME, 1, 1},
787 {"norelatime", MS_RELATIME, 0, 1},
788 {"strictatime", MS_STRICTATIME, 1, 1},
789 {"nostrictatime", MS_STRICTATIME, 0, 1},
790 {"dirsync", MS_DIRSYNC, 1, 1},
791 {"symfollow", MS_NOSYMFOLLOW, 0, 1},
792 {"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
793 {NULL, 0, 0, 0}
794};
795
796static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
797{
798 int i;
799
800 for (i = 0; mount_flags[i].opt != NULL; i++) {
801 const char *opt = mount_flags[i].opt;
802 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
803 *on = mount_flags[i].on;
804 *flag = mount_flags[i].flag;
805 if (!mount_flags[i].safe && getuid() != 0) {
806 *flag = 0;
807 fprintf(stderr,
808 "%s: unsafe option %s ignored\n",
809 progname, opt);
810 }
811 return 1;
812 }
813 }
814 return 0;
815}
816
817static int add_option(char **optsp, const char *opt, unsigned expand)
818{
819 char *newopts;
820 if (*optsp == NULL)
821 newopts = strdup(opt);
822 else {
823 unsigned oldsize = strlen(*optsp);
824 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
825 newopts = (char *) realloc(*optsp, newsize);
826 if (newopts)
827 sprintf(newopts + oldsize, ",%s", opt);
828 }
829 if (newopts == NULL) {
830 fprintf(stderr, "%s: failed to allocate memory\n", progname);
831 return -1;
832 }
833 *optsp = newopts;
834 return 0;
835}
836
837static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
838{
839 int i;
840 int l;
841
842 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
843 return -1;
844
845 for (i = 0; mount_flags[i].opt != NULL; i++) {
846 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
847 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
848 return -1;
849 }
850
851 if (add_option(mnt_optsp, opts, 0) == -1)
852 return -1;
853 /* remove comma from end of opts*/
854 l = strlen(*mnt_optsp);
855 if ((*mnt_optsp)[l-1] == ',')
856 (*mnt_optsp)[l-1] = '\0';
857 if (getuid() != 0) {
858 const char *user = get_user_name();
859 if (user == NULL)
860 return -1;
861
862 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
863 return -1;
864 strcat(*mnt_optsp, user);
865 }
866 return 0;
867}
868
869static int opt_eq(const char *s, unsigned len, const char *opt)
870{
871 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
872 return 1;
873 else
874 return 0;
875}
876
877static int get_string_opt(const char *s, unsigned len, const char *opt,
878 char **val)
879{
880 int i;
881 unsigned opt_len = strlen(opt);
882 char *d;
883
884 if (*val)
885 free(*val);
886 *val = (char *) malloc(len - opt_len + 1);
887 if (!*val) {
888 fprintf(stderr, "%s: failed to allocate memory\n", progname);
889 return 0;
890 }
891
892 d = *val;
893 s += opt_len;
894 len -= opt_len;
895 for (i = 0; i < len; i++) {
896 if (s[i] == '\\' && i + 1 < len)
897 i++;
898 *d++ = s[i];
899 }
900 *d = '\0';
901 return 1;
902}
903
904/* The kernel silently truncates the "data" argument to PAGE_SIZE-1 characters.
905 * This can be dangerous if it e.g. truncates the option "group_id=1000" to
906 * "group_id=1".
907 * This wrapper detects this case and bails out with an error.
908 */
909static int mount_notrunc(const char *source, const char *target,
910 const char *filesystemtype, unsigned long mountflags,
911 const char *data) {
912 if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
913 fprintf(stderr, "%s: mount options too long\n", progname);
914 errno = EINVAL;
915 return -1;
916 }
917 return mount(source, target, filesystemtype, mountflags, data);
918}
919
920
921static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
922 int fd, const char *opts, const char *dev, char **sourcep,
923 char **mnt_optsp)
924{
925 int res;
926 int flags = MS_NOSUID | MS_NODEV;
927 char *optbuf;
928 char *mnt_opts = NULL;
929 const char *s;
930 char *d;
931 char *fsname = NULL;
932 char *subtype = NULL;
933 char *source = NULL;
934 char *type = NULL;
935 int blkdev = 0;
936
937 optbuf = (char *) malloc(strlen(opts) + 128);
938 if (!optbuf) {
939 fprintf(stderr, "%s: failed to allocate memory\n", progname);
940 return -1;
941 }
942
943 for (s = opts, d = optbuf; *s;) {
944 unsigned len;
945 const char *fsname_str = "fsname=";
946 const char *subtype_str = "subtype=";
947 bool escape_ok = begins_with(s, fsname_str) ||
948 begins_with(s, subtype_str);
949 for (len = 0; s[len]; len++) {
950 if (escape_ok && s[len] == '\\' && s[len + 1])
951 len++;
952 else if (s[len] == ',')
953 break;
954 }
955 if (begins_with(s, fsname_str)) {
956 if (!get_string_opt(s, len, fsname_str, &fsname))
957 goto err;
958 } else if (begins_with(s, subtype_str)) {
959 if (!get_string_opt(s, len, subtype_str, &subtype))
960 goto err;
961 } else if (opt_eq(s, len, "blkdev")) {
962 if (getuid() != 0) {
963 fprintf(stderr,
964 "%s: option blkdev is privileged\n",
965 progname);
966 goto err;
967 }
968 blkdev = 1;
969 } else if (opt_eq(s, len, "auto_unmount")) {
970 auto_unmount = 1;
971 } else if (!opt_eq(s, len, "nonempty") &&
972 !begins_with(s, "fd=") &&
973 !begins_with(s, "rootmode=") &&
974 !begins_with(s, "user_id=") &&
975 !begins_with(s, "group_id=")) {
976 int on;
977 int flag;
978 int skip_option = 0;
979 if (opt_eq(s, len, "large_read")) {
980 struct utsname utsname;
981 unsigned kmaj, kmin;
982 res = uname(&utsname);
983 if (res == 0 &&
984 sscanf(utsname.release, "%u.%u",
985 &kmaj, &kmin) == 2 &&
986 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
987 fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
988 skip_option = 1;
989 }
990 }
991 if (getuid() != 0 && !user_allow_other &&
992 (opt_eq(s, len, "allow_other") ||
993 opt_eq(s, len, "allow_root"))) {
994 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
995 goto err;
996 }
997 if (!skip_option) {
998 if (find_mount_flag(s, len, &on, &flag)) {
999 if (on)
1000 flags |= flag;
1001 else
1002 flags &= ~flag;
1003 } else if (opt_eq(s, len, "default_permissions") ||
1004 opt_eq(s, len, "allow_other") ||
1005 begins_with(s, "max_read=") ||
1006 begins_with(s, "blksize=")) {
1007 memcpy(d, s, len);
1008 d += len;
1009 *d++ = ',';
1010 } else {
1011 fprintf(stderr, "%s: unknown option '%.*s'\n", progname, len, s);
1012 exit(1);
1013 }
1014 }
1015 }
1016 s += len;
1017 if (*s)
1018 s++;
1019 }
1020 *d = '\0';
1021 res = get_mnt_opts(flags, optbuf, &mnt_opts);
1022 if (res == -1)
1023 goto err;
1024
1025 sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
1026 fd, rootmode, getuid(), getgid());
1027
1028 source = malloc((fsname ? strlen(fsname) : 0) +
1029 (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
1030
1031 type = malloc((subtype ? strlen(subtype) : 0) + 32);
1032 if (!type || !source) {
1033 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1034 goto err;
1035 }
1036
1037 if (subtype)
1038 sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
1039 else
1040 strcpy(type, blkdev ? "fuseblk" : "fuse");
1041
1042 if (fsname)
1043 strcpy(source, fsname);
1044 else
1045 strcpy(source, subtype ? subtype : dev);
1046
1047 res = mount_notrunc(source, mnt, type, flags, optbuf);
1048 if (res == -1 && errno == ENODEV && subtype) {
1049 /* Probably missing subtype support */
1050 strcpy(type, blkdev ? "fuseblk" : "fuse");
1051 if (fsname) {
1052 if (!blkdev)
1053 sprintf(source, "%s#%s", subtype, fsname);
1054 } else {
1055 strcpy(source, type);
1056 }
1057
1058 res = mount_notrunc(source, mnt, type, flags, optbuf);
1059 }
1060 if (res == -1 && errno == EINVAL) {
1061 /* It could be an old version not supporting group_id */
1062 sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
1063 fd, rootmode, getuid());
1064 res = mount_notrunc(source, mnt, type, flags, optbuf);
1065 }
1066 if (res == -1) {
1067 int errno_save = errno;
1068 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
1069 fprintf(stderr, "%s: 'fuseblk' support missing\n",
1070 progname);
1071 else
1072 fprintf(stderr, "%s: mount failed: %s\n", progname,
1073 strerror(errno_save));
1074 goto err;
1075 }
1076 *sourcep = source;
1077 *typep = type;
1078 *mnt_optsp = mnt_opts;
1079 free(fsname);
1080 free(optbuf);
1081
1082 return 0;
1083
1084err:
1085 free(fsname);
1086 free(subtype);
1087 free(source);
1088 free(type);
1089 free(mnt_opts);
1090 free(optbuf);
1091 return -1;
1092}
1093
1094static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
1095{
1096 int res;
1097 const char *mnt = *mntp;
1098 const char *origmnt = mnt;
1099 struct statfs fs_buf;
1100 size_t i;
1101
1102 res = lstat(mnt, stbuf);
1103 if (res == -1) {
1104 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1105 progname, mnt, strerror(errno));
1106 return -1;
1107 }
1108
1109 /* No permission checking is done for root */
1110 if (getuid() == 0)
1111 return 0;
1112
1113 if (S_ISDIR(stbuf->st_mode)) {
1114 res = chdir(mnt);
1115 if (res == -1) {
1116 fprintf(stderr,
1117 "%s: failed to chdir to mountpoint: %s\n",
1118 progname, strerror(errno));
1119 return -1;
1120 }
1121 mnt = *mntp = ".";
1122 res = lstat(mnt, stbuf);
1123 if (res == -1) {
1124 fprintf(stderr,
1125 "%s: failed to access mountpoint %s: %s\n",
1126 progname, origmnt, strerror(errno));
1127 return -1;
1128 }
1129
1130 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
1131 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
1132 progname, origmnt);
1133 return -1;
1134 }
1135
1136 res = access(mnt, W_OK);
1137 if (res == -1) {
1138 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
1139 progname, origmnt);
1140 return -1;
1141 }
1142 } else if (S_ISREG(stbuf->st_mode)) {
1143 static char procfile[256];
1144 *mountpoint_fd = open(mnt, O_WRONLY);
1145 if (*mountpoint_fd == -1) {
1146 fprintf(stderr, "%s: failed to open %s: %s\n",
1147 progname, mnt, strerror(errno));
1148 return -1;
1149 }
1150 res = fstat(*mountpoint_fd, stbuf);
1151 if (res == -1) {
1152 fprintf(stderr,
1153 "%s: failed to access mountpoint %s: %s\n",
1154 progname, mnt, strerror(errno));
1155 return -1;
1156 }
1157 if (!S_ISREG(stbuf->st_mode)) {
1158 fprintf(stderr,
1159 "%s: mountpoint %s is no longer a regular file\n",
1160 progname, mnt);
1161 return -1;
1162 }
1163
1164 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
1165 *mntp = procfile;
1166 } else {
1167 fprintf(stderr,
1168 "%s: mountpoint %s is not a directory or a regular file\n",
1169 progname, mnt);
1170 return -1;
1171 }
1172
1173 /* Do not permit mounting over anything in procfs - it has a couple
1174 * places to which we have "write access" without being supposed to be
1175 * able to just put anything we want there.
1176 * Luckily, without allow_other, we can't get other users to actually
1177 * use any fake information we try to put there anyway.
1178 * Use a whitelist to be safe. */
1179 if (statfs(*mntp, &fs_buf)) {
1180 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1181 progname, mnt, strerror(errno));
1182 return -1;
1183 }
1184
1185 /* Define permitted filesystems for the mount target. This was
1186 * originally the same list as used by the ecryptfs mount helper
1187 * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
1188 * but got expanded as we found more filesystems that needed to be
1189 * overlaid. */
1190 typeof(fs_buf.f_type) f_type_whitelist[] = {
1191 0x61756673 /* AUFS_SUPER_MAGIC */,
1192 0x00000187 /* AUTOFS_SUPER_MAGIC */,
1193 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
1194 0x9123683E /* BTRFS_SUPER_MAGIC */,
1195 0x00C36400 /* CEPH_SUPER_MAGIC */,
1196 0xFF534D42 /* CIFS_MAGIC_NUMBER */,
1197 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
1198 0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
1199 0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
1200 0xF2F52010 /* F2FS_SUPER_MAGIC */,
1201 0x65735546 /* FUSE_SUPER_MAGIC */,
1202 0x01161970 /* GFS2_MAGIC */,
1203 0x47504653 /* GPFS_SUPER_MAGIC */,
1204 0x0000482b /* HFSPLUS_SUPER_MAGIC */,
1205 0x000072B6 /* JFFS2_SUPER_MAGIC */,
1206 0x3153464A /* JFS_SUPER_MAGIC */,
1207 0x0BD00BD0 /* LL_SUPER_MAGIC */,
1208 0X00004D44 /* MSDOS_SUPER_MAGIC */,
1209 0x0000564C /* NCP_SUPER_MAGIC */,
1210 0x00006969 /* NFS_SUPER_MAGIC */,
1211 0x00003434 /* NILFS_SUPER_MAGIC */,
1212 0x5346544E /* NTFS_SB_MAGIC */,
1213 0x7366746E /* NTFS3_SUPER_MAGIC */,
1214 0x5346414f /* OPENAFS_SUPER_MAGIC */,
1215 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
1216 0xAAD7AAEA /* PANFS_SUPER_MAGIC */,
1217 0x52654973 /* REISERFS_SUPER_MAGIC */,
1218 0xFE534D42 /* SMB2_SUPER_MAGIC */,
1219 0x73717368 /* SQUASHFS_MAGIC */,
1220 0x01021994 /* TMPFS_MAGIC */,
1221 0x24051905 /* UBIFS_SUPER_MAGIC */,
1222 0x18031977 /* WEKAFS_SUPER_MAGIC */,
1223#if __SIZEOF_LONG__ > 4
1224 0x736675005346544e /* UFSD */,
1225#endif
1226 0x58465342 /* XFS_SB_MAGIC */,
1227 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
1228 0x858458f6 /* RAMFS_MAGIC */,
1229 };
1230 for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
1231 if (f_type_whitelist[i] == fs_buf.f_type)
1232 return 0;
1233 }
1234
1235 fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
1236 progname, (unsigned long)fs_buf.f_type);
1237 return -1;
1238}
1239
1240static int open_fuse_device(const char *dev)
1241{
1242 int fd;
1243
1244 drop_privs();
1245 fd = open(dev, O_RDWR);
1246 if (fd == -1) {
1247 if (errno == ENODEV || errno == ENOENT)/* check for ENOENT too, for the udev case */
1248 fprintf(stderr,
1249 "%s: fuse device %s not found. Kernel module not loaded?\n",
1250 progname, dev);
1251 else
1252 fprintf(stderr,
1253 "%s: failed to open %s: %s\n", progname, dev, strerror(errno));
1254 }
1255 restore_privs();
1256 return fd;
1257}
1258
1259static int mount_fuse(const char *mnt, const char *opts, const char **type)
1260{
1261 int res;
1262 int fd;
1263 const char *dev = getenv(FUSE_KERN_DEVICE_ENV) ?: FUSE_DEV;
1264 struct stat stbuf;
1265 char *source = NULL;
1266 char *mnt_opts = NULL;
1267 const char *real_mnt = mnt;
1268 int mountpoint_fd = -1;
1269 char *do_mount_opts = NULL;
1270 char *x_opts = NULL;
1271
1272 fd = open_fuse_device(dev);
1273 if (fd == -1)
1274 return -1;
1275
1276 drop_privs();
1277 read_conf();
1278
1279 if (getuid() != 0 && mount_max != -1) {
1280 int mount_count = count_fuse_fs();
1281 if (mount_count >= mount_max) {
1282 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
1283 goto fail_close_fd;
1284 }
1285 }
1286
1287 // Extract any options starting with "x-"
1288 res= extract_x_options(opts, &do_mount_opts, &x_opts);
1289 if (res)
1290 goto fail_close_fd;
1291
1292 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1293 restore_privs();
1294 if (res != -1)
1295 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
1296 fd, do_mount_opts, dev, &source, &mnt_opts);
1297
1298 if (mountpoint_fd != -1)
1299 close(mountpoint_fd);
1300
1301 if (res == -1)
1302 goto fail_close_fd;
1303
1304 res = chdir("/");
1305 if (res == -1) {
1306 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1307 goto fail_close_fd;
1308 }
1309
1310 if (geteuid() == 0) {
1311 if (x_opts && strlen(x_opts) > 0) {
1312 /*
1313 * Add back the options starting with "x-" to opts from
1314 * do_mount. +2 for ',' and '\0'
1315 */
1316 size_t mnt_opts_len = strlen(mnt_opts);
1317 size_t x_mnt_opts_len = mnt_opts_len+
1318 strlen(x_opts) + 2;
1319 char *x_mnt_opts = calloc(1, x_mnt_opts_len);
1320
1321 if (mnt_opts_len) {
1322 strcpy(x_mnt_opts, mnt_opts);
1323 strncat(x_mnt_opts, ",", 2);
1324 }
1325
1326 strncat(x_mnt_opts, x_opts,
1327 x_mnt_opts_len - mnt_opts_len - 2);
1328
1329 free(mnt_opts);
1330 mnt_opts = x_mnt_opts;
1331 }
1332
1333 res = add_mount(source, mnt, *type, mnt_opts);
1334 if (res == -1) {
1335 /* Can't clean up mount in a non-racy way */
1336 goto fail_close_fd;
1337 }
1338 }
1339
1340out_free:
1341 free(source);
1342 free(mnt_opts);
1343 free(x_opts);
1344 free(do_mount_opts);
1345
1346 return fd;
1347
1348fail_close_fd:
1349 close(fd);
1350 fd = -1;
1351 goto out_free;
1352}
1353
1354static int send_fd(int sock_fd, int fd)
1355{
1356 int retval;
1357 struct msghdr msg;
1358 struct cmsghdr *p_cmsg;
1359 struct iovec vec;
1360 size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
1361 int *p_fds;
1362 char sendchar = 0;
1363
1364 msg.msg_control = cmsgbuf;
1365 msg.msg_controllen = sizeof(cmsgbuf);
1366 p_cmsg = CMSG_FIRSTHDR(&msg);
1367 p_cmsg->cmsg_level = SOL_SOCKET;
1368 p_cmsg->cmsg_type = SCM_RIGHTS;
1369 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1370 p_fds = (int *) CMSG_DATA(p_cmsg);
1371 *p_fds = fd;
1372 msg.msg_controllen = p_cmsg->cmsg_len;
1373 msg.msg_name = NULL;
1374 msg.msg_namelen = 0;
1375 msg.msg_iov = &vec;
1376 msg.msg_iovlen = 1;
1377 msg.msg_flags = 0;
1378 /* "To pass file descriptors or credentials you need to send/read at
1379 * least one byte" (man 7 unix) */
1380 vec.iov_base = &sendchar;
1381 vec.iov_len = sizeof(sendchar);
1382 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1383 if (retval != 1) {
1384 perror("sending file descriptor");
1385 return -1;
1386 }
1387 return 0;
1388}
1389
1390/* Helper for should_auto_unmount
1391 *
1392 * fusermount typically has the s-bit set - initial open of `mnt` was as root
1393 * and got EACCESS as 'allow_other' was not specified.
1394 * Try opening `mnt` again with uid and guid of the calling process.
1395 */
1396static int recheck_ENOTCONN_as_owner(const char *mnt)
1397{
1398 int pid = fork();
1399 if(pid == -1) {
1400 perror("fuse: recheck_ENOTCONN_as_owner can't fork");
1401 _exit(EXIT_FAILURE);
1402 } else if(pid == 0) {
1403 uid_t uid = getuid();
1404 gid_t gid = getgid();
1405 if(setresgid(gid, gid, gid) == -1) {
1406 perror("fuse: can't set resgid");
1407 _exit(EXIT_FAILURE);
1408 }
1409 if(setresuid(uid, uid, uid) == -1) {
1410 perror("fuse: can't set resuid");
1411 _exit(EXIT_FAILURE);
1412 }
1413
1414 int fd = open(mnt, O_RDONLY);
1415 if(fd == -1 && errno == ENOTCONN)
1416 _exit(EXIT_SUCCESS);
1417 else
1418 _exit(EXIT_FAILURE);
1419 } else {
1420 int status;
1421 int res = waitpid(pid, &status, 0);
1422 if (res == -1) {
1423 perror("fuse: waiting for child failed");
1424 _exit(EXIT_FAILURE);
1425 }
1426 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
1427 }
1428}
1429
1430/* The parent fuse process has died: decide whether to auto_unmount.
1431 *
1432 * In the normal case (umount or fusermount -u), the filesystem
1433 * has already been unmounted. If we simply unmount again we can
1434 * cause problems with stacked mounts (e.g. autofs).
1435 *
1436 * So we unmount here only in abnormal case where fuse process has
1437 * died without unmount happening. To detect this, we first look in
1438 * the mount table to make sure the mountpoint is still mounted and
1439 * has proper type. If so, we then see if opening the mount dir is
1440 * returning 'Transport endpoint is not connected'.
1441 *
1442 * The order of these is important, because if autofs is in use,
1443 * opening the dir to check for ENOTCONN will cause a new mount
1444 * in the normal case where filesystem has been unmounted cleanly.
1445 */
1446static int should_auto_unmount(const char *mnt, const char *type)
1447{
1448 char *copy;
1449 const char *last;
1450 int result = 0;
1451 int fd;
1452
1453 copy = strdup(mnt);
1454 if (copy == NULL) {
1455 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1456 return 0;
1457 }
1458
1459 if (chdir_to_parent(copy, &last) == -1)
1460 goto out;
1461 if (check_is_mount(last, mnt, type) == -1)
1462 goto out;
1463
1464 fd = open(mnt, O_RDONLY);
1465
1466 if (fd != -1) {
1467 close(fd);
1468 } else {
1469 switch(errno) {
1470 case ENOTCONN:
1471 result = 1;
1472 break;
1473 case EACCES:
1474 result = recheck_ENOTCONN_as_owner(mnt);
1475 break;
1476 default:
1477 result = 0;
1478 break;
1479 }
1480 }
1481out:
1482 free(copy);
1483 return result;
1484}
1485
1486static void usage(void)
1487{
1488 printf("%s: [options] mountpoint\n"
1489 "Options:\n"
1490 " -h print help\n"
1491 " -V print version\n"
1492 " -o opt[,opt...] mount options\n"
1493 " -u unmount\n"
1494 " -q quiet\n"
1495 " -z lazy unmount\n",
1496 progname);
1497 exit(1);
1498}
1499
1500static void show_version(void)
1501{
1502 printf("fusermount3 version: %s\n", PACKAGE_VERSION);
1503 exit(0);
1504}
1505
1506static void close_range_loop(int min_fd, int max_fd, int cfd)
1507{
1508 for (int fd = min_fd; fd <= max_fd; fd++)
1509 if (fd != cfd)
1510 close(fd);
1511}
1512
1513/*
1514 * Close all inherited fds that are not needed
1515 * Ideally these wouldn't come up at all, applications should better
1516 * use FD_CLOEXEC / O_CLOEXEC
1517 */
1518static int close_inherited_fds(int cfd)
1519{
1520 int rc = -1;
1521 int nullfd;
1522
1523 /* We can't even report an error */
1524 if (cfd <= STDERR_FILENO)
1525 return -EINVAL;
1526
1527#ifdef HAVE_CLOSE_RANGE
1528 if (cfd < STDERR_FILENO + 2) {
1529 close_range_loop(STDERR_FILENO + 1, cfd - 1, cfd);
1530 } else {
1531 rc = close_range(STDERR_FILENO + 1, cfd - 1, 0);
1532 if (rc < 0)
1533 goto fallback;
1534 }
1535
1536 /* Close high range */
1537 rc = close_range(cfd + 1, ~0U, 0);
1538#else
1539 goto fallback; /* make use of fallback to avoid compiler warnings */
1540#endif
1541
1542fallback:
1543 if (rc < 0) {
1544 int max_fd = sysconf(_SC_OPEN_MAX) - 1;
1545
1546 close_range_loop(STDERR_FILENO + 1, max_fd, cfd);
1547 }
1548
1549 nullfd = open("/dev/null", O_RDWR);
1550 if (nullfd < 0) {
1551 perror("fusermount: cannot open /dev/null");
1552 return -errno;
1553 }
1554
1555 /* Redirect stdin, stdout, stderr to /dev/null */
1556 dup2(nullfd, STDIN_FILENO);
1557 dup2(nullfd, STDOUT_FILENO);
1558 dup2(nullfd, STDERR_FILENO);
1559 if (nullfd > STDERR_FILENO)
1560 close(nullfd);
1561
1562 return 0;
1563}
1564
1565int main(int argc, char *argv[])
1566{
1567 sigset_t sigset;
1568 int ch;
1569 int fd;
1570 int res;
1571 char *origmnt;
1572 char *mnt;
1573 static int unmount = 0;
1574 static int lazy = 0;
1575 static int quiet = 0;
1576 char *commfd = NULL;
1577 long cfd;
1578 const char *opts = "";
1579 const char *type = NULL;
1580 int setup_auto_unmount_only = 0;
1581
1582 static const struct option long_opts[] = {
1583 {"unmount", no_argument, NULL, 'u'},
1584 {"lazy", no_argument, NULL, 'z'},
1585 {"quiet", no_argument, NULL, 'q'},
1586 {"help", no_argument, NULL, 'h'},
1587 {"version", no_argument, NULL, 'V'},
1588 {"options", required_argument, NULL, 'o'},
1589 // Note: auto-unmount and comm-fd don't have short versions.
1590 // They'ne meant for internal use by mount.c
1591 {"auto-unmount", no_argument, NULL, 'U'},
1592 {"comm-fd", required_argument, NULL, 'c'},
1593 {0, 0, 0, 0}};
1594
1595 progname = strdup(argc > 0 ? argv[0] : "fusermount");
1596 if (progname == NULL) {
1597 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1598 exit(1);
1599 }
1600
1601 while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
1602 NULL)) != -1) {
1603 switch (ch) {
1604 case 'h':
1605 usage();
1606 break;
1607
1608 case 'V':
1609 show_version();
1610 break;
1611
1612 case 'o':
1613 opts = optarg;
1614 break;
1615
1616 case 'u':
1617 unmount = 1;
1618 break;
1619 case 'U':
1620 unmount = 1;
1621 auto_unmount = 1;
1622 setup_auto_unmount_only = 1;
1623 break;
1624 case 'c':
1625 commfd = optarg;
1626 break;
1627 case 'z':
1628 lazy = 1;
1629 break;
1630
1631 case 'q':
1632 quiet = 1;
1633 break;
1634
1635 default:
1636 exit(1);
1637 }
1638 }
1639
1640 if (lazy && !unmount) {
1641 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1642 exit(1);
1643 }
1644
1645 if (optind >= argc) {
1646 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1647 exit(1);
1648 } else if (argc > optind + 1) {
1649 fprintf(stderr, "%s: extra arguments after the mountpoint\n",
1650 progname);
1651 exit(1);
1652 }
1653
1654 origmnt = argv[optind];
1655
1656 drop_privs();
1657 mnt = fuse_mnt_resolve_path(progname, origmnt);
1658 if (mnt != NULL) {
1659 res = chdir("/");
1660 if (res == -1) {
1661 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1662 goto err_out;
1663 }
1664 }
1665 restore_privs();
1666 if (mnt == NULL)
1667 exit(1);
1668
1669 umask(033);
1670 if (!setup_auto_unmount_only && unmount)
1671 goto do_unmount;
1672
1673 if(commfd == NULL)
1674 commfd = getenv(FUSE_COMMFD_ENV);
1675 if (commfd == NULL) {
1676 fprintf(stderr, "%s: old style mounting not supported\n",
1677 progname);
1678 goto err_out;
1679 }
1680
1681 res = libfuse_strtol(commfd, &cfd);
1682 if (res) {
1683 fprintf(stderr,
1684 "%s: invalid _FUSE_COMMFD: %s\n",
1685 progname, commfd);
1686 goto err_out;
1687
1688 }
1689
1690 {
1691 struct stat statbuf;
1692 fstat(cfd, &statbuf);
1693 if(!S_ISSOCK(statbuf.st_mode)) {
1694 fprintf(stderr,
1695 "%s: file descriptor %li is not a socket, can't send fuse fd\n",
1696 progname, cfd);
1697 goto err_out;
1698 }
1699 }
1700
1701 if (setup_auto_unmount_only)
1702 goto wait_for_auto_unmount;
1703
1704 fd = mount_fuse(mnt, opts, &type);
1705 if (fd == -1)
1706 goto err_out;
1707
1708 res = send_fd(cfd, fd);
1709 if (res != 0) {
1710 umount2(mnt, MNT_DETACH); /* lazy umount */
1711 goto err_out;
1712 }
1713 close(fd);
1714
1715 if (!auto_unmount) {
1716 free(mnt);
1717 free((void*) type);
1718 return 0;
1719 }
1720
1721wait_for_auto_unmount:
1722 /* Become a daemon and wait for the parent to exit or die.
1723 ie For the control socket to get closed.
1724 Btw, we don't want to use daemon() function here because
1725 it forks and messes with the file descriptors. */
1726
1727 res = close_inherited_fds(cfd);
1728 if (res < 0)
1729 exit(EXIT_FAILURE);
1730
1731 setsid();
1732 res = chdir("/");
1733 if (res == -1) {
1734 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1735 goto err_out;
1736 }
1737
1738 sigfillset(&sigset);
1739 sigprocmask(SIG_BLOCK, &sigset, NULL);
1740
1741 lazy = 1;
1742 quiet = 1;
1743
1744 while (1) {
1745 unsigned char buf[16];
1746 int n = recv(cfd, buf, sizeof(buf), 0);
1747 if (!n)
1748 break;
1749
1750 if (n < 0) {
1751 if (errno == EINTR)
1752 continue;
1753 break;
1754 }
1755 }
1756
1757 if (!should_auto_unmount(mnt, type)) {
1758 goto success_out;
1759 }
1760
1761do_unmount:
1762 if (geteuid() == 0)
1763 res = unmount_fuse(mnt, quiet, lazy);
1764 else {
1765 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
1766 if (res == -1 && !quiet)
1767 fprintf(stderr,
1768 "%s: failed to unmount %s: %s\n",
1769 progname, mnt, strerror(errno));
1770 }
1771 if (res == -1)
1772 goto err_out;
1773
1774success_out:
1775 free((void*) type);
1776 free(mnt);
1777 return 0;
1778
1779err_out:
1780 free((void*) type);
1781 free(mnt);
1782 exit(1);
1783}
fuse-3.18.2/doc/html/util_2fusermount_8c_source.html0000644000175000017500000101416515156613443021434 0ustar berndbernd libfuse: util/fusermount.c Source File
libfuse
fusermount.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8/* This program does the mounting and unmounting of FUSE filesystems */
9
10#define _GNU_SOURCE /* for clone,strchrnul and close_range */
11#include "fuse_config.h"
12#include "mount_util.h"
13#include "util.h"
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <ctype.h>
19#include <unistd.h>
20#include <getopt.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <pwd.h>
24#include <paths.h>
25#include <mntent.h>
26#include <sys/wait.h>
27#include <sys/stat.h>
28#include <sys/param.h>
29
30#include "fuse_mount_compat.h"
31
32#include <sys/fsuid.h>
33#include <sys/socket.h>
34#include <sys/utsname.h>
35#include <sched.h>
36#include <stdbool.h>
37#include <sys/vfs.h>
38
39#if defined HAVE_CLOSE_RANGE && defined linux
40#include <linux/close_range.h>
41#endif
42
43#if defined HAVE_LISTMOUNT
44#include <linux/mount.h>
45#include <syscall.h>
46#include <stdint.h>
47#endif
48
49#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
50#define FUSE_KERN_DEVICE_ENV "FUSE_KERN_DEVICE"
51
52#define FUSE_DEV "/dev/fuse"
53
54static const char *progname;
55
56static int user_allow_other = 0;
57static int mount_max = 1000;
58
59static int auto_unmount = 0;
60
61#ifdef GETMNTENT_NEEDS_UNESCAPING
62// Older versions of musl libc don't unescape entries in /etc/mtab
63
64// unescapes octal sequences like \040 in-place
65// That's ok, because unescaping can not extend the length of the string.
66static void unescape(char *buf) {
67 char *src = buf;
68 char *dest = buf;
69 while (1) {
70 char *next_src = strchrnul(src, '\\');
71 int offset = next_src - src;
72 memmove(dest, src, offset);
73 src = next_src;
74 dest += offset;
75
76 if(*src == '\0') {
77 *dest = *src;
78 return;
79 }
80 src++;
81
82 if('0' <= src[0] && src[0] < '2' &&
83 '0' <= src[1] && src[1] < '8' &&
84 '0' <= src[2] && src[2] < '8') {
85 *dest++ = (src[0] - '0') << 6
86 | (src[1] - '0') << 3
87 | (src[2] - '0') << 0;
88 src += 3;
89 } else if (src[0] == '\\') {
90 *dest++ = '\\';
91 src += 1;
92 } else {
93 *dest++ = '\\';
94 }
95 }
96}
97
98static struct mntent *GETMNTENT(FILE *stream)
99{
100 struct mntent *entp = getmntent(stream);
101 if(entp != NULL) {
102 unescape(entp->mnt_fsname);
103 unescape(entp->mnt_dir);
104 unescape(entp->mnt_type);
105 unescape(entp->mnt_opts);
106 }
107 return entp;
108}
109#else
110#define GETMNTENT getmntent
111#endif // GETMNTENT_NEEDS_UNESCAPING
112
113/*
114 * Take a ',' separated option string and extract "x-" options
115 */
116static int extract_x_options(const char *original, char **non_x_opts,
117 char **x_opts)
118{
119 size_t orig_len;
120 const char *opt, *opt_end;
121
122 orig_len = strlen(original) + 1;
123
124 *non_x_opts = calloc(1, orig_len);
125 *x_opts = calloc(1, orig_len);
126
127 size_t non_x_opts_len = orig_len;
128 size_t x_opts_len = orig_len;
129
130 if (*non_x_opts == NULL || *x_opts == NULL) {
131 fprintf(stderr, "%s: Failed to allocate %zuB.\n",
132 __func__, orig_len);
133 return -ENOMEM;
134 }
135
136 for (opt = original; opt < original + orig_len; opt = opt_end + 1) {
137 char *opt_buf;
138
139 opt_end = strchr(opt, ',');
140 if (opt_end == NULL)
141 opt_end = original + orig_len;
142
143 size_t opt_len = opt_end - opt;
144 size_t opt_len_left = orig_len - (opt - original);
145 size_t buf_len;
146 bool is_x_opts;
147
148 if (strncmp(opt, "x-", MIN(2, opt_len_left)) == 0) {
149 buf_len = x_opts_len;
150 is_x_opts = true;
151 opt_buf = *x_opts;
152 } else {
153 buf_len = non_x_opts_len;
154 is_x_opts = false;
155 opt_buf = *non_x_opts;
156 }
157
158 if (buf_len < orig_len) {
159 strncat(opt_buf, ",", 2);
160 buf_len -= 1;
161 }
162
163 /* omits ',' */
164 if ((ssize_t)(buf_len - opt_len) < 0) {
165 /* This would be a bug */
166 fprintf(stderr, "%s: no buf space left in copy, orig='%s'\n",
167 __func__, original);
168 return -EIO;
169 }
170
171 strncat(opt_buf, opt, opt_end - opt);
172 buf_len -= opt_len;
173
174 if (is_x_opts)
175 x_opts_len = buf_len;
176 else
177 non_x_opts_len = buf_len;
178 }
179
180 return 0;
181}
182
183static const char *get_user_name(void)
184{
185 struct passwd *pw = getpwuid(getuid());
186 if (pw != NULL && pw->pw_name != NULL)
187 return pw->pw_name;
188 else {
189 fprintf(stderr, "%s: could not determine username\n", progname);
190 return NULL;
191 }
192}
193
194static uid_t oldfsuid;
195static gid_t oldfsgid;
196
197static void drop_privs(void)
198{
199 if (getuid() != 0) {
200 oldfsuid = setfsuid(getuid());
201 oldfsgid = setfsgid(getgid());
202 }
203}
204
205static void restore_privs(void)
206{
207 if (getuid() != 0) {
208 setfsuid(oldfsuid);
209 setfsgid(oldfsgid);
210 }
211}
212
213#ifndef IGNORE_MTAB
214/*
215 * Make sure that /etc/mtab is checked and updated atomically
216 */
217static int lock_umount(void)
218{
219 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
220 int mtablock;
221 int res;
222 struct stat mtab_stat;
223
224 /* /etc/mtab could be a symlink to /proc/mounts */
225 if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
226 return -1;
227
228 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
229 if (mtablock == -1) {
230 fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
231 progname, strerror(errno));
232 return -1;
233 }
234 res = lockf(mtablock, F_LOCK, 0);
235 if (res < 0) {
236 fprintf(stderr, "%s: error getting lock: %s\n", progname,
237 strerror(errno));
238 close(mtablock);
239 return -1;
240 }
241
242 return mtablock;
243}
244
245static void unlock_umount(int mtablock)
246{
247 if (mtablock >= 0) {
248 int res;
249
250 res = lockf(mtablock, F_ULOCK, 0);
251 if (res < 0) {
252 fprintf(stderr, "%s: error releasing lock: %s\n",
253 progname, strerror(errno));
254 }
255 close(mtablock);
256 }
257}
258
259static int add_mount(const char *source, const char *mnt, const char *type,
260 const char *opts)
261{
262 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
263}
264
265static int may_unmount(const char *mnt, int quiet)
266{
267 struct mntent *entp;
268 FILE *fp;
269 const char *user = NULL;
270 char uidstr[32];
271 unsigned uidlen = 0;
272 int found;
273 const char *mtab = _PATH_MOUNTED;
274
275 user = get_user_name();
276 if (user == NULL)
277 return -1;
278
279 fp = setmntent(mtab, "r");
280 if (fp == NULL) {
281 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
282 strerror(errno));
283 return -1;
284 }
285
286 uidlen = sprintf(uidstr, "%u", getuid());
287
288 found = 0;
289 while ((entp = GETMNTENT(fp)) != NULL) {
290 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
291 (strcmp(entp->mnt_type, "fuse") == 0 ||
292 strcmp(entp->mnt_type, "fuseblk") == 0 ||
293 strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
294 strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
295 char *p = strstr(entp->mnt_opts, "user=");
296 if (p &&
297 (p == entp->mnt_opts || *(p-1) == ',') &&
298 strcmp(p + 5, user) == 0) {
299 found = 1;
300 break;
301 }
302 /* /etc/mtab is a link pointing to
303 /proc/mounts: */
304 else if ((p =
305 strstr(entp->mnt_opts, "user_id=")) &&
306 (p == entp->mnt_opts ||
307 *(p-1) == ',') &&
308 strncmp(p + 8, uidstr, uidlen) == 0 &&
309 (*(p+8+uidlen) == ',' ||
310 *(p+8+uidlen) == '\0')) {
311 found = 1;
312 break;
313 }
314 }
315 }
316 endmntent(fp);
317
318 if (!found) {
319 if (!quiet)
320 fprintf(stderr,
321 "%s: entry for %s not found in %s\n",
322 progname, mnt, mtab);
323 return -1;
324 }
325
326 return 0;
327}
328#endif
329
330/*
331 * Check whether the file specified in "fusermount3 -u" is really a
332 * mountpoint and not a symlink. This is necessary otherwise the user
333 * could move the mountpoint away and replace it with a symlink
334 * pointing to an arbitrary mount, thereby tricking fusermount3 into
335 * unmounting that (umount(2) will follow symlinks).
336 *
337 * This is the child process running in a separate mount namespace, so
338 * we don't mess with the global namespace and if the process is
339 * killed for any reason, mounts are automatically cleaned up.
340 *
341 * First make sure nothing is propagated back into the parent
342 * namespace by marking all mounts "private".
343 *
344 * Then bind mount parent onto a stable base where the user can't move
345 * it around.
346 *
347 * Finally check /proc/mounts for an entry matching the requested
348 * mountpoint. If it's found then we are OK, and the user can't move
349 * it around within the parent directory as rename() will return
350 * EBUSY. Be careful to ignore any mounts that existed before the
351 * bind.
352 */
353static int check_is_mount_child(void *p)
354{
355 const char **a = p;
356 const char *last = a[0];
357 const char *mnt = a[1];
358 const char *type = a[2];
359 int res;
360 const char *procmounts = "/proc/mounts";
361 int found;
362 FILE *fp;
363 struct mntent *entp;
364 int count;
365
366 res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
367 if (res == -1) {
368 fprintf(stderr, "%s: failed to mark mounts private: %s\n",
369 progname, strerror(errno));
370 return 1;
371 }
372
373 fp = setmntent(procmounts, "r");
374 if (fp == NULL) {
375 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
376 procmounts, strerror(errno));
377 return 1;
378 }
379
380 count = 0;
381 while (GETMNTENT(fp) != NULL)
382 count++;
383 endmntent(fp);
384
385 fp = setmntent(procmounts, "r");
386 if (fp == NULL) {
387 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
388 procmounts, strerror(errno));
389 return 1;
390 }
391
392 res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
393 if (res == -1) {
394 fprintf(stderr, "%s: failed to bind parent to /: %s\n",
395 progname, strerror(errno));
396 return 1;
397 }
398
399 found = 0;
400 while ((entp = GETMNTENT(fp)) != NULL) {
401 if (count > 0) {
402 count--;
403 continue;
404 }
405 if (entp->mnt_dir[0] == '/' &&
406 strcmp(entp->mnt_dir + 1, last) == 0 &&
407 (!type || strcmp(entp->mnt_type, type) == 0)) {
408 found = 1;
409 break;
410 }
411 }
412 endmntent(fp);
413
414 if (!found) {
415 fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
416 return 1;
417 }
418
419 return 0;
420}
421
422static pid_t clone_newns(void *a)
423{
424 char buf[131072];
425 char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
426
427#ifdef __ia64__
428 extern int __clone2(int (*fn)(void *),
429 void *child_stack_base, size_t stack_size,
430 int flags, void *arg, pid_t *ptid,
431 void *tls, pid_t *ctid);
432
433 return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
434 CLONE_NEWNS, a, NULL, NULL, NULL);
435#else
436 return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
437#endif
438}
439
440static int check_is_mount(const char *last, const char *mnt, const char *type)
441{
442 pid_t pid, p;
443 int status;
444 const char *a[3] = { last, mnt, type };
445
446 pid = clone_newns((void *) a);
447 if (pid == (pid_t) -1) {
448 fprintf(stderr, "%s: failed to clone namespace: %s\n",
449 progname, strerror(errno));
450 return -1;
451 }
452 p = waitpid(pid, &status, __WCLONE);
453 if (p == (pid_t) -1) {
454 fprintf(stderr, "%s: waitpid failed: %s\n",
455 progname, strerror(errno));
456 return -1;
457 }
458 if (!WIFEXITED(status)) {
459 fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
460 progname, status);
461 return -1;
462 }
463 if (WEXITSTATUS(status) != 0)
464 return -1;
465
466 return 0;
467}
468
469static int chdir_to_parent(char *copy, const char **lastp)
470{
471 char *tmp;
472 const char *parent;
473 char buf[65536];
474 int res;
475
476 tmp = strrchr(copy, '/');
477 if (tmp == NULL || tmp[1] == '\0') {
478 fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
479 progname, copy);
480 return -1;
481 }
482 if (tmp != copy) {
483 *tmp = '\0';
484 parent = copy;
485 *lastp = tmp + 1;
486 } else if (tmp[1] != '\0') {
487 *lastp = tmp + 1;
488 parent = "/";
489 } else {
490 *lastp = ".";
491 parent = "/";
492 }
493
494 res = chdir(parent);
495 if (res == -1) {
496 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
497 progname, parent, strerror(errno));
498 return -1;
499 }
500
501 if (getcwd(buf, sizeof(buf)) == NULL) {
502 fprintf(stderr, "%s: failed to obtain current directory: %s\n",
503 progname, strerror(errno));
504 return -1;
505 }
506 if (strcmp(buf, parent) != 0) {
507 fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
508 parent, buf);
509 return -1;
510
511 }
512
513 return 0;
514}
515
516#ifndef IGNORE_MTAB
517static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
518{
519 int res;
520 char *copy;
521 const char *last;
522 int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW;
523
524 if (getuid() != 0) {
525 res = may_unmount(mnt, quiet);
526 if (res == -1)
527 return -1;
528 }
529
530 copy = strdup(mnt);
531 if (copy == NULL) {
532 fprintf(stderr, "%s: failed to allocate memory\n", progname);
533 return -1;
534 }
535
536 drop_privs();
537 res = chdir_to_parent(copy, &last);
538 if (res == -1) {
539 restore_privs();
540 goto out;
541 }
542
543 res = umount2(last, umount_flags);
544 restore_privs();
545 if (res == -1 && !quiet) {
546 fprintf(stderr, "%s: failed to unmount %s: %s\n",
547 progname, mnt, strerror(errno));
548 }
549
550out:
551 free(copy);
552 if (res == -1)
553 return -1;
554
555 res = chdir("/");
556 if (res == -1) {
557 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
558 return -1;
559 }
560
561 return fuse_mnt_remove_mount(progname, mnt);
562}
563
564static int unmount_fuse(const char *mnt, int quiet, int lazy)
565{
566 int res;
567 int mtablock = lock_umount();
568
569 res = unmount_fuse_locked(mnt, quiet, lazy);
570 unlock_umount(mtablock);
571
572 return res;
573}
574
575static int count_fuse_fs_mtab(void)
576{
577 struct mntent *entp;
578 int count = 0;
579 const char *mtab = _PATH_MOUNTED;
580 FILE *fp = setmntent(mtab, "r");
581 if (fp == NULL) {
582 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
583 strerror(errno));
584 return -1;
585 }
586 while ((entp = GETMNTENT(fp)) != NULL) {
587 if (strcmp(entp->mnt_type, "fuse") == 0 ||
588 strncmp(entp->mnt_type, "fuse.", 5) == 0)
589 count ++;
590 }
591 endmntent(fp);
592 return count;
593}
594
595#ifdef HAVE_LISTMOUNT
596static int count_fuse_fs_ls_mnt(void)
597{
598 #define SMBUF_SIZE 1024
599 #define MNT_ID_LEN 128
600
601 int fuse_count = 0;
602 int n_mounts = 0;
603 int ret = 0;
604 uint64_t mnt_ids[MNT_ID_LEN];
605 unsigned char smbuf[SMBUF_SIZE];
606 struct mnt_id_req req = {
607 .size = sizeof(struct mnt_id_req),
608 };
609 struct statmount *sm;
610
611 for (;;) {
612 req.mnt_id = LSMT_ROOT;
613
614 n_mounts = syscall(SYS_listmount, &req, &mnt_ids, MNT_ID_LEN, 0);
615 if (n_mounts == -1) {
616 if (errno != ENOSYS) {
617 fprintf(stderr, "%s: failed to list mounts: %s\n", progname,
618 strerror(errno));
619 }
620 return -1;
621 }
622
623 for (int i = 0; i < n_mounts; i++) {
624 req.mnt_id = mnt_ids[i];
625 req.param = STATMOUNT_FS_TYPE;
626 ret = syscall(SYS_statmount, &req, &smbuf, SMBUF_SIZE, 0);
627 if (ret) {
628 if (errno == ENOENT)
629 continue;
630
631 fprintf(stderr, "%s: failed to stat mount %lld: %s\n", progname,
632 req.mnt_id, strerror(errno));
633 return -1;
634 }
635
636 sm = (struct statmount *)smbuf;
637 if (sm->mask & STATMOUNT_FS_TYPE &&
638 strcmp(&sm->str[sm->fs_type], "fuse") == 0)
639 fuse_count++;
640 }
641
642 if (n_mounts < MNT_ID_LEN)
643 break;
644 req.param = mnt_ids[MNT_ID_LEN - 1];
645 }
646 return fuse_count;
647}
648
649static int count_fuse_fs(void)
650{
651 int count = count_fuse_fs_ls_mnt();
652
653 return count >= 0 ? count : count_fuse_fs_mtab();
654}
655#else
656static int count_fuse_fs(void)
657{
658 return count_fuse_fs_mtab();
659}
660#endif
661
662#else /* IGNORE_MTAB */
663static int count_fuse_fs(void)
664{
665 return 0;
666}
667
668static int add_mount(const char *source, const char *mnt, const char *type,
669 const char *opts)
670{
671 (void) source;
672 (void) mnt;
673 (void) type;
674 (void) opts;
675 return 0;
676}
677
678static int unmount_fuse(const char *mnt, int quiet, int lazy)
679{
680 (void) quiet;
681 return fuse_mnt_umount(progname, mnt, mnt, lazy);
682}
683#endif /* IGNORE_MTAB */
684
685static void strip_line(char *line)
686{
687 char *s = strchr(line, '#');
688 if (s != NULL)
689 s[0] = '\0';
690 for (s = line + strlen(line) - 1;
691 s >= line && isspace((unsigned char) *s); s--);
692 s[1] = '\0';
693 for (s = line; isspace((unsigned char) *s); s++);
694 if (s != line)
695 memmove(line, s, strlen(s)+1);
696}
697
698static void parse_line(char *line, int linenum)
699{
700 int tmp;
701 if (strcmp(line, "user_allow_other") == 0)
702 user_allow_other = 1;
703 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
704 mount_max = tmp;
705 else if(line[0])
706 fprintf(stderr,
707 "%s: unknown parameter in %s at line %i: '%s'\n",
708 progname, FUSE_CONF, linenum, line);
709}
710
711static void read_conf(void)
712{
713 FILE *fp = fopen(FUSE_CONF, "r");
714 if (fp != NULL) {
715 int linenum = 1;
716 char line[256];
717 int isnewline = 1;
718 while (fgets(line, sizeof(line), fp) != NULL) {
719 if (isnewline) {
720 if (line[strlen(line)-1] == '\n') {
721 strip_line(line);
722 parse_line(line, linenum);
723 } else {
724 isnewline = 0;
725 }
726 } else if(line[strlen(line)-1] == '\n') {
727 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
728
729 isnewline = 1;
730 }
731 if (isnewline)
732 linenum ++;
733 }
734 if (!isnewline) {
735 fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
736
737 }
738 if (ferror(fp)) {
739 fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
740 exit(1);
741 }
742 fclose(fp);
743 } else if (errno != ENOENT) {
744 bool fatal = (errno != EACCES && errno != ELOOP &&
745 errno != ENAMETOOLONG && errno != ENOTDIR &&
746 errno != EOVERFLOW);
747 fprintf(stderr, "%s: failed to open %s: %s\n",
748 progname, FUSE_CONF, strerror(errno));
749 if (fatal)
750 exit(1);
751 }
752}
753
754static int begins_with(const char *s, const char *beg)
755{
756 if (strncmp(s, beg, strlen(beg)) == 0)
757 return 1;
758 else
759 return 0;
760}
761
762struct mount_flags {
763 const char *opt;
764 unsigned long flag;
765 int on;
766 int safe;
767};
768
769static struct mount_flags mount_flags[] = {
770 {"rw", MS_RDONLY, 0, 1},
771 {"ro", MS_RDONLY, 1, 1},
772 {"suid", MS_NOSUID, 0, 0},
773 {"nosuid", MS_NOSUID, 1, 1},
774 {"dev", MS_NODEV, 0, 0},
775 {"nodev", MS_NODEV, 1, 1},
776 {"exec", MS_NOEXEC, 0, 1},
777 {"noexec", MS_NOEXEC, 1, 1},
778 {"async", MS_SYNCHRONOUS, 0, 1},
779 {"sync", MS_SYNCHRONOUS, 1, 1},
780 {"atime", MS_NOATIME, 0, 1},
781 {"noatime", MS_NOATIME, 1, 1},
782 {"diratime", MS_NODIRATIME, 0, 1},
783 {"nodiratime", MS_NODIRATIME, 1, 1},
784 {"lazytime", MS_LAZYTIME, 1, 1},
785 {"nolazytime", MS_LAZYTIME, 0, 1},
786 {"relatime", MS_RELATIME, 1, 1},
787 {"norelatime", MS_RELATIME, 0, 1},
788 {"strictatime", MS_STRICTATIME, 1, 1},
789 {"nostrictatime", MS_STRICTATIME, 0, 1},
790 {"dirsync", MS_DIRSYNC, 1, 1},
791 {"symfollow", MS_NOSYMFOLLOW, 0, 1},
792 {"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
793 {NULL, 0, 0, 0}
794};
795
796static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
797{
798 int i;
799
800 for (i = 0; mount_flags[i].opt != NULL; i++) {
801 const char *opt = mount_flags[i].opt;
802 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
803 *on = mount_flags[i].on;
804 *flag = mount_flags[i].flag;
805 if (!mount_flags[i].safe && getuid() != 0) {
806 *flag = 0;
807 fprintf(stderr,
808 "%s: unsafe option %s ignored\n",
809 progname, opt);
810 }
811 return 1;
812 }
813 }
814 return 0;
815}
816
817static int add_option(char **optsp, const char *opt, unsigned expand)
818{
819 char *newopts;
820 if (*optsp == NULL)
821 newopts = strdup(opt);
822 else {
823 unsigned oldsize = strlen(*optsp);
824 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
825 newopts = (char *) realloc(*optsp, newsize);
826 if (newopts)
827 sprintf(newopts + oldsize, ",%s", opt);
828 }
829 if (newopts == NULL) {
830 fprintf(stderr, "%s: failed to allocate memory\n", progname);
831 return -1;
832 }
833 *optsp = newopts;
834 return 0;
835}
836
837static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
838{
839 int i;
840 int l;
841
842 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
843 return -1;
844
845 for (i = 0; mount_flags[i].opt != NULL; i++) {
846 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
847 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
848 return -1;
849 }
850
851 if (add_option(mnt_optsp, opts, 0) == -1)
852 return -1;
853 /* remove comma from end of opts*/
854 l = strlen(*mnt_optsp);
855 if ((*mnt_optsp)[l-1] == ',')
856 (*mnt_optsp)[l-1] = '\0';
857 if (getuid() != 0) {
858 const char *user = get_user_name();
859 if (user == NULL)
860 return -1;
861
862 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
863 return -1;
864 strcat(*mnt_optsp, user);
865 }
866 return 0;
867}
868
869static int opt_eq(const char *s, unsigned len, const char *opt)
870{
871 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
872 return 1;
873 else
874 return 0;
875}
876
877static int get_string_opt(const char *s, unsigned len, const char *opt,
878 char **val)
879{
880 int i;
881 unsigned opt_len = strlen(opt);
882 char *d;
883
884 if (*val)
885 free(*val);
886 *val = (char *) malloc(len - opt_len + 1);
887 if (!*val) {
888 fprintf(stderr, "%s: failed to allocate memory\n", progname);
889 return 0;
890 }
891
892 d = *val;
893 s += opt_len;
894 len -= opt_len;
895 for (i = 0; i < len; i++) {
896 if (s[i] == '\\' && i + 1 < len)
897 i++;
898 *d++ = s[i];
899 }
900 *d = '\0';
901 return 1;
902}
903
904/* The kernel silently truncates the "data" argument to PAGE_SIZE-1 characters.
905 * This can be dangerous if it e.g. truncates the option "group_id=1000" to
906 * "group_id=1".
907 * This wrapper detects this case and bails out with an error.
908 */
909static int mount_notrunc(const char *source, const char *target,
910 const char *filesystemtype, unsigned long mountflags,
911 const char *data) {
912 if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
913 fprintf(stderr, "%s: mount options too long\n", progname);
914 errno = EINVAL;
915 return -1;
916 }
917 return mount(source, target, filesystemtype, mountflags, data);
918}
919
920
921static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
922 int fd, const char *opts, const char *dev, char **sourcep,
923 char **mnt_optsp)
924{
925 int res;
926 int flags = MS_NOSUID | MS_NODEV;
927 char *optbuf;
928 char *mnt_opts = NULL;
929 const char *s;
930 char *d;
931 char *fsname = NULL;
932 char *subtype = NULL;
933 char *source = NULL;
934 char *type = NULL;
935 int blkdev = 0;
936
937 optbuf = (char *) malloc(strlen(opts) + 128);
938 if (!optbuf) {
939 fprintf(stderr, "%s: failed to allocate memory\n", progname);
940 return -1;
941 }
942
943 for (s = opts, d = optbuf; *s;) {
944 unsigned len;
945 const char *fsname_str = "fsname=";
946 const char *subtype_str = "subtype=";
947 bool escape_ok = begins_with(s, fsname_str) ||
948 begins_with(s, subtype_str);
949 for (len = 0; s[len]; len++) {
950 if (escape_ok && s[len] == '\\' && s[len + 1])
951 len++;
952 else if (s[len] == ',')
953 break;
954 }
955 if (begins_with(s, fsname_str)) {
956 if (!get_string_opt(s, len, fsname_str, &fsname))
957 goto err;
958 } else if (begins_with(s, subtype_str)) {
959 if (!get_string_opt(s, len, subtype_str, &subtype))
960 goto err;
961 } else if (opt_eq(s, len, "blkdev")) {
962 if (getuid() != 0) {
963 fprintf(stderr,
964 "%s: option blkdev is privileged\n",
965 progname);
966 goto err;
967 }
968 blkdev = 1;
969 } else if (opt_eq(s, len, "auto_unmount")) {
970 auto_unmount = 1;
971 } else if (!opt_eq(s, len, "nonempty") &&
972 !begins_with(s, "fd=") &&
973 !begins_with(s, "rootmode=") &&
974 !begins_with(s, "user_id=") &&
975 !begins_with(s, "group_id=")) {
976 int on;
977 int flag;
978 int skip_option = 0;
979 if (opt_eq(s, len, "large_read")) {
980 struct utsname utsname;
981 unsigned kmaj, kmin;
982 res = uname(&utsname);
983 if (res == 0 &&
984 sscanf(utsname.release, "%u.%u",
985 &kmaj, &kmin) == 2 &&
986 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
987 fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
988 skip_option = 1;
989 }
990 }
991 if (getuid() != 0 && !user_allow_other &&
992 (opt_eq(s, len, "allow_other") ||
993 opt_eq(s, len, "allow_root"))) {
994 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
995 goto err;
996 }
997 if (!skip_option) {
998 if (find_mount_flag(s, len, &on, &flag)) {
999 if (on)
1000 flags |= flag;
1001 else
1002 flags &= ~flag;
1003 } else if (opt_eq(s, len, "default_permissions") ||
1004 opt_eq(s, len, "allow_other") ||
1005 begins_with(s, "max_read=") ||
1006 begins_with(s, "blksize=")) {
1007 memcpy(d, s, len);
1008 d += len;
1009 *d++ = ',';
1010 } else {
1011 fprintf(stderr, "%s: unknown option '%.*s'\n", progname, len, s);
1012 exit(1);
1013 }
1014 }
1015 }
1016 s += len;
1017 if (*s)
1018 s++;
1019 }
1020 *d = '\0';
1021 res = get_mnt_opts(flags, optbuf, &mnt_opts);
1022 if (res == -1)
1023 goto err;
1024
1025 sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
1026 fd, rootmode, getuid(), getgid());
1027
1028 source = malloc((fsname ? strlen(fsname) : 0) +
1029 (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
1030
1031 type = malloc((subtype ? strlen(subtype) : 0) + 32);
1032 if (!type || !source) {
1033 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1034 goto err;
1035 }
1036
1037 if (subtype)
1038 sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
1039 else
1040 strcpy(type, blkdev ? "fuseblk" : "fuse");
1041
1042 if (fsname)
1043 strcpy(source, fsname);
1044 else
1045 strcpy(source, subtype ? subtype : dev);
1046
1047 res = mount_notrunc(source, mnt, type, flags, optbuf);
1048 if (res == -1 && errno == ENODEV && subtype) {
1049 /* Probably missing subtype support */
1050 strcpy(type, blkdev ? "fuseblk" : "fuse");
1051 if (fsname) {
1052 if (!blkdev)
1053 sprintf(source, "%s#%s", subtype, fsname);
1054 } else {
1055 strcpy(source, type);
1056 }
1057
1058 res = mount_notrunc(source, mnt, type, flags, optbuf);
1059 }
1060 if (res == -1 && errno == EINVAL) {
1061 /* It could be an old version not supporting group_id */
1062 sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
1063 fd, rootmode, getuid());
1064 res = mount_notrunc(source, mnt, type, flags, optbuf);
1065 }
1066 if (res == -1) {
1067 int errno_save = errno;
1068 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
1069 fprintf(stderr, "%s: 'fuseblk' support missing\n",
1070 progname);
1071 else
1072 fprintf(stderr, "%s: mount failed: %s\n", progname,
1073 strerror(errno_save));
1074 goto err;
1075 }
1076 *sourcep = source;
1077 *typep = type;
1078 *mnt_optsp = mnt_opts;
1079 free(fsname);
1080 free(optbuf);
1081
1082 return 0;
1083
1084err:
1085 free(fsname);
1086 free(subtype);
1087 free(source);
1088 free(type);
1089 free(mnt_opts);
1090 free(optbuf);
1091 return -1;
1092}
1093
1094static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
1095{
1096 int res;
1097 const char *mnt = *mntp;
1098 const char *origmnt = mnt;
1099 struct statfs fs_buf;
1100 size_t i;
1101
1102 res = lstat(mnt, stbuf);
1103 if (res == -1) {
1104 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1105 progname, mnt, strerror(errno));
1106 return -1;
1107 }
1108
1109 /* No permission checking is done for root */
1110 if (getuid() == 0)
1111 return 0;
1112
1113 if (S_ISDIR(stbuf->st_mode)) {
1114 res = chdir(mnt);
1115 if (res == -1) {
1116 fprintf(stderr,
1117 "%s: failed to chdir to mountpoint: %s\n",
1118 progname, strerror(errno));
1119 return -1;
1120 }
1121 mnt = *mntp = ".";
1122 res = lstat(mnt, stbuf);
1123 if (res == -1) {
1124 fprintf(stderr,
1125 "%s: failed to access mountpoint %s: %s\n",
1126 progname, origmnt, strerror(errno));
1127 return -1;
1128 }
1129
1130 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
1131 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
1132 progname, origmnt);
1133 return -1;
1134 }
1135
1136 res = access(mnt, W_OK);
1137 if (res == -1) {
1138 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
1139 progname, origmnt);
1140 return -1;
1141 }
1142 } else if (S_ISREG(stbuf->st_mode)) {
1143 static char procfile[256];
1144 *mountpoint_fd = open(mnt, O_WRONLY);
1145 if (*mountpoint_fd == -1) {
1146 fprintf(stderr, "%s: failed to open %s: %s\n",
1147 progname, mnt, strerror(errno));
1148 return -1;
1149 }
1150 res = fstat(*mountpoint_fd, stbuf);
1151 if (res == -1) {
1152 fprintf(stderr,
1153 "%s: failed to access mountpoint %s: %s\n",
1154 progname, mnt, strerror(errno));
1155 return -1;
1156 }
1157 if (!S_ISREG(stbuf->st_mode)) {
1158 fprintf(stderr,
1159 "%s: mountpoint %s is no longer a regular file\n",
1160 progname, mnt);
1161 return -1;
1162 }
1163
1164 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
1165 *mntp = procfile;
1166 } else {
1167 fprintf(stderr,
1168 "%s: mountpoint %s is not a directory or a regular file\n",
1169 progname, mnt);
1170 return -1;
1171 }
1172
1173 /* Do not permit mounting over anything in procfs - it has a couple
1174 * places to which we have "write access" without being supposed to be
1175 * able to just put anything we want there.
1176 * Luckily, without allow_other, we can't get other users to actually
1177 * use any fake information we try to put there anyway.
1178 * Use a whitelist to be safe. */
1179 if (statfs(*mntp, &fs_buf)) {
1180 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1181 progname, mnt, strerror(errno));
1182 return -1;
1183 }
1184
1185 /* Define permitted filesystems for the mount target. This was
1186 * originally the same list as used by the ecryptfs mount helper
1187 * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
1188 * but got expanded as we found more filesystems that needed to be
1189 * overlaid. */
1190 typeof(fs_buf.f_type) f_type_whitelist[] = {
1191 0x61756673 /* AUFS_SUPER_MAGIC */,
1192 0x00000187 /* AUTOFS_SUPER_MAGIC */,
1193 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
1194 0x9123683E /* BTRFS_SUPER_MAGIC */,
1195 0x00C36400 /* CEPH_SUPER_MAGIC */,
1196 0xFF534D42 /* CIFS_MAGIC_NUMBER */,
1197 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
1198 0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
1199 0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
1200 0xF2F52010 /* F2FS_SUPER_MAGIC */,
1201 0x65735546 /* FUSE_SUPER_MAGIC */,
1202 0x01161970 /* GFS2_MAGIC */,
1203 0x47504653 /* GPFS_SUPER_MAGIC */,
1204 0x0000482b /* HFSPLUS_SUPER_MAGIC */,
1205 0x000072B6 /* JFFS2_SUPER_MAGIC */,
1206 0x3153464A /* JFS_SUPER_MAGIC */,
1207 0x0BD00BD0 /* LL_SUPER_MAGIC */,
1208 0X00004D44 /* MSDOS_SUPER_MAGIC */,
1209 0x0000564C /* NCP_SUPER_MAGIC */,
1210 0x00006969 /* NFS_SUPER_MAGIC */,
1211 0x00003434 /* NILFS_SUPER_MAGIC */,
1212 0x5346544E /* NTFS_SB_MAGIC */,
1213 0x7366746E /* NTFS3_SUPER_MAGIC */,
1214 0x5346414f /* OPENAFS_SUPER_MAGIC */,
1215 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
1216 0xAAD7AAEA /* PANFS_SUPER_MAGIC */,
1217 0x52654973 /* REISERFS_SUPER_MAGIC */,
1218 0xFE534D42 /* SMB2_SUPER_MAGIC */,
1219 0x73717368 /* SQUASHFS_MAGIC */,
1220 0x01021994 /* TMPFS_MAGIC */,
1221 0x24051905 /* UBIFS_SUPER_MAGIC */,
1222 0x18031977 /* WEKAFS_SUPER_MAGIC */,
1223#if __SIZEOF_LONG__ > 4
1224 0x736675005346544e /* UFSD */,
1225#endif
1226 0x58465342 /* XFS_SB_MAGIC */,
1227 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
1228 0x858458f6 /* RAMFS_MAGIC */,
1229 };
1230 for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
1231 if (f_type_whitelist[i] == fs_buf.f_type)
1232 return 0;
1233 }
1234
1235 fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
1236 progname, (unsigned long)fs_buf.f_type);
1237 return -1;
1238}
1239
1240static int open_fuse_device(const char *dev)
1241{
1242 int fd;
1243
1244 drop_privs();
1245 fd = open(dev, O_RDWR);
1246 if (fd == -1) {
1247 if (errno == ENODEV || errno == ENOENT)/* check for ENOENT too, for the udev case */
1248 fprintf(stderr,
1249 "%s: fuse device %s not found. Kernel module not loaded?\n",
1250 progname, dev);
1251 else
1252 fprintf(stderr,
1253 "%s: failed to open %s: %s\n", progname, dev, strerror(errno));
1254 }
1255 restore_privs();
1256 return fd;
1257}
1258
1259static int mount_fuse(const char *mnt, const char *opts, const char **type)
1260{
1261 int res;
1262 int fd;
1263 const char *dev = getenv(FUSE_KERN_DEVICE_ENV) ?: FUSE_DEV;
1264 struct stat stbuf;
1265 char *source = NULL;
1266 char *mnt_opts = NULL;
1267 const char *real_mnt = mnt;
1268 int mountpoint_fd = -1;
1269 char *do_mount_opts = NULL;
1270 char *x_opts = NULL;
1271
1272 fd = open_fuse_device(dev);
1273 if (fd == -1)
1274 return -1;
1275
1276 drop_privs();
1277 read_conf();
1278
1279 if (getuid() != 0 && mount_max != -1) {
1280 int mount_count = count_fuse_fs();
1281 if (mount_count >= mount_max) {
1282 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
1283 goto fail_close_fd;
1284 }
1285 }
1286
1287 // Extract any options starting with "x-"
1288 res= extract_x_options(opts, &do_mount_opts, &x_opts);
1289 if (res)
1290 goto fail_close_fd;
1291
1292 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1293 restore_privs();
1294 if (res != -1)
1295 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
1296 fd, do_mount_opts, dev, &source, &mnt_opts);
1297
1298 if (mountpoint_fd != -1)
1299 close(mountpoint_fd);
1300
1301 if (res == -1)
1302 goto fail_close_fd;
1303
1304 res = chdir("/");
1305 if (res == -1) {
1306 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1307 goto fail_close_fd;
1308 }
1309
1310 if (geteuid() == 0) {
1311 if (x_opts && strlen(x_opts) > 0) {
1312 /*
1313 * Add back the options starting with "x-" to opts from
1314 * do_mount. +2 for ',' and '\0'
1315 */
1316 size_t mnt_opts_len = strlen(mnt_opts);
1317 size_t x_mnt_opts_len = mnt_opts_len+
1318 strlen(x_opts) + 2;
1319 char *x_mnt_opts = calloc(1, x_mnt_opts_len);
1320
1321 if (mnt_opts_len) {
1322 strcpy(x_mnt_opts, mnt_opts);
1323 strncat(x_mnt_opts, ",", 2);
1324 }
1325
1326 strncat(x_mnt_opts, x_opts,
1327 x_mnt_opts_len - mnt_opts_len - 2);
1328
1329 free(mnt_opts);
1330 mnt_opts = x_mnt_opts;
1331 }
1332
1333 res = add_mount(source, mnt, *type, mnt_opts);
1334 if (res == -1) {
1335 /* Can't clean up mount in a non-racy way */
1336 goto fail_close_fd;
1337 }
1338 }
1339
1340out_free:
1341 free(source);
1342 free(mnt_opts);
1343 free(x_opts);
1344 free(do_mount_opts);
1345
1346 return fd;
1347
1348fail_close_fd:
1349 close(fd);
1350 fd = -1;
1351 goto out_free;
1352}
1353
1354static int send_fd(int sock_fd, int fd)
1355{
1356 int retval;
1357 struct msghdr msg;
1358 struct cmsghdr *p_cmsg;
1359 struct iovec vec;
1360 size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
1361 int *p_fds;
1362 char sendchar = 0;
1363
1364 msg.msg_control = cmsgbuf;
1365 msg.msg_controllen = sizeof(cmsgbuf);
1366 p_cmsg = CMSG_FIRSTHDR(&msg);
1367 p_cmsg->cmsg_level = SOL_SOCKET;
1368 p_cmsg->cmsg_type = SCM_RIGHTS;
1369 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1370 p_fds = (int *) CMSG_DATA(p_cmsg);
1371 *p_fds = fd;
1372 msg.msg_controllen = p_cmsg->cmsg_len;
1373 msg.msg_name = NULL;
1374 msg.msg_namelen = 0;
1375 msg.msg_iov = &vec;
1376 msg.msg_iovlen = 1;
1377 msg.msg_flags = 0;
1378 /* "To pass file descriptors or credentials you need to send/read at
1379 * least one byte" (man 7 unix) */
1380 vec.iov_base = &sendchar;
1381 vec.iov_len = sizeof(sendchar);
1382 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1383 if (retval != 1) {
1384 perror("sending file descriptor");
1385 return -1;
1386 }
1387 return 0;
1388}
1389
1390/* Helper for should_auto_unmount
1391 *
1392 * fusermount typically has the s-bit set - initial open of `mnt` was as root
1393 * and got EACCESS as 'allow_other' was not specified.
1394 * Try opening `mnt` again with uid and guid of the calling process.
1395 */
1396static int recheck_ENOTCONN_as_owner(const char *mnt)
1397{
1398 int pid = fork();
1399 if(pid == -1) {
1400 perror("fuse: recheck_ENOTCONN_as_owner can't fork");
1401 _exit(EXIT_FAILURE);
1402 } else if(pid == 0) {
1403 uid_t uid = getuid();
1404 gid_t gid = getgid();
1405 if(setresgid(gid, gid, gid) == -1) {
1406 perror("fuse: can't set resgid");
1407 _exit(EXIT_FAILURE);
1408 }
1409 if(setresuid(uid, uid, uid) == -1) {
1410 perror("fuse: can't set resuid");
1411 _exit(EXIT_FAILURE);
1412 }
1413
1414 int fd = open(mnt, O_RDONLY);
1415 if(fd == -1 && errno == ENOTCONN)
1416 _exit(EXIT_SUCCESS);
1417 else
1418 _exit(EXIT_FAILURE);
1419 } else {
1420 int status;
1421 int res = waitpid(pid, &status, 0);
1422 if (res == -1) {
1423 perror("fuse: waiting for child failed");
1424 _exit(EXIT_FAILURE);
1425 }
1426 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
1427 }
1428}
1429
1430/* The parent fuse process has died: decide whether to auto_unmount.
1431 *
1432 * In the normal case (umount or fusermount -u), the filesystem
1433 * has already been unmounted. If we simply unmount again we can
1434 * cause problems with stacked mounts (e.g. autofs).
1435 *
1436 * So we unmount here only in abnormal case where fuse process has
1437 * died without unmount happening. To detect this, we first look in
1438 * the mount table to make sure the mountpoint is still mounted and
1439 * has proper type. If so, we then see if opening the mount dir is
1440 * returning 'Transport endpoint is not connected'.
1441 *
1442 * The order of these is important, because if autofs is in use,
1443 * opening the dir to check for ENOTCONN will cause a new mount
1444 * in the normal case where filesystem has been unmounted cleanly.
1445 */
1446static int should_auto_unmount(const char *mnt, const char *type)
1447{
1448 char *copy;
1449 const char *last;
1450 int result = 0;
1451 int fd;
1452
1453 copy = strdup(mnt);
1454 if (copy == NULL) {
1455 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1456 return 0;
1457 }
1458
1459 if (chdir_to_parent(copy, &last) == -1)
1460 goto out;
1461 if (check_is_mount(last, mnt, type) == -1)
1462 goto out;
1463
1464 fd = open(mnt, O_RDONLY);
1465
1466 if (fd != -1) {
1467 close(fd);
1468 } else {
1469 switch(errno) {
1470 case ENOTCONN:
1471 result = 1;
1472 break;
1473 case EACCES:
1474 result = recheck_ENOTCONN_as_owner(mnt);
1475 break;
1476 default:
1477 result = 0;
1478 break;
1479 }
1480 }
1481out:
1482 free(copy);
1483 return result;
1484}
1485
1486static void usage(void)
1487{
1488 printf("%s: [options] mountpoint\n"
1489 "Options:\n"
1490 " -h print help\n"
1491 " -V print version\n"
1492 " -o opt[,opt...] mount options\n"
1493 " -u unmount\n"
1494 " -q quiet\n"
1495 " -z lazy unmount\n",
1496 progname);
1497 exit(1);
1498}
1499
1500static void show_version(void)
1501{
1502 printf("fusermount3 version: %s\n", PACKAGE_VERSION);
1503 exit(0);
1504}
1505
1506static void close_range_loop(int min_fd, int max_fd, int cfd)
1507{
1508 for (int fd = min_fd; fd <= max_fd; fd++)
1509 if (fd != cfd)
1510 close(fd);
1511}
1512
1513/*
1514 * Close all inherited fds that are not needed
1515 * Ideally these wouldn't come up at all, applications should better
1516 * use FD_CLOEXEC / O_CLOEXEC
1517 */
1518static int close_inherited_fds(int cfd)
1519{
1520 int rc = -1;
1521 int nullfd;
1522
1523 /* We can't even report an error */
1524 if (cfd <= STDERR_FILENO)
1525 return -EINVAL;
1526
1527#ifdef HAVE_CLOSE_RANGE
1528 if (cfd < STDERR_FILENO + 2) {
1529 close_range_loop(STDERR_FILENO + 1, cfd - 1, cfd);
1530 } else {
1531 rc = close_range(STDERR_FILENO + 1, cfd - 1, 0);
1532 if (rc < 0)
1533 goto fallback;
1534 }
1535
1536 /* Close high range */
1537 rc = close_range(cfd + 1, ~0U, 0);
1538#else
1539 goto fallback; /* make use of fallback to avoid compiler warnings */
1540#endif
1541
1542fallback:
1543 if (rc < 0) {
1544 int max_fd = sysconf(_SC_OPEN_MAX) - 1;
1545
1546 close_range_loop(STDERR_FILENO + 1, max_fd, cfd);
1547 }
1548
1549 nullfd = open("/dev/null", O_RDWR);
1550 if (nullfd < 0) {
1551 perror("fusermount: cannot open /dev/null");
1552 return -errno;
1553 }
1554
1555 /* Redirect stdin, stdout, stderr to /dev/null */
1556 dup2(nullfd, STDIN_FILENO);
1557 dup2(nullfd, STDOUT_FILENO);
1558 dup2(nullfd, STDERR_FILENO);
1559 if (nullfd > STDERR_FILENO)
1560 close(nullfd);
1561
1562 return 0;
1563}
1564
1565int main(int argc, char *argv[])
1566{
1567 sigset_t sigset;
1568 int ch;
1569 int fd;
1570 int res;
1571 char *origmnt;
1572 char *mnt;
1573 static int unmount = 0;
1574 static int lazy = 0;
1575 static int quiet = 0;
1576 char *commfd = NULL;
1577 long cfd;
1578 const char *opts = "";
1579 const char *type = NULL;
1580 int setup_auto_unmount_only = 0;
1581
1582 static const struct option long_opts[] = {
1583 {"unmount", no_argument, NULL, 'u'},
1584 {"lazy", no_argument, NULL, 'z'},
1585 {"quiet", no_argument, NULL, 'q'},
1586 {"help", no_argument, NULL, 'h'},
1587 {"version", no_argument, NULL, 'V'},
1588 {"options", required_argument, NULL, 'o'},
1589 // Note: auto-unmount and comm-fd don't have short versions.
1590 // They'ne meant for internal use by mount.c
1591 {"auto-unmount", no_argument, NULL, 'U'},
1592 {"comm-fd", required_argument, NULL, 'c'},
1593 {0, 0, 0, 0}};
1594
1595 progname = strdup(argc > 0 ? argv[0] : "fusermount");
1596 if (progname == NULL) {
1597 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1598 exit(1);
1599 }
1600
1601 while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
1602 NULL)) != -1) {
1603 switch (ch) {
1604 case 'h':
1605 usage();
1606 break;
1607
1608 case 'V':
1609 show_version();
1610 break;
1611
1612 case 'o':
1613 opts = optarg;
1614 break;
1615
1616 case 'u':
1617 unmount = 1;
1618 break;
1619 case 'U':
1620 unmount = 1;
1621 auto_unmount = 1;
1622 setup_auto_unmount_only = 1;
1623 break;
1624 case 'c':
1625 commfd = optarg;
1626 break;
1627 case 'z':
1628 lazy = 1;
1629 break;
1630
1631 case 'q':
1632 quiet = 1;
1633 break;
1634
1635 default:
1636 exit(1);
1637 }
1638 }
1639
1640 if (lazy && !unmount) {
1641 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1642 exit(1);
1643 }
1644
1645 if (optind >= argc) {
1646 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1647 exit(1);
1648 } else if (argc > optind + 1) {
1649 fprintf(stderr, "%s: extra arguments after the mountpoint\n",
1650 progname);
1651 exit(1);
1652 }
1653
1654 origmnt = argv[optind];
1655
1656 drop_privs();
1657 mnt = fuse_mnt_resolve_path(progname, origmnt);
1658 if (mnt != NULL) {
1659 res = chdir("/");
1660 if (res == -1) {
1661 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1662 goto err_out;
1663 }
1664 }
1665 restore_privs();
1666 if (mnt == NULL)
1667 exit(1);
1668
1669 umask(033);
1670 if (!setup_auto_unmount_only && unmount)
1671 goto do_unmount;
1672
1673 if(commfd == NULL)
1674 commfd = getenv(FUSE_COMMFD_ENV);
1675 if (commfd == NULL) {
1676 fprintf(stderr, "%s: old style mounting not supported\n",
1677 progname);
1678 goto err_out;
1679 }
1680
1681 res = libfuse_strtol(commfd, &cfd);
1682 if (res) {
1683 fprintf(stderr,
1684 "%s: invalid _FUSE_COMMFD: %s\n",
1685 progname, commfd);
1686 goto err_out;
1687
1688 }
1689
1690 {
1691 struct stat statbuf;
1692 fstat(cfd, &statbuf);
1693 if(!S_ISSOCK(statbuf.st_mode)) {
1694 fprintf(stderr,
1695 "%s: file descriptor %li is not a socket, can't send fuse fd\n",
1696 progname, cfd);
1697 goto err_out;
1698 }
1699 }
1700
1701 if (setup_auto_unmount_only)
1702 goto wait_for_auto_unmount;
1703
1704 fd = mount_fuse(mnt, opts, &type);
1705 if (fd == -1)
1706 goto err_out;
1707
1708 res = send_fd(cfd, fd);
1709 if (res != 0) {
1710 umount2(mnt, MNT_DETACH); /* lazy umount */
1711 goto err_out;
1712 }
1713 close(fd);
1714
1715 if (!auto_unmount) {
1716 free(mnt);
1717 free((void*) type);
1718 return 0;
1719 }
1720
1721wait_for_auto_unmount:
1722 /* Become a daemon and wait for the parent to exit or die.
1723 ie For the control socket to get closed.
1724 Btw, we don't want to use daemon() function here because
1725 it forks and messes with the file descriptors. */
1726
1727 res = close_inherited_fds(cfd);
1728 if (res < 0)
1729 exit(EXIT_FAILURE);
1730
1731 setsid();
1732 res = chdir("/");
1733 if (res == -1) {
1734 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1735 goto err_out;
1736 }
1737
1738 sigfillset(&sigset);
1739 sigprocmask(SIG_BLOCK, &sigset, NULL);
1740
1741 lazy = 1;
1742 quiet = 1;
1743
1744 while (1) {
1745 unsigned char buf[16];
1746 int n = recv(cfd, buf, sizeof(buf), 0);
1747 if (!n)
1748 break;
1749
1750 if (n < 0) {
1751 if (errno == EINTR)
1752 continue;
1753 break;
1754 }
1755 }
1756
1757 if (!should_auto_unmount(mnt, type)) {
1758 goto success_out;
1759 }
1760
1761do_unmount:
1762 if (geteuid() == 0)
1763 res = unmount_fuse(mnt, quiet, lazy);
1764 else {
1765 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
1766 if (res == -1 && !quiet)
1767 fprintf(stderr,
1768 "%s: failed to unmount %s: %s\n",
1769 progname, mnt, strerror(errno));
1770 }
1771 if (res == -1)
1772 goto err_out;
1773
1774success_out:
1775 free((void*) type);
1776 free(mnt);
1777 return 0;
1778
1779err_out:
1780 free((void*) type);
1781 free(mnt);
1782 exit(1);
1783}
fuse-3.18.2/doc/html/fuse-3_817_84_2util_2mount_8fuse_8c_source.html0000644000175000017500000021164715156613443023662 0ustar berndbernd libfuse: fuse-3.17.4/util/mount.fuse.c Source File
libfuse
mount.fuse.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file COPYING.
7*/
8
9#include "fuse_config.h"
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15#include <errno.h>
16#include <stdint.h>
17#include <fcntl.h>
18#include <pwd.h>
19#include <sys/wait.h>
20
21#ifdef linux
22#include <sys/prctl.h>
23#include <sys/syscall.h>
24#include <linux/capability.h>
25#include <linux/securebits.h>
26/* for 2.6 kernels */
27#if !defined(SECBIT_KEEP_CAPS) && defined(SECURE_KEEP_CAPS)
28#define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS))
29#endif
30#if !defined(SECBIT_KEEP_CAPS_LOCKED) && defined(SECURE_KEEP_CAPS_LOCKED)
31#define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED))
32#endif
33#if !defined(SECBIT_NO_SETUID_FIXUP) && defined(SECURE_NO_SETUID_FIXUP)
34#define SECBIT_NO_SETUID_FIXUP (issecure_mask(SECURE_NO_SETUID_FIXUP))
35#endif
36#if !defined(SECBIT_NO_SETUID_FIXUP_LOCKED) && defined(SECURE_NO_SETUID_FIXUP_LOCKED)
37#define SECBIT_NO_SETUID_FIXUP_LOCKED (issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED))
38#endif
39#if !defined(SECBIT_NOROOT) && defined(SECURE_NOROOT)
40#define SECBIT_NOROOT (issecure_mask(SECURE_NOROOT))
41#endif
42#if !defined(SECBIT_NOROOT_LOCKED) && defined(SECURE_NOROOT_LOCKED)
43#define SECBIT_NOROOT_LOCKED (issecure_mask(SECURE_NOROOT_LOCKED))
44#endif
45#endif
46/* linux < 3.5 */
47#ifndef PR_SET_NO_NEW_PRIVS
48#define PR_SET_NO_NEW_PRIVS 38
49#endif
50
51#include "fuse.h"
52
53static char *progname;
54
55static char *xstrdup(const char *s)
56{
57 char *t = strdup(s);
58 if (!t) {
59 fprintf(stderr, "%s: failed to allocate memory\n", progname);
60 exit(1);
61 }
62 return t;
63}
64
65static void *xrealloc(void *oldptr, size_t size)
66{
67 void *ptr = realloc(oldptr, size);
68 if (!ptr) {
69 fprintf(stderr, "%s: failed to allocate memory\n", progname);
70 exit(1);
71 }
72 return ptr;
73}
74
75static void add_arg(char **cmdp, const char *opt)
76{
77 size_t optlen = strlen(opt);
78 size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
79 if (optlen >= (SIZE_MAX - cmdlen - 4)/4) {
80 fprintf(stderr, "%s: argument too long\n", progname);
81 exit(1);
82 }
83 char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
84 char *s;
85 s = cmd + cmdlen;
86 if (*cmdp)
87 *s++ = ' ';
88
89 *s++ = '\'';
90 for (; *opt; opt++) {
91 if (*opt == '\'') {
92 *s++ = '\'';
93 *s++ = '\\';
94 *s++ = '\'';
95 *s++ = '\'';
96 } else
97 *s++ = *opt;
98 }
99 *s++ = '\'';
100 *s = '\0';
101 *cmdp = cmd;
102}
103
104static char *add_option(const char *opt, char *options)
105{
106 int oldlen = options ? strlen(options) : 0;
107
108 options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
109 if (!oldlen)
110 strcpy(options, opt);
111 else {
112 strcat(options, ",");
113 strcat(options, opt);
114 }
115 return options;
116}
117
118static int prepare_fuse_fd(const char *mountpoint, const char* subtype,
119 const char *options)
120{
121 int fuse_fd = -1;
122 int flags = -1;
123 int subtype_len = strlen(subtype) + 9;
124 char* options_copy = xrealloc(NULL, subtype_len);
125
126 snprintf(options_copy, subtype_len, "subtype=%s", subtype);
127 options_copy = add_option(options, options_copy);
128 fuse_fd = fuse_open_channel(mountpoint, options_copy);
129 if (fuse_fd == -1) {
130 exit(1);
131 }
132
133 flags = fcntl(fuse_fd, F_GETFD);
134 if (flags == -1 || fcntl(fuse_fd, F_SETFD, flags & ~FD_CLOEXEC) == 1) {
135 fprintf(stderr, "%s: Failed to clear CLOEXEC: %s\n",
136 progname, strerror(errno));
137 exit(1);
138 }
139
140 return fuse_fd;
141}
142
143#ifdef linux
144static uint64_t get_capabilities(void)
145{
146 /*
147 * This invokes the capset syscall directly to avoid the libcap
148 * dependency, which isn't really justified just for this.
149 */
150 struct __user_cap_header_struct header = {
151 .version = _LINUX_CAPABILITY_VERSION_3,
152 .pid = 0,
153 };
154 struct __user_cap_data_struct data[2];
155 memset(data, 0, sizeof(data));
156 if (syscall(SYS_capget, &header, data) == -1) {
157 fprintf(stderr, "%s: Failed to get capabilities: %s\n",
158 progname, strerror(errno));
159 exit(1);
160 }
161
162 return data[0].effective | ((uint64_t) data[1].effective << 32);
163}
164
165static void set_capabilities(uint64_t caps)
166{
167 /*
168 * This invokes the capset syscall directly to avoid the libcap
169 * dependency, which isn't really justified just for this.
170 */
171 struct __user_cap_header_struct header = {
172 .version = _LINUX_CAPABILITY_VERSION_3,
173 .pid = 0,
174 };
175 struct __user_cap_data_struct data[2];
176 memset(data, 0, sizeof(data));
177 data[0].effective = data[0].permitted = caps;
178 data[1].effective = data[1].permitted = caps >> 32;
179 if (syscall(SYS_capset, &header, data) == -1) {
180 fprintf(stderr, "%s: Failed to set capabilities: %s\n",
181 progname, strerror(errno));
182 exit(1);
183 }
184}
185
186static void drop_and_lock_capabilities(void)
187{
188 /* Set and lock securebits. */
189 if (prctl(PR_SET_SECUREBITS,
190 SECBIT_KEEP_CAPS_LOCKED |
191 SECBIT_NO_SETUID_FIXUP |
192 SECBIT_NO_SETUID_FIXUP_LOCKED |
193 SECBIT_NOROOT |
194 SECBIT_NOROOT_LOCKED) == -1) {
195 fprintf(stderr, "%s: Failed to set securebits %s\n",
196 progname, strerror(errno));
197 exit(1);
198 }
199
200 /* Clear the capability bounding set. */
201 int cap;
202 for (cap = 0; ; cap++) {
203 int cap_status = prctl(PR_CAPBSET_READ, cap);
204 if (cap_status == 0) {
205 continue;
206 }
207 if (cap_status == -1 && errno == EINVAL) {
208 break;
209 }
210
211 if (cap_status != 1) {
212 fprintf(stderr,
213 "%s: Failed to get capability %u: %s\n",
214 progname, cap, strerror(errno));
215 exit(1);
216 }
217 if (prctl(PR_CAPBSET_DROP, cap) == -1) {
218 fprintf(stderr,
219 "%s: Failed to drop capability %u: %s\n",
220 progname, cap, strerror(errno));
221 }
222 }
223
224 /* Drop capabilities. */
225 set_capabilities(0);
226
227 /* Prevent re-acquisition of privileges. */
228 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
229 fprintf(stderr, "%s: Failed to set no_new_privs: %s\n",
230 progname, strerror(errno));
231 exit(1);
232 }
233}
234#endif
235
236int main(int argc, char *argv[])
237{
238 char *type = NULL;
239 char *source;
240 char *dup_source = NULL;
241 const char *mountpoint;
242 char *basename;
243 char *options = NULL;
244 char *command = NULL;
245 char *setuid_name = NULL;
246 int i;
247 int dev = 1;
248 int suid = 1;
249 int pass_fuse_fd = 0;
250 int fuse_fd = 0;
251 int drop_privileges = 0;
252 char *dev_fd_mountpoint = NULL;
253
254 progname = argv[0];
255 basename = strrchr(argv[0], '/');
256 if (basename)
257 basename++;
258 else
259 basename = argv[0];
260
261 if (strncmp(basename, "mount.fuse.", 11) == 0)
262 type = basename + 11;
263 if (strncmp(basename, "mount.fuseblk.", 14) == 0)
264 type = basename + 14;
265
266 if (type && !type[0])
267 type = NULL;
268
269 if (argc < 3) {
270 fprintf(stderr,
271 "usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
272 progname, type ? "source" : "type#[source]");
273 exit(1);
274 }
275
276 source = argv[1];
277 if (!source[0])
278 source = NULL;
279
280 mountpoint = argv[2];
281
282 for (i = 3; i < argc; i++) {
283 if (strcmp(argv[i], "-v") == 0) {
284 continue;
285 } else if (strcmp(argv[i], "-t") == 0) {
286 i++;
287
288 if (i == argc) {
289 fprintf(stderr,
290 "%s: missing argument to option '-t'\n",
291 progname);
292 exit(1);
293 }
294 type = argv[i];
295 if (strncmp(type, "fuse.", 5) == 0)
296 type += 5;
297 else if (strncmp(type, "fuseblk.", 8) == 0)
298 type += 8;
299
300 if (!type[0]) {
301 fprintf(stderr,
302 "%s: empty type given as argument to option '-t'\n",
303 progname);
304 exit(1);
305 }
306 } else if (strcmp(argv[i], "-o") == 0) {
307 char *opts;
308 char *opt;
309 i++;
310 if (i == argc)
311 break;
312
313 opts = xstrdup(argv[i]);
314 opt = strtok(opts, ",");
315 while (opt) {
316 int j;
317 int ignore = 0;
318 const char *ignore_opts[] = { "",
319 "user",
320 "nofail",
321 "nouser",
322 "users",
323 "auto",
324 "noauto",
325 "_netdev",
326 NULL};
327 if (strncmp(opt, "setuid=", 7) == 0) {
328 setuid_name = xstrdup(opt + 7);
329 ignore = 1;
330 } else if (strcmp(opt,
331 "drop_privileges") == 0) {
332 pass_fuse_fd = 1;
333 drop_privileges = 1;
334 ignore = 1;
335 }
336 for (j = 0; ignore_opts[j]; j++)
337 if (strcmp(opt, ignore_opts[j]) == 0)
338 ignore = 1;
339
340 if (!ignore) {
341 if (strcmp(opt, "nodev") == 0)
342 dev = 0;
343 else if (strcmp(opt, "nosuid") == 0)
344 suid = 0;
345
346 options = add_option(opt, options);
347 }
348 opt = strtok(NULL, ",");
349 }
350 free(opts);
351 }
352 }
353
354 if (drop_privileges) {
355 uint64_t required_caps = CAP_TO_MASK(CAP_SETPCAP) |
356 CAP_TO_MASK(CAP_SYS_ADMIN);
357 if ((get_capabilities() & required_caps) != required_caps) {
358 fprintf(stderr, "%s: drop_privileges was requested, which launches the FUSE file system fully unprivileged. In order to do so %s must be run with privileges, please invoke with CAP_SYS_ADMIN and CAP_SETPCAP (e.g. as root).\n",
359 progname, progname);
360 exit(1);
361 }
362 }
363
364 if (dev)
365 options = add_option("dev", options);
366 if (suid)
367 options = add_option("suid", options);
368
369 if (!type) {
370 if (source) {
371 dup_source = xstrdup(source);
372 type = dup_source;
373 source = strchr(type, '#');
374 if (source)
375 *source++ = '\0';
376 if (!type[0]) {
377 fprintf(stderr, "%s: empty filesystem type\n",
378 progname);
379 exit(1);
380 }
381 } else {
382 fprintf(stderr, "%s: empty source\n", progname);
383 exit(1);
384 }
385 }
386
387 if (setuid_name && setuid_name[0]) {
388#ifdef linux
389 if (drop_privileges) {
390 /*
391 * Make securebits more permissive before calling
392 * setuid(). Specifically, if SECBIT_KEEP_CAPS and
393 * SECBIT_NO_SETUID_FIXUP weren't set, setuid() would
394 * have the side effect of dropping all capabilities,
395 * and we need to retain CAP_SETPCAP in order to drop
396 * all privileges before exec().
397 */
398 if (prctl(PR_SET_SECUREBITS,
399 SECBIT_KEEP_CAPS |
400 SECBIT_NO_SETUID_FIXUP) == -1) {
401 fprintf(stderr,
402 "%s: Failed to set securebits %s\n",
403 progname, strerror(errno));
404 exit(1);
405 }
406 }
407#endif
408
409 struct passwd *pwd = getpwnam(setuid_name);
410 if (!pwd || setgid(pwd->pw_gid) == -1 || setuid(pwd->pw_uid) == -1) {
411 fprintf(stderr, "%s: Failed to setuid to %s: %s\n",
412 progname, setuid_name, strerror(errno));
413 exit(1);
414 }
415 } else if (!getenv("HOME")) {
416 /* Hack to make filesystems work in the boot environment */
417 setenv("HOME", "/root", 0);
418 }
419
420 if (pass_fuse_fd) {
421 fuse_fd = prepare_fuse_fd(mountpoint, type, options);
422 dev_fd_mountpoint = xrealloc(NULL, 20);
423 snprintf(dev_fd_mountpoint, 20, "/dev/fd/%u", fuse_fd);
424 mountpoint = dev_fd_mountpoint;
425 }
426
427#ifdef linux
428 if (drop_privileges) {
429 drop_and_lock_capabilities();
430 }
431#endif
432 add_arg(&command, type);
433 if (source)
434 add_arg(&command, source);
435 add_arg(&command, mountpoint);
436 if (options) {
437 add_arg(&command, "-o");
438 add_arg(&command, options);
439 }
440
441 free(options);
442 free(dev_fd_mountpoint);
443 free(dup_source);
444 free(setuid_name);
445
446 execl("/bin/sh", "/bin/sh", "-c", command, NULL);
447 fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname,
448 strerror(errno));
449
450 if (pass_fuse_fd)
451 close(fuse_fd);
452 free(command);
453 return 1;
454}
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
fuse-3.18.2/doc/html/fuse-3_818_81_2util_2mount_8fuse_8c_source.html0000644000175000017500000021165015156613443023652 0ustar berndbernd libfuse: fuse-3.18.1/util/mount.fuse.c Source File
libfuse
mount.fuse.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
9#include "fuse_config.h"
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15#include <errno.h>
16#include <stdint.h>
17#include <fcntl.h>
18#include <pwd.h>
19#include <sys/wait.h>
20
21#ifdef linux
22#include <sys/prctl.h>
23#include <sys/syscall.h>
24#include <linux/capability.h>
25#include <linux/securebits.h>
26/* for 2.6 kernels */
27#if !defined(SECBIT_KEEP_CAPS) && defined(SECURE_KEEP_CAPS)
28#define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS))
29#endif
30#if !defined(SECBIT_KEEP_CAPS_LOCKED) && defined(SECURE_KEEP_CAPS_LOCKED)
31#define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED))
32#endif
33#if !defined(SECBIT_NO_SETUID_FIXUP) && defined(SECURE_NO_SETUID_FIXUP)
34#define SECBIT_NO_SETUID_FIXUP (issecure_mask(SECURE_NO_SETUID_FIXUP))
35#endif
36#if !defined(SECBIT_NO_SETUID_FIXUP_LOCKED) && defined(SECURE_NO_SETUID_FIXUP_LOCKED)
37#define SECBIT_NO_SETUID_FIXUP_LOCKED (issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED))
38#endif
39#if !defined(SECBIT_NOROOT) && defined(SECURE_NOROOT)
40#define SECBIT_NOROOT (issecure_mask(SECURE_NOROOT))
41#endif
42#if !defined(SECBIT_NOROOT_LOCKED) && defined(SECURE_NOROOT_LOCKED)
43#define SECBIT_NOROOT_LOCKED (issecure_mask(SECURE_NOROOT_LOCKED))
44#endif
45#endif
46/* linux < 3.5 */
47#ifndef PR_SET_NO_NEW_PRIVS
48#define PR_SET_NO_NEW_PRIVS 38
49#endif
50
51#include "fuse.h"
52
53static char *progname;
54
55static char *xstrdup(const char *s)
56{
57 char *t = strdup(s);
58 if (!t) {
59 fprintf(stderr, "%s: failed to allocate memory\n", progname);
60 exit(1);
61 }
62 return t;
63}
64
65static void *xrealloc(void *oldptr, size_t size)
66{
67 void *ptr = realloc(oldptr, size);
68 if (!ptr) {
69 fprintf(stderr, "%s: failed to allocate memory\n", progname);
70 exit(1);
71 }
72 return ptr;
73}
74
75static void add_arg(char **cmdp, const char *opt)
76{
77 size_t optlen = strlen(opt);
78 size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
79 if (optlen >= (SIZE_MAX - cmdlen - 4)/4) {
80 fprintf(stderr, "%s: argument too long\n", progname);
81 exit(1);
82 }
83 char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
84 char *s;
85 s = cmd + cmdlen;
86 if (*cmdp)
87 *s++ = ' ';
88
89 *s++ = '\'';
90 for (; *opt; opt++) {
91 if (*opt == '\'') {
92 *s++ = '\'';
93 *s++ = '\\';
94 *s++ = '\'';
95 *s++ = '\'';
96 } else
97 *s++ = *opt;
98 }
99 *s++ = '\'';
100 *s = '\0';
101 *cmdp = cmd;
102}
103
104static char *add_option(const char *opt, char *options)
105{
106 int oldlen = options ? strlen(options) : 0;
107
108 options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
109 if (!oldlen)
110 strcpy(options, opt);
111 else {
112 strcat(options, ",");
113 strcat(options, opt);
114 }
115 return options;
116}
117
118static int prepare_fuse_fd(const char *mountpoint, const char* subtype,
119 const char *options)
120{
121 int fuse_fd = -1;
122 int flags = -1;
123 int subtype_len = strlen(subtype) + 9;
124 char* options_copy = xrealloc(NULL, subtype_len);
125
126 snprintf(options_copy, subtype_len, "subtype=%s", subtype);
127 options_copy = add_option(options, options_copy);
128 fuse_fd = fuse_open_channel(mountpoint, options_copy);
129 if (fuse_fd == -1) {
130 exit(1);
131 }
132
133 flags = fcntl(fuse_fd, F_GETFD);
134 if (flags == -1 || fcntl(fuse_fd, F_SETFD, flags & ~FD_CLOEXEC) == 1) {
135 fprintf(stderr, "%s: Failed to clear CLOEXEC: %s\n",
136 progname, strerror(errno));
137 exit(1);
138 }
139
140 return fuse_fd;
141}
142
143#ifdef linux
144static uint64_t get_capabilities(void)
145{
146 /*
147 * This invokes the capset syscall directly to avoid the libcap
148 * dependency, which isn't really justified just for this.
149 */
150 struct __user_cap_header_struct header = {
151 .version = _LINUX_CAPABILITY_VERSION_3,
152 .pid = 0,
153 };
154 struct __user_cap_data_struct data[2];
155 memset(data, 0, sizeof(data));
156 if (syscall(SYS_capget, &header, data) == -1) {
157 fprintf(stderr, "%s: Failed to get capabilities: %s\n",
158 progname, strerror(errno));
159 exit(1);
160 }
161
162 return data[0].effective | ((uint64_t) data[1].effective << 32);
163}
164
165static void set_capabilities(uint64_t caps)
166{
167 /*
168 * This invokes the capset syscall directly to avoid the libcap
169 * dependency, which isn't really justified just for this.
170 */
171 struct __user_cap_header_struct header = {
172 .version = _LINUX_CAPABILITY_VERSION_3,
173 .pid = 0,
174 };
175 struct __user_cap_data_struct data[2];
176 memset(data, 0, sizeof(data));
177 data[0].effective = data[0].permitted = caps;
178 data[1].effective = data[1].permitted = caps >> 32;
179 if (syscall(SYS_capset, &header, data) == -1) {
180 fprintf(stderr, "%s: Failed to set capabilities: %s\n",
181 progname, strerror(errno));
182 exit(1);
183 }
184}
185
186static void drop_and_lock_capabilities(void)
187{
188 /* Set and lock securebits. */
189 if (prctl(PR_SET_SECUREBITS,
190 SECBIT_KEEP_CAPS_LOCKED |
191 SECBIT_NO_SETUID_FIXUP |
192 SECBIT_NO_SETUID_FIXUP_LOCKED |
193 SECBIT_NOROOT |
194 SECBIT_NOROOT_LOCKED) == -1) {
195 fprintf(stderr, "%s: Failed to set securebits %s\n",
196 progname, strerror(errno));
197 exit(1);
198 }
199
200 /* Clear the capability bounding set. */
201 int cap;
202 for (cap = 0; ; cap++) {
203 int cap_status = prctl(PR_CAPBSET_READ, cap);
204 if (cap_status == 0) {
205 continue;
206 }
207 if (cap_status == -1 && errno == EINVAL) {
208 break;
209 }
210
211 if (cap_status != 1) {
212 fprintf(stderr,
213 "%s: Failed to get capability %u: %s\n",
214 progname, cap, strerror(errno));
215 exit(1);
216 }
217 if (prctl(PR_CAPBSET_DROP, cap) == -1) {
218 fprintf(stderr,
219 "%s: Failed to drop capability %u: %s\n",
220 progname, cap, strerror(errno));
221 }
222 }
223
224 /* Drop capabilities. */
225 set_capabilities(0);
226
227 /* Prevent re-acquisition of privileges. */
228 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
229 fprintf(stderr, "%s: Failed to set no_new_privs: %s\n",
230 progname, strerror(errno));
231 exit(1);
232 }
233}
234#endif
235
236int main(int argc, char *argv[])
237{
238 char *type = NULL;
239 char *source;
240 char *dup_source = NULL;
241 const char *mountpoint;
242 char *basename;
243 char *options = NULL;
244 char *command = NULL;
245 char *setuid_name = NULL;
246 int i;
247 int dev = 1;
248 int suid = 1;
249 int pass_fuse_fd = 0;
250 int fuse_fd = 0;
251 int drop_privileges = 0;
252 char *dev_fd_mountpoint = NULL;
253
254 progname = argv[0];
255 basename = strrchr(argv[0], '/');
256 if (basename)
257 basename++;
258 else
259 basename = argv[0];
260
261 if (strncmp(basename, "mount.fuse.", 11) == 0)
262 type = basename + 11;
263 if (strncmp(basename, "mount.fuseblk.", 14) == 0)
264 type = basename + 14;
265
266 if (type && !type[0])
267 type = NULL;
268
269 if (argc < 3) {
270 fprintf(stderr,
271 "usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
272 progname, type ? "source" : "type#[source]");
273 exit(1);
274 }
275
276 source = argv[1];
277 if (!source[0])
278 source = NULL;
279
280 mountpoint = argv[2];
281
282 for (i = 3; i < argc; i++) {
283 if (strcmp(argv[i], "-v") == 0) {
284 continue;
285 } else if (strcmp(argv[i], "-t") == 0) {
286 i++;
287
288 if (i == argc) {
289 fprintf(stderr,
290 "%s: missing argument to option '-t'\n",
291 progname);
292 exit(1);
293 }
294 type = argv[i];
295 if (strncmp(type, "fuse.", 5) == 0)
296 type += 5;
297 else if (strncmp(type, "fuseblk.", 8) == 0)
298 type += 8;
299
300 if (!type[0]) {
301 fprintf(stderr,
302 "%s: empty type given as argument to option '-t'\n",
303 progname);
304 exit(1);
305 }
306 } else if (strcmp(argv[i], "-o") == 0) {
307 char *opts;
308 char *opt;
309 i++;
310 if (i == argc)
311 break;
312
313 opts = xstrdup(argv[i]);
314 opt = strtok(opts, ",");
315 while (opt) {
316 int j;
317 int ignore = 0;
318 const char *ignore_opts[] = { "",
319 "user",
320 "nofail",
321 "nouser",
322 "users",
323 "auto",
324 "noauto",
325 "_netdev",
326 NULL};
327 if (strncmp(opt, "setuid=", 7) == 0) {
328 setuid_name = xstrdup(opt + 7);
329 ignore = 1;
330 } else if (strcmp(opt,
331 "drop_privileges") == 0) {
332 pass_fuse_fd = 1;
333 drop_privileges = 1;
334 ignore = 1;
335 }
336 for (j = 0; ignore_opts[j]; j++)
337 if (strcmp(opt, ignore_opts[j]) == 0)
338 ignore = 1;
339
340 if (!ignore) {
341 if (strcmp(opt, "nodev") == 0)
342 dev = 0;
343 else if (strcmp(opt, "nosuid") == 0)
344 suid = 0;
345
346 options = add_option(opt, options);
347 }
348 opt = strtok(NULL, ",");
349 }
350 free(opts);
351 }
352 }
353
354 if (drop_privileges) {
355 uint64_t required_caps = CAP_TO_MASK(CAP_SETPCAP) |
356 CAP_TO_MASK(CAP_SYS_ADMIN);
357 if ((get_capabilities() & required_caps) != required_caps) {
358 fprintf(stderr, "%s: drop_privileges was requested, which launches the FUSE file system fully unprivileged. In order to do so %s must be run with privileges, please invoke with CAP_SYS_ADMIN and CAP_SETPCAP (e.g. as root).\n",
359 progname, progname);
360 exit(1);
361 }
362 }
363
364 if (dev)
365 options = add_option("dev", options);
366 if (suid)
367 options = add_option("suid", options);
368
369 if (!type) {
370 if (source) {
371 dup_source = xstrdup(source);
372 type = dup_source;
373 source = strchr(type, '#');
374 if (source)
375 *source++ = '\0';
376 if (!type[0]) {
377 fprintf(stderr, "%s: empty filesystem type\n",
378 progname);
379 exit(1);
380 }
381 } else {
382 fprintf(stderr, "%s: empty source\n", progname);
383 exit(1);
384 }
385 }
386
387 if (setuid_name && setuid_name[0]) {
388#ifdef linux
389 if (drop_privileges) {
390 /*
391 * Make securebits more permissive before calling
392 * setuid(). Specifically, if SECBIT_KEEP_CAPS and
393 * SECBIT_NO_SETUID_FIXUP weren't set, setuid() would
394 * have the side effect of dropping all capabilities,
395 * and we need to retain CAP_SETPCAP in order to drop
396 * all privileges before exec().
397 */
398 if (prctl(PR_SET_SECUREBITS,
399 SECBIT_KEEP_CAPS |
400 SECBIT_NO_SETUID_FIXUP) == -1) {
401 fprintf(stderr,
402 "%s: Failed to set securebits %s\n",
403 progname, strerror(errno));
404 exit(1);
405 }
406 }
407#endif
408
409 struct passwd *pwd = getpwnam(setuid_name);
410 if (!pwd || setgid(pwd->pw_gid) == -1 || setuid(pwd->pw_uid) == -1) {
411 fprintf(stderr, "%s: Failed to setuid to %s: %s\n",
412 progname, setuid_name, strerror(errno));
413 exit(1);
414 }
415 } else if (!getenv("HOME")) {
416 /* Hack to make filesystems work in the boot environment */
417 setenv("HOME", "/root", 0);
418 }
419
420 if (pass_fuse_fd) {
421 fuse_fd = prepare_fuse_fd(mountpoint, type, options);
422 dev_fd_mountpoint = xrealloc(NULL, 20);
423 snprintf(dev_fd_mountpoint, 20, "/dev/fd/%u", fuse_fd);
424 mountpoint = dev_fd_mountpoint;
425 }
426
427#ifdef linux
428 if (drop_privileges) {
429 drop_and_lock_capabilities();
430 }
431#endif
432 add_arg(&command, type);
433 if (source)
434 add_arg(&command, source);
435 add_arg(&command, mountpoint);
436 if (options) {
437 add_arg(&command, "-o");
438 add_arg(&command, options);
439 }
440
441 free(options);
442 free(dev_fd_mountpoint);
443 free(dup_source);
444 free(setuid_name);
445
446 execl("/bin/sh", "/bin/sh", "-c", command, NULL);
447 fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname,
448 strerror(errno));
449
450 if (pass_fuse_fd)
451 close(fuse_fd);
452 free(command);
453 return 1;
454}
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
fuse-3.18.2/doc/html/util_2mount_8fuse_8c_source.html0000644000175000017500000021146515156613443021502 0ustar berndbernd libfuse: util/mount.fuse.c Source File
libfuse
mount.fuse.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
9#include "fuse_config.h"
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15#include <errno.h>
16#include <stdint.h>
17#include <fcntl.h>
18#include <pwd.h>
19#include <sys/wait.h>
20
21#ifdef linux
22#include <sys/prctl.h>
23#include <sys/syscall.h>
24#include <linux/capability.h>
25#include <linux/securebits.h>
26/* for 2.6 kernels */
27#if !defined(SECBIT_KEEP_CAPS) && defined(SECURE_KEEP_CAPS)
28#define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS))
29#endif
30#if !defined(SECBIT_KEEP_CAPS_LOCKED) && defined(SECURE_KEEP_CAPS_LOCKED)
31#define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED))
32#endif
33#if !defined(SECBIT_NO_SETUID_FIXUP) && defined(SECURE_NO_SETUID_FIXUP)
34#define SECBIT_NO_SETUID_FIXUP (issecure_mask(SECURE_NO_SETUID_FIXUP))
35#endif
36#if !defined(SECBIT_NO_SETUID_FIXUP_LOCKED) && defined(SECURE_NO_SETUID_FIXUP_LOCKED)
37#define SECBIT_NO_SETUID_FIXUP_LOCKED (issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED))
38#endif
39#if !defined(SECBIT_NOROOT) && defined(SECURE_NOROOT)
40#define SECBIT_NOROOT (issecure_mask(SECURE_NOROOT))
41#endif
42#if !defined(SECBIT_NOROOT_LOCKED) && defined(SECURE_NOROOT_LOCKED)
43#define SECBIT_NOROOT_LOCKED (issecure_mask(SECURE_NOROOT_LOCKED))
44#endif
45#endif
46/* linux < 3.5 */
47#ifndef PR_SET_NO_NEW_PRIVS
48#define PR_SET_NO_NEW_PRIVS 38
49#endif
50
51#include "fuse.h"
52
53static char *progname;
54
55static char *xstrdup(const char *s)
56{
57 char *t = strdup(s);
58 if (!t) {
59 fprintf(stderr, "%s: failed to allocate memory\n", progname);
60 exit(1);
61 }
62 return t;
63}
64
65static void *xrealloc(void *oldptr, size_t size)
66{
67 void *ptr = realloc(oldptr, size);
68 if (!ptr) {
69 fprintf(stderr, "%s: failed to allocate memory\n", progname);
70 exit(1);
71 }
72 return ptr;
73}
74
75static void add_arg(char **cmdp, const char *opt)
76{
77 size_t optlen = strlen(opt);
78 size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
79 if (optlen >= (SIZE_MAX - cmdlen - 4)/4) {
80 fprintf(stderr, "%s: argument too long\n", progname);
81 exit(1);
82 }
83 char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
84 char *s;
85 s = cmd + cmdlen;
86 if (*cmdp)
87 *s++ = ' ';
88
89 *s++ = '\'';
90 for (; *opt; opt++) {
91 if (*opt == '\'') {
92 *s++ = '\'';
93 *s++ = '\\';
94 *s++ = '\'';
95 *s++ = '\'';
96 } else
97 *s++ = *opt;
98 }
99 *s++ = '\'';
100 *s = '\0';
101 *cmdp = cmd;
102}
103
104static char *add_option(const char *opt, char *options)
105{
106 int oldlen = options ? strlen(options) : 0;
107
108 options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
109 if (!oldlen)
110 strcpy(options, opt);
111 else {
112 strcat(options, ",");
113 strcat(options, opt);
114 }
115 return options;
116}
117
118static int prepare_fuse_fd(const char *mountpoint, const char* subtype,
119 const char *options)
120{
121 int fuse_fd = -1;
122 int flags = -1;
123 int subtype_len = strlen(subtype) + 9;
124 char* options_copy = xrealloc(NULL, subtype_len);
125
126 snprintf(options_copy, subtype_len, "subtype=%s", subtype);
127 options_copy = add_option(options, options_copy);
128 fuse_fd = fuse_open_channel(mountpoint, options_copy);
129 if (fuse_fd == -1) {
130 exit(1);
131 }
132
133 flags = fcntl(fuse_fd, F_GETFD);
134 if (flags == -1 || fcntl(fuse_fd, F_SETFD, flags & ~FD_CLOEXEC) == 1) {
135 fprintf(stderr, "%s: Failed to clear CLOEXEC: %s\n",
136 progname, strerror(errno));
137 exit(1);
138 }
139
140 return fuse_fd;
141}
142
143#ifdef linux
144static uint64_t get_capabilities(void)
145{
146 /*
147 * This invokes the capset syscall directly to avoid the libcap
148 * dependency, which isn't really justified just for this.
149 */
150 struct __user_cap_header_struct header = {
151 .version = _LINUX_CAPABILITY_VERSION_3,
152 .pid = 0,
153 };
154 struct __user_cap_data_struct data[2];
155 memset(data, 0, sizeof(data));
156 if (syscall(SYS_capget, &header, data) == -1) {
157 fprintf(stderr, "%s: Failed to get capabilities: %s\n",
158 progname, strerror(errno));
159 exit(1);
160 }
161
162 return data[0].effective | ((uint64_t) data[1].effective << 32);
163}
164
165static void set_capabilities(uint64_t caps)
166{
167 /*
168 * This invokes the capset syscall directly to avoid the libcap
169 * dependency, which isn't really justified just for this.
170 */
171 struct __user_cap_header_struct header = {
172 .version = _LINUX_CAPABILITY_VERSION_3,
173 .pid = 0,
174 };
175 struct __user_cap_data_struct data[2];
176 memset(data, 0, sizeof(data));
177 data[0].effective = data[0].permitted = caps;
178 data[1].effective = data[1].permitted = caps >> 32;
179 if (syscall(SYS_capset, &header, data) == -1) {
180 fprintf(stderr, "%s: Failed to set capabilities: %s\n",
181 progname, strerror(errno));
182 exit(1);
183 }
184}
185
186static void drop_and_lock_capabilities(void)
187{
188 /* Set and lock securebits. */
189 if (prctl(PR_SET_SECUREBITS,
190 SECBIT_KEEP_CAPS_LOCKED |
191 SECBIT_NO_SETUID_FIXUP |
192 SECBIT_NO_SETUID_FIXUP_LOCKED |
193 SECBIT_NOROOT |
194 SECBIT_NOROOT_LOCKED) == -1) {
195 fprintf(stderr, "%s: Failed to set securebits %s\n",
196 progname, strerror(errno));
197 exit(1);
198 }
199
200 /* Clear the capability bounding set. */
201 int cap;
202 for (cap = 0; ; cap++) {
203 int cap_status = prctl(PR_CAPBSET_READ, cap);
204 if (cap_status == 0) {
205 continue;
206 }
207 if (cap_status == -1 && errno == EINVAL) {
208 break;
209 }
210
211 if (cap_status != 1) {
212 fprintf(stderr,
213 "%s: Failed to get capability %u: %s\n",
214 progname, cap, strerror(errno));
215 exit(1);
216 }
217 if (prctl(PR_CAPBSET_DROP, cap) == -1) {
218 fprintf(stderr,
219 "%s: Failed to drop capability %u: %s\n",
220 progname, cap, strerror(errno));
221 }
222 }
223
224 /* Drop capabilities. */
225 set_capabilities(0);
226
227 /* Prevent re-acquisition of privileges. */
228 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
229 fprintf(stderr, "%s: Failed to set no_new_privs: %s\n",
230 progname, strerror(errno));
231 exit(1);
232 }
233}
234#endif
235
236int main(int argc, char *argv[])
237{
238 char *type = NULL;
239 char *source;
240 char *dup_source = NULL;
241 const char *mountpoint;
242 char *basename;
243 char *options = NULL;
244 char *command = NULL;
245 char *setuid_name = NULL;
246 int i;
247 int dev = 1;
248 int suid = 1;
249 int pass_fuse_fd = 0;
250 int fuse_fd = 0;
251 int drop_privileges = 0;
252 char *dev_fd_mountpoint = NULL;
253
254 progname = argv[0];
255 basename = strrchr(argv[0], '/');
256 if (basename)
257 basename++;
258 else
259 basename = argv[0];
260
261 if (strncmp(basename, "mount.fuse.", 11) == 0)
262 type = basename + 11;
263 if (strncmp(basename, "mount.fuseblk.", 14) == 0)
264 type = basename + 14;
265
266 if (type && !type[0])
267 type = NULL;
268
269 if (argc < 3) {
270 fprintf(stderr,
271 "usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
272 progname, type ? "source" : "type#[source]");
273 exit(1);
274 }
275
276 source = argv[1];
277 if (!source[0])
278 source = NULL;
279
280 mountpoint = argv[2];
281
282 for (i = 3; i < argc; i++) {
283 if (strcmp(argv[i], "-v") == 0) {
284 continue;
285 } else if (strcmp(argv[i], "-t") == 0) {
286 i++;
287
288 if (i == argc) {
289 fprintf(stderr,
290 "%s: missing argument to option '-t'\n",
291 progname);
292 exit(1);
293 }
294 type = argv[i];
295 if (strncmp(type, "fuse.", 5) == 0)
296 type += 5;
297 else if (strncmp(type, "fuseblk.", 8) == 0)
298 type += 8;
299
300 if (!type[0]) {
301 fprintf(stderr,
302 "%s: empty type given as argument to option '-t'\n",
303 progname);
304 exit(1);
305 }
306 } else if (strcmp(argv[i], "-o") == 0) {
307 char *opts;
308 char *opt;
309 i++;
310 if (i == argc)
311 break;
312
313 opts = xstrdup(argv[i]);
314 opt = strtok(opts, ",");
315 while (opt) {
316 int j;
317 int ignore = 0;
318 const char *ignore_opts[] = { "",
319 "user",
320 "nofail",
321 "nouser",
322 "users",
323 "auto",
324 "noauto",
325 "_netdev",
326 NULL};
327 if (strncmp(opt, "setuid=", 7) == 0) {
328 setuid_name = xstrdup(opt + 7);
329 ignore = 1;
330 } else if (strcmp(opt,
331 "drop_privileges") == 0) {
332 pass_fuse_fd = 1;
333 drop_privileges = 1;
334 ignore = 1;
335 }
336 for (j = 0; ignore_opts[j]; j++)
337 if (strcmp(opt, ignore_opts[j]) == 0)
338 ignore = 1;
339
340 if (!ignore) {
341 if (strcmp(opt, "nodev") == 0)
342 dev = 0;
343 else if (strcmp(opt, "nosuid") == 0)
344 suid = 0;
345
346 options = add_option(opt, options);
347 }
348 opt = strtok(NULL, ",");
349 }
350 free(opts);
351 }
352 }
353
354 if (drop_privileges) {
355 uint64_t required_caps = CAP_TO_MASK(CAP_SETPCAP) |
356 CAP_TO_MASK(CAP_SYS_ADMIN);
357 if ((get_capabilities() & required_caps) != required_caps) {
358 fprintf(stderr, "%s: drop_privileges was requested, which launches the FUSE file system fully unprivileged. In order to do so %s must be run with privileges, please invoke with CAP_SYS_ADMIN and CAP_SETPCAP (e.g. as root).\n",
359 progname, progname);
360 exit(1);
361 }
362 }
363
364 if (dev)
365 options = add_option("dev", options);
366 if (suid)
367 options = add_option("suid", options);
368
369 if (!type) {
370 if (source) {
371 dup_source = xstrdup(source);
372 type = dup_source;
373 source = strchr(type, '#');
374 if (source)
375 *source++ = '\0';
376 if (!type[0]) {
377 fprintf(stderr, "%s: empty filesystem type\n",
378 progname);
379 exit(1);
380 }
381 } else {
382 fprintf(stderr, "%s: empty source\n", progname);
383 exit(1);
384 }
385 }
386
387 if (setuid_name && setuid_name[0]) {
388#ifdef linux
389 if (drop_privileges) {
390 /*
391 * Make securebits more permissive before calling
392 * setuid(). Specifically, if SECBIT_KEEP_CAPS and
393 * SECBIT_NO_SETUID_FIXUP weren't set, setuid() would
394 * have the side effect of dropping all capabilities,
395 * and we need to retain CAP_SETPCAP in order to drop
396 * all privileges before exec().
397 */
398 if (prctl(PR_SET_SECUREBITS,
399 SECBIT_KEEP_CAPS |
400 SECBIT_NO_SETUID_FIXUP) == -1) {
401 fprintf(stderr,
402 "%s: Failed to set securebits %s\n",
403 progname, strerror(errno));
404 exit(1);
405 }
406 }
407#endif
408
409 struct passwd *pwd = getpwnam(setuid_name);
410 if (!pwd || setgid(pwd->pw_gid) == -1 || setuid(pwd->pw_uid) == -1) {
411 fprintf(stderr, "%s: Failed to setuid to %s: %s\n",
412 progname, setuid_name, strerror(errno));
413 exit(1);
414 }
415 } else if (!getenv("HOME")) {
416 /* Hack to make filesystems work in the boot environment */
417 setenv("HOME", "/root", 0);
418 }
419
420 if (pass_fuse_fd) {
421 fuse_fd = prepare_fuse_fd(mountpoint, type, options);
422 dev_fd_mountpoint = xrealloc(NULL, 20);
423 snprintf(dev_fd_mountpoint, 20, "/dev/fd/%u", fuse_fd);
424 mountpoint = dev_fd_mountpoint;
425 }
426
427#ifdef linux
428 if (drop_privileges) {
429 drop_and_lock_capabilities();
430 }
431#endif
432 add_arg(&command, type);
433 if (source)
434 add_arg(&command, source);
435 add_arg(&command, mountpoint);
436 if (options) {
437 add_arg(&command, "-o");
438 add_arg(&command, options);
439 }
440
441 free(options);
442 free(dev_fd_mountpoint);
443 free(dup_source);
444 free(setuid_name);
445
446 execl("/bin/sh", "/bin/sh", "-c", command, NULL);
447 fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname,
448 strerror(errno));
449
450 if (pass_fuse_fd)
451 close(fuse_fd);
452 free(command);
453 return 1;
454}
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
fuse-3.18.2/doc/html/example_2cuse_8c.html0000644000175000017500000011750115156613443017257 0ustar berndbernd libfuse: example/cuse.c File Reference
libfuse
cuse.c File Reference
#include <cuse_lowlevel.h>
#include <fuse_opt.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This example demonstrates how to implement a character device in userspace ("CUSE"). This is only allowed for root. The character device should appear in /dev under the specified name. It can be tested with the cuse_client.c program.

Mount the file system with:

cuse -f --name=mydevice

You should now have a new /dev/mydevice character device. To "unmount" it, kill the "cuse" process.

To compile this example, run

gcc -Wall cuse.c `pkg-config fuse3 --cflags --libs` -o cuse

Source code

/*
CUSE example: Character device in Userspace
Copyright (C) 2008-2009 SUSE Linux Products GmbH
Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <cuse_lowlevel.h>
#include <fuse_opt.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ioctl.h"
static void *cusexmp_buf;
static size_t cusexmp_size;
static const char *usage =
"usage: cusexmp [options]\n"
"\n"
"options:\n"
" --help|-h print this help message\n"
" --maj=MAJ|-M MAJ device major number\n"
" --min=MIN|-m MIN device minor number\n"
" --name=NAME|-n NAME device name (mandatory)\n"
" -d -o debug enable debug output (implies -f)\n"
" -f foreground operation\n"
" -s disable multi-threaded operation\n"
"\n";
static int cusexmp_resize(size_t new_size)
{
void *new_buf;
if (new_size == cusexmp_size)
return 0;
new_buf = realloc(cusexmp_buf, new_size);
if (!new_buf && new_size)
return -ENOMEM;
if (new_size > cusexmp_size)
memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
cusexmp_buf = new_buf;
cusexmp_size = new_size;
return 0;
}
static int cusexmp_expand(size_t new_size)
{
if (new_size > cusexmp_size)
return cusexmp_resize(new_size);
return 0;
}
static void cusexmp_init(void *userdata, struct fuse_conn_info *conn)
{
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
{
fuse_reply_open(req, fi);
}
static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
struct fuse_file_info *fi)
{
(void)fi;
if (off >= cusexmp_size)
off = cusexmp_size;
if (size > cusexmp_size - off)
size = cusexmp_size - off;
fuse_reply_buf(req, cusexmp_buf + off, size);
}
static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
off_t off, struct fuse_file_info *fi)
{
(void)fi;
if (cusexmp_expand(off + size)) {
fuse_reply_err(req, ENOMEM);
return;
}
memcpy(cusexmp_buf + off, buf, size);
fuse_reply_write(req, size);
}
static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
size_t in_bufsz, size_t out_bufsz, int is_read)
{
const struct fioc_rw_arg *arg;
struct iovec in_iov[2], out_iov[3], iov[3];
size_t cur_size;
/* read in arg */
in_iov[0].iov_base = addr;
in_iov[0].iov_len = sizeof(*arg);
if (!in_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
return;
}
arg = in_buf;
in_buf += sizeof(*arg);
in_bufsz -= sizeof(*arg);
/* prepare size outputs */
out_iov[0].iov_base =
addr + offsetof(struct fioc_rw_arg, prev_size);
out_iov[0].iov_len = sizeof(arg->prev_size);
out_iov[1].iov_base =
addr + offsetof(struct fioc_rw_arg, new_size);
out_iov[1].iov_len = sizeof(arg->new_size);
/* prepare client buf */
if (is_read) {
out_iov[2].iov_base = arg->buf;
out_iov[2].iov_len = arg->size;
if (!out_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
return;
}
} else {
in_iov[1].iov_base = arg->buf;
in_iov[1].iov_len = arg->size;
if (arg->size && !in_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
return;
}
}
/* we're all set */
cur_size = cusexmp_size;
iov[0].iov_base = &cur_size;
iov[0].iov_len = sizeof(cur_size);
iov[1].iov_base = &cusexmp_size;
iov[1].iov_len = sizeof(cusexmp_size);
if (is_read) {
size_t off = arg->offset;
size_t size = arg->size;
if (off >= cusexmp_size)
off = cusexmp_size;
if (size > cusexmp_size - off)
size = cusexmp_size - off;
iov[2].iov_base = cusexmp_buf + off;
iov[2].iov_len = size;
fuse_reply_ioctl_iov(req, size, iov, 3);
} else {
if (cusexmp_expand(arg->offset + in_bufsz)) {
fuse_reply_err(req, ENOMEM);
return;
}
memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
}
}
static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
struct fuse_file_info *fi, unsigned flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{
int is_read = 0;
(void)fi;
if (flags & FUSE_IOCTL_COMPAT) {
fuse_reply_err(req, ENOSYS);
return;
}
switch (cmd) {
case FIOC_GET_SIZE:
if (!out_bufsz) {
struct iovec iov = { arg, sizeof(size_t) };
fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
} else
fuse_reply_ioctl(req, 0, &cusexmp_size,
sizeof(cusexmp_size));
break;
case FIOC_SET_SIZE:
if (!in_bufsz) {
struct iovec iov = { arg, sizeof(size_t) };
fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
} else {
cusexmp_resize(*(size_t *)in_buf);
fuse_reply_ioctl(req, 0, NULL, 0);
}
break;
case FIOC_READ:
is_read = 1;
/* fall through */
case FIOC_WRITE:
fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
break;
default:
fuse_reply_err(req, EINVAL);
}
}
struct cusexmp_param {
unsigned major;
unsigned minor;
char *dev_name;
int is_help;
};
#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
static const struct fuse_opt cusexmp_opts[] = {
CUSEXMP_OPT("-M %u", major),
CUSEXMP_OPT("--maj=%u", major),
CUSEXMP_OPT("-m %u", minor),
CUSEXMP_OPT("--min=%u", minor),
CUSEXMP_OPT("-n %s", dev_name),
CUSEXMP_OPT("--name=%s", dev_name),
FUSE_OPT_KEY("-h", 0),
FUSE_OPT_KEY("--help", 0),
};
static int cusexmp_process_arg(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct cusexmp_param *param = data;
(void)outargs;
(void)arg;
switch (key) {
case 0:
param->is_help = 1;
fprintf(stderr, "%s", usage);
return fuse_opt_add_arg(outargs, "-ho");
default:
return 1;
}
}
static const struct cuse_lowlevel_ops cusexmp_clop = {
.init = cusexmp_init,
.open = cusexmp_open,
.read = cusexmp_read,
.write = cusexmp_write,
.ioctl = cusexmp_ioctl,
};
int main(int argc, char **argv)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct cusexmp_param param = { 0, 0, NULL, 0 };
char dev_name[128] = "DEVNAME=";
const char *dev_info_argv[] = { dev_name };
struct cuse_info ci;
int ret = 1;
if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
printf("failed to parse option\n");
free(param.dev_name);
goto out;
}
if (!param.is_help) {
if (!param.dev_name) {
fprintf(stderr, "Error: device name missing\n");
goto out;
}
strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME="));
free(param.dev_name);
}
memset(&ci, 0, sizeof(ci));
ci.dev_major = param.major;
ci.dev_minor = param.minor;
ci.dev_info_argc = 1;
ci.dev_info_argv = dev_info_argv;
ci.flags = CUSE_UNRESTRICTED_IOCTL;
ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL);
out:
return ret;
}
#define FUSE_IOCTL_COMPAT
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt

Definition in file cuse.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2cuse_8c.html0000644000175000017500000011774215156613443021442 0ustar berndbernd libfuse: fuse-3.18.1/example/cuse.c File Reference
libfuse
cuse.c File Reference
#include <cuse_lowlevel.h>
#include <fuse_opt.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This example demonstrates how to implement a character device in userspace ("CUSE"). This is only allowed for root. The character device should appear in /dev under the specified name. It can be tested with the cuse_client.c program.

Mount the file system with:

cuse -f --name=mydevice

You should now have a new /dev/mydevice character device. To "unmount" it, kill the "cuse" process.

To compile this example, run

gcc -Wall cuse.c `pkg-config fuse3 --cflags --libs` -o cuse

Source code

/*
CUSE example: Character device in Userspace
Copyright (C) 2008-2009 SUSE Linux Products GmbH
Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <cuse_lowlevel.h>
#include <fuse_opt.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ioctl.h"
static void *cusexmp_buf;
static size_t cusexmp_size;
static const char *usage =
"usage: cusexmp [options]\n"
"\n"
"options:\n"
" --help|-h print this help message\n"
" --maj=MAJ|-M MAJ device major number\n"
" --min=MIN|-m MIN device minor number\n"
" --name=NAME|-n NAME device name (mandatory)\n"
" -d -o debug enable debug output (implies -f)\n"
" -f foreground operation\n"
" -s disable multi-threaded operation\n"
"\n";
static int cusexmp_resize(size_t new_size)
{
void *new_buf;
if (new_size == cusexmp_size)
return 0;
new_buf = realloc(cusexmp_buf, new_size);
if (!new_buf && new_size)
return -ENOMEM;
if (new_size > cusexmp_size)
memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
cusexmp_buf = new_buf;
cusexmp_size = new_size;
return 0;
}
static int cusexmp_expand(size_t new_size)
{
if (new_size > cusexmp_size)
return cusexmp_resize(new_size);
return 0;
}
static void cusexmp_init(void *userdata, struct fuse_conn_info *conn)
{
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
{
fuse_reply_open(req, fi);
}
static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
struct fuse_file_info *fi)
{
(void)fi;
if (off >= cusexmp_size)
off = cusexmp_size;
if (size > cusexmp_size - off)
size = cusexmp_size - off;
fuse_reply_buf(req, cusexmp_buf + off, size);
}
static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
off_t off, struct fuse_file_info *fi)
{
(void)fi;
if (cusexmp_expand(off + size)) {
fuse_reply_err(req, ENOMEM);
return;
}
memcpy(cusexmp_buf + off, buf, size);
fuse_reply_write(req, size);
}
static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
size_t in_bufsz, size_t out_bufsz, int is_read)
{
const struct fioc_rw_arg *arg;
struct iovec in_iov[2], out_iov[3], iov[3];
size_t cur_size;
/* read in arg */
in_iov[0].iov_base = addr;
in_iov[0].iov_len = sizeof(*arg);
if (!in_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
return;
}
arg = in_buf;
in_buf += sizeof(*arg);
in_bufsz -= sizeof(*arg);
/* prepare size outputs */
out_iov[0].iov_base =
addr + offsetof(struct fioc_rw_arg, prev_size);
out_iov[0].iov_len = sizeof(arg->prev_size);
out_iov[1].iov_base =
addr + offsetof(struct fioc_rw_arg, new_size);
out_iov[1].iov_len = sizeof(arg->new_size);
/* prepare client buf */
if (is_read) {
out_iov[2].iov_base = arg->buf;
out_iov[2].iov_len = arg->size;
if (!out_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
return;
}
} else {
in_iov[1].iov_base = arg->buf;
in_iov[1].iov_len = arg->size;
if (arg->size && !in_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
return;
}
}
/* we're all set */
cur_size = cusexmp_size;
iov[0].iov_base = &cur_size;
iov[0].iov_len = sizeof(cur_size);
iov[1].iov_base = &cusexmp_size;
iov[1].iov_len = sizeof(cusexmp_size);
if (is_read) {
size_t off = arg->offset;
size_t size = arg->size;
if (off >= cusexmp_size)
off = cusexmp_size;
if (size > cusexmp_size - off)
size = cusexmp_size - off;
iov[2].iov_base = cusexmp_buf + off;
iov[2].iov_len = size;
fuse_reply_ioctl_iov(req, size, iov, 3);
} else {
if (cusexmp_expand(arg->offset + in_bufsz)) {
fuse_reply_err(req, ENOMEM);
return;
}
memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
}
}
static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
struct fuse_file_info *fi, unsigned flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{
int is_read = 0;
(void)fi;
if (flags & FUSE_IOCTL_COMPAT) {
fuse_reply_err(req, ENOSYS);
return;
}
switch (cmd) {
case FIOC_GET_SIZE:
if (!out_bufsz) {
struct iovec iov = { arg, sizeof(size_t) };
fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
} else
fuse_reply_ioctl(req, 0, &cusexmp_size,
sizeof(cusexmp_size));
break;
case FIOC_SET_SIZE:
if (!in_bufsz) {
struct iovec iov = { arg, sizeof(size_t) };
fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
} else {
cusexmp_resize(*(size_t *)in_buf);
fuse_reply_ioctl(req, 0, NULL, 0);
}
break;
case FIOC_READ:
is_read = 1;
/* fall through */
case FIOC_WRITE:
fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
break;
default:
fuse_reply_err(req, EINVAL);
}
}
struct cusexmp_param {
unsigned major;
unsigned minor;
char *dev_name;
int is_help;
};
#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
static const struct fuse_opt cusexmp_opts[] = {
CUSEXMP_OPT("-M %u", major),
CUSEXMP_OPT("--maj=%u", major),
CUSEXMP_OPT("-m %u", minor),
CUSEXMP_OPT("--min=%u", minor),
CUSEXMP_OPT("-n %s", dev_name),
CUSEXMP_OPT("--name=%s", dev_name),
FUSE_OPT_KEY("-h", 0),
FUSE_OPT_KEY("--help", 0),
};
static int cusexmp_process_arg(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct cusexmp_param *param = data;
(void)outargs;
(void)arg;
switch (key) {
case 0:
param->is_help = 1;
fprintf(stderr, "%s", usage);
return fuse_opt_add_arg(outargs, "-ho");
default:
return 1;
}
}
static const struct cuse_lowlevel_ops cusexmp_clop = {
.init = cusexmp_init,
.open = cusexmp_open,
.read = cusexmp_read,
.write = cusexmp_write,
.ioctl = cusexmp_ioctl,
};
int main(int argc, char **argv)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct cusexmp_param param = { 0, 0, NULL, 0 };
char dev_name[128] = "DEVNAME=";
const char *dev_info_argv[] = { dev_name };
struct cuse_info ci;
int ret = 1;
if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
printf("failed to parse option\n");
free(param.dev_name);
goto out;
}
if (!param.is_help) {
if (!param.dev_name) {
fprintf(stderr, "Error: device name missing\n");
goto out;
}
strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME="));
free(param.dev_name);
}
memset(&ci, 0, sizeof(ci));
ci.dev_major = param.major;
ci.dev_minor = param.minor;
ci.dev_info_argc = 1;
ci.dev_info_argv = dev_info_argv;
ci.flags = CUSE_UNRESTRICTED_IOCTL;
ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL);
out:
return ret;
}
#define FUSE_IOCTL_COMPAT
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt

Definition in file cuse.c.

fuse-3.18.2/doc/html/example_2cuse__client_8c.html0000644000175000017500000003200515156613443020747 0ustar berndbernd libfuse: example/cuse_client.c File Reference
libfuse
cuse_client.c File Reference
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This program tests the cuse.c example file system.

Example usage (assuming that /dev/foobar is a CUSE device provided by the cuse.c example file system):

$ cuse_client /dev/foobar s
0

$ echo "hello" | cuse_client /dev/foobar w 6
Writing 6 bytes
transferred 6 bytes (0 -> 6)

$ cuse_client /dev/foobar s
6

$ cuse_client /dev/foobar r 10
hello
transferred 6 bytes (6 -> 6)

Compiling this example

gcc -Wall cuse_client.c -o cuse_client

Source Code

/*
FUSE fioclient: FUSE ioctl example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"
const char *usage =
"Usage: cuse_client FIOC_FILE COMMAND\n"
"\n"
"COMMANDS\n"
" s [SIZE] : get size if SIZE is omitted, set size otherwise\n"
" r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n"
" w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n"
"\n";
static int do_rw(int fd, int is_read, size_t size, off_t offset,
size_t *prev_size, size_t *new_size)
{
struct fioc_rw_arg arg = { .offset = offset };
ssize_t ret;
arg.buf = calloc(1, size);
if (!arg.buf) {
fprintf(stderr, "failed to allocated %zu bytes\n", size);
return -1;
}
if (is_read) {
arg.size = size;
ret = ioctl(fd, FIOC_READ, &arg);
if (ret >= 0)
fwrite(arg.buf, 1, ret, stdout);
} else {
arg.size = fread(arg.buf, 1, size, stdin);
fprintf(stderr, "Writing %zu bytes\n", arg.size);
ret = ioctl(fd, FIOC_WRITE, &arg);
}
if (ret >= 0) {
*prev_size = arg.prev_size;
*new_size = arg.new_size;
} else
perror("ioctl");
free(arg.buf);
return ret;
}
int main(int argc, char **argv)
{
size_t param[2] = { };
size_t size, prev_size = 0, new_size = 0;
char cmd;
int fd, i, rc;
if (argc < 3)
goto usage;
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
cmd = tolower(argv[2][0]);
argc -= 3;
argv += 3;
for (i = 0; i < argc; i++) {
char *endp;
param[i] = strtoul(argv[i], &endp, 0);
if (endp == argv[i] || *endp != '\0')
goto usage;
}
switch (cmd) {
case 's':
if (!argc) {
if (ioctl(fd, FIOC_GET_SIZE, &size)) {
perror("ioctl");
goto error;
}
printf("%zu\n", size);
} else {
size = param[0];
if (ioctl(fd, FIOC_SET_SIZE, &size)) {
perror("ioctl");
goto error;
}
}
close(fd);
return 0;
case 'r':
case 'w':
rc = do_rw(fd, cmd == 'r', param[0], param[1],
&prev_size, &new_size);
if (rc < 0)
goto error;
fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n",
rc, prev_size, new_size);
close(fd);
return 0;
}
usage:
fprintf(stderr, "%s", usage);
return 1;
error:
close(fd);
return 1;
}

Definition in file cuse_client.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2cuse__client_8c.html0000644000175000017500000003224615156613443023132 0ustar berndbernd libfuse: fuse-3.18.1/example/cuse_client.c File Reference
libfuse
cuse_client.c File Reference
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This program tests the cuse.c example file system.

Example usage (assuming that /dev/foobar is a CUSE device provided by the cuse.c example file system):

$ cuse_client /dev/foobar s
0

$ echo "hello" | cuse_client /dev/foobar w 6
Writing 6 bytes
transferred 6 bytes (0 -> 6)

$ cuse_client /dev/foobar s
6

$ cuse_client /dev/foobar r 10
hello
transferred 6 bytes (6 -> 6)

Compiling this example

gcc -Wall cuse_client.c -o cuse_client

Source Code

/*
FUSE fioclient: FUSE ioctl example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"
const char *usage =
"Usage: cuse_client FIOC_FILE COMMAND\n"
"\n"
"COMMANDS\n"
" s [SIZE] : get size if SIZE is omitted, set size otherwise\n"
" r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n"
" w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n"
"\n";
static int do_rw(int fd, int is_read, size_t size, off_t offset,
size_t *prev_size, size_t *new_size)
{
struct fioc_rw_arg arg = { .offset = offset };
ssize_t ret;
arg.buf = calloc(1, size);
if (!arg.buf) {
fprintf(stderr, "failed to allocated %zu bytes\n", size);
return -1;
}
if (is_read) {
arg.size = size;
ret = ioctl(fd, FIOC_READ, &arg);
if (ret >= 0)
fwrite(arg.buf, 1, ret, stdout);
} else {
arg.size = fread(arg.buf, 1, size, stdin);
fprintf(stderr, "Writing %zu bytes\n", arg.size);
ret = ioctl(fd, FIOC_WRITE, &arg);
}
if (ret >= 0) {
*prev_size = arg.prev_size;
*new_size = arg.new_size;
} else
perror("ioctl");
free(arg.buf);
return ret;
}
int main(int argc, char **argv)
{
size_t param[2] = { };
size_t size, prev_size = 0, new_size = 0;
char cmd;
int fd, i, rc;
if (argc < 3)
goto usage;
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
cmd = tolower(argv[2][0]);
argc -= 3;
argv += 3;
for (i = 0; i < argc; i++) {
char *endp;
param[i] = strtoul(argv[i], &endp, 0);
if (endp == argv[i] || *endp != '\0')
goto usage;
}
switch (cmd) {
case 's':
if (!argc) {
if (ioctl(fd, FIOC_GET_SIZE, &size)) {
perror("ioctl");
goto error;
}
printf("%zu\n", size);
} else {
size = param[0];
if (ioctl(fd, FIOC_SET_SIZE, &size)) {
perror("ioctl");
goto error;
}
}
close(fd);
return 0;
case 'r':
case 'w':
rc = do_rw(fd, cmd == 'r', param[0], param[1],
&prev_size, &new_size);
if (rc < 0)
goto error;
fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n",
rc, prev_size, new_size);
close(fd);
return 0;
}
usage:
fprintf(stderr, "%s", usage);
return 1;
error:
close(fd);
return 1;
}

Definition in file cuse_client.c.

fuse-3.18.2/doc/html/example_2hello_8c.html0000644000175000017500000007112415156613443017423 0ustar berndbernd libfuse: example/hello.c File Reference
libfuse
hello.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using high-level API

Compile with:

gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>
/*
* Command line options
*
* We can't set default values for the char* fields here because
* fuse_opt_parse would attempt to free() them when the user specifies
* different values on the command line.
*/
static struct options {
const char *filename;
const char *contents;
int show_help;
} options;
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--name=%s", filename),
OPTION("--contents=%s", contents),
OPTION("-h", show_help),
OPTION("--help", show_help),
};
static void *hello_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->kernel_cache = 1;
/* Test setting flags the old way */
return NULL;
}
static int hello_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int res = 0;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else if (strcmp(path+1, options.filename) == 0) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(options.contents);
} else
res = -ENOENT;
return res;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
if (strcmp(path+1, options.filename) != 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
size_t len;
(void) fi;
if(strcmp(path+1, options.filename) != 0)
return -ENOENT;
len = strlen(options.contents);
if (offset < len) {
if (offset + size > len)
size = len - offset;
memcpy(buf, options.contents + offset, size);
} else
size = 0;
return size;
}
static const struct fuse_operations hello_oper = {
.init = hello_init,
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --name=<s> Name of the \"hello\" file\n"
" (default: \"hello\")\n"
" --contents=<s> Contents \"hello\" file\n"
" (default \"Hello, World!\\n\")\n"
"\n");
}
int main(int argc, char *argv[])
{
int ret;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
/* Set defaults -- we have to use strdup so that
fuse_opt_parse can free the defaults if other
values are specified */
options.filename = strdup("hello");
options.contents = strdup("Hello World!\n");
/* Parse options */
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
/* When --help is specified, first print our own file-system
specific help text, then signal fuse_main to show
additional help (by adding `--help` to the options again)
without usage: line (by setting argv[0] to the empty
string) */
if (options.show_help) {
show_help(argv[0]);
assert(fuse_opt_add_arg(&args, "--help") == 0);
args.argv[0][0] = '\0';
}
ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
return ret;
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file hello.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2test_2hello_8c.html0000644000175000017500000007133215156613443021126 0ustar berndbernd libfuse: fuse-3.17.4/test/hello.c File Reference
libfuse
hello.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using high-level API

Compile with:

gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>
/*
* Command line options
*
* We can't set default values for the char* fields here because
* fuse_opt_parse would attempt to free() them when the user specifies
* different values on the command line.
*/
static struct options {
const char *filename;
const char *contents;
int show_help;
} options;
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--name=%s", filename),
OPTION("--contents=%s", contents),
OPTION("-h", show_help),
OPTION("--help", show_help),
};
static void *hello_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->kernel_cache = 1;
/* Test setting flags the old way */
return NULL;
}
static int hello_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int res = 0;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else if (strcmp(path+1, options.filename) == 0) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(options.contents);
} else
res = -ENOENT;
return res;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
if (strcmp(path+1, options.filename) != 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
size_t len;
(void) fi;
if(strcmp(path+1, options.filename) != 0)
return -ENOENT;
len = strlen(options.contents);
if (offset < len) {
if (offset + size > len)
size = len - offset;
memcpy(buf, options.contents + offset, size);
} else
size = 0;
return size;
}
static const struct fuse_operations hello_oper = {
.init = hello_init,
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --name=<s> Name of the \"hello\" file\n"
" (default: \"hello\")\n"
" --contents=<s> Contents \"hello\" file\n"
" (default \"Hello, World!\\n\")\n"
"\n");
}
int main(int argc, char *argv[])
{
int ret;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
/* Set defaults -- we have to use strdup so that
fuse_opt_parse can free the defaults if other
values are specified */
options.filename = strdup("hello");
options.contents = strdup("Hello World!\n");
/* Parse options */
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
/* When --help is specified, first print our own file-system
specific help text, then signal fuse_main to show
additional help (by adding `--help` to the options again)
without usage: line (by setting argv[0] to the empty
string) */
if (options.show_help) {
show_help(argv[0]);
assert(fuse_opt_add_arg(&args, "--help") == 0);
args.argv[0][0] = '\0';
}
ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
return ret;
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file hello.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2hello_8c.html0000644000175000017500000007134615156613443021605 0ustar berndbernd libfuse: fuse-3.18.1/example/hello.c File Reference
libfuse
hello.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using high-level API

Compile with:

gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>
/*
* Command line options
*
* We can't set default values for the char* fields here because
* fuse_opt_parse would attempt to free() them when the user specifies
* different values on the command line.
*/
static struct options {
const char *filename;
const char *contents;
int show_help;
} options;
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--name=%s", filename),
OPTION("--contents=%s", contents),
OPTION("-h", show_help),
OPTION("--help", show_help),
};
static void *hello_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->kernel_cache = 1;
/* Test setting flags the old way */
return NULL;
}
static int hello_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int res = 0;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else if (strcmp(path+1, options.filename) == 0) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(options.contents);
} else
res = -ENOENT;
return res;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
if (strcmp(path+1, options.filename) != 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
size_t len;
(void) fi;
if(strcmp(path+1, options.filename) != 0)
return -ENOENT;
len = strlen(options.contents);
if (offset < len) {
if (offset + size > len)
size = len - offset;
memcpy(buf, options.contents + offset, size);
} else
size = 0;
return size;
}
static const struct fuse_operations hello_oper = {
.init = hello_init,
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --name=<s> Name of the \"hello\" file\n"
" (default: \"hello\")\n"
" --contents=<s> Contents \"hello\" file\n"
" (default \"Hello, World!\\n\")\n"
"\n");
}
int main(int argc, char *argv[])
{
int ret;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
/* Set defaults -- we have to use strdup so that
fuse_opt_parse can free the defaults if other
values are specified */
options.filename = strdup("hello");
options.contents = strdup("Hello World!\n");
/* Parse options */
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
/* When --help is specified, first print our own file-system
specific help text, then signal fuse_main to show
additional help (by adding `--help` to the options again)
without usage: line (by setting argv[0] to the empty
string) */
if (options.show_help) {
show_help(argv[0]);
assert(fuse_opt_add_arg(&args, "--help") == 0);
args.argv[0][0] = '\0';
}
ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
return ret;
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file hello.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2test_2hello_8c.html0000644000175000017500000007133215156613443021124 0ustar berndbernd libfuse: fuse-3.18.1/test/hello.c File Reference
libfuse
hello.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using high-level API

Compile with:

gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>
/*
* Command line options
*
* We can't set default values for the char* fields here because
* fuse_opt_parse would attempt to free() them when the user specifies
* different values on the command line.
*/
static struct options {
const char *filename;
const char *contents;
int show_help;
} options;
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--name=%s", filename),
OPTION("--contents=%s", contents),
OPTION("-h", show_help),
OPTION("--help", show_help),
};
static void *hello_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->kernel_cache = 1;
/* Test setting flags the old way */
return NULL;
}
static int hello_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int res = 0;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else if (strcmp(path+1, options.filename) == 0) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(options.contents);
} else
res = -ENOENT;
return res;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
if (strcmp(path+1, options.filename) != 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
size_t len;
(void) fi;
if(strcmp(path+1, options.filename) != 0)
return -ENOENT;
len = strlen(options.contents);
if (offset < len) {
if (offset + size > len)
size = len - offset;
memcpy(buf, options.contents + offset, size);
} else
size = 0;
return size;
}
static const struct fuse_operations hello_oper = {
.init = hello_init,
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --name=<s> Name of the \"hello\" file\n"
" (default: \"hello\")\n"
" --contents=<s> Contents \"hello\" file\n"
" (default \"Hello, World!\\n\")\n"
"\n");
}
int main(int argc, char *argv[])
{
int ret;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
/* Set defaults -- we have to use strdup so that
fuse_opt_parse can free the defaults if other
values are specified */
options.filename = strdup("hello");
options.contents = strdup("Hello World!\n");
/* Parse options */
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
/* When --help is specified, first print our own file-system
specific help text, then signal fuse_main to show
additional help (by adding `--help` to the options again)
without usage: line (by setting argv[0] to the empty
string) */
if (options.show_help) {
show_help(argv[0]);
assert(fuse_opt_add_arg(&args, "--help") == 0);
args.argv[0][0] = '\0';
}
ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
return ret;
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file hello.c.

fuse-3.18.2/doc/html/test_2hello_8c.html0000644000175000017500000007111115156613443016743 0ustar berndbernd libfuse: test/hello.c File Reference
libfuse
hello.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using high-level API

Compile with:

gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>
/*
* Command line options
*
* We can't set default values for the char* fields here because
* fuse_opt_parse would attempt to free() them when the user specifies
* different values on the command line.
*/
static struct options {
const char *filename;
const char *contents;
int show_help;
} options;
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--name=%s", filename),
OPTION("--contents=%s", contents),
OPTION("-h", show_help),
OPTION("--help", show_help),
};
static void *hello_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->kernel_cache = 1;
/* Test setting flags the old way */
return NULL;
}
static int hello_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int res = 0;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else if (strcmp(path+1, options.filename) == 0) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(options.contents);
} else
res = -ENOENT;
return res;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
if (strcmp(path+1, options.filename) != 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
size_t len;
(void) fi;
if(strcmp(path+1, options.filename) != 0)
return -ENOENT;
len = strlen(options.contents);
if (offset < len) {
if (offset + size > len)
size = len - offset;
memcpy(buf, options.contents + offset, size);
} else
size = 0;
return size;
}
static const struct fuse_operations hello_oper = {
.init = hello_init,
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --name=<s> Name of the \"hello\" file\n"
" (default: \"hello\")\n"
" --contents=<s> Contents \"hello\" file\n"
" (default \"Hello, World!\\n\")\n"
"\n");
}
int main(int argc, char *argv[])
{
int ret;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
/* Set defaults -- we have to use strdup so that
fuse_opt_parse can free the defaults if other
values are specified */
options.filename = strdup("hello");
options.contents = strdup("Hello World!\n");
/* Parse options */
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
/* When --help is specified, first print our own file-system
specific help text, then signal fuse_main to show
additional help (by adding `--help` to the options again)
without usage: line (by setting argv[0] to the empty
string) */
if (options.show_help) {
show_help(argv[0]);
assert(fuse_opt_add_arg(&args, "--help") == 0);
args.argv[0][0] = '\0';
}
ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
return ret;
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file hello.c.

fuse-3.18.2/doc/html/example_2hello__ll_8c.html0000644000175000017500000013102515156613443020246 0ustar berndbernd libfuse: example/hello_ll.c File Reference
libfuse
hello_ll.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using low-level API

Compile with:

gcc -Wall hello_ll.c `pkg-config fuse3 --cflags --libs` -o hello_ll

Note: If the pkg-config command fails due to the absence of the fuse3.pc file, you should configure the path to the fuse3.pc file in the PKG_CONFIG_PATH variable.

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.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_init(void *userdata, struct fuse_conn_info *conn)
{
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
/* Test setting flags the old way */
conn->want &= ~FUSE_CAP_ASYNC_READ;
}
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,
{
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 void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
(void)size;
assert(ino == 1 || 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 == 1 || 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 == 1 || 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 = {
.init = hello_ll_init,
.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[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
se = fuse_session_new(&args, &hello_ll_oper,
sizeof(hello_ll_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file hello_ll.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2hello__ll_8c.html0000644000175000017500000013124715156613443022430 0ustar berndbernd libfuse: fuse-3.18.1/example/hello_ll.c File Reference
libfuse
hello_ll.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using low-level API

Compile with:

gcc -Wall hello_ll.c `pkg-config fuse3 --cflags --libs` -o hello_ll

Note: If the pkg-config command fails due to the absence of the fuse3.pc file, you should configure the path to the fuse3.pc file in the PKG_CONFIG_PATH variable.

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.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_init(void *userdata, struct fuse_conn_info *conn)
{
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
/* Test setting flags the old way */
conn->want &= ~FUSE_CAP_ASYNC_READ;
}
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,
{
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 void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
(void)size;
assert(ino == 1 || 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 == 1 || 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 == 1 || 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 = {
.init = hello_ll_init,
.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[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
se = fuse_session_new(&args, &hello_ll_oper,
sizeof(hello_ll_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file hello_ll.c.

fuse-3.18.2/doc/html/example_2hello__ll__uds_8c.html0000644000175000017500000013117015156613443021261 0ustar berndbernd libfuse: example/hello_ll_uds.c File Reference
libfuse
hello_ll_uds.c File Reference
#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>

Go to the source code of this file.

Detailed Description

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

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.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_init(void *userdata, struct fuse_conn_info *conn)
{
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
/* Test setting flags the old way */
conn->want &= ~FUSE_CAP_ASYNC_READ;
}
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,
{
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 void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
(void)size;
assert(ino == 1 || 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 == 1 || 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 == 1 || 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 = {
.init = hello_ll_init,
.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[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
se = fuse_session_new(&args, &hello_ll_oper,
sizeof(hello_ll_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file hello_ll_uds.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2hello__ll__uds_8c.html0000644000175000017500000013141215156613443023434 0ustar berndbernd libfuse: fuse-3.18.1/example/hello_ll_uds.c File Reference
libfuse
hello_ll_uds.c File Reference
#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>

Go to the source code of this file.

Detailed Description

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

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.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_init(void *userdata, struct fuse_conn_info *conn)
{
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
/* Test setting flags the old way */
conn->want &= ~FUSE_CAP_ASYNC_READ;
}
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,
{
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 void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
(void)size;
assert(ino == 1 || 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 == 1 || 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 == 1 || 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 = {
.init = hello_ll_init,
.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[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
se = fuse_session_new(&args, &hello_ll_oper,
sizeof(hello_ll_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file hello_ll_uds.c.

fuse-3.18.2/doc/html/example_2invalidate__path_8c.html0000644000175000017500000012363415156613443021617 0ustar berndbernd libfuse: example/invalidate_path.c File Reference
libfuse
invalidate_path.c File Reference
#include <fuse.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with two files:

  • 'current-time', whose contents change dynamically: it always contains the current time (same as in notify_inval_inode.c).
  • 'growing', whose size changes dynamically, growing by 1 byte after each update. This aims to check if cached file metadata is also invalidated.

Compilation

gcc -Wall invalidate_path.c `pkg-config fuse3 --cflags --libs` -o invalidate_path

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
(C) 2017 EditShare LLC <slawek.rudnicki@editshare.com>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 34
#include <fuse.h>
#include <fuse_lowlevel.h> /* for fuse_cmdline_opts */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define TIME_FILE_NAME "current_time"
#define TIME_FILE_INO 2
#define GROW_FILE_NAME "growing"
#define GROW_FILE_INO 3
static char time_file_contents[MAX_STR_LEN];
static size_t grow_file_size;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
{
(void) conn;
cfg->entry_timeout = NO_TIMEOUT;
cfg->attr_timeout = NO_TIMEOUT;
cfg->negative_timeout = 0;
return NULL;
}
static int xmp_getattr(const char *path,
struct stat *stbuf, struct fuse_file_info* fi) {
(void) fi;
if (strcmp(path, "/") == 0) {
stbuf->st_ino = 1;
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
} else if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
stbuf->st_ino = TIME_FILE_INO;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(time_file_contents);
} else if (strcmp(path, "/" GROW_FILE_NAME) == 0) {
stbuf->st_ino = GROW_FILE_INO;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = grow_file_size;
} else {
return -ENOENT;
}
return 0;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags) {
(void) fi;
(void) offset;
(void) flags;
if (strcmp(path, "/") != 0) {
return -ENOTDIR;
} else {
(void) filler;
(void) buf;
struct stat file_stat;
xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL);
filler(buf, TIME_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL);
filler(buf, GROW_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
}
static int xmp_open(const char *path, struct fuse_file_info *fi) {
(void) path;
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) {
(void) fi;
(void) offset;
if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
int file_length = strlen(time_file_contents);
int to_copy = offset + size <= file_length
? size
: file_length - offset;
memcpy(buf, time_file_contents, to_copy);
return to_copy;
} else {
assert(strcmp(path, "/" GROW_FILE_NAME) == 0);
int to_copy = offset + size <= grow_file_size
? size
: grow_file_size - offset;
memset(buf, 'x', to_copy);
return to_copy;
}
}
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.readdir = xmp_readdir,
.open = xmp_open,
.read = xmp_read,
};
static void update_fs(void) {
static int count = 0;
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
int time_file_size = strftime(time_file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(time_file_size != 0);
grow_file_size = count++;
}
static int invalidate(struct fuse *fuse, const char *path) {
int status = fuse_invalidate_path(fuse, path);
if (status == -ENOENT) {
return 0;
} else {
return status;
}
}
static void* update_fs_loop(void *data) {
struct fuse *fuse = (struct fuse*) data;
while (1) {
update_fs();
if (!options.no_notify) {
assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0);
assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0);
}
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\n");
}
int main(int argc, char *argv[]) {
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse *fuse;
struct fuse_cmdline_opts opts;
struct fuse_loop_config config;
int res;
/* Initialize the files */
update_fs();
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
res = 0;
goto out1;
} else if (opts.show_help) {
show_help(argv[0]);
fuse_lib_help(&args);
res = 0;
goto out1;
} else if (!opts.mountpoint) {
fprintf(stderr, "error: no mountpoint specified\n");
res = 1;
goto out1;
}
fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL);
if (fuse == NULL) {
res = 1;
goto out1;
}
if (fuse_mount(fuse,opts.mountpoint) != 0) {
res = 1;
goto out2;
}
if (fuse_daemonize(opts.foreground) != 0) {
res = 1;
goto out3;
}
pthread_t updater; /* Start thread to update file contents */
int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n", strerror(ret));
return 1;
};
struct fuse_session *se = fuse_get_session(fuse);
if (fuse_set_signal_handlers(se) != 0) {
res = 1;
goto out3;
}
if (opts.singlethread)
res = fuse_loop(fuse);
else {
config.clone_fd = opts.clone_fd;
config.max_idle_threads = opts.max_idle_threads;
res = fuse_loop_mt(fuse, &config);
}
if (res)
res = 1;
out3:
fuse_unmount(fuse);
out2:
fuse_destroy(fuse);
out1:
free(opts.mountpoint);
return res;
}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t keep_cache
Definition fuse_common.h:69
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file invalidate_path.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2invalidate__path_8c.html0000644000175000017500000012405715156613443023773 0ustar berndbernd libfuse: fuse-3.18.1/example/invalidate_path.c File Reference
libfuse
invalidate_path.c File Reference
#include <fuse.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with two files:

  • 'current-time', whose contents change dynamically: it always contains the current time (same as in notify_inval_inode.c).
  • 'growing', whose size changes dynamically, growing by 1 byte after each update. This aims to check if cached file metadata is also invalidated.

Compilation

gcc -Wall invalidate_path.c `pkg-config fuse3 --cflags --libs` -o invalidate_path

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
(C) 2017 EditShare LLC <slawek.rudnicki@editshare.com>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 34
#include <fuse.h>
#include <fuse_lowlevel.h> /* for fuse_cmdline_opts */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define TIME_FILE_NAME "current_time"
#define TIME_FILE_INO 2
#define GROW_FILE_NAME "growing"
#define GROW_FILE_INO 3
static char time_file_contents[MAX_STR_LEN];
static size_t grow_file_size;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
{
(void) conn;
cfg->entry_timeout = NO_TIMEOUT;
cfg->attr_timeout = NO_TIMEOUT;
cfg->negative_timeout = 0;
return NULL;
}
static int xmp_getattr(const char *path,
struct stat *stbuf, struct fuse_file_info* fi) {
(void) fi;
if (strcmp(path, "/") == 0) {
stbuf->st_ino = 1;
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
} else if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
stbuf->st_ino = TIME_FILE_INO;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(time_file_contents);
} else if (strcmp(path, "/" GROW_FILE_NAME) == 0) {
stbuf->st_ino = GROW_FILE_INO;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = grow_file_size;
} else {
return -ENOENT;
}
return 0;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags) {
(void) fi;
(void) offset;
(void) flags;
if (strcmp(path, "/") != 0) {
return -ENOTDIR;
} else {
(void) filler;
(void) buf;
struct stat file_stat;
xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL);
filler(buf, TIME_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL);
filler(buf, GROW_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
}
static int xmp_open(const char *path, struct fuse_file_info *fi) {
(void) path;
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) {
(void) fi;
(void) offset;
if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
int file_length = strlen(time_file_contents);
int to_copy = offset + size <= file_length
? size
: file_length - offset;
memcpy(buf, time_file_contents, to_copy);
return to_copy;
} else {
assert(strcmp(path, "/" GROW_FILE_NAME) == 0);
int to_copy = offset + size <= grow_file_size
? size
: grow_file_size - offset;
memset(buf, 'x', to_copy);
return to_copy;
}
}
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.readdir = xmp_readdir,
.open = xmp_open,
.read = xmp_read,
};
static void update_fs(void) {
static int count = 0;
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
int time_file_size = strftime(time_file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(time_file_size != 0);
grow_file_size = count++;
}
static int invalidate(struct fuse *fuse, const char *path) {
int status = fuse_invalidate_path(fuse, path);
if (status == -ENOENT) {
return 0;
} else {
return status;
}
}
static void* update_fs_loop(void *data) {
struct fuse *fuse = (struct fuse*) data;
while (1) {
update_fs();
if (!options.no_notify) {
assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0);
assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0);
}
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\n");
}
int main(int argc, char *argv[]) {
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse *fuse;
struct fuse_cmdline_opts opts;
struct fuse_loop_config config;
int res;
/* Initialize the files */
update_fs();
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
res = 0;
goto out1;
} else if (opts.show_help) {
show_help(argv[0]);
fuse_lib_help(&args);
res = 0;
goto out1;
} else if (!opts.mountpoint) {
fprintf(stderr, "error: no mountpoint specified\n");
res = 1;
goto out1;
}
fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL);
if (fuse == NULL) {
res = 1;
goto out1;
}
if (fuse_mount(fuse,opts.mountpoint) != 0) {
res = 1;
goto out2;
}
if (fuse_daemonize(opts.foreground) != 0) {
res = 1;
goto out3;
}
pthread_t updater; /* Start thread to update file contents */
int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n", strerror(ret));
return 1;
};
struct fuse_session *se = fuse_get_session(fuse);
if (fuse_set_signal_handlers(se) != 0) {
res = 1;
goto out3;
}
if (opts.singlethread)
res = fuse_loop(fuse);
else {
config.clone_fd = opts.clone_fd;
config.max_idle_threads = opts.max_idle_threads;
res = fuse_loop_mt(fuse, &config);
}
if (res)
res = 1;
out3:
fuse_unmount(fuse);
out2:
fuse_destroy(fuse);
out1:
free(opts.mountpoint);
return res;
}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t keep_cache
Definition fuse_common.h:69
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file invalidate_path.c.

fuse-3.18.2/doc/html/example_2ioctl_8c.html0000644000175000017500000005460315156613443017435 0ustar berndbernd libfuse: example/ioctl.c File Reference
libfuse
ioctl.c File Reference
#include <fuse.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This example illustrates how to write a FUSE file system that can process (a restricted set of) ioctls. It can be tested with the ioctl_client.c program.

Compile with:

gcc -Wall ioctl.c `pkg-config fuse3 --cflags --libs` -o ioctl

Source code

/*
FUSE fioc: FUSE ioctl example
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 35
#include <fuse.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "ioctl.h"
#define FIOC_NAME "fioc"
enum {
FIOC_NONE,
FIOC_ROOT,
FIOC_FILE,
};
static void *fioc_buf;
static size_t fioc_size;
static int fioc_resize(size_t new_size)
{
void *new_buf;
if (new_size == fioc_size)
return 0;
new_buf = realloc(fioc_buf, new_size);
if (!new_buf && new_size)
return -ENOMEM;
if (new_size > fioc_size)
memset(new_buf + fioc_size, 0, new_size - fioc_size);
fioc_buf = new_buf;
fioc_size = new_size;
return 0;
}
static int fioc_expand(size_t new_size)
{
if (new_size > fioc_size)
return fioc_resize(new_size);
return 0;
}
static int fioc_file_type(const char *path)
{
if (strcmp(path, "/") == 0)
return FIOC_ROOT;
if (strcmp(path, "/" FIOC_NAME) == 0)
return FIOC_FILE;
return FIOC_NONE;
}
static int fioc_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
stbuf->st_uid = getuid();
stbuf->st_gid = getgid();
stbuf->st_atime = stbuf->st_mtime = time(NULL);
switch (fioc_file_type(path)) {
case FIOC_ROOT:
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
break;
case FIOC_FILE:
stbuf->st_mode = S_IFREG | 0644;
stbuf->st_nlink = 1;
stbuf->st_size = fioc_size;
break;
case FIOC_NONE:
return -ENOENT;
}
return 0;
}
static int fioc_open(const char *path, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_NONE)
return 0;
return -ENOENT;
}
static int fioc_do_read(char *buf, size_t size, off_t offset)
{
if (offset >= fioc_size)
return 0;
if (size > fioc_size - offset)
size = fioc_size - offset;
memcpy(buf, fioc_buf + offset, size);
return size;
}
static int fioc_read(const char *path, char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_do_read(buf, size, offset);
}
static int fioc_do_write(const char *buf, size_t size, off_t offset)
{
if (fioc_expand(offset + size))
return -ENOMEM;
memcpy(fioc_buf + offset, buf, size);
return size;
}
static int fioc_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_do_write(buf, size, offset);
}
static int fioc_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_resize(size);
}
static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) fi;
(void) offset;
(void) flags;
if (fioc_file_type(path) != FIOC_ROOT)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, FIOC_NAME, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int fioc_ioctl(const char *path, unsigned int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags, void *data)
{
(void) arg;
(void) fi;
(void) flags;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
if (flags & FUSE_IOCTL_COMPAT)
return -ENOSYS;
switch (cmd) {
case FIOC_GET_SIZE:
*(size_t *)data = fioc_size;
return 0;
case FIOC_SET_SIZE:
fioc_resize(*(size_t *)data);
return 0;
}
return -EINVAL;
}
static const struct fuse_operations fioc_oper = {
.getattr = fioc_getattr,
.readdir = fioc_readdir,
.truncate = fioc_truncate,
.open = fioc_open,
.read = fioc_read,
.write = fioc_write,
.ioctl = fioc_ioctl,
};
int main(int argc, char *argv[])
{
return fuse_main(argc, argv, &fioc_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_IOCTL_COMPAT
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361

Definition in file ioctl.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2ioctl_8c.html0000644000175000017500000005504315156613443021610 0ustar berndbernd libfuse: fuse-3.18.1/example/ioctl.c File Reference
libfuse
ioctl.c File Reference
#include <fuse.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This example illustrates how to write a FUSE file system that can process (a restricted set of) ioctls. It can be tested with the ioctl_client.c program.

Compile with:

gcc -Wall ioctl.c `pkg-config fuse3 --cflags --libs` -o ioctl

Source code

/*
FUSE fioc: FUSE ioctl example
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 35
#include <fuse.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "ioctl.h"
#define FIOC_NAME "fioc"
enum {
FIOC_NONE,
FIOC_ROOT,
FIOC_FILE,
};
static void *fioc_buf;
static size_t fioc_size;
static int fioc_resize(size_t new_size)
{
void *new_buf;
if (new_size == fioc_size)
return 0;
new_buf = realloc(fioc_buf, new_size);
if (!new_buf && new_size)
return -ENOMEM;
if (new_size > fioc_size)
memset(new_buf + fioc_size, 0, new_size - fioc_size);
fioc_buf = new_buf;
fioc_size = new_size;
return 0;
}
static int fioc_expand(size_t new_size)
{
if (new_size > fioc_size)
return fioc_resize(new_size);
return 0;
}
static int fioc_file_type(const char *path)
{
if (strcmp(path, "/") == 0)
return FIOC_ROOT;
if (strcmp(path, "/" FIOC_NAME) == 0)
return FIOC_FILE;
return FIOC_NONE;
}
static int fioc_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
stbuf->st_uid = getuid();
stbuf->st_gid = getgid();
stbuf->st_atime = stbuf->st_mtime = time(NULL);
switch (fioc_file_type(path)) {
case FIOC_ROOT:
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
break;
case FIOC_FILE:
stbuf->st_mode = S_IFREG | 0644;
stbuf->st_nlink = 1;
stbuf->st_size = fioc_size;
break;
case FIOC_NONE:
return -ENOENT;
}
return 0;
}
static int fioc_open(const char *path, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_NONE)
return 0;
return -ENOENT;
}
static int fioc_do_read(char *buf, size_t size, off_t offset)
{
if (offset >= fioc_size)
return 0;
if (size > fioc_size - offset)
size = fioc_size - offset;
memcpy(buf, fioc_buf + offset, size);
return size;
}
static int fioc_read(const char *path, char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_do_read(buf, size, offset);
}
static int fioc_do_write(const char *buf, size_t size, off_t offset)
{
if (fioc_expand(offset + size))
return -ENOMEM;
memcpy(fioc_buf + offset, buf, size);
return size;
}
static int fioc_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_do_write(buf, size, offset);
}
static int fioc_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_resize(size);
}
static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) fi;
(void) offset;
(void) flags;
if (fioc_file_type(path) != FIOC_ROOT)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, FIOC_NAME, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int fioc_ioctl(const char *path, unsigned int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags, void *data)
{
(void) arg;
(void) fi;
(void) flags;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
if (flags & FUSE_IOCTL_COMPAT)
return -ENOSYS;
switch (cmd) {
case FIOC_GET_SIZE:
*(size_t *)data = fioc_size;
return 0;
case FIOC_SET_SIZE:
fioc_resize(*(size_t *)data);
return 0;
}
return -EINVAL;
}
static const struct fuse_operations fioc_oper = {
.getattr = fioc_getattr,
.readdir = fioc_readdir,
.truncate = fioc_truncate,
.open = fioc_open,
.read = fioc_read,
.write = fioc_write,
.ioctl = fioc_ioctl,
};
int main(int argc, char *argv[])
{
return fuse_main(argc, argv, &fioc_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_IOCTL_COMPAT
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361

Definition in file ioctl.c.

fuse-3.18.2/doc/html/example_2ioctl_8h.html0000644000175000017500000001232315156613443017433 0ustar berndbernd libfuse: example/ioctl.h File Reference
libfuse
ioctl.h File Reference
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/ioctl.h>

Go to the source code of this file.

Detailed Description

Header file to share definitions between the ioctl.c example file system and the ioctl_client.c test program.

/*
FUSE-ioctl: ioctl support for FUSE
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
enum {
FIOC_GET_SIZE = _IOR('E', 0, size_t),
FIOC_SET_SIZE = _IOW('E', 1, size_t),
/*
* The following two ioctls don't follow usual encoding rules
* and transfer variable amount of data.
*/
FIOC_READ = _IO('E', 2),
FIOC_WRITE = _IO('E', 3),
};
struct fioc_rw_arg {
off_t offset;
void *buf;
size_t size;
size_t prev_size; /* out param for previous total size */
size_t new_size; /* out param for new total size */
};

Definition in file ioctl.h.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2ioctl_8h.html0000644000175000017500000001254415156613443021614 0ustar berndbernd libfuse: fuse-3.18.1/example/ioctl.h File Reference
libfuse
ioctl.h File Reference
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/ioctl.h>

Go to the source code of this file.

Detailed Description

Header file to share definitions between the ioctl.c example file system and the ioctl_client.c test program.

/*
FUSE-ioctl: ioctl support for FUSE
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
enum {
FIOC_GET_SIZE = _IOR('E', 0, size_t),
FIOC_SET_SIZE = _IOW('E', 1, size_t),
/*
* The following two ioctls don't follow usual encoding rules
* and transfer variable amount of data.
*/
FIOC_READ = _IO('E', 2),
FIOC_WRITE = _IO('E', 3),
};
struct fioc_rw_arg {
off_t offset;
void *buf;
size_t size;
size_t prev_size; /* out param for previous total size */
size_t new_size; /* out param for new total size */
};

Definition in file ioctl.h.

fuse-3.18.2/doc/html/example_2ioctl__client_8c.html0000644000175000017500000001723215156613443021127 0ustar berndbernd libfuse: example/ioctl_client.c File Reference
libfuse
ioctl_client.c File Reference
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This program tests the ioctl.c example file systsem.

Compile with:

gcc -Wall ioctl_client.c -o ioctl_client

Source code

/*
FUSE fioclient: FUSE ioctl example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"
const char *usage =
"Usage: fioclient FIOC_FILE [size]\n"
"\n"
"Get size if <size> is omitted, set size otherwise\n"
"\n";
int main(int argc, char **argv)
{
size_t size;
int fd;
int ret = 0;
if (argc < 2) {
fprintf(stderr, "%s", usage);
return 1;
}
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
if (argc == 2) {
if (ioctl(fd, FIOC_GET_SIZE, &size)) {
perror("ioctl");
ret = 1;
goto out;
}
printf("%zu\n", size);
} else {
size = strtoul(argv[2], NULL, 0);
if (ioctl(fd, FIOC_SET_SIZE, &size)) {
perror("ioctl");
ret = 1;
goto out;
}
}
out:
close(fd);
return ret;
}

Definition in file ioctl_client.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2ioctl__client_8c.html0000644000175000017500000001747215156613443023311 0ustar berndbernd libfuse: fuse-3.18.1/example/ioctl_client.c File Reference
libfuse
ioctl_client.c File Reference
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This program tests the ioctl.c example file systsem.

Compile with:

gcc -Wall ioctl_client.c -o ioctl_client

Source code

/*
FUSE fioclient: FUSE ioctl example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"
const char *usage =
"Usage: fioclient FIOC_FILE [size]\n"
"\n"
"Get size if <size> is omitted, set size otherwise\n"
"\n";
int main(int argc, char **argv)
{
size_t size;
int fd;
int ret = 0;
if (argc < 2) {
fprintf(stderr, "%s", usage);
return 1;
}
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
if (argc == 2) {
if (ioctl(fd, FIOC_GET_SIZE, &size)) {
perror("ioctl");
ret = 1;
goto out;
}
printf("%zu\n", size);
} else {
size = strtoul(argv[2], NULL, 0);
if (ioctl(fd, FIOC_SET_SIZE, &size)) {
perror("ioctl");
ret = 1;
goto out;
}
}
out:
close(fd);
return ret;
}

Definition in file ioctl_client.c.

fuse-3.18.2/doc/html/example_2notify__inval__entry_8c.html0000644000175000017500000015420015156613443022535 0ustar berndbernd libfuse: example/notify_inval_entry.c File Reference
libfuse
notify_inval_entry.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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>

Go to the source code of this file.

Detailed Description

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() and fuse_lowlevel_notify_expire_entry() functions.

To see the effect, first start the file system with the --no-notify

$ notify_inval_entry --update-interval=1 --timeout=30 --no-notify mnt/

Observe that ls always prints the correct directory contents (since readdir output is not cached)::

$ ls mnt; sleep 1; ls mnt; sleep 1; ls mnt
Time_is_15h_48m_33s  current_time
Time_is_15h_48m_34s  current_time
Time_is_15h_48m_35s  current_time

However, if you try to access a file by name the kernel will report that it still exists:

$ file=$(ls mnt/); echo $file
Time_is_15h_50m_09s
$ sleep 5; stat mnt/$file
  File: ‘mnt/Time_is_15h_50m_09s’
  Size: 32                Blocks: 0          IO Block: 4096   regular file
Device: 2ah/42d     Inode: 3           Links: 1
Access: (0444/-r--r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 1969-12-31 16:00:00.000000000 -0800
Modify: 1969-12-31 16:00:00.000000000 -0800
Change: 1969-12-31 16:00:00.000000000 -0800
 Birth: -

Only once the kernel cache timeout has been reached will the stat call fail:

$ sleep 30; stat mnt/$file
stat: cannot stat ‘mnt/Time_is_15h_50m_09s’: No such file or directory

In contrast, if you enable notifications you will be unable to stat the file as soon as the file system updates its name:

$ notify_inval_entry --update-interval=1 --timeout=30 mnt/
$ file=$(ls mnt/); stat mnt/$file
  File: ‘mnt/Time_is_20h_42m_11s’
  Size: 0                 Blocks: 0          IO Block: 4096   regular empty file
Device: 2ah/42d     Inode: 2           Links: 1
Access: (0000/----------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 1969-12-31 16:00:00.000000000 -0800
Modify: 1969-12-31 16:00:00.000000000 -0800
Change: 1969-12-31 16:00:00.000000000 -0800
 Birth: -
$ 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

Another possible command-line option is –inc-epoch, which will use the FUSE low-level function fuse_lowlevel_notify_increment_epoch() instead. This will function will force the invalidation of all dentries next time they are revalidated. Note that –inc-epoch and –only-expire options are mutually exclusive.

Compilation

gcc -Wall notify_inval_entry.c `pkg-config fuse3 --cflags --libs` -o notify_inval_entry

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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>
#define MAX_STR_LEN 128
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;
int inc_epoch;
};
static struct options options = {
.timeout = 5,
.no_notify = 0,
.update_interval = 1,
.only_expire = 0,
.inc_epoch = 0,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
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),
OPTION("--inc-epoch", inc_epoch),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == file_ino) {
stbuf->st_mode = S_IFREG | 0000;
stbuf->st_nlink = 1;
stbuf->st_size = 0;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, file_name) == 0) {
e.ino = file_ino;
lookup_cnt++;
} else
goto err_out;
e.attr_timeout = options.timeout;
e.entry_timeout = options.timeout;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == file_ino)
lookup_cnt -= nlookup;
else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, options.timeout);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, file_name, file_ino);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.forget = tfs_forget,
};
static void update_fs(void) {
time_t t;
struct tm *now;
ssize_t ret;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
ret = strftime(file_name, MAX_STR_LEN,
"Time_is_%Hh_%Mm_%Ss", now);
assert(ret != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
char *old_name;
int ret = 0;
while(!fuse_session_exited(se)) {
old_name = strdup(file_name);
update_fs();
if (!options.no_notify && lookup_cnt) {
if(options.only_expire) { // 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");
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 if (options.inc_epoch) { // increment epoch
if (ret == -ENOSYS) {
printf("fuse_lowlevel_notify_increment_epoch not supported by kernel\n");
break;
}
assert(ret == 0);
} else { // invalidate entry
(se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
}
}
free(old_name);
sleep(options.update_interval);
}
if (ret == -ENOSYS) {
printf("Exiting...\n");
// Make sure to exit now, rather than on next request from userspace
pthread_kill(main_thread, SIGPIPE);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --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"
" --inc-epoch Increment epoch, invalidating all dentries\n"
"\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;
struct fuse_loop_config *config;
pthread_t updater;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if (options.only_expire && options.inc_epoch) {
printf("'only-expire' and 'inc-epoch' options are exclusive\n");
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), &se);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
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) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread) {
ret = fuse_session_loop(se);
} else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_inval_entry.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2notify__inval__entry_8c.html0000644000175000017500000015442115156613443024716 0ustar berndbernd libfuse: fuse-3.18.1/example/notify_inval_entry.c File Reference
libfuse
notify_inval_entry.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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>

Go to the source code of this file.

Detailed Description

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() and fuse_lowlevel_notify_expire_entry() functions.

To see the effect, first start the file system with the --no-notify

$ notify_inval_entry --update-interval=1 --timeout=30 --no-notify mnt/

Observe that ls always prints the correct directory contents (since readdir output is not cached)::

$ ls mnt; sleep 1; ls mnt; sleep 1; ls mnt
Time_is_15h_48m_33s  current_time
Time_is_15h_48m_34s  current_time
Time_is_15h_48m_35s  current_time

However, if you try to access a file by name the kernel will report that it still exists:

$ file=$(ls mnt/); echo $file
Time_is_15h_50m_09s
$ sleep 5; stat mnt/$file
  File: ‘mnt/Time_is_15h_50m_09s’
  Size: 32                Blocks: 0          IO Block: 4096   regular file
Device: 2ah/42d     Inode: 3           Links: 1
Access: (0444/-r--r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 1969-12-31 16:00:00.000000000 -0800
Modify: 1969-12-31 16:00:00.000000000 -0800
Change: 1969-12-31 16:00:00.000000000 -0800
 Birth: -

Only once the kernel cache timeout has been reached will the stat call fail:

$ sleep 30; stat mnt/$file
stat: cannot stat ‘mnt/Time_is_15h_50m_09s’: No such file or directory

In contrast, if you enable notifications you will be unable to stat the file as soon as the file system updates its name:

$ notify_inval_entry --update-interval=1 --timeout=30 mnt/
$ file=$(ls mnt/); stat mnt/$file
  File: ‘mnt/Time_is_20h_42m_11s’
  Size: 0                 Blocks: 0          IO Block: 4096   regular empty file
Device: 2ah/42d     Inode: 2           Links: 1
Access: (0000/----------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 1969-12-31 16:00:00.000000000 -0800
Modify: 1969-12-31 16:00:00.000000000 -0800
Change: 1969-12-31 16:00:00.000000000 -0800
 Birth: -
$ 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

Another possible command-line option is –inc-epoch, which will use the FUSE low-level function fuse_lowlevel_notify_increment_epoch() instead. This will function will force the invalidation of all dentries next time they are revalidated. Note that –inc-epoch and –only-expire options are mutually exclusive.

Compilation

gcc -Wall notify_inval_entry.c `pkg-config fuse3 --cflags --libs` -o notify_inval_entry

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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>
#define MAX_STR_LEN 128
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;
int inc_epoch;
};
static struct options options = {
.timeout = 5,
.no_notify = 0,
.update_interval = 1,
.only_expire = 0,
.inc_epoch = 0,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
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),
OPTION("--inc-epoch", inc_epoch),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == file_ino) {
stbuf->st_mode = S_IFREG | 0000;
stbuf->st_nlink = 1;
stbuf->st_size = 0;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, file_name) == 0) {
e.ino = file_ino;
lookup_cnt++;
} else
goto err_out;
e.attr_timeout = options.timeout;
e.entry_timeout = options.timeout;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == file_ino)
lookup_cnt -= nlookup;
else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, options.timeout);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, file_name, file_ino);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.forget = tfs_forget,
};
static void update_fs(void) {
time_t t;
struct tm *now;
ssize_t ret;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
ret = strftime(file_name, MAX_STR_LEN,
"Time_is_%Hh_%Mm_%Ss", now);
assert(ret != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
char *old_name;
int ret = 0;
while(!fuse_session_exited(se)) {
old_name = strdup(file_name);
update_fs();
if (!options.no_notify && lookup_cnt) {
if(options.only_expire) { // 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");
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 if (options.inc_epoch) { // increment epoch
if (ret == -ENOSYS) {
printf("fuse_lowlevel_notify_increment_epoch not supported by kernel\n");
break;
}
assert(ret == 0);
} else { // invalidate entry
(se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
}
}
free(old_name);
sleep(options.update_interval);
}
if (ret == -ENOSYS) {
printf("Exiting...\n");
// Make sure to exit now, rather than on next request from userspace
pthread_kill(main_thread, SIGPIPE);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --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"
" --inc-epoch Increment epoch, invalidating all dentries\n"
"\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;
struct fuse_loop_config *config;
pthread_t updater;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if (options.only_expire && options.inc_epoch) {
printf("'only-expire' and 'inc-epoch' options are exclusive\n");
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), &se);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
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) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread) {
ret = fuse_session_loop(se);
} else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_inval_entry.c.

fuse-3.18.2/doc/html/example_2notify__inval__inode_8c.html0000644000175000017500000015015015156613443022472 0ustar berndbernd libfuse: example/notify_inval_inode.c File Reference
libfuse
notify_inval_inode.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdatomic.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with a single file whose contents change dynamically: it always contains the current time.

While notify_store_retrieve.c uses fuse_lowlevel_notify_store() to actively push the updated data into the kernel cache, this example uses fuse_lowlevel_notify_inval_inode() to notify the kernel that the cache has to be invalidated - but the kernel still has to explicitly request the updated data on the next read.

To see the effect, first start the file system with the --no-notify option:

$ notify_inval_inode –update-interval=1 –no-notify mnt/

Observe that the output never changes, even though the file system updates it once per second. This is because the contents are cached in the kernel:

$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18

If you instead enable the notification functions, the changes become visible:

 $ notify_inval_inode --update-interval=1 mnt/
 $ for i in 1 2 3 4 5; do
 >     cat mnt/current_time
 >     sleep 1
 > done
 The current time is 15:58:40
 The current time is 15:58:41
 The current time is 15:58:42
 The current time is 15:58:43
 The current time is 15:58:44

Compilation

gcc -Wall notify_inval_inode.c `pkg-config fuse3 --cflags --libs` -o notify_inval_inode

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdatomic.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define FILE_INO 2
#define FILE_NAME "current_time"
static char file_contents[MAX_STR_LEN];
static int lookup_cnt = 0;
static size_t file_size;
static _Atomic bool is_stop = false;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == FILE_INO) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = file_size;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_destroy(void *userarg)
{
(void)userarg;
is_stop = true;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, FILE_NAME) == 0) {
e.ino = FILE_INO;
lookup_cnt++;
} else
goto err_out;
e.attr_timeout = NO_TIMEOUT;
e.entry_timeout = NO_TIMEOUT;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == FILE_INO)
lookup_cnt -= nlookup;
else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, FILE_NAME, FILE_INO);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static void tfs_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
if (ino == FUSE_ROOT_ID)
fuse_reply_err(req, EISDIR);
else if ((fi->flags & O_ACCMODE) != O_RDONLY)
fuse_reply_err(req, EACCES);
else if (ino == FILE_INO)
fuse_reply_open(req, fi);
else {
// This should not happen
fprintf(stderr, "Got open for non-existing inode!\n");
fuse_reply_err(req, ENOENT);
}
}
static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
assert(ino == FILE_INO);
reply_buf_limited(req, file_contents, file_size, off, size);
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.destroy = tfs_destroy,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.open = tfs_open,
.read = tfs_read,
.forget = tfs_forget,
};
static void update_fs(void) {
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
file_size = strftime(file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(file_size != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
while(!is_stop) {
update_fs();
if (!options.no_notify && lookup_cnt) {
/* Only send notification if the kernel is aware of the inode */
/* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
* might come up during umount, when kernel side already releases
* all inodes, but does not send FUSE_DESTROY yet.
*/
int ret =
fuse_lowlevel_notify_inval_inode(se, FILE_INO, 0, 0);
if ((ret != 0 && !is_stop) &&
ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
fprintf(stderr,
"ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
strerror(-ret), -ret);
abort();
}
}
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\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;
struct fuse_loop_config *config;
pthread_t updater;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0) {
ret = 1;
goto err_out1;
}
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Start thread to update file contents */
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_inval_inode.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2notify__inval__inode_8c.html0000644000175000017500000015037115156613443024653 0ustar berndbernd libfuse: fuse-3.18.1/example/notify_inval_inode.c File Reference
libfuse
notify_inval_inode.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdatomic.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with a single file whose contents change dynamically: it always contains the current time.

While notify_store_retrieve.c uses fuse_lowlevel_notify_store() to actively push the updated data into the kernel cache, this example uses fuse_lowlevel_notify_inval_inode() to notify the kernel that the cache has to be invalidated - but the kernel still has to explicitly request the updated data on the next read.

To see the effect, first start the file system with the --no-notify option:

$ notify_inval_inode –update-interval=1 –no-notify mnt/

Observe that the output never changes, even though the file system updates it once per second. This is because the contents are cached in the kernel:

$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18

If you instead enable the notification functions, the changes become visible:

 $ notify_inval_inode --update-interval=1 mnt/
 $ for i in 1 2 3 4 5; do
 >     cat mnt/current_time
 >     sleep 1
 > done
 The current time is 15:58:40
 The current time is 15:58:41
 The current time is 15:58:42
 The current time is 15:58:43
 The current time is 15:58:44

Compilation

gcc -Wall notify_inval_inode.c `pkg-config fuse3 --cflags --libs` -o notify_inval_inode

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdatomic.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define FILE_INO 2
#define FILE_NAME "current_time"
static char file_contents[MAX_STR_LEN];
static int lookup_cnt = 0;
static size_t file_size;
static _Atomic bool is_stop = false;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == FILE_INO) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = file_size;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_destroy(void *userarg)
{
(void)userarg;
is_stop = true;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, FILE_NAME) == 0) {
e.ino = FILE_INO;
lookup_cnt++;
} else
goto err_out;
e.attr_timeout = NO_TIMEOUT;
e.entry_timeout = NO_TIMEOUT;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == FILE_INO)
lookup_cnt -= nlookup;
else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, FILE_NAME, FILE_INO);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static void tfs_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
if (ino == FUSE_ROOT_ID)
fuse_reply_err(req, EISDIR);
else if ((fi->flags & O_ACCMODE) != O_RDONLY)
fuse_reply_err(req, EACCES);
else if (ino == FILE_INO)
fuse_reply_open(req, fi);
else {
// This should not happen
fprintf(stderr, "Got open for non-existing inode!\n");
fuse_reply_err(req, ENOENT);
}
}
static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
assert(ino == FILE_INO);
reply_buf_limited(req, file_contents, file_size, off, size);
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.destroy = tfs_destroy,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.open = tfs_open,
.read = tfs_read,
.forget = tfs_forget,
};
static void update_fs(void) {
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
file_size = strftime(file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(file_size != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
while(!is_stop) {
update_fs();
if (!options.no_notify && lookup_cnt) {
/* Only send notification if the kernel is aware of the inode */
/* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
* might come up during umount, when kernel side already releases
* all inodes, but does not send FUSE_DESTROY yet.
*/
int ret =
fuse_lowlevel_notify_inval_inode(se, FILE_INO, 0, 0);
if ((ret != 0 && !is_stop) &&
ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
fprintf(stderr,
"ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
strerror(-ret), -ret);
abort();
}
}
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\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;
struct fuse_loop_config *config;
pthread_t updater;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0) {
ret = 1;
goto err_out1;
}
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Start thread to update file contents */
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_inval_inode.c.

fuse-3.18.2/doc/html/example_2notify__store__retrieve_8c.html0000644000175000017500000017001715156613443023250 0ustar berndbernd libfuse: example/notify_store_retrieve.c File Reference
libfuse
notify_store_retrieve.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with a single file whose contents change dynamically: it always contains the current time.

While notify_inval_inode.c uses fuse_lowlevel_notify_inval_inode() to let the kernel know that it has to invalidate the cache, this example actively pushes the updated data into the kernel cache using fuse_lowlevel_notify_store().

To see the effect, first start the file system with the --no-notify option:

$ notify_store_retrieve --update-interval=1 --no-notify mnt/

Observe that the output never changes, even though the file system updates it once per second. This is because the contents are cached in the kernel:

$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18

If you instead enable the notification functions, the changes become visible:

$ notify_store_retrieve --update-interval=1 mnt/
$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:40
The current time is 15:58:41
The current time is 15:58:42
The current time is 15:58:43
The current time is 15:58:44

Compilation

gcc -Wall notify_store_retrieve.c `pkg-config fuse3 --cflags --libs` -o notify_store_retrieve

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define FILE_INO 2
#define FILE_NAME "current_time"
static char file_contents[MAX_STR_LEN];
static int lookup_cnt = 0;
static int open_cnt = 0;
static size_t file_size;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
/* Keep track if we ever stored data (==1), and
received it back correctly (==2) */
static int retrieve_status = 0;
static bool is_umount = false;
/* updater thread tid */
static pthread_t updater;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == FILE_INO) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = file_size;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, FILE_NAME) == 0) {
e.ino = FILE_INO;
} else
goto err_out;
e.attr_timeout = NO_TIMEOUT;
e.entry_timeout = NO_TIMEOUT;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
/*
* must only be set when the kernel knows about the entry,
* otherwise update_fs_loop() might see a positive count, but kernel
* would not have the entry yet
*/
if (e.ino == FILE_INO) {
pthread_mutex_lock(&lock);
lookup_cnt++;
pthread_mutex_unlock(&lock);
}
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == FILE_INO) {
pthread_mutex_lock(&lock);
lookup_cnt -= nlookup;
pthread_mutex_unlock(&lock);
} else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, FILE_NAME, FILE_INO);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static void tfs_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
if (ino == FUSE_ROOT_ID)
fuse_reply_err(req, EISDIR);
else if ((fi->flags & O_ACCMODE) != O_RDONLY)
fuse_reply_err(req, EACCES);
else if (ino == FILE_INO) {
fuse_reply_open(req, fi);
pthread_mutex_lock(&lock);
open_cnt++;
pthread_mutex_unlock(&lock);
} else {
// This should not happen
fprintf(stderr, "Got open for non-existing inode!\n");
fuse_reply_err(req, ENOENT);
}
}
static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
assert(ino == FILE_INO);
reply_buf_limited(req, file_contents, file_size, off, size);
}
static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
off_t offset, struct fuse_bufvec *data) {
struct fuse_bufvec bufv;
char buf[MAX_STR_LEN];
char *expected;
ssize_t ret;
assert(ino == FILE_INO);
assert(offset == 0);
expected = (char*) cookie;
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = MAX_STR_LEN;
bufv.buf[0].mem = buf;
bufv.buf[0].flags = 0;
ret = fuse_buf_copy(&bufv, data, 0);
assert(ret > 0);
assert(strncmp(buf, expected, ret) == 0);
free(expected);
retrieve_status = 2;
}
static void tfs_destroy(void *userdata)
{
(void)userdata;
is_umount = true;
pthread_join(updater, NULL);
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.open = tfs_open,
.read = tfs_read,
.forget = tfs_forget,
.retrieve_reply = tfs_retrieve_reply,
.destroy = tfs_destroy,
};
static void update_fs(void) {
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
file_size = strftime(file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(file_size != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
struct fuse_bufvec bufv;
int ret;
while(!is_umount) {
update_fs();
pthread_mutex_lock(&lock);
if (!options.no_notify && open_cnt && lookup_cnt) {
/* Only send notification if the kernel
is aware of the inode */
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = file_size;
bufv.buf[0].mem = file_contents;
bufv.buf[0].flags = 0;
/*
* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
* might come up during umount, when kernel side already releases
* all inodes, but does not send FUSE_DESTROY yet.
*/
ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
if ((ret != 0 && !is_umount) &&
ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
fprintf(stderr,
"ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
strerror(-ret), -ret);
abort();
}
/* To make sure that everything worked correctly, ask the
kernel to send us back the stored data */
ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
0, (void*) strdup(file_contents));
assert((ret == 0 || is_umount) || ret == -ENOENT || ret == -EBADF ||
ret != -ENODEV);
if(retrieve_status == 0)
retrieve_status = 1;
}
pthread_mutex_unlock(&lock);
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\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;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Start thread to update file contents */
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
assert(retrieve_status != 1);
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
struct fuse_buf buf[1]
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_store_retrieve.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2example_2notify__store__retrieve_8c.html0000644000175000017500000017024015156613443025424 0ustar berndbernd libfuse: fuse-3.17.4/example/notify_store_retrieve.c File Reference
libfuse
notify_store_retrieve.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with a single file whose contents change dynamically: it always contains the current time.

While notify_inval_inode.c uses fuse_lowlevel_notify_inval_inode() to let the kernel know that it has to invalidate the cache, this example actively pushes the updated data into the kernel cache using fuse_lowlevel_notify_store().

To see the effect, first start the file system with the --no-notify option:

$ notify_store_retrieve --update-interval=1 --no-notify mnt/

Observe that the output never changes, even though the file system updates it once per second. This is because the contents are cached in the kernel:

$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18

If you instead enable the notification functions, the changes become visible:

$ notify_store_retrieve --update-interval=1 mnt/
$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:40
The current time is 15:58:41
The current time is 15:58:42
The current time is 15:58:43
The current time is 15:58:44

Compilation

gcc -Wall notify_store_retrieve.c `pkg-config fuse3 --cflags --libs` -o notify_store_retrieve

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define FILE_INO 2
#define FILE_NAME "current_time"
static char file_contents[MAX_STR_LEN];
static int lookup_cnt = 0;
static int open_cnt = 0;
static size_t file_size;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
/* Keep track if we ever stored data (==1), and
received it back correctly (==2) */
static int retrieve_status = 0;
static bool is_umount = false;
/* updater thread tid */
static pthread_t updater;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == FILE_INO) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = file_size;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, FILE_NAME) == 0) {
e.ino = FILE_INO;
} else
goto err_out;
e.attr_timeout = NO_TIMEOUT;
e.entry_timeout = NO_TIMEOUT;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
/*
* must only be set when the kernel knows about the entry,
* otherwise update_fs_loop() might see a positive count, but kernel
* would not have the entry yet
*/
if (e.ino == FILE_INO) {
pthread_mutex_lock(&lock);
lookup_cnt++;
pthread_mutex_unlock(&lock);
}
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == FILE_INO) {
pthread_mutex_lock(&lock);
lookup_cnt -= nlookup;
pthread_mutex_unlock(&lock);
} else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, FILE_NAME, FILE_INO);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static void tfs_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
if (ino == FUSE_ROOT_ID)
fuse_reply_err(req, EISDIR);
else if ((fi->flags & O_ACCMODE) != O_RDONLY)
fuse_reply_err(req, EACCES);
else if (ino == FILE_INO) {
fuse_reply_open(req, fi);
pthread_mutex_lock(&lock);
open_cnt++;
pthread_mutex_unlock(&lock);
} else {
// This should not happen
fprintf(stderr, "Got open for non-existing inode!\n");
fuse_reply_err(req, ENOENT);
}
}
static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
assert(ino == FILE_INO);
reply_buf_limited(req, file_contents, file_size, off, size);
}
static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
off_t offset, struct fuse_bufvec *data) {
struct fuse_bufvec bufv;
char buf[MAX_STR_LEN];
char *expected;
ssize_t ret;
assert(ino == FILE_INO);
assert(offset == 0);
expected = (char*) cookie;
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = MAX_STR_LEN;
bufv.buf[0].mem = buf;
bufv.buf[0].flags = 0;
ret = fuse_buf_copy(&bufv, data, 0);
assert(ret > 0);
assert(strncmp(buf, expected, ret) == 0);
free(expected);
retrieve_status = 2;
}
static void tfs_destroy(void *userdata)
{
(void)userdata;
is_umount = true;
pthread_join(updater, NULL);
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.open = tfs_open,
.read = tfs_read,
.forget = tfs_forget,
.retrieve_reply = tfs_retrieve_reply,
.destroy = tfs_destroy,
};
static void update_fs(void) {
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
file_size = strftime(file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(file_size != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
struct fuse_bufvec bufv;
int ret;
while(!is_umount) {
update_fs();
pthread_mutex_lock(&lock);
if (!options.no_notify && open_cnt && lookup_cnt) {
/* Only send notification if the kernel
is aware of the inode */
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = file_size;
bufv.buf[0].mem = file_contents;
bufv.buf[0].flags = 0;
/*
* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
* might come up during umount, when kernel side already releases
* all inodes, but does not send FUSE_DESTROY yet.
*/
ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
if ((ret != 0 && !is_umount) &&
ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
fprintf(stderr,
"ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
strerror(-ret), -ret);
abort();
}
/* To make sure that everything worked correctly, ask the
kernel to send us back the stored data */
ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
0, (void*) strdup(file_contents));
assert((ret == 0 || is_umount) || ret == -ENOENT || ret == -EBADF ||
ret != -ENODEV);
if(retrieve_status == 0)
retrieve_status = 1;
}
pthread_mutex_unlock(&lock);
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\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;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Start thread to update file contents */
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
assert(retrieve_status != 1);
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
struct fuse_buf buf[1]
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_store_retrieve.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2notify__store__retrieve_8c.html0000644000175000017500000017024015156613443025422 0ustar berndbernd libfuse: fuse-3.18.1/example/notify_store_retrieve.c File Reference
libfuse
notify_store_retrieve.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with a single file whose contents change dynamically: it always contains the current time.

While notify_inval_inode.c uses fuse_lowlevel_notify_inval_inode() to let the kernel know that it has to invalidate the cache, this example actively pushes the updated data into the kernel cache using fuse_lowlevel_notify_store().

To see the effect, first start the file system with the --no-notify option:

$ notify_store_retrieve --update-interval=1 --no-notify mnt/

Observe that the output never changes, even though the file system updates it once per second. This is because the contents are cached in the kernel:

$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18

If you instead enable the notification functions, the changes become visible:

$ notify_store_retrieve --update-interval=1 mnt/
$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:40
The current time is 15:58:41
The current time is 15:58:42
The current time is 15:58:43
The current time is 15:58:44

Compilation

gcc -Wall notify_store_retrieve.c `pkg-config fuse3 --cflags --libs` -o notify_store_retrieve

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define FILE_INO 2
#define FILE_NAME "current_time"
static char file_contents[MAX_STR_LEN];
static int lookup_cnt = 0;
static int open_cnt = 0;
static size_t file_size;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
/* Keep track if we ever stored data (==1), and
received it back correctly (==2) */
static int retrieve_status = 0;
static bool is_umount = false;
/* updater thread tid */
static pthread_t updater;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == FILE_INO) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = file_size;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, FILE_NAME) == 0) {
e.ino = FILE_INO;
} else
goto err_out;
e.attr_timeout = NO_TIMEOUT;
e.entry_timeout = NO_TIMEOUT;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
/*
* must only be set when the kernel knows about the entry,
* otherwise update_fs_loop() might see a positive count, but kernel
* would not have the entry yet
*/
if (e.ino == FILE_INO) {
pthread_mutex_lock(&lock);
lookup_cnt++;
pthread_mutex_unlock(&lock);
}
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == FILE_INO) {
pthread_mutex_lock(&lock);
lookup_cnt -= nlookup;
pthread_mutex_unlock(&lock);
} else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, FILE_NAME, FILE_INO);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static void tfs_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
if (ino == FUSE_ROOT_ID)
fuse_reply_err(req, EISDIR);
else if ((fi->flags & O_ACCMODE) != O_RDONLY)
fuse_reply_err(req, EACCES);
else if (ino == FILE_INO) {
fuse_reply_open(req, fi);
pthread_mutex_lock(&lock);
open_cnt++;
pthread_mutex_unlock(&lock);
} else {
// This should not happen
fprintf(stderr, "Got open for non-existing inode!\n");
fuse_reply_err(req, ENOENT);
}
}
static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
assert(ino == FILE_INO);
reply_buf_limited(req, file_contents, file_size, off, size);
}
static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
off_t offset, struct fuse_bufvec *data) {
struct fuse_bufvec bufv;
char buf[MAX_STR_LEN];
char *expected;
ssize_t ret;
assert(ino == FILE_INO);
assert(offset == 0);
expected = (char*) cookie;
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = MAX_STR_LEN;
bufv.buf[0].mem = buf;
bufv.buf[0].flags = 0;
ret = fuse_buf_copy(&bufv, data, 0);
assert(ret > 0);
assert(strncmp(buf, expected, ret) == 0);
free(expected);
retrieve_status = 2;
}
static void tfs_destroy(void *userdata)
{
(void)userdata;
is_umount = true;
pthread_join(updater, NULL);
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.open = tfs_open,
.read = tfs_read,
.forget = tfs_forget,
.retrieve_reply = tfs_retrieve_reply,
.destroy = tfs_destroy,
};
static void update_fs(void) {
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
file_size = strftime(file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(file_size != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
struct fuse_bufvec bufv;
int ret;
while(!is_umount) {
update_fs();
pthread_mutex_lock(&lock);
if (!options.no_notify && open_cnt && lookup_cnt) {
/* Only send notification if the kernel
is aware of the inode */
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = file_size;
bufv.buf[0].mem = file_contents;
bufv.buf[0].flags = 0;
/*
* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
* might come up during umount, when kernel side already releases
* all inodes, but does not send FUSE_DESTROY yet.
*/
ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
if ((ret != 0 && !is_umount) &&
ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
fprintf(stderr,
"ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
strerror(-ret), -ret);
abort();
}
/* To make sure that everything worked correctly, ask the
kernel to send us back the stored data */
ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
0, (void*) strdup(file_contents));
assert((ret == 0 || is_umount) || ret == -ENOENT || ret == -EBADF ||
ret != -ENODEV);
if(retrieve_status == 0)
retrieve_status = 1;
}
pthread_mutex_unlock(&lock);
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\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;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Start thread to update file contents */
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
assert(retrieve_status != 1);
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
struct fuse_buf buf[1]
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_store_retrieve.c.

fuse-3.18.2/doc/html/example_2null_8c.html0000644000175000017500000022032015156613443017264 0ustar berndbernd libfuse: example/null.c File Reference
libfuse
null.c File Reference
#include <fuse.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>

Go to the source code of this file.

Detailed Description

This "filesystem" provides only a single file. The mountpoint needs to be a file rather than a directory. All writes to the file will be discarded, and reading the file always returns \0.

Compile with:

gcc -Wall null.c `pkg-config fuse3 --cflags --libs` -o null

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#include <fuse.h>
#ifdef HAVE_LIBULOCKMGR
#include <ulockmgr.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <sys/file.h> /* flock(2) */
#include "passthrough_helpers.h"
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = 1;
cfg->nullpath_ok = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
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_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
struct xmp_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static int xmp_opendir(const char *path, struct fuse_file_info *fi)
{
int res;
struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
if (d == NULL)
return -ENOMEM;
d->dp = opendir(path);
if (d->dp == NULL) {
res = -errno;
free(d);
return res;
}
d->offset = 0;
d->entry = NULL;
fi->fh = (unsigned long) d;
return 0;
}
static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
{
return (struct xmp_dirp *) (uintptr_t) fi->fh;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
if (offset != d->offset) {
#ifndef __FreeBSD__
seekdir(d->dp, offset);
#else
/* Subtract the one that we add when calling
telldir() below */
seekdir(d->dp, offset-1);
#endif
d->entry = NULL;
d->offset = offset;
}
while (1) {
struct stat st;
off_t nextoff;
if (!d->entry) {
d->entry = readdir(d->dp);
if (!d->entry)
break;
}
#ifdef HAVE_FSTATAT
if (flags & FUSE_READDIR_PLUS) {
int res;
res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
AT_SYMLINK_NOFOLLOW);
if (res != -1)
fill_flags |= FUSE_FILL_DIR_PLUS;
}
#endif
if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
memset(&st, 0, sizeof(st));
st.st_ino = d->entry->d_ino;
st.st_mode = d->entry->d_type << 12;
}
nextoff = telldir(d->dp);
#ifdef __FreeBSD__
/* Under FreeBSD, telldir() may return 0 the first time
it is called. But for libfuse, an offset of zero
means that offsets are not supported, so we shift
everything by one. */
nextoff++;
#endif
if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
break;
d->entry = NULL;
d->offset = nextoff;
}
return 0;
}
static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
closedir(d->dp);
free(d);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
if (S_ISFIFO(mode))
res = mkfifo(path, mode);
else
res = mknod(path, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to, unsigned int flags)
{
int res;
/* When we have renameat2() in libc, then we can implement flags */
if (flags)
return -EINVAL;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = fchmod(fi->fh, mode);
else
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
int res;
if (fi)
res = fchown(fi->fh, uid, gid);
else
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
int res;
/* don't use utime/utimes since they follow symlinks */
if (fi)
res = futimens(fi->fh, ts);
else
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
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_open(const char *path, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags);
if (fd == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = fd;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void) path;
res = pread(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
size_t size, off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec *src;
(void) path;
src = malloc(sizeof(struct fuse_bufvec));
if (src == NULL)
return -ENOMEM;
*src = FUSE_BUFVEC_INIT(size);
src->buf[0].fd = fi->fh;
src->buf[0].pos = offset;
*bufp = src;
return 0;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res;
(void) path;
res = pwrite(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
(void) path;
dst.buf[0].fd = fi->fh;
dst.buf[0].pos = offset;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_flush(const char *path, struct fuse_file_info *fi)
{
int res;
(void) path;
/* This is called from every close on an open file, so call the
close on the underlying filesystem. But since flush may be
called multiple times for an open file, this must not really
close the file. This is important if used on a network
filesystem like NFS which flush the data/metadata on close() */
res = close(dup(fi->fh));
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
int res;
(void) path;
#ifndef HAVE_FDATASYNC
(void) isdatasync;
#else
if (isdatasync)
res = fdatasync(fi->fh);
else
#endif
res = fsync(fi->fh);
if (res == -1)
return -errno;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
(void) path;
return do_fallocate(fi->fh, mode, offset, length);
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_LIBULOCKMGR
static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
(void) path;
return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
sizeof(fi->lock_owner));
}
#endif
static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
{
int res;
(void) path;
res = flock(fi->fh, op);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t off_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t off_out, size_t len, int flags)
{
ssize_t res;
(void) path_in;
(void) path_out;
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res == -1)
return -errno;
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
off_t res;
(void) path;
res = lseek(fi->fh, off, whence);
if (res == -1)
return -errno;
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.opendir = xmp_opendir,
.readdir = xmp_readdir,
.releasedir = xmp_releasedir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.create = xmp_create,
.open = xmp_open,
.read = xmp_read,
.read_buf = xmp_read_buf,
.write = xmp_write,
.write_buf = xmp_write_buf,
.statfs = xmp_statfs,
.flush = xmp_flush,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_LIBULOCKMGR
.lock = xmp_lock,
#endif
.flock = xmp_flock,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file null.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2example_2null_8c.html0000644000175000017500000022054115156613443021447 0ustar berndbernd libfuse: fuse-3.17.4/example/null.c File Reference
libfuse
null.c File Reference
#include <fuse.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>

Go to the source code of this file.

Detailed Description

This "filesystem" provides only a single file. The mountpoint needs to be a file rather than a directory. All writes to the file will be discarded, and reading the file always returns \0.

Compile with:

gcc -Wall null.c `pkg-config fuse3 --cflags --libs` -o null

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#include <fuse.h>
#ifdef HAVE_LIBULOCKMGR
#include <ulockmgr.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <sys/file.h> /* flock(2) */
#include "passthrough_helpers.h"
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = 1;
cfg->nullpath_ok = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
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_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
struct xmp_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static int xmp_opendir(const char *path, struct fuse_file_info *fi)
{
int res;
struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
if (d == NULL)
return -ENOMEM;
d->dp = opendir(path);
if (d->dp == NULL) {
res = -errno;
free(d);
return res;
}
d->offset = 0;
d->entry = NULL;
fi->fh = (unsigned long) d;
return 0;
}
static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
{
return (struct xmp_dirp *) (uintptr_t) fi->fh;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
if (offset != d->offset) {
#ifndef __FreeBSD__
seekdir(d->dp, offset);
#else
/* Subtract the one that we add when calling
telldir() below */
seekdir(d->dp, offset-1);
#endif
d->entry = NULL;
d->offset = offset;
}
while (1) {
struct stat st;
off_t nextoff;
if (!d->entry) {
d->entry = readdir(d->dp);
if (!d->entry)
break;
}
#ifdef HAVE_FSTATAT
if (flags & FUSE_READDIR_PLUS) {
int res;
res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
AT_SYMLINK_NOFOLLOW);
if (res != -1)
fill_flags |= FUSE_FILL_DIR_PLUS;
}
#endif
if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
memset(&st, 0, sizeof(st));
st.st_ino = d->entry->d_ino;
st.st_mode = d->entry->d_type << 12;
}
nextoff = telldir(d->dp);
#ifdef __FreeBSD__
/* Under FreeBSD, telldir() may return 0 the first time
it is called. But for libfuse, an offset of zero
means that offsets are not supported, so we shift
everything by one. */
nextoff++;
#endif
if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
break;
d->entry = NULL;
d->offset = nextoff;
}
return 0;
}
static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
closedir(d->dp);
free(d);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
if (S_ISFIFO(mode))
res = mkfifo(path, mode);
else
res = mknod(path, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to, unsigned int flags)
{
int res;
/* When we have renameat2() in libc, then we can implement flags */
if (flags)
return -EINVAL;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = fchmod(fi->fh, mode);
else
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
int res;
if (fi)
res = fchown(fi->fh, uid, gid);
else
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
int res;
/* don't use utime/utimes since they follow symlinks */
if (fi)
res = futimens(fi->fh, ts);
else
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
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_open(const char *path, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags);
if (fd == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = fd;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void) path;
res = pread(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
size_t size, off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec *src;
(void) path;
src = malloc(sizeof(struct fuse_bufvec));
if (src == NULL)
return -ENOMEM;
*src = FUSE_BUFVEC_INIT(size);
src->buf[0].fd = fi->fh;
src->buf[0].pos = offset;
*bufp = src;
return 0;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res;
(void) path;
res = pwrite(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
(void) path;
dst.buf[0].fd = fi->fh;
dst.buf[0].pos = offset;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_flush(const char *path, struct fuse_file_info *fi)
{
int res;
(void) path;
/* This is called from every close on an open file, so call the
close on the underlying filesystem. But since flush may be
called multiple times for an open file, this must not really
close the file. This is important if used on a network
filesystem like NFS which flush the data/metadata on close() */
res = close(dup(fi->fh));
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
int res;
(void) path;
#ifndef HAVE_FDATASYNC
(void) isdatasync;
#else
if (isdatasync)
res = fdatasync(fi->fh);
else
#endif
res = fsync(fi->fh);
if (res == -1)
return -errno;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
(void) path;
return do_fallocate(fi->fh, mode, offset, length);
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_LIBULOCKMGR
static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
(void) path;
return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
sizeof(fi->lock_owner));
}
#endif
static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
{
int res;
(void) path;
res = flock(fi->fh, op);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t off_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t off_out, size_t len, int flags)
{
ssize_t res;
(void) path_in;
(void) path_out;
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res == -1)
return -errno;
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
off_t res;
(void) path;
res = lseek(fi->fh, off, whence);
if (res == -1)
return -errno;
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.opendir = xmp_opendir,
.readdir = xmp_readdir,
.releasedir = xmp_releasedir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.create = xmp_create,
.open = xmp_open,
.read = xmp_read,
.read_buf = xmp_read_buf,
.write = xmp_write,
.write_buf = xmp_write_buf,
.statfs = xmp_statfs,
.flush = xmp_flush,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_LIBULOCKMGR
.lock = xmp_lock,
#endif
.flock = xmp_flock,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file null.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2null_8c.html0000644000175000017500000022054115156613443021445 0ustar berndbernd libfuse: fuse-3.18.1/example/null.c File Reference
libfuse
null.c File Reference
#include <fuse.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>

Go to the source code of this file.

Detailed Description

This "filesystem" provides only a single file. The mountpoint needs to be a file rather than a directory. All writes to the file will be discarded, and reading the file always returns \0.

Compile with:

gcc -Wall null.c `pkg-config fuse3 --cflags --libs` -o null

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#include <fuse.h>
#ifdef HAVE_LIBULOCKMGR
#include <ulockmgr.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <sys/file.h> /* flock(2) */
#include "passthrough_helpers.h"
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = 1;
cfg->nullpath_ok = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
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_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
struct xmp_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static int xmp_opendir(const char *path, struct fuse_file_info *fi)
{
int res;
struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
if (d == NULL)
return -ENOMEM;
d->dp = opendir(path);
if (d->dp == NULL) {
res = -errno;
free(d);
return res;
}
d->offset = 0;
d->entry = NULL;
fi->fh = (unsigned long) d;
return 0;
}
static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
{
return (struct xmp_dirp *) (uintptr_t) fi->fh;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
if (offset != d->offset) {
#ifndef __FreeBSD__
seekdir(d->dp, offset);
#else
/* Subtract the one that we add when calling
telldir() below */
seekdir(d->dp, offset-1);
#endif
d->entry = NULL;
d->offset = offset;
}
while (1) {
struct stat st;
off_t nextoff;
if (!d->entry) {
d->entry = readdir(d->dp);
if (!d->entry)
break;
}
#ifdef HAVE_FSTATAT
if (flags & FUSE_READDIR_PLUS) {
int res;
res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
AT_SYMLINK_NOFOLLOW);
if (res != -1)
fill_flags |= FUSE_FILL_DIR_PLUS;
}
#endif
if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
memset(&st, 0, sizeof(st));
st.st_ino = d->entry->d_ino;
st.st_mode = d->entry->d_type << 12;
}
nextoff = telldir(d->dp);
#ifdef __FreeBSD__
/* Under FreeBSD, telldir() may return 0 the first time
it is called. But for libfuse, an offset of zero
means that offsets are not supported, so we shift
everything by one. */
nextoff++;
#endif
if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
break;
d->entry = NULL;
d->offset = nextoff;
}
return 0;
}
static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
closedir(d->dp);
free(d);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
if (S_ISFIFO(mode))
res = mkfifo(path, mode);
else
res = mknod(path, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to, unsigned int flags)
{
int res;
/* When we have renameat2() in libc, then we can implement flags */
if (flags)
return -EINVAL;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = fchmod(fi->fh, mode);
else
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
int res;
if (fi)
res = fchown(fi->fh, uid, gid);
else
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
int res;
/* don't use utime/utimes since they follow symlinks */
if (fi)
res = futimens(fi->fh, ts);
else
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
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_open(const char *path, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags);
if (fd == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = fd;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void) path;
res = pread(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
size_t size, off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec *src;
(void) path;
src = malloc(sizeof(struct fuse_bufvec));
if (src == NULL)
return -ENOMEM;
*src = FUSE_BUFVEC_INIT(size);
src->buf[0].fd = fi->fh;
src->buf[0].pos = offset;
*bufp = src;
return 0;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res;
(void) path;
res = pwrite(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
(void) path;
dst.buf[0].fd = fi->fh;
dst.buf[0].pos = offset;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_flush(const char *path, struct fuse_file_info *fi)
{
int res;
(void) path;
/* This is called from every close on an open file, so call the
close on the underlying filesystem. But since flush may be
called multiple times for an open file, this must not really
close the file. This is important if used on a network
filesystem like NFS which flush the data/metadata on close() */
res = close(dup(fi->fh));
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
int res;
(void) path;
#ifndef HAVE_FDATASYNC
(void) isdatasync;
#else
if (isdatasync)
res = fdatasync(fi->fh);
else
#endif
res = fsync(fi->fh);
if (res == -1)
return -errno;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
(void) path;
return do_fallocate(fi->fh, mode, offset, length);
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_LIBULOCKMGR
static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
(void) path;
return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
sizeof(fi->lock_owner));
}
#endif
static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
{
int res;
(void) path;
res = flock(fi->fh, op);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t off_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t off_out, size_t len, int flags)
{
ssize_t res;
(void) path_in;
(void) path_out;
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res == -1)
return -errno;
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
off_t res;
(void) path;
res = lseek(fi->fh, off, whence);
if (res == -1)
return -errno;
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.opendir = xmp_opendir,
.readdir = xmp_readdir,
.releasedir = xmp_releasedir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.create = xmp_create,
.open = xmp_open,
.read = xmp_read,
.read_buf = xmp_read_buf,
.write = xmp_write,
.write_buf = xmp_write_buf,
.statfs = xmp_statfs,
.flush = xmp_flush,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_LIBULOCKMGR
.lock = xmp_lock,
#endif
.flock = xmp_flock,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file null.c.

fuse-3.18.2/doc/html/example_2passthrough_8c.html0000644000175000017500000015505215156613443020672 0ustar berndbernd libfuse: example/passthrough.c File Reference
libfuse
passthrough.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. Its performance is terrible.

Compile with

gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#ifdef linux
/* For pread()/pwrite()/utimensat() */
#define _XOPEN_SOURCE 700
#endif
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include "passthrough_helpers.h"
static int fill_dir_plus = 0;
static int readdir_zero_ino;
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = !readdir_zero_ino;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
if (!cfg->auto_cache) {
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)
{
(void) fi;
int res;
res = lstat(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
DIR *dp;
struct dirent *de;
(void) offset;
(void) fi;
(void) flags;
dp = opendir(path);
if (dp == NULL)
return -errno;
while ((de = readdir(dp)) != NULL) {
struct stat st;
if (fill_dir_plus) {
fstatat(dirfd(dp), de->d_name, &st,
AT_SYMLINK_NOFOLLOW);
} else {
memset(&st, 0, sizeof(st));
st.st_ino = de->d_ino;
st.st_mode = de->d_type << 12;
}
if (readdir_zero_ino)
st.st_ino = 0;
if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
break;
}
closedir(dp);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
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;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
(void) fi;
int res;
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
(void) fi;
int res;
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if (fi != NULL)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
(void) fi;
int res;
/* don't use utime/utimes since they follow symlinks */
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
static int xmp_create(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
res = open(path, fi->flags, mode);
if (res == -1)
return -errno;
fi->fh = res;
return 0;
}
static int xmp_open(const char *path, struct fuse_file_info *fi)
{
int res;
res = open(path, fi->flags);
if (res == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = res;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int fd;
int res;
if(fi == NULL)
fd = open(path, O_RDONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = pread(fd, buf, size, offset);
if (res == -1)
res = -errno;
if(fi == NULL)
close(fd);
return res;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
if(fi == NULL)
fd = open(path, O_WRONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = pwrite(fd, buf, size, offset);
if (res == -1)
res = -errno;
if(fi == NULL)
close(fd);
return res;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
/* Just a stub. This method is optional and can safely be left
unimplemented */
(void) path;
(void) isdatasync;
(void) fi;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
if(fi == NULL)
fd = open(path, O_WRONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = do_fallocate(fd, mode, offset, length);
if(fi == NULL)
close(fd);
return res;
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t offset_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t offset_out, size_t len, int flags)
{
int fd_in, fd_out;
ssize_t res;
if(fi_in == NULL)
fd_in = open(path_in, O_RDONLY);
else
fd_in = fi_in->fh;
if (fd_in == -1)
return -errno;
if(fi_out == NULL)
fd_out = open(path_out, O_WRONLY);
else
fd_out = fi_out->fh;
if (fd_out == -1) {
close(fd_in);
return -errno;
}
res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
flags);
if (res == -1)
res = -errno;
if (fi_out == NULL)
close(fd_out);
if (fi_in == NULL)
close(fd_in);
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
int fd;
off_t res;
if (fi == NULL)
fd = open(path, O_RDONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = lseek(fd, off, whence);
if (res == -1)
res = -errno;
if (fi == NULL)
close(fd);
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.readdir = xmp_readdir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.open = xmp_open,
.create = xmp_create,
.read = xmp_read,
.write = xmp_write,
.statfs = xmp_statfs,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
enum { MAX_ARGS = 10 };
int i,new_argc;
char *new_argv[MAX_ARGS];
umask(0);
/* Process the "--plus" option apart */
for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
if (!strcmp(argv[i], "--plus")) {
fill_dir_plus = FUSE_FILL_DIR_PLUS;
} else if (!strcmp(argv[i], "--readdir-zero-inodes")) {
// Return zero inodes from readdir
readdir_zero_ino = 1;
} else {
new_argv[new_argc++] = argv[i];
}
}
return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_readdir_flags
Definition fuse.h:42
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t auto_cache
Definition fuse.h:253
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file passthrough.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2example_2passthrough_8c.html0000644000175000017500000015527315156613443023055 0ustar berndbernd libfuse: fuse-3.17.4/example/passthrough.c File Reference
libfuse
passthrough.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. Its performance is terrible.

Compile with

gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#ifdef linux
/* For pread()/pwrite()/utimensat() */
#define _XOPEN_SOURCE 700
#endif
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include "passthrough_helpers.h"
static int fill_dir_plus = 0;
static int readdir_zero_ino;
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = !readdir_zero_ino;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
if (!cfg->auto_cache) {
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)
{
(void) fi;
int res;
res = lstat(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
DIR *dp;
struct dirent *de;
(void) offset;
(void) fi;
(void) flags;
dp = opendir(path);
if (dp == NULL)
return -errno;
while ((de = readdir(dp)) != NULL) {
struct stat st;
if (fill_dir_plus) {
fstatat(dirfd(dp), de->d_name, &st,
AT_SYMLINK_NOFOLLOW);
} else {
memset(&st, 0, sizeof(st));
st.st_ino = de->d_ino;
st.st_mode = de->d_type << 12;
}
if (readdir_zero_ino)
st.st_ino = 0;
if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
break;
}
closedir(dp);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
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;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
(void) fi;
int res;
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
(void) fi;
int res;
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if (fi != NULL)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
(void) fi;
int res;
/* don't use utime/utimes since they follow symlinks */
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
static int xmp_create(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
res = open(path, fi->flags, mode);
if (res == -1)
return -errno;
fi->fh = res;
return 0;
}
static int xmp_open(const char *path, struct fuse_file_info *fi)
{
int res;
res = open(path, fi->flags);
if (res == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = res;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int fd;
int res;
if(fi == NULL)
fd = open(path, O_RDONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = pread(fd, buf, size, offset);
if (res == -1)
res = -errno;
if(fi == NULL)
close(fd);
return res;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
if(fi == NULL)
fd = open(path, O_WRONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = pwrite(fd, buf, size, offset);
if (res == -1)
res = -errno;
if(fi == NULL)
close(fd);
return res;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
/* Just a stub. This method is optional and can safely be left
unimplemented */
(void) path;
(void) isdatasync;
(void) fi;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
if(fi == NULL)
fd = open(path, O_WRONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = do_fallocate(fd, mode, offset, length);
if(fi == NULL)
close(fd);
return res;
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t offset_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t offset_out, size_t len, int flags)
{
int fd_in, fd_out;
ssize_t res;
if(fi_in == NULL)
fd_in = open(path_in, O_RDONLY);
else
fd_in = fi_in->fh;
if (fd_in == -1)
return -errno;
if(fi_out == NULL)
fd_out = open(path_out, O_WRONLY);
else
fd_out = fi_out->fh;
if (fd_out == -1) {
close(fd_in);
return -errno;
}
res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
flags);
if (res == -1)
res = -errno;
if (fi_out == NULL)
close(fd_out);
if (fi_in == NULL)
close(fd_in);
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
int fd;
off_t res;
if (fi == NULL)
fd = open(path, O_RDONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = lseek(fd, off, whence);
if (res == -1)
res = -errno;
if (fi == NULL)
close(fd);
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.readdir = xmp_readdir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.open = xmp_open,
.create = xmp_create,
.read = xmp_read,
.write = xmp_write,
.statfs = xmp_statfs,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
enum { MAX_ARGS = 10 };
int i,new_argc;
char *new_argv[MAX_ARGS];
umask(0);
/* Process the "--plus" option apart */
for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
if (!strcmp(argv[i], "--plus")) {
fill_dir_plus = FUSE_FILL_DIR_PLUS;
} else if (!strcmp(argv[i], "--readdir-zero-inodes")) {
// Return zero inodes from readdir
readdir_zero_ino = 1;
} else {
new_argv[new_argc++] = argv[i];
}
}
return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_readdir_flags
Definition fuse.h:42
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t auto_cache
Definition fuse.h:253
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file passthrough.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2passthrough_8c.html0000644000175000017500000015527315156613443023053 0ustar berndbernd libfuse: fuse-3.18.1/example/passthrough.c File Reference
libfuse
passthrough.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. Its performance is terrible.

Compile with

gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#ifdef linux
/* For pread()/pwrite()/utimensat() */
#define _XOPEN_SOURCE 700
#endif
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include "passthrough_helpers.h"
static int fill_dir_plus = 0;
static int readdir_zero_ino;
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = !readdir_zero_ino;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
if (!cfg->auto_cache) {
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)
{
(void) fi;
int res;
res = lstat(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
DIR *dp;
struct dirent *de;
(void) offset;
(void) fi;
(void) flags;
dp = opendir(path);
if (dp == NULL)
return -errno;
while ((de = readdir(dp)) != NULL) {
struct stat st;
if (fill_dir_plus) {
fstatat(dirfd(dp), de->d_name, &st,
AT_SYMLINK_NOFOLLOW);
} else {
memset(&st, 0, sizeof(st));
st.st_ino = de->d_ino;
st.st_mode = de->d_type << 12;
}
if (readdir_zero_ino)
st.st_ino = 0;
if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
break;
}
closedir(dp);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
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;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
(void) fi;
int res;
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
(void) fi;
int res;
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if (fi != NULL)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
(void) fi;
int res;
/* don't use utime/utimes since they follow symlinks */
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
static int xmp_create(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
res = open(path, fi->flags, mode);
if (res == -1)
return -errno;
fi->fh = res;
return 0;
}
static int xmp_open(const char *path, struct fuse_file_info *fi)
{
int res;
res = open(path, fi->flags);
if (res == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = res;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int fd;
int res;
if(fi == NULL)
fd = open(path, O_RDONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = pread(fd, buf, size, offset);
if (res == -1)
res = -errno;
if(fi == NULL)
close(fd);
return res;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
if(fi == NULL)
fd = open(path, O_WRONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = pwrite(fd, buf, size, offset);
if (res == -1)
res = -errno;
if(fi == NULL)
close(fd);
return res;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
/* Just a stub. This method is optional and can safely be left
unimplemented */
(void) path;
(void) isdatasync;
(void) fi;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
if(fi == NULL)
fd = open(path, O_WRONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = do_fallocate(fd, mode, offset, length);
if(fi == NULL)
close(fd);
return res;
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t offset_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t offset_out, size_t len, int flags)
{
int fd_in, fd_out;
ssize_t res;
if(fi_in == NULL)
fd_in = open(path_in, O_RDONLY);
else
fd_in = fi_in->fh;
if (fd_in == -1)
return -errno;
if(fi_out == NULL)
fd_out = open(path_out, O_WRONLY);
else
fd_out = fi_out->fh;
if (fd_out == -1) {
close(fd_in);
return -errno;
}
res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
flags);
if (res == -1)
res = -errno;
if (fi_out == NULL)
close(fd_out);
if (fi_in == NULL)
close(fd_in);
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
int fd;
off_t res;
if (fi == NULL)
fd = open(path, O_RDONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = lseek(fd, off, whence);
if (res == -1)
res = -errno;
if (fi == NULL)
close(fd);
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.readdir = xmp_readdir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.open = xmp_open,
.create = xmp_create,
.read = xmp_read,
.write = xmp_write,
.statfs = xmp_statfs,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
enum { MAX_ARGS = 10 };
int i,new_argc;
char *new_argv[MAX_ARGS];
umask(0);
/* Process the "--plus" option apart */
for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
if (!strcmp(argv[i], "--plus")) {
fill_dir_plus = FUSE_FILL_DIR_PLUS;
} else if (!strcmp(argv[i], "--readdir-zero-inodes")) {
// Return zero inodes from readdir
readdir_zero_ino = 1;
} else {
new_argv[new_argc++] = argv[i];
}
}
return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_readdir_flags
Definition fuse.h:42
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t auto_cache
Definition fuse.h:253
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file passthrough.c.

fuse-3.18.2/doc/html/example_2passthrough__fh_8c.html0000644000175000017500000022117215156613443021503 0ustar berndbernd libfuse: example/passthrough_fh.c File Reference
libfuse
passthrough_fh.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/file.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. This implementation is a little more sophisticated than the one in passthrough.c, so performance is not quite as bad.

Compile with:

gcc -Wall passthrough_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o passthrough_fh

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#include <fuse.h>
#ifdef HAVE_LIBULOCKMGR
#include <ulockmgr.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <sys/file.h> /* flock(2) */
#include "passthrough_helpers.h"
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = 1;
cfg->nullpath_ok = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
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_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
struct xmp_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static int xmp_opendir(const char *path, struct fuse_file_info *fi)
{
int res;
struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
if (d == NULL)
return -ENOMEM;
d->dp = opendir(path);
if (d->dp == NULL) {
res = -errno;
free(d);
return res;
}
d->offset = 0;
d->entry = NULL;
fi->fh = (unsigned long) d;
return 0;
}
static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
{
return (struct xmp_dirp *) (uintptr_t) fi->fh;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
if (offset != d->offset) {
#ifndef __FreeBSD__
seekdir(d->dp, offset);
#else
/* Subtract the one that we add when calling
telldir() below */
seekdir(d->dp, offset-1);
#endif
d->entry = NULL;
d->offset = offset;
}
while (1) {
struct stat st;
off_t nextoff;
if (!d->entry) {
d->entry = readdir(d->dp);
if (!d->entry)
break;
}
#ifdef HAVE_FSTATAT
if (flags & FUSE_READDIR_PLUS) {
int res;
res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
AT_SYMLINK_NOFOLLOW);
if (res != -1)
fill_flags |= FUSE_FILL_DIR_PLUS;
}
#endif
if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
memset(&st, 0, sizeof(st));
st.st_ino = d->entry->d_ino;
st.st_mode = d->entry->d_type << 12;
}
nextoff = telldir(d->dp);
#ifdef __FreeBSD__
/* Under FreeBSD, telldir() may return 0 the first time
it is called. But for libfuse, an offset of zero
means that offsets are not supported, so we shift
everything by one. */
nextoff++;
#endif
if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
break;
d->entry = NULL;
d->offset = nextoff;
}
return 0;
}
static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
closedir(d->dp);
free(d);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
if (S_ISFIFO(mode))
res = mkfifo(path, mode);
else
res = mknod(path, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to, unsigned int flags)
{
int res;
/* When we have renameat2() in libc, then we can implement flags */
if (flags)
return -EINVAL;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = fchmod(fi->fh, mode);
else
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
int res;
if (fi)
res = fchown(fi->fh, uid, gid);
else
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
int res;
/* don't use utime/utimes since they follow symlinks */
if (fi)
res = futimens(fi->fh, ts);
else
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
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_open(const char *path, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags);
if (fd == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = fd;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void) path;
res = pread(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
size_t size, off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec *src;
(void) path;
src = malloc(sizeof(struct fuse_bufvec));
if (src == NULL)
return -ENOMEM;
*src = FUSE_BUFVEC_INIT(size);
src->buf[0].fd = fi->fh;
src->buf[0].pos = offset;
*bufp = src;
return 0;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res;
(void) path;
res = pwrite(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
(void) path;
dst.buf[0].fd = fi->fh;
dst.buf[0].pos = offset;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_flush(const char *path, struct fuse_file_info *fi)
{
int res;
(void) path;
/* This is called from every close on an open file, so call the
close on the underlying filesystem. But since flush may be
called multiple times for an open file, this must not really
close the file. This is important if used on a network
filesystem like NFS which flush the data/metadata on close() */
res = close(dup(fi->fh));
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
int res;
(void) path;
#ifndef HAVE_FDATASYNC
(void) isdatasync;
#else
if (isdatasync)
res = fdatasync(fi->fh);
else
#endif
res = fsync(fi->fh);
if (res == -1)
return -errno;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
(void) path;
return do_fallocate(fi->fh, mode, offset, length);
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_LIBULOCKMGR
static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
(void) path;
return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
sizeof(fi->lock_owner));
}
#endif
static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
{
int res;
(void) path;
res = flock(fi->fh, op);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t off_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t off_out, size_t len, int flags)
{
ssize_t res;
(void) path_in;
(void) path_out;
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res == -1)
return -errno;
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
off_t res;
(void) path;
res = lseek(fi->fh, off, whence);
if (res == -1)
return -errno;
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.opendir = xmp_opendir,
.readdir = xmp_readdir,
.releasedir = xmp_releasedir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.create = xmp_create,
.open = xmp_open,
.read = xmp_read,
.read_buf = xmp_read_buf,
.write = xmp_write,
.write_buf = xmp_write_buf,
.statfs = xmp_statfs,
.flush = xmp_flush,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_LIBULOCKMGR
.lock = xmp_lock,
#endif
.flock = xmp_flock,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file passthrough_fh.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2example_2passthrough__fh_8c.html0000644000175000017500000022131515156613443023660 0ustar berndbernd libfuse: fuse-3.17.4/example/passthrough_fh.c File Reference
libfuse
passthrough_fh.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/file.h>

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. This implementation is a little more sophisticated than the one in passthrough.c, so performance is not quite as bad.

Compile with:

gcc -Wall passthrough_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o passthrough_fh

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#include <fuse.h>
#ifdef HAVE_LIBULOCKMGR
#include <ulockmgr.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <sys/file.h> /* flock(2) */
#include "passthrough_helpers.h"
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = 1;
cfg->nullpath_ok = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
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_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
struct xmp_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static int xmp_opendir(const char *path, struct fuse_file_info *fi)
{
int res;
struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
if (d == NULL)
return -ENOMEM;
d->dp = opendir(path);
if (d->dp == NULL) {
res = -errno;
free(d);
return res;
}
d->offset = 0;
d->entry = NULL;
fi->fh = (unsigned long) d;
return 0;
}
static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
{
return (struct xmp_dirp *) (uintptr_t) fi->fh;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
if (offset != d->offset) {
#ifndef __FreeBSD__
seekdir(d->dp, offset);
#else
/* Subtract the one that we add when calling
telldir() below */
seekdir(d->dp, offset-1);
#endif
d->entry = NULL;
d->offset = offset;
}
while (1) {
struct stat st;
off_t nextoff;
if (!d->entry) {
d->entry = readdir(d->dp);
if (!d->entry)
break;
}
#ifdef HAVE_FSTATAT
if (flags & FUSE_READDIR_PLUS) {
int res;
res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
AT_SYMLINK_NOFOLLOW);
if (res != -1)
fill_flags |= FUSE_FILL_DIR_PLUS;
}
#endif
if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
memset(&st, 0, sizeof(st));
st.st_ino = d->entry->d_ino;
st.st_mode = d->entry->d_type << 12;
}
nextoff = telldir(d->dp);
#ifdef __FreeBSD__
/* Under FreeBSD, telldir() may return 0 the first time
it is called. But for libfuse, an offset of zero
means that offsets are not supported, so we shift
everything by one. */
nextoff++;
#endif
if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
break;
d->entry = NULL;
d->offset = nextoff;
}
return 0;
}
static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
closedir(d->dp);
free(d);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
if (S_ISFIFO(mode))
res = mkfifo(path, mode);
else
res = mknod(path, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to, unsigned int flags)
{
int res;
/* When we have renameat2() in libc, then we can implement flags */
if (flags)
return -EINVAL;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = fchmod(fi->fh, mode);
else
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
int res;
if (fi)
res = fchown(fi->fh, uid, gid);
else
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
int res;
/* don't use utime/utimes since they follow symlinks */
if (fi)
res = futimens(fi->fh, ts);
else
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
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_open(const char *path, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags);
if (fd == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = fd;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void) path;
res = pread(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
size_t size, off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec *src;
(void) path;
src = malloc(sizeof(struct fuse_bufvec));
if (src == NULL)
return -ENOMEM;
*src = FUSE_BUFVEC_INIT(size);
src->buf[0].fd = fi->fh;
src->buf[0].pos = offset;
*bufp = src;
return 0;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res;
(void) path;
res = pwrite(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
(void) path;
dst.buf[0].fd = fi->fh;
dst.buf[0].pos = offset;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_flush(const char *path, struct fuse_file_info *fi)
{
int res;
(void) path;
/* This is called from every close on an open file, so call the
close on the underlying filesystem. But since flush may be
called multiple times for an open file, this must not really
close the file. This is important if used on a network
filesystem like NFS which flush the data/metadata on close() */
res = close(dup(fi->fh));
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
int res;
(void) path;
#ifndef HAVE_FDATASYNC
(void) isdatasync;
#else
if (isdatasync)
res = fdatasync(fi->fh);
else
#endif
res = fsync(fi->fh);
if (res == -1)
return -errno;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
(void) path;
return do_fallocate(fi->fh, mode, offset, length);
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_LIBULOCKMGR
static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
(void) path;
return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
sizeof(fi->lock_owner));
}
#endif
static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
{
int res;
(void) path;
res = flock(fi->fh, op);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t off_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t off_out, size_t len, int flags)
{
ssize_t res;
(void) path_in;
(void) path_out;
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res == -1)
return -errno;
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
off_t res;
(void) path;
res = lseek(fi->fh, off, whence);
if (res == -1)
return -errno;
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.opendir = xmp_opendir,
.readdir = xmp_readdir,
.releasedir = xmp_releasedir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.create = xmp_create,
.open = xmp_open,
.read = xmp_read,
.read_buf = xmp_read_buf,
.write = xmp_write,
.write_buf = xmp_write_buf,
.statfs = xmp_statfs,
.flush = xmp_flush,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_LIBULOCKMGR
.lock = xmp_lock,
#endif
.flock = xmp_flock,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file passthrough_fh.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2passthrough__fh_8c.html0000644000175000017500000022141315156613443023655 0ustar berndbernd libfuse: fuse-3.18.1/example/passthrough_fh.c File Reference
libfuse
passthrough_fh.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/file.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. This implementation is a little more sophisticated than the one in passthrough.c, so performance is not quite as bad.

Compile with:

gcc -Wall passthrough_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o passthrough_fh

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#include <fuse.h>
#ifdef HAVE_LIBULOCKMGR
#include <ulockmgr.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <sys/file.h> /* flock(2) */
#include "passthrough_helpers.h"
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = 1;
cfg->nullpath_ok = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
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_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
struct xmp_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static int xmp_opendir(const char *path, struct fuse_file_info *fi)
{
int res;
struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
if (d == NULL)
return -ENOMEM;
d->dp = opendir(path);
if (d->dp == NULL) {
res = -errno;
free(d);
return res;
}
d->offset = 0;
d->entry = NULL;
fi->fh = (unsigned long) d;
return 0;
}
static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
{
return (struct xmp_dirp *) (uintptr_t) fi->fh;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
if (offset != d->offset) {
#ifndef __FreeBSD__
seekdir(d->dp, offset);
#else
/* Subtract the one that we add when calling
telldir() below */
seekdir(d->dp, offset-1);
#endif
d->entry = NULL;
d->offset = offset;
}
while (1) {
struct stat st;
off_t nextoff;
if (!d->entry) {
d->entry = readdir(d->dp);
if (!d->entry)
break;
}
#ifdef HAVE_FSTATAT
if (flags & FUSE_READDIR_PLUS) {
int res;
res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
AT_SYMLINK_NOFOLLOW);
if (res != -1)
fill_flags |= FUSE_FILL_DIR_PLUS;
}
#endif
if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
memset(&st, 0, sizeof(st));
st.st_ino = d->entry->d_ino;
st.st_mode = d->entry->d_type << 12;
}
nextoff = telldir(d->dp);
#ifdef __FreeBSD__
/* Under FreeBSD, telldir() may return 0 the first time
it is called. But for libfuse, an offset of zero
means that offsets are not supported, so we shift
everything by one. */
nextoff++;
#endif
if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
break;
d->entry = NULL;
d->offset = nextoff;
}
return 0;
}
static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
closedir(d->dp);
free(d);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
if (S_ISFIFO(mode))
res = mkfifo(path, mode);
else
res = mknod(path, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to, unsigned int flags)
{
int res;
/* When we have renameat2() in libc, then we can implement flags */
if (flags)
return -EINVAL;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = fchmod(fi->fh, mode);
else
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
int res;
if (fi)
res = fchown(fi->fh, uid, gid);
else
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
int res;
/* don't use utime/utimes since they follow symlinks */
if (fi)
res = futimens(fi->fh, ts);
else
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
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_open(const char *path, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags);
if (fd == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = fd;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void) path;
res = pread(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
size_t size, off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec *src;
(void) path;
src = malloc(sizeof(struct fuse_bufvec));
if (src == NULL)
return -ENOMEM;
*src = FUSE_BUFVEC_INIT(size);
src->buf[0].fd = fi->fh;
src->buf[0].pos = offset;
*bufp = src;
return 0;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res;
(void) path;
res = pwrite(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
(void) path;
dst.buf[0].fd = fi->fh;
dst.buf[0].pos = offset;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_flush(const char *path, struct fuse_file_info *fi)
{
int res;
(void) path;
/* This is called from every close on an open file, so call the
close on the underlying filesystem. But since flush may be
called multiple times for an open file, this must not really
close the file. This is important if used on a network
filesystem like NFS which flush the data/metadata on close() */
res = close(dup(fi->fh));
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
int res;
(void) path;
#ifndef HAVE_FDATASYNC
(void) isdatasync;
#else
if (isdatasync)
res = fdatasync(fi->fh);
else
#endif
res = fsync(fi->fh);
if (res == -1)
return -errno;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
(void) path;
return do_fallocate(fi->fh, mode, offset, length);
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_LIBULOCKMGR
static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
(void) path;
return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
sizeof(fi->lock_owner));
}
#endif
static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
{
int res;
(void) path;
res = flock(fi->fh, op);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t off_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t off_out, size_t len, int flags)
{
ssize_t res;
(void) path_in;
(void) path_out;
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res == -1)
return -errno;
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
off_t res;
(void) path;
res = lseek(fi->fh, off, whence);
if (res == -1)
return -errno;
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.opendir = xmp_opendir,
.readdir = xmp_readdir,
.releasedir = xmp_releasedir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.create = xmp_create,
.open = xmp_open,
.read = xmp_read,
.read_buf = xmp_read_buf,
.write = xmp_write,
.write_buf = xmp_write_buf,
.statfs = xmp_statfs,
.flush = xmp_flush,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_LIBULOCKMGR
.lock = xmp_lock,
#endif
.flock = xmp_flock,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file passthrough_fh.c.

fuse-3.18.2/doc/html/example_2passthrough__ll_8c.html0000644000175000017500000052611115156613443021516 0ustar berndbernd libfuse: example/passthrough_ll.c File Reference
libfuse
passthrough_ll.c File Reference
#include <fuse_lowlevel.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/file.h>
#include <sys/xattr.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. In contrast to passthrough.c and passthrough_fh.c, this implementation uses the low-level API. Its performance should be the least bad among the three.

When writeback caching is enabled (-o writeback mount option), it is only possible to write to files for which the mounting user has read permissions. This is because the writeback cache requires the kernel to be able to issue read requests for all files (which the passthrough filesystem cannot satisfy if it can't read the file in the underlying filesystem).

Compile with:

gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define _GNU_SOURCE
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#include <fuse_lowlevel.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/file.h>
#include <sys/xattr.h>
#include "passthrough_helpers.h"
/* We are re-using pointers to our `struct lo_inode` and `struct
lo_dirp` elements as inodes. This means that we must be able to
store uintptr_t values in a fuse_ino_t variable. The following
incantation checks this condition at compile time. */
#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
"fuse_ino_t too small to hold uintptr_t values!");
#else
struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
{ unsigned _uintptr_to_must_hold_fuse_ino_t:
((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
#endif
struct lo_inode {
struct lo_inode *next; /* protected by lo->mutex */
struct lo_inode *prev; /* protected by lo->mutex */
int fd;
ino_t ino;
dev_t dev;
uint64_t refcount; /* protected by lo->mutex */
};
enum {
CACHE_NEVER,
CACHE_NORMAL,
CACHE_ALWAYS,
};
struct lo_data {
pthread_mutex_t mutex;
int debug;
int writeback;
int flock;
int xattr;
char *source;
double timeout;
int cache;
int timeout_set;
struct lo_inode root; /* protected by lo->mutex */
};
static const struct fuse_opt lo_opts[] = {
{ "writeback",
offsetof(struct lo_data, writeback), 1 },
{ "no_writeback",
offsetof(struct lo_data, writeback), 0 },
{ "source=%s",
offsetof(struct lo_data, source), 0 },
{ "flock",
offsetof(struct lo_data, flock), 1 },
{ "no_flock",
offsetof(struct lo_data, flock), 0 },
{ "xattr",
offsetof(struct lo_data, xattr), 1 },
{ "no_xattr",
offsetof(struct lo_data, xattr), 0 },
{ "timeout=%lf",
offsetof(struct lo_data, timeout), 0 },
{ "timeout=",
offsetof(struct lo_data, timeout_set), 1 },
{ "cache=never",
offsetof(struct lo_data, cache), CACHE_NEVER },
{ "cache=auto",
offsetof(struct lo_data, cache), CACHE_NORMAL },
{ "cache=always",
offsetof(struct lo_data, cache), CACHE_ALWAYS },
};
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);
}
static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
{
if (ino == FUSE_ROOT_ID)
return &lo_data(req)->root;
else
return (struct lo_inode *) (uintptr_t) ino;
}
static int lo_fd(fuse_req_t req, fuse_ino_t ino)
{
return lo_inode(req, ino)->fd;
}
static bool lo_debug(fuse_req_t req)
{
return lo_data(req)->debug != 0;
}
static void lo_init(void *userdata,
struct fuse_conn_info *conn)
{
struct lo_data *lo = (struct lo_data *)userdata;
bool has_flag;
if (lo->writeback) {
if (lo->debug && has_flag)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating writeback\n");
}
if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
if (lo->debug && has_flag)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating flock locks\n");
}
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void lo_destroy(void *userdata)
{
struct lo_data *lo = (struct lo_data*) userdata;
while (lo->root.next != &lo->root) {
struct lo_inode* next = lo->root.next;
lo->root.next = next->next;
close(next->fd);
free(next);
}
}
static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
int res;
struct stat buf;
struct lo_data *lo = lo_data(req);
int fd = fi ? fi->fh : lo_fd(req, ino);
(void) fi;
res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
return (void) fuse_reply_err(req, errno);
fuse_reply_attr(req, &buf, lo->timeout);
}
static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
int valid, struct fuse_file_info *fi)
{
int saverr;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
int ifd = inode->fd;
int res;
if (valid & FUSE_SET_ATTR_MODE) {
if (fi) {
res = fchmod(fi->fh, attr->st_mode);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = chmod(procname, attr->st_mode);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
attr->st_uid : (uid_t) -1;
gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
attr->st_gid : (gid_t) -1;
res = fchownat(ifd, "", uid, gid,
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
}
if (valid & FUSE_SET_ATTR_SIZE) {
if (fi) {
res = ftruncate(fi->fh, attr->st_size);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = truncate(procname, attr->st_size);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
struct timespec tv[2];
tv[0].tv_sec = 0;
tv[1].tv_sec = 0;
tv[0].tv_nsec = UTIME_OMIT;
tv[1].tv_nsec = UTIME_OMIT;
if (valid & FUSE_SET_ATTR_ATIME_NOW)
tv[0].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_ATIME)
tv[0] = attr->st_atim;
if (valid & FUSE_SET_ATTR_MTIME_NOW)
tv[1].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_MTIME)
tv[1] = attr->st_mtim;
if (fi)
res = futimens(fi->fh, tv);
else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = utimensat(AT_FDCWD, procname, tv, 0);
}
if (res == -1)
goto out_err;
}
return lo_getattr(req, ino, fi);
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
{
struct lo_inode *p;
struct lo_inode *ret = NULL;
pthread_mutex_lock(&lo->mutex);
for (p = lo->root.next; p != &lo->root; p = p->next) {
if (p->ino == st->st_ino && p->dev == st->st_dev) {
assert(p->refcount > 0);
ret = p;
ret->refcount++;
break;
}
}
pthread_mutex_unlock(&lo->mutex);
return ret;
}
static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo)
{
struct lo_inode *inode = NULL;
struct lo_inode *prev, *next;
inode = calloc(1, sizeof(struct lo_inode));
if (!inode)
return NULL;
inode->refcount = 1;
inode->fd = fd;
inode->ino = e->attr.st_ino;
inode->dev = e->attr.st_dev;
pthread_mutex_lock(&lo->mutex);
prev = &lo->root;
next = prev->next;
next->prev = inode;
inode->next = next;
inode->prev = prev;
prev->next = inode;
pthread_mutex_unlock(&lo->mutex);
return inode;
}
static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e)
{
int res;
struct lo_data *lo = lo_data(req);
memset(e, 0, sizeof(*e));
e->attr_timeout = lo->timeout;
e->entry_timeout = lo->timeout;
res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
return errno;
e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%d -> %lli\n",
(unsigned long long) parent, fd, (unsigned long long) e->ino);
return 0;
}
static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
struct fuse_entry_param *e)
{
int newfd;
int res;
int saverr;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode;
memset(e, 0, sizeof(*e));
e->attr_timeout = lo->timeout;
e->entry_timeout = lo->timeout;
newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
if (newfd == -1)
goto out_err;
res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
inode = lo_find(lo_data(req), &e->attr);
if (inode) {
close(newfd);
newfd = -1;
} else {
inode = create_new_inode(newfd, e, lo);
if (!inode)
goto out_err;
}
e->ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name, (unsigned long long) e->ino);
return 0;
out_err:
saverr = errno;
if (newfd != -1)
close(newfd);
return saverr;
}
static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
parent, name);
err = lo_do_lookup(req, parent, name, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_entry(req, &e);
}
static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev,
const char *link)
{
int res;
int saverr;
struct lo_inode *dir = lo_inode(req, parent);
struct fuse_entry_param e;
res = mknod_wrapper(dir->fd, name, link, mode, rdev);
saverr = errno;
if (res == -1)
goto out;
saverr = lo_do_lookup(req, parent, name, &e);
if (saverr)
goto out;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name, (unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out:
fuse_reply_err(req, saverr);
}
static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev)
{
lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
}
static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode)
{
lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
}
static void lo_symlink(fuse_req_t req, const char *link,
fuse_ino_t parent, const char *name)
{
lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
}
static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
const char *name)
{
int res;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
struct fuse_entry_param e;
char procname[64];
int saverr;
memset(&e, 0, sizeof(struct fuse_entry_param));
e.attr_timeout = lo->timeout;
e.entry_timeout = lo->timeout;
sprintf(procname, "/proc/self/fd/%i", inode->fd);
res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
AT_SYMLINK_FOLLOW);
if (res == -1)
goto out_err;
res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
pthread_mutex_lock(&lo->mutex);
inode->refcount++;
pthread_mutex_unlock(&lo->mutex);
e.ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name,
(unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char *newname,
unsigned int flags)
{
int res;
if (flags) {
fuse_reply_err(req, EINVAL);
return;
}
res = renameat(lo_fd(req, parent), name,
lo_fd(req, newparent), newname);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, 0);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
{
if (!inode)
return;
pthread_mutex_lock(&lo->mutex);
assert(inode->refcount >= n);
inode->refcount -= n;
if (!inode->refcount) {
struct lo_inode *prev, *next;
prev = inode->prev;
next = inode->next;
next->prev = prev;
prev->next = next;
pthread_mutex_unlock(&lo->mutex);
close(inode->fd);
free(inode);
} else {
pthread_mutex_unlock(&lo->mutex);
}
}
static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
(unsigned long long) ino,
(unsigned long long) inode->refcount,
(unsigned long long) nlookup);
}
unref_inode(lo, inode, nlookup);
}
static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{
lo_forget_one(req, ino, nlookup);
}
static void lo_forget_multi(fuse_req_t req, size_t count,
struct fuse_forget_data *forgets)
{
int i;
for (i = 0; i < count; i++)
lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
}
static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
{
char buf[PATH_MAX + 1];
int res;
res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
if (res == -1)
return (void) fuse_reply_err(req, errno);
if (res == sizeof(buf))
return (void) fuse_reply_err(req, ENAMETOOLONG);
buf[res] = '\0';
}
struct lo_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
{
return (struct lo_dirp *) (uintptr_t) fi->fh;
}
static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int error = ENOMEM;
struct lo_data *lo = lo_data(req);
struct lo_dirp *d;
int fd = -1;
d = calloc(1, sizeof(struct lo_dirp));
if (d == NULL)
goto out_err;
fd = openat(lo_fd(req, ino), ".", O_RDONLY);
if (fd == -1)
goto out_errno;
d->dp = fdopendir(fd);
if (d->dp == NULL)
goto out_errno;
d->offset = 0;
d->entry = NULL;
fi->fh = (uintptr_t) d;
if (lo->cache != CACHE_NEVER)
fi->cache_readdir = 1;
if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
fuse_reply_open(req, fi);
return;
out_errno:
error = errno;
out_err:
if (d) {
if (fd != -1)
close(fd);
free(d);
}
fuse_reply_err(req, error);
}
static int is_dot_or_dotdot(const char *name)
{
return name[0] == '.' && (name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0'));
}
static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi, int plus)
{
struct lo_dirp *d = lo_dirp(fi);
char *buf;
char *p;
size_t rem = size;
int err;
(void) ino;
buf = calloc(1, size);
if (!buf) {
err = ENOMEM;
goto error;
}
p = buf;
if (offset != d->offset) {
seekdir(d->dp, offset);
d->entry = NULL;
d->offset = offset;
}
while (1) {
size_t entsize;
off_t nextoff;
const char *name;
if (!d->entry) {
errno = 0;
d->entry = readdir(d->dp);
if (!d->entry) {
if (errno) { // Error
err = errno;
goto error;
} else { // End of stream
break;
}
}
}
nextoff = d->entry->d_off;
name = d->entry->d_name;
fuse_ino_t entry_ino = 0;
if (plus) {
struct fuse_entry_param e;
if (is_dot_or_dotdot(name)) {
e = (struct fuse_entry_param) {
.attr.st_ino = d->entry->d_ino,
.attr.st_mode = d->entry->d_type << 12,
};
} else {
err = lo_do_lookup(req, ino, name, &e);
if (err)
goto error;
entry_ino = e.ino;
}
entsize = fuse_add_direntry_plus(req, p, rem, name,
&e, nextoff);
} else {
struct stat st = {
.st_ino = d->entry->d_ino,
.st_mode = d->entry->d_type << 12,
};
entsize = fuse_add_direntry(req, p, rem, name,
&st, nextoff);
}
if (entsize > rem) {
if (entry_ino != 0)
lo_forget_one(req, entry_ino, 1);
break;
}
p += entsize;
rem -= entsize;
d->entry = NULL;
d->offset = nextoff;
}
err = 0;
error:
// If there's an error, we can only signal it if we haven't stored
// any entries yet - otherwise we'd end up with wrong lookup
// counts for the entries that are already in the buffer. So we
// return what we've collected until that point.
if (err && rem == size)
fuse_reply_err(req, err);
else
fuse_reply_buf(req, buf, size - rem);
free(buf);
}
static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
lo_do_readdir(req, ino, size, offset, fi, 0);
}
static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
lo_do_readdir(req, ino, size, offset, fi, 1);
}
static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
struct lo_dirp *d = lo_dirp(fi);
(void) ino;
closedir(d->dp);
free(d);
fuse_reply_err(req, 0);
}
static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent,
mode_t mode, struct fuse_file_info *fi)
{
int fd;
struct lo_data *lo = lo_data(req);
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n",
parent);
fd = openat(lo_fd(req, parent), ".",
(fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
err = fill_entry_param_new_inode(req, parent, fd, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_create(req, &e, fi);
}
static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, struct fuse_file_info *fi)
{
int fd;
struct lo_data *lo = lo_data(req);
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
parent, name);
fd = openat(lo_fd(req, parent), name,
(fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
err = lo_do_lookup(req, parent, name, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_create(req, &e, fi);
}
static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
int fd = dirfd(lo_dirp(fi)->dp);
(void) ino;
if (datasync)
res = fdatasync(fd);
else
res = fsync(fd);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int fd;
char buf[64];
struct lo_data *lo = lo_data(req);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
ino, fi->flags);
/* With writeback cache, kernel may send read requests even
when userspace opened write-only */
if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
fi->flags &= ~O_ACCMODE;
fi->flags |= O_RDWR;
}
/* With writeback cache, O_APPEND is handled by the kernel.
This breaks atomicity (since the file may change in the
underlying filesystem, so that the kernel's idea of the
end of the file isn't accurate anymore). In this example,
we just accept that. A more rigorous filesystem may want
to return an error here */
if (lo->writeback && (fi->flags & O_APPEND))
fi->flags &= ~O_APPEND;
sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
fd = open(buf, fi->flags & ~O_NOFOLLOW);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file in the kernel). */
if (fi->flags & O_DIRECT)
fi->direct_io = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
fuse_reply_open(req, fi);
}
static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
(void) ino;
close(fi->fh);
fuse_reply_err(req, 0);
}
static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int res;
(void) ino;
res = close(dup(fi->fh));
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
(void) ino;
if (datasync)
res = fdatasync(fi->fh);
else
res = fsync(fi->fh);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
"off=%lu)\n", ino, size, (unsigned long) offset);
buf.buf[0].fd = fi->fh;
buf.buf[0].pos = offset;
}
static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
struct fuse_bufvec *in_buf, off_t off,
struct fuse_file_info *fi)
{
(void) ino;
ssize_t res;
struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
out_buf.buf[0].fd = fi->fh;
out_buf.buf[0].pos = off;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%jd)\n",
ino, out_buf.buf[0].size, (intmax_t) off);
res = fuse_buf_copy(&out_buf, in_buf, 0);
if(res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_write(req, (size_t) res);
}
static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
{
int res;
struct statvfs stbuf;
res = fstatvfs(lo_fd(req, ino), &stbuf);
if (res == -1)
fuse_reply_err(req, errno);
else
fuse_reply_statfs(req, &stbuf);
}
static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
int err;
(void) ino;
err = -do_fallocate(fi->fh, mode, offset, length);
fuse_reply_err(req, err);
}
static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
int op)
{
int res;
(void) ino;
res = flock(fi->fh, op);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
ino, name, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = getxattr(procname, name, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
fuse_reply_buf(req, value, ret);
} else {
ret = getxattr(procname, name, NULL, 0);
if (ret == -1)
goto out_err;
fuse_reply_xattr(req, ret);
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
fuse_reply_err(req, saverr);
goto out_free;
}
static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
ino, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = listxattr(procname, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
fuse_reply_buf(req, value, ret);
} else {
ret = listxattr(procname, NULL, 0);
if (ret == -1)
goto out_err;
fuse_reply_xattr(req, ret);
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
fuse_reply_err(req, saverr);
goto out_free;
}
static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags)
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
ino, name, value, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = setxattr(procname, name, value, size, flags);
saverr = ret == -1 ? errno : 0;
out:
fuse_reply_err(req, saverr);
}
static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
ino, name);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = removexattr(procname, name);
saverr = ret == -1 ? errno : 0;
out:
fuse_reply_err(req, saverr);
}
#ifdef HAVE_COPY_FILE_RANGE
static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
struct fuse_file_info *fi_in,
fuse_ino_t ino_out, off_t off_out,
struct fuse_file_info *fi_out, size_t len,
int flags)
{
ssize_t res;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"%s(ino=%lld fd=%lld off=%jd ino=%lld fd=%lld off=%jd, size=%zd, flags=0x%x)\n",
__func__, (unsigned long long)ino_in,
(unsigned long long)fi_in->fh,
(intmax_t) off_in, (unsigned long long)ino_out,
(unsigned long long)fi_out->fh, (intmax_t) off_out,
len, flags);
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res < 0)
fuse_reply_err(req, errno);
else
fuse_reply_write(req, res);
}
#endif
static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
struct fuse_file_info *fi)
{
off_t res;
(void)ino;
res = lseek(fi->fh, off, whence);
if (res != -1)
fuse_reply_lseek(req, res);
else
fuse_reply_err(req, errno);
}
#ifdef HAVE_STATX
static void lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
struct fuse_file_info *fi)
{
struct lo_data *lo = lo_data(req);
struct statx buf;
int res;
int fd;
if (fi)
fd = fi->fh;
else
fd = lo_fd(req, ino);
res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, &buf);
if (res == -1)
fuse_reply_err(req, errno);
else
fuse_reply_statx(req, 0, &buf, lo->timeout);
}
#endif
static const struct fuse_lowlevel_ops lo_oper = {
.init = lo_init,
.destroy = lo_destroy,
.lookup = lo_lookup,
.mkdir = lo_mkdir,
.mknod = lo_mknod,
.symlink = lo_symlink,
.link = lo_link,
.unlink = lo_unlink,
.rmdir = lo_rmdir,
.rename = lo_rename,
.forget = lo_forget,
.forget_multi = lo_forget_multi,
.getattr = lo_getattr,
.setattr = lo_setattr,
.readlink = lo_readlink,
.opendir = lo_opendir,
.readdir = lo_readdir,
.readdirplus = lo_readdirplus,
.releasedir = lo_releasedir,
.fsyncdir = lo_fsyncdir,
.create = lo_create,
.tmpfile = lo_tmpfile,
.open = lo_open,
.release = lo_release,
.flush = lo_flush,
.fsync = lo_fsync,
.read = lo_read,
.write_buf = lo_write_buf,
.statfs = lo_statfs,
.fallocate = lo_fallocate,
.flock = lo_flock,
.getxattr = lo_getxattr,
.listxattr = lo_listxattr,
.setxattr = lo_setxattr,
.removexattr = lo_removexattr,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = lo_copy_file_range,
#endif
.lseek = lo_lseek,
#ifdef HAVE_STATX
.statx = lo_statx,
#endif
};
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
struct lo_data lo = { .debug = 0,
.writeback = 0 };
int ret = -1;
/* Don't mask creation mode, kernel already did that */
umask(0);
pthread_mutex_init(&lo.mutex, NULL);
lo.root.next = lo.root.prev = &lo.root;
lo.root.fd = -1;
lo.cache = CACHE_NORMAL;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
passthrough_ll_help();
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
return 1;
lo.debug = opts.debug;
lo.root.refcount = 2;
if (lo.source) {
struct stat stat;
int res;
res = lstat(lo.source, &stat);
if (res == -1) {
fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
lo.source);
exit(1);
}
if (!S_ISDIR(stat.st_mode)) {
fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
exit(1);
}
} else {
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) {
case CACHE_NEVER:
lo.timeout = 0.0;
break;
case CACHE_NORMAL:
lo.timeout = 1.0;
break;
case CACHE_ALWAYS:
lo.timeout = 86400.0;
break;
}
} else if (lo.timeout < 0) {
fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
lo.timeout);
exit(1);
}
lo.root.fd = open(lo.source, O_PATH);
if (lo.root.fd == -1) {
fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
lo.source);
exit(1);
}
se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
if (lo.root.fd >= 0)
close(lo.root.fd);
free(lo.source);
return ret ? 1 : 0;
}
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
off_t pos
size_t size
struct fuse_buf buf[1]
uint32_t no_interrupt
uint32_t capable
double entry_timeout
fuse_ino_t ino
double attr_timeout
struct stat attr
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file passthrough_ll.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2example_2passthrough__ll_8c.html0000644000175000017500000052671415156613443023705 0ustar berndbernd libfuse: fuse-3.17.4/example/passthrough_ll.c File Reference
libfuse
passthrough_ll.c File Reference
#include <fuse_lowlevel.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/file.h>
#include <sys/xattr.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. In contrast to passthrough.c and passthrough_fh.c, this implementation uses the low-level API. Its performance should be the least bad among the three, but many operations are not implemented. In particular, it is not possible to remove files (or directories) because the code necessary to defer actual removal until the file is not opened anymore would make the example much more complicated.

When writeback caching is enabled (-o writeback mount option), it is only possible to write to files for which the mounting user has read permissions. This is because the writeback cache requires the kernel to be able to issue read requests for all files (which the passthrough filesystem cannot satisfy if it can't read the file in the underlying filesystem).

Compile with:

gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define _GNU_SOURCE
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#include <fuse_lowlevel.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/file.h>
#include <sys/xattr.h>
#include "passthrough_helpers.h"
/* We are re-using pointers to our `struct lo_inode` and `struct
lo_dirp` elements as inodes. This means that we must be able to
store uintptr_t values in a fuse_ino_t variable. The following
incantation checks this condition at compile time. */
#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
"fuse_ino_t too small to hold uintptr_t values!");
#else
struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
{ unsigned _uintptr_to_must_hold_fuse_ino_t:
((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
#endif
struct lo_inode {
struct lo_inode *next; /* protected by lo->mutex */
struct lo_inode *prev; /* protected by lo->mutex */
int fd;
ino_t ino;
dev_t dev;
uint64_t refcount; /* protected by lo->mutex */
};
enum {
CACHE_NEVER,
CACHE_NORMAL,
CACHE_ALWAYS,
};
struct lo_data {
pthread_mutex_t mutex;
int debug;
int writeback;
int flock;
int xattr;
char *source;
double timeout;
int cache;
int timeout_set;
struct lo_inode root; /* protected by lo->mutex */
};
static const struct fuse_opt lo_opts[] = {
{ "writeback",
offsetof(struct lo_data, writeback), 1 },
{ "no_writeback",
offsetof(struct lo_data, writeback), 0 },
{ "source=%s",
offsetof(struct lo_data, source), 0 },
{ "flock",
offsetof(struct lo_data, flock), 1 },
{ "no_flock",
offsetof(struct lo_data, flock), 0 },
{ "xattr",
offsetof(struct lo_data, xattr), 1 },
{ "no_xattr",
offsetof(struct lo_data, xattr), 0 },
{ "timeout=%lf",
offsetof(struct lo_data, timeout), 0 },
{ "timeout=",
offsetof(struct lo_data, timeout_set), 1 },
{ "cache=never",
offsetof(struct lo_data, cache), CACHE_NEVER },
{ "cache=auto",
offsetof(struct lo_data, cache), CACHE_NORMAL },
{ "cache=always",
offsetof(struct lo_data, cache), CACHE_ALWAYS },
};
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);
}
static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
{
if (ino == FUSE_ROOT_ID)
return &lo_data(req)->root;
else
return (struct lo_inode *) (uintptr_t) ino;
}
static int lo_fd(fuse_req_t req, fuse_ino_t ino)
{
return lo_inode(req, ino)->fd;
}
static bool lo_debug(fuse_req_t req)
{
return lo_data(req)->debug != 0;
}
static void lo_init(void *userdata,
struct fuse_conn_info *conn)
{
struct lo_data *lo = (struct lo_data *)userdata;
bool has_flag;
if (lo->writeback) {
if (lo->debug && has_flag)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating writeback\n");
}
if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
if (lo->debug && has_flag)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating flock locks\n");
}
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void lo_destroy(void *userdata)
{
struct lo_data *lo = (struct lo_data*) userdata;
while (lo->root.next != &lo->root) {
struct lo_inode* next = lo->root.next;
lo->root.next = next->next;
close(next->fd);
free(next);
}
}
static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
int res;
struct stat buf;
struct lo_data *lo = lo_data(req);
int fd = fi ? fi->fh : lo_fd(req, ino);
(void) fi;
res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
return (void) fuse_reply_err(req, errno);
fuse_reply_attr(req, &buf, lo->timeout);
}
static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
int valid, struct fuse_file_info *fi)
{
int saverr;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
int ifd = inode->fd;
int res;
if (valid & FUSE_SET_ATTR_MODE) {
if (fi) {
res = fchmod(fi->fh, attr->st_mode);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = chmod(procname, attr->st_mode);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
attr->st_uid : (uid_t) -1;
gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
attr->st_gid : (gid_t) -1;
res = fchownat(ifd, "", uid, gid,
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
}
if (valid & FUSE_SET_ATTR_SIZE) {
if (fi) {
res = ftruncate(fi->fh, attr->st_size);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = truncate(procname, attr->st_size);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
struct timespec tv[2];
tv[0].tv_sec = 0;
tv[1].tv_sec = 0;
tv[0].tv_nsec = UTIME_OMIT;
tv[1].tv_nsec = UTIME_OMIT;
if (valid & FUSE_SET_ATTR_ATIME_NOW)
tv[0].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_ATIME)
tv[0] = attr->st_atim;
if (valid & FUSE_SET_ATTR_MTIME_NOW)
tv[1].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_MTIME)
tv[1] = attr->st_mtim;
if (fi)
res = futimens(fi->fh, tv);
else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = utimensat(AT_FDCWD, procname, tv, 0);
}
if (res == -1)
goto out_err;
}
return lo_getattr(req, ino, fi);
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
{
struct lo_inode *p;
struct lo_inode *ret = NULL;
pthread_mutex_lock(&lo->mutex);
for (p = lo->root.next; p != &lo->root; p = p->next) {
if (p->ino == st->st_ino && p->dev == st->st_dev) {
assert(p->refcount > 0);
ret = p;
ret->refcount++;
break;
}
}
pthread_mutex_unlock(&lo->mutex);
return ret;
}
static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo)
{
struct lo_inode *inode = NULL;
struct lo_inode *prev, *next;
inode = calloc(1, sizeof(struct lo_inode));
if (!inode)
return NULL;
inode->refcount = 1;
inode->fd = fd;
inode->ino = e->attr.st_ino;
inode->dev = e->attr.st_dev;
pthread_mutex_lock(&lo->mutex);
prev = &lo->root;
next = prev->next;
next->prev = inode;
inode->next = next;
inode->prev = prev;
prev->next = inode;
pthread_mutex_unlock(&lo->mutex);
return inode;
}
static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e)
{
int res;
struct lo_data *lo = lo_data(req);
memset(e, 0, sizeof(*e));
e->attr_timeout = lo->timeout;
e->entry_timeout = lo->timeout;
res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
return errno;
e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%d -> %lli\n",
(unsigned long long) parent, fd, (unsigned long long) e->ino);
return 0;
}
static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
struct fuse_entry_param *e)
{
int newfd;
int res;
int saverr;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode;
memset(e, 0, sizeof(*e));
e->attr_timeout = lo->timeout;
e->entry_timeout = lo->timeout;
newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
if (newfd == -1)
goto out_err;
res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
inode = lo_find(lo_data(req), &e->attr);
if (inode) {
close(newfd);
newfd = -1;
} else {
inode = create_new_inode(newfd, e, lo);
if (!inode)
goto out_err;
}
e->ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name, (unsigned long long) e->ino);
return 0;
out_err:
saverr = errno;
if (newfd != -1)
close(newfd);
return saverr;
}
static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
parent, name);
err = lo_do_lookup(req, parent, name, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_entry(req, &e);
}
static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev,
const char *link)
{
int res;
int saverr;
struct lo_inode *dir = lo_inode(req, parent);
struct fuse_entry_param e;
res = mknod_wrapper(dir->fd, name, link, mode, rdev);
saverr = errno;
if (res == -1)
goto out;
saverr = lo_do_lookup(req, parent, name, &e);
if (saverr)
goto out;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name, (unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out:
fuse_reply_err(req, saverr);
}
static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev)
{
lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
}
static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode)
{
lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
}
static void lo_symlink(fuse_req_t req, const char *link,
fuse_ino_t parent, const char *name)
{
lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
}
static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
const char *name)
{
int res;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
struct fuse_entry_param e;
char procname[64];
int saverr;
memset(&e, 0, sizeof(struct fuse_entry_param));
e.attr_timeout = lo->timeout;
e.entry_timeout = lo->timeout;
sprintf(procname, "/proc/self/fd/%i", inode->fd);
res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
AT_SYMLINK_FOLLOW);
if (res == -1)
goto out_err;
res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
pthread_mutex_lock(&lo->mutex);
inode->refcount++;
pthread_mutex_unlock(&lo->mutex);
e.ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name,
(unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char *newname,
unsigned int flags)
{
int res;
if (flags) {
fuse_reply_err(req, EINVAL);
return;
}
res = renameat(lo_fd(req, parent), name,
lo_fd(req, newparent), newname);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, 0);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
{
if (!inode)
return;
pthread_mutex_lock(&lo->mutex);
assert(inode->refcount >= n);
inode->refcount -= n;
if (!inode->refcount) {
struct lo_inode *prev, *next;
prev = inode->prev;
next = inode->next;
next->prev = prev;
prev->next = next;
pthread_mutex_unlock(&lo->mutex);
close(inode->fd);
free(inode);
} else {
pthread_mutex_unlock(&lo->mutex);
}
}
static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
(unsigned long long) ino,
(unsigned long long) inode->refcount,
(unsigned long long) nlookup);
}
unref_inode(lo, inode, nlookup);
}
static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{
lo_forget_one(req, ino, nlookup);
}
static void lo_forget_multi(fuse_req_t req, size_t count,
struct fuse_forget_data *forgets)
{
int i;
for (i = 0; i < count; i++)
lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
}
static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
{
char buf[PATH_MAX + 1];
int res;
res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
if (res == -1)
return (void) fuse_reply_err(req, errno);
if (res == sizeof(buf))
return (void) fuse_reply_err(req, ENAMETOOLONG);
buf[res] = '\0';
}
struct lo_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
{
return (struct lo_dirp *) (uintptr_t) fi->fh;
}
static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int error = ENOMEM;
struct lo_data *lo = lo_data(req);
struct lo_dirp *d;
int fd = -1;
d = calloc(1, sizeof(struct lo_dirp));
if (d == NULL)
goto out_err;
fd = openat(lo_fd(req, ino), ".", O_RDONLY);
if (fd == -1)
goto out_errno;
d->dp = fdopendir(fd);
if (d->dp == NULL)
goto out_errno;
d->offset = 0;
d->entry = NULL;
fi->fh = (uintptr_t) d;
if (lo->cache != CACHE_NEVER)
fi->cache_readdir = 1;
if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
fuse_reply_open(req, fi);
return;
out_errno:
error = errno;
out_err:
if (d) {
if (fd != -1)
close(fd);
free(d);
}
fuse_reply_err(req, error);
}
static int is_dot_or_dotdot(const char *name)
{
return name[0] == '.' && (name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0'));
}
static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi, int plus)
{
struct lo_dirp *d = lo_dirp(fi);
char *buf;
char *p;
size_t rem = size;
int err;
(void) ino;
buf = calloc(1, size);
if (!buf) {
err = ENOMEM;
goto error;
}
p = buf;
if (offset != d->offset) {
seekdir(d->dp, offset);
d->entry = NULL;
d->offset = offset;
}
while (1) {
size_t entsize;
off_t nextoff;
const char *name;
if (!d->entry) {
errno = 0;
d->entry = readdir(d->dp);
if (!d->entry) {
if (errno) { // Error
err = errno;
goto error;
} else { // End of stream
break;
}
}
}
nextoff = d->entry->d_off;
name = d->entry->d_name;
fuse_ino_t entry_ino = 0;
if (plus) {
struct fuse_entry_param e;
if (is_dot_or_dotdot(name)) {
e = (struct fuse_entry_param) {
.attr.st_ino = d->entry->d_ino,
.attr.st_mode = d->entry->d_type << 12,
};
} else {
err = lo_do_lookup(req, ino, name, &e);
if (err)
goto error;
entry_ino = e.ino;
}
entsize = fuse_add_direntry_plus(req, p, rem, name,
&e, nextoff);
} else {
struct stat st = {
.st_ino = d->entry->d_ino,
.st_mode = d->entry->d_type << 12,
};
entsize = fuse_add_direntry(req, p, rem, name,
&st, nextoff);
}
if (entsize > rem) {
if (entry_ino != 0)
lo_forget_one(req, entry_ino, 1);
break;
}
p += entsize;
rem -= entsize;
d->entry = NULL;
d->offset = nextoff;
}
err = 0;
error:
// If there's an error, we can only signal it if we haven't stored
// any entries yet - otherwise we'd end up with wrong lookup
// counts for the entries that are already in the buffer. So we
// return what we've collected until that point.
if (err && rem == size)
fuse_reply_err(req, err);
else
fuse_reply_buf(req, buf, size - rem);
free(buf);
}
static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
lo_do_readdir(req, ino, size, offset, fi, 0);
}
static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
lo_do_readdir(req, ino, size, offset, fi, 1);
}
static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
struct lo_dirp *d = lo_dirp(fi);
(void) ino;
closedir(d->dp);
free(d);
fuse_reply_err(req, 0);
}
static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent,
mode_t mode, struct fuse_file_info *fi)
{
int fd;
struct lo_data *lo = lo_data(req);
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n",
parent);
fd = openat(lo_fd(req, parent), ".",
(fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
err = fill_entry_param_new_inode(req, parent, fd, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_create(req, &e, fi);
}
static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, struct fuse_file_info *fi)
{
int fd;
struct lo_data *lo = lo_data(req);
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
parent, name);
fd = openat(lo_fd(req, parent), name,
(fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
err = lo_do_lookup(req, parent, name, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_create(req, &e, fi);
}
static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
int fd = dirfd(lo_dirp(fi)->dp);
(void) ino;
if (datasync)
res = fdatasync(fd);
else
res = fsync(fd);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int fd;
char buf[64];
struct lo_data *lo = lo_data(req);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
ino, fi->flags);
/* With writeback cache, kernel may send read requests even
when userspace opened write-only */
if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
fi->flags &= ~O_ACCMODE;
fi->flags |= O_RDWR;
}
/* With writeback cache, O_APPEND is handled by the kernel.
This breaks atomicity (since the file may change in the
underlying filesystem, so that the kernel's idea of the
end of the file isn't accurate anymore). In this example,
we just accept that. A more rigorous filesystem may want
to return an error here */
if (lo->writeback && (fi->flags & O_APPEND))
fi->flags &= ~O_APPEND;
sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
fd = open(buf, fi->flags & ~O_NOFOLLOW);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file in the kernel). */
if (fi->flags & O_DIRECT)
fi->direct_io = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
fuse_reply_open(req, fi);
}
static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
(void) ino;
close(fi->fh);
fuse_reply_err(req, 0);
}
static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int res;
(void) ino;
res = close(dup(fi->fh));
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
(void) ino;
if (datasync)
res = fdatasync(fi->fh);
else
res = fsync(fi->fh);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
"off=%lu)\n", ino, size, (unsigned long) offset);
buf.buf[0].fd = fi->fh;
buf.buf[0].pos = offset;
}
static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
struct fuse_bufvec *in_buf, off_t off,
struct fuse_file_info *fi)
{
(void) ino;
ssize_t res;
struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
out_buf.buf[0].fd = fi->fh;
out_buf.buf[0].pos = off;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%jd)\n",
ino, out_buf.buf[0].size, (intmax_t) off);
res = fuse_buf_copy(&out_buf, in_buf, 0);
if(res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_write(req, (size_t) res);
}
static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
{
int res;
struct statvfs stbuf;
res = fstatvfs(lo_fd(req, ino), &stbuf);
if (res == -1)
fuse_reply_err(req, errno);
else
fuse_reply_statfs(req, &stbuf);
}
static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
int err;
(void) ino;
err = -do_fallocate(fi->fh, mode, offset, length);
fuse_reply_err(req, err);
}
static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
int op)
{
int res;
(void) ino;
res = flock(fi->fh, op);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
ino, name, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = getxattr(procname, name, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
fuse_reply_buf(req, value, ret);
} else {
ret = getxattr(procname, name, NULL, 0);
if (ret == -1)
goto out_err;
fuse_reply_xattr(req, ret);
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
fuse_reply_err(req, saverr);
goto out_free;
}
static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
ino, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = listxattr(procname, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
fuse_reply_buf(req, value, ret);
} else {
ret = listxattr(procname, NULL, 0);
if (ret == -1)
goto out_err;
fuse_reply_xattr(req, ret);
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
fuse_reply_err(req, saverr);
goto out_free;
}
static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags)
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
ino, name, value, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = setxattr(procname, name, value, size, flags);
saverr = ret == -1 ? errno : 0;
out:
fuse_reply_err(req, saverr);
}
static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
ino, name);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = removexattr(procname, name);
saverr = ret == -1 ? errno : 0;
out:
fuse_reply_err(req, saverr);
}
#ifdef HAVE_COPY_FILE_RANGE
static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
struct fuse_file_info *fi_in,
fuse_ino_t ino_out, off_t off_out,
struct fuse_file_info *fi_out, size_t len,
int flags)
{
ssize_t res;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"%s(ino=%lld fd=%lld off=%jd ino=%lld fd=%lld off=%jd, size=%zd, flags=0x%x)\n",
__func__, (unsigned long long)ino_in,
(unsigned long long)fi_in->fh,
(intmax_t) off_in, (unsigned long long)ino_out,
(unsigned long long)fi_out->fh, (intmax_t) off_out,
len, flags);
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res < 0)
fuse_reply_err(req, errno);
else
fuse_reply_write(req, res);
}
#endif
static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
struct fuse_file_info *fi)
{
off_t res;
(void)ino;
res = lseek(fi->fh, off, whence);
if (res != -1)
fuse_reply_lseek(req, res);
else
fuse_reply_err(req, errno);
}
#ifdef HAVE_STATX
static void lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
struct fuse_file_info *fi)
{
struct lo_data *lo = lo_data(req);
struct statx buf;
int res;
int fd;
if (fi)
fd = fi->fh;
else
fd = lo_fd(req, ino);
res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, &buf);
if (res == -1)
fuse_reply_err(req, errno);
else
fuse_reply_statx(req, 0, &buf, lo->timeout);
}
#endif
static const struct fuse_lowlevel_ops lo_oper = {
.init = lo_init,
.destroy = lo_destroy,
.lookup = lo_lookup,
.mkdir = lo_mkdir,
.mknod = lo_mknod,
.symlink = lo_symlink,
.link = lo_link,
.unlink = lo_unlink,
.rmdir = lo_rmdir,
.rename = lo_rename,
.forget = lo_forget,
.forget_multi = lo_forget_multi,
.getattr = lo_getattr,
.setattr = lo_setattr,
.readlink = lo_readlink,
.opendir = lo_opendir,
.readdir = lo_readdir,
.readdirplus = lo_readdirplus,
.releasedir = lo_releasedir,
.fsyncdir = lo_fsyncdir,
.create = lo_create,
.tmpfile = lo_tmpfile,
.open = lo_open,
.release = lo_release,
.flush = lo_flush,
.fsync = lo_fsync,
.read = lo_read,
.write_buf = lo_write_buf,
.statfs = lo_statfs,
.fallocate = lo_fallocate,
.flock = lo_flock,
.getxattr = lo_getxattr,
.listxattr = lo_listxattr,
.setxattr = lo_setxattr,
.removexattr = lo_removexattr,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = lo_copy_file_range,
#endif
.lseek = lo_lseek,
#ifdef HAVE_STATX
.statx = lo_statx,
#endif
};
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
struct lo_data lo = { .debug = 0,
.writeback = 0 };
int ret = -1;
/* Don't mask creation mode, kernel already did that */
umask(0);
pthread_mutex_init(&lo.mutex, NULL);
lo.root.next = lo.root.prev = &lo.root;
lo.root.fd = -1;
lo.cache = CACHE_NORMAL;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
passthrough_ll_help();
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
return 1;
lo.debug = opts.debug;
lo.root.refcount = 2;
if (lo.source) {
struct stat stat;
int res;
res = lstat(lo.source, &stat);
if (res == -1) {
fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
lo.source);
exit(1);
}
if (!S_ISDIR(stat.st_mode)) {
fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
exit(1);
}
} else {
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) {
case CACHE_NEVER:
lo.timeout = 0.0;
break;
case CACHE_NORMAL:
lo.timeout = 1.0;
break;
case CACHE_ALWAYS:
lo.timeout = 86400.0;
break;
}
} else if (lo.timeout < 0) {
fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
lo.timeout);
exit(1);
}
lo.root.fd = open(lo.source, O_PATH);
if (lo.root.fd == -1) {
fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
lo.source);
exit(1);
}
se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
if (lo.root.fd >= 0)
close(lo.root.fd);
free(lo.source);
return ret ? 1 : 0;
}
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
off_t pos
size_t size
struct fuse_buf buf[1]
uint32_t no_interrupt
uint32_t capable
double entry_timeout
fuse_ino_t ino
double attr_timeout
struct stat attr
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file passthrough_ll.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2passthrough__ll_8c.html0000644000175000017500000052633215156613443023677 0ustar berndbernd libfuse: fuse-3.18.1/example/passthrough_ll.c File Reference
libfuse
passthrough_ll.c File Reference
#include <fuse_lowlevel.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/file.h>
#include <sys/xattr.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. In contrast to passthrough.c and passthrough_fh.c, this implementation uses the low-level API. Its performance should be the least bad among the three.

When writeback caching is enabled (-o writeback mount option), it is only possible to write to files for which the mounting user has read permissions. This is because the writeback cache requires the kernel to be able to issue read requests for all files (which the passthrough filesystem cannot satisfy if it can't read the file in the underlying filesystem).

Compile with:

gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define _GNU_SOURCE
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#include <fuse_lowlevel.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/file.h>
#include <sys/xattr.h>
#include "passthrough_helpers.h"
/* We are re-using pointers to our `struct lo_inode` and `struct
lo_dirp` elements as inodes. This means that we must be able to
store uintptr_t values in a fuse_ino_t variable. The following
incantation checks this condition at compile time. */
#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
"fuse_ino_t too small to hold uintptr_t values!");
#else
struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
{ unsigned _uintptr_to_must_hold_fuse_ino_t:
((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
#endif
struct lo_inode {
struct lo_inode *next; /* protected by lo->mutex */
struct lo_inode *prev; /* protected by lo->mutex */
int fd;
ino_t ino;
dev_t dev;
uint64_t refcount; /* protected by lo->mutex */
};
enum {
CACHE_NEVER,
CACHE_NORMAL,
CACHE_ALWAYS,
};
struct lo_data {
pthread_mutex_t mutex;
int debug;
int writeback;
int flock;
int xattr;
char *source;
double timeout;
int cache;
int timeout_set;
struct lo_inode root; /* protected by lo->mutex */
};
static const struct fuse_opt lo_opts[] = {
{ "writeback",
offsetof(struct lo_data, writeback), 1 },
{ "no_writeback",
offsetof(struct lo_data, writeback), 0 },
{ "source=%s",
offsetof(struct lo_data, source), 0 },
{ "flock",
offsetof(struct lo_data, flock), 1 },
{ "no_flock",
offsetof(struct lo_data, flock), 0 },
{ "xattr",
offsetof(struct lo_data, xattr), 1 },
{ "no_xattr",
offsetof(struct lo_data, xattr), 0 },
{ "timeout=%lf",
offsetof(struct lo_data, timeout), 0 },
{ "timeout=",
offsetof(struct lo_data, timeout_set), 1 },
{ "cache=never",
offsetof(struct lo_data, cache), CACHE_NEVER },
{ "cache=auto",
offsetof(struct lo_data, cache), CACHE_NORMAL },
{ "cache=always",
offsetof(struct lo_data, cache), CACHE_ALWAYS },
};
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);
}
static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
{
if (ino == FUSE_ROOT_ID)
return &lo_data(req)->root;
else
return (struct lo_inode *) (uintptr_t) ino;
}
static int lo_fd(fuse_req_t req, fuse_ino_t ino)
{
return lo_inode(req, ino)->fd;
}
static bool lo_debug(fuse_req_t req)
{
return lo_data(req)->debug != 0;
}
static void lo_init(void *userdata,
struct fuse_conn_info *conn)
{
struct lo_data *lo = (struct lo_data *)userdata;
bool has_flag;
if (lo->writeback) {
if (lo->debug && has_flag)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating writeback\n");
}
if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
if (lo->debug && has_flag)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating flock locks\n");
}
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void lo_destroy(void *userdata)
{
struct lo_data *lo = (struct lo_data*) userdata;
while (lo->root.next != &lo->root) {
struct lo_inode* next = lo->root.next;
lo->root.next = next->next;
close(next->fd);
free(next);
}
}
static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
int res;
struct stat buf;
struct lo_data *lo = lo_data(req);
int fd = fi ? fi->fh : lo_fd(req, ino);
(void) fi;
res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
return (void) fuse_reply_err(req, errno);
fuse_reply_attr(req, &buf, lo->timeout);
}
static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
int valid, struct fuse_file_info *fi)
{
int saverr;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
int ifd = inode->fd;
int res;
if (valid & FUSE_SET_ATTR_MODE) {
if (fi) {
res = fchmod(fi->fh, attr->st_mode);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = chmod(procname, attr->st_mode);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
attr->st_uid : (uid_t) -1;
gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
attr->st_gid : (gid_t) -1;
res = fchownat(ifd, "", uid, gid,
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
}
if (valid & FUSE_SET_ATTR_SIZE) {
if (fi) {
res = ftruncate(fi->fh, attr->st_size);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = truncate(procname, attr->st_size);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
struct timespec tv[2];
tv[0].tv_sec = 0;
tv[1].tv_sec = 0;
tv[0].tv_nsec = UTIME_OMIT;
tv[1].tv_nsec = UTIME_OMIT;
if (valid & FUSE_SET_ATTR_ATIME_NOW)
tv[0].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_ATIME)
tv[0] = attr->st_atim;
if (valid & FUSE_SET_ATTR_MTIME_NOW)
tv[1].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_MTIME)
tv[1] = attr->st_mtim;
if (fi)
res = futimens(fi->fh, tv);
else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = utimensat(AT_FDCWD, procname, tv, 0);
}
if (res == -1)
goto out_err;
}
return lo_getattr(req, ino, fi);
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
{
struct lo_inode *p;
struct lo_inode *ret = NULL;
pthread_mutex_lock(&lo->mutex);
for (p = lo->root.next; p != &lo->root; p = p->next) {
if (p->ino == st->st_ino && p->dev == st->st_dev) {
assert(p->refcount > 0);
ret = p;
ret->refcount++;
break;
}
}
pthread_mutex_unlock(&lo->mutex);
return ret;
}
static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo)
{
struct lo_inode *inode = NULL;
struct lo_inode *prev, *next;
inode = calloc(1, sizeof(struct lo_inode));
if (!inode)
return NULL;
inode->refcount = 1;
inode->fd = fd;
inode->ino = e->attr.st_ino;
inode->dev = e->attr.st_dev;
pthread_mutex_lock(&lo->mutex);
prev = &lo->root;
next = prev->next;
next->prev = inode;
inode->next = next;
inode->prev = prev;
prev->next = inode;
pthread_mutex_unlock(&lo->mutex);
return inode;
}
static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e)
{
int res;
struct lo_data *lo = lo_data(req);
memset(e, 0, sizeof(*e));
e->attr_timeout = lo->timeout;
e->entry_timeout = lo->timeout;
res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
return errno;
e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%d -> %lli\n",
(unsigned long long) parent, fd, (unsigned long long) e->ino);
return 0;
}
static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
struct fuse_entry_param *e)
{
int newfd;
int res;
int saverr;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode;
memset(e, 0, sizeof(*e));
e->attr_timeout = lo->timeout;
e->entry_timeout = lo->timeout;
newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
if (newfd == -1)
goto out_err;
res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
inode = lo_find(lo_data(req), &e->attr);
if (inode) {
close(newfd);
newfd = -1;
} else {
inode = create_new_inode(newfd, e, lo);
if (!inode)
goto out_err;
}
e->ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name, (unsigned long long) e->ino);
return 0;
out_err:
saverr = errno;
if (newfd != -1)
close(newfd);
return saverr;
}
static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
parent, name);
err = lo_do_lookup(req, parent, name, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_entry(req, &e);
}
static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev,
const char *link)
{
int res;
int saverr;
struct lo_inode *dir = lo_inode(req, parent);
struct fuse_entry_param e;
res = mknod_wrapper(dir->fd, name, link, mode, rdev);
saverr = errno;
if (res == -1)
goto out;
saverr = lo_do_lookup(req, parent, name, &e);
if (saverr)
goto out;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name, (unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out:
fuse_reply_err(req, saverr);
}
static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev)
{
lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
}
static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode)
{
lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
}
static void lo_symlink(fuse_req_t req, const char *link,
fuse_ino_t parent, const char *name)
{
lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
}
static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
const char *name)
{
int res;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
struct fuse_entry_param e;
char procname[64];
int saverr;
memset(&e, 0, sizeof(struct fuse_entry_param));
e.attr_timeout = lo->timeout;
e.entry_timeout = lo->timeout;
sprintf(procname, "/proc/self/fd/%i", inode->fd);
res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
AT_SYMLINK_FOLLOW);
if (res == -1)
goto out_err;
res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
pthread_mutex_lock(&lo->mutex);
inode->refcount++;
pthread_mutex_unlock(&lo->mutex);
e.ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name,
(unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char *newname,
unsigned int flags)
{
int res;
if (flags) {
fuse_reply_err(req, EINVAL);
return;
}
res = renameat(lo_fd(req, parent), name,
lo_fd(req, newparent), newname);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, 0);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
{
if (!inode)
return;
pthread_mutex_lock(&lo->mutex);
assert(inode->refcount >= n);
inode->refcount -= n;
if (!inode->refcount) {
struct lo_inode *prev, *next;
prev = inode->prev;
next = inode->next;
next->prev = prev;
prev->next = next;
pthread_mutex_unlock(&lo->mutex);
close(inode->fd);
free(inode);
} else {
pthread_mutex_unlock(&lo->mutex);
}
}
static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
(unsigned long long) ino,
(unsigned long long) inode->refcount,
(unsigned long long) nlookup);
}
unref_inode(lo, inode, nlookup);
}
static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{
lo_forget_one(req, ino, nlookup);
}
static void lo_forget_multi(fuse_req_t req, size_t count,
struct fuse_forget_data *forgets)
{
int i;
for (i = 0; i < count; i++)
lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
}
static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
{
char buf[PATH_MAX + 1];
int res;
res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
if (res == -1)
return (void) fuse_reply_err(req, errno);
if (res == sizeof(buf))
return (void) fuse_reply_err(req, ENAMETOOLONG);
buf[res] = '\0';
}
struct lo_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
{
return (struct lo_dirp *) (uintptr_t) fi->fh;
}
static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int error = ENOMEM;
struct lo_data *lo = lo_data(req);
struct lo_dirp *d;
int fd = -1;
d = calloc(1, sizeof(struct lo_dirp));
if (d == NULL)
goto out_err;
fd = openat(lo_fd(req, ino), ".", O_RDONLY);
if (fd == -1)
goto out_errno;
d->dp = fdopendir(fd);
if (d->dp == NULL)
goto out_errno;
d->offset = 0;
d->entry = NULL;
fi->fh = (uintptr_t) d;
if (lo->cache != CACHE_NEVER)
fi->cache_readdir = 1;
if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
fuse_reply_open(req, fi);
return;
out_errno:
error = errno;
out_err:
if (d) {
if (fd != -1)
close(fd);
free(d);
}
fuse_reply_err(req, error);
}
static int is_dot_or_dotdot(const char *name)
{
return name[0] == '.' && (name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0'));
}
static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi, int plus)
{
struct lo_dirp *d = lo_dirp(fi);
char *buf;
char *p;
size_t rem = size;
int err;
(void) ino;
buf = calloc(1, size);
if (!buf) {
err = ENOMEM;
goto error;
}
p = buf;
if (offset != d->offset) {
seekdir(d->dp, offset);
d->entry = NULL;
d->offset = offset;
}
while (1) {
size_t entsize;
off_t nextoff;
const char *name;
if (!d->entry) {
errno = 0;
d->entry = readdir(d->dp);
if (!d->entry) {
if (errno) { // Error
err = errno;
goto error;
} else { // End of stream
break;
}
}
}
nextoff = d->entry->d_off;
name = d->entry->d_name;
fuse_ino_t entry_ino = 0;
if (plus) {
struct fuse_entry_param e;
if (is_dot_or_dotdot(name)) {
e = (struct fuse_entry_param) {
.attr.st_ino = d->entry->d_ino,
.attr.st_mode = d->entry->d_type << 12,
};
} else {
err = lo_do_lookup(req, ino, name, &e);
if (err)
goto error;
entry_ino = e.ino;
}
entsize = fuse_add_direntry_plus(req, p, rem, name,
&e, nextoff);
} else {
struct stat st = {
.st_ino = d->entry->d_ino,
.st_mode = d->entry->d_type << 12,
};
entsize = fuse_add_direntry(req, p, rem, name,
&st, nextoff);
}
if (entsize > rem) {
if (entry_ino != 0)
lo_forget_one(req, entry_ino, 1);
break;
}
p += entsize;
rem -= entsize;
d->entry = NULL;
d->offset = nextoff;
}
err = 0;
error:
// If there's an error, we can only signal it if we haven't stored
// any entries yet - otherwise we'd end up with wrong lookup
// counts for the entries that are already in the buffer. So we
// return what we've collected until that point.
if (err && rem == size)
fuse_reply_err(req, err);
else
fuse_reply_buf(req, buf, size - rem);
free(buf);
}
static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
lo_do_readdir(req, ino, size, offset, fi, 0);
}
static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
lo_do_readdir(req, ino, size, offset, fi, 1);
}
static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
struct lo_dirp *d = lo_dirp(fi);
(void) ino;
closedir(d->dp);
free(d);
fuse_reply_err(req, 0);
}
static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent,
mode_t mode, struct fuse_file_info *fi)
{
int fd;
struct lo_data *lo = lo_data(req);
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n",
parent);
fd = openat(lo_fd(req, parent), ".",
(fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
err = fill_entry_param_new_inode(req, parent, fd, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_create(req, &e, fi);
}
static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, struct fuse_file_info *fi)
{
int fd;
struct lo_data *lo = lo_data(req);
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
parent, name);
fd = openat(lo_fd(req, parent), name,
(fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
err = lo_do_lookup(req, parent, name, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_create(req, &e, fi);
}
static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
int fd = dirfd(lo_dirp(fi)->dp);
(void) ino;
if (datasync)
res = fdatasync(fd);
else
res = fsync(fd);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int fd;
char buf[64];
struct lo_data *lo = lo_data(req);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
ino, fi->flags);
/* With writeback cache, kernel may send read requests even
when userspace opened write-only */
if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
fi->flags &= ~O_ACCMODE;
fi->flags |= O_RDWR;
}
/* With writeback cache, O_APPEND is handled by the kernel.
This breaks atomicity (since the file may change in the
underlying filesystem, so that the kernel's idea of the
end of the file isn't accurate anymore). In this example,
we just accept that. A more rigorous filesystem may want
to return an error here */
if (lo->writeback && (fi->flags & O_APPEND))
fi->flags &= ~O_APPEND;
sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
fd = open(buf, fi->flags & ~O_NOFOLLOW);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file in the kernel). */
if (fi->flags & O_DIRECT)
fi->direct_io = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
fuse_reply_open(req, fi);
}
static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
(void) ino;
close(fi->fh);
fuse_reply_err(req, 0);
}
static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int res;
(void) ino;
res = close(dup(fi->fh));
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
(void) ino;
if (datasync)
res = fdatasync(fi->fh);
else
res = fsync(fi->fh);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
"off=%lu)\n", ino, size, (unsigned long) offset);
buf.buf[0].fd = fi->fh;
buf.buf[0].pos = offset;
}
static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
struct fuse_bufvec *in_buf, off_t off,
struct fuse_file_info *fi)
{
(void) ino;
ssize_t res;
struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
out_buf.buf[0].fd = fi->fh;
out_buf.buf[0].pos = off;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%jd)\n",
ino, out_buf.buf[0].size, (intmax_t) off);
res = fuse_buf_copy(&out_buf, in_buf, 0);
if(res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_write(req, (size_t) res);
}
static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
{
int res;
struct statvfs stbuf;
res = fstatvfs(lo_fd(req, ino), &stbuf);
if (res == -1)
fuse_reply_err(req, errno);
else
fuse_reply_statfs(req, &stbuf);
}
static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
int err;
(void) ino;
err = -do_fallocate(fi->fh, mode, offset, length);
fuse_reply_err(req, err);
}
static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
int op)
{
int res;
(void) ino;
res = flock(fi->fh, op);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
ino, name, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = getxattr(procname, name, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
fuse_reply_buf(req, value, ret);
} else {
ret = getxattr(procname, name, NULL, 0);
if (ret == -1)
goto out_err;
fuse_reply_xattr(req, ret);
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
fuse_reply_err(req, saverr);
goto out_free;
}
static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
ino, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = listxattr(procname, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
fuse_reply_buf(req, value, ret);
} else {
ret = listxattr(procname, NULL, 0);
if (ret == -1)
goto out_err;
fuse_reply_xattr(req, ret);
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
fuse_reply_err(req, saverr);
goto out_free;
}
static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags)
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
ino, name, value, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = setxattr(procname, name, value, size, flags);
saverr = ret == -1 ? errno : 0;
out:
fuse_reply_err(req, saverr);
}
static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
ino, name);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = removexattr(procname, name);
saverr = ret == -1 ? errno : 0;
out:
fuse_reply_err(req, saverr);
}
#ifdef HAVE_COPY_FILE_RANGE
static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
struct fuse_file_info *fi_in,
fuse_ino_t ino_out, off_t off_out,
struct fuse_file_info *fi_out, size_t len,
int flags)
{
ssize_t res;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"%s(ino=%lld fd=%lld off=%jd ino=%lld fd=%lld off=%jd, size=%zd, flags=0x%x)\n",
__func__, (unsigned long long)ino_in,
(unsigned long long)fi_in->fh,
(intmax_t) off_in, (unsigned long long)ino_out,
(unsigned long long)fi_out->fh, (intmax_t) off_out,
len, flags);
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res < 0)
fuse_reply_err(req, errno);
else
fuse_reply_write(req, res);
}
#endif
static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
struct fuse_file_info *fi)
{
off_t res;
(void)ino;
res = lseek(fi->fh, off, whence);
if (res != -1)
fuse_reply_lseek(req, res);
else
fuse_reply_err(req, errno);
}
#ifdef HAVE_STATX
static void lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
struct fuse_file_info *fi)
{
struct lo_data *lo = lo_data(req);
struct statx buf;
int res;
int fd;
if (fi)
fd = fi->fh;
else
fd = lo_fd(req, ino);
res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, &buf);
if (res == -1)
fuse_reply_err(req, errno);
else
fuse_reply_statx(req, 0, &buf, lo->timeout);
}
#endif
static const struct fuse_lowlevel_ops lo_oper = {
.init = lo_init,
.destroy = lo_destroy,
.lookup = lo_lookup,
.mkdir = lo_mkdir,
.mknod = lo_mknod,
.symlink = lo_symlink,
.link = lo_link,
.unlink = lo_unlink,
.rmdir = lo_rmdir,
.rename = lo_rename,
.forget = lo_forget,
.forget_multi = lo_forget_multi,
.getattr = lo_getattr,
.setattr = lo_setattr,
.readlink = lo_readlink,
.opendir = lo_opendir,
.readdir = lo_readdir,
.readdirplus = lo_readdirplus,
.releasedir = lo_releasedir,
.fsyncdir = lo_fsyncdir,
.create = lo_create,
.tmpfile = lo_tmpfile,
.open = lo_open,
.release = lo_release,
.flush = lo_flush,
.fsync = lo_fsync,
.read = lo_read,
.write_buf = lo_write_buf,
.statfs = lo_statfs,
.fallocate = lo_fallocate,
.flock = lo_flock,
.getxattr = lo_getxattr,
.listxattr = lo_listxattr,
.setxattr = lo_setxattr,
.removexattr = lo_removexattr,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = lo_copy_file_range,
#endif
.lseek = lo_lseek,
#ifdef HAVE_STATX
.statx = lo_statx,
#endif
};
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
struct lo_data lo = { .debug = 0,
.writeback = 0 };
int ret = -1;
/* Don't mask creation mode, kernel already did that */
umask(0);
pthread_mutex_init(&lo.mutex, NULL);
lo.root.next = lo.root.prev = &lo.root;
lo.root.fd = -1;
lo.cache = CACHE_NORMAL;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
passthrough_ll_help();
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
return 1;
lo.debug = opts.debug;
lo.root.refcount = 2;
if (lo.source) {
struct stat stat;
int res;
res = lstat(lo.source, &stat);
if (res == -1) {
fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
lo.source);
exit(1);
}
if (!S_ISDIR(stat.st_mode)) {
fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
exit(1);
}
} else {
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) {
case CACHE_NEVER:
lo.timeout = 0.0;
break;
case CACHE_NORMAL:
lo.timeout = 1.0;
break;
case CACHE_ALWAYS:
lo.timeout = 86400.0;
break;
}
} else if (lo.timeout < 0) {
fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
lo.timeout);
exit(1);
}
lo.root.fd = open(lo.source, O_PATH);
if (lo.root.fd == -1) {
fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
lo.source);
exit(1);
}
se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
if (lo.root.fd >= 0)
close(lo.root.fd);
free(lo.source);
return ret ? 1 : 0;
}
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
off_t pos
size_t size
struct fuse_buf buf[1]
uint32_t no_interrupt
uint32_t capable
double entry_timeout
fuse_ino_t ino
double attr_timeout
struct stat attr
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file passthrough_ll.c.

fuse-3.18.2/doc/html/example_2poll_8c.html0000644000175000017500000007755415156613443017303 0ustar berndbernd libfuse: example/poll.c File Reference
libfuse
poll.c File Reference
#include <fuse.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <poll.h>
#include <stdbool.h>

Go to the source code of this file.

Detailed Description

This example illustrates how to write a FUSE file system that supports polling for changes that don't come through the kernel. It can be tested with the poll_client.c program.

Compile with:

gcc -Wall poll.c `pkg-config fuse3 --cflags --libs` -o poll

Source code

/*
FUSE fsel: FUSE select example
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <poll.h>
#include <stdbool.h>
/*
* fsel_open_mask is used to limit the number of opens to 1 per file.
* This is to use file index (0-F) as fh as poll support requires
* unique fh per open file. Lifting this would require proper open
* file management.
*/
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 up to 10 chars */
#define FSEL_FILES 16
static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
static _Atomic bool fsel_stop = false;
static pthread_t fsel_producer_thread;
static int fsel_path_index(const char *path)
{
char ch = path[1];
if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
return -1;
return ch <= '9' ? ch - '0' : ch - 'A' + 10;
}
static void fsel_destroy(void *private_data)
{
(void)private_data;
fsel_stop = true;
pthread_join(fsel_producer_thread, NULL);
}
static int fsel_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int idx;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0555;
stbuf->st_nlink = 2;
return 0;
}
idx = fsel_path_index(path);
if (idx < 0)
return -ENOENT;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = fsel_cnt[idx];
return 0;
}
static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
char name[2] = { };
int i;
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
for (i = 0; i < FSEL_FILES; i++) {
name[0] = fsel_hex_map[i];
filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
}
return 0;
}
static int fsel_open(const char *path, struct fuse_file_info *fi)
{
int idx = fsel_path_index(path);
if (idx < 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
if (fsel_open_mask & (1 << idx))
return -EBUSY;
fsel_open_mask |= (1 << idx);
/*
* fsel files are nonseekable somewhat pipe-like files which
* gets filled up periodically by producer thread and consumed
* on read. Tell FUSE as such.
*/
fi->fh = idx;
fi->direct_io = 1;
fi->nonseekable = 1;
return 0;
}
static int fsel_release(const char *path, struct fuse_file_info *fi)
{
int idx = fi->fh;
(void) path;
fsel_open_mask &= ~(1 << idx);
return 0;
}
static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int idx = fi->fh;
(void) path;
(void) offset;
pthread_mutex_lock(&fsel_mutex);
if (fsel_cnt[idx] < size)
size = fsel_cnt[idx];
printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
fsel_cnt[idx] -= size;
pthread_mutex_unlock(&fsel_mutex);
memset(buf, fsel_hex_map[idx], size);
return size;
}
static int fsel_poll(const char *path, struct fuse_file_info *fi,
struct fuse_pollhandle *ph, unsigned *reventsp)
{
static unsigned polled_zero;
int idx = fi->fh;
(void) path;
/*
* Poll notification requires pointer to struct fuse which
* can't be obtained when using fuse_main(). As notification
* happens only after poll is called, fill it here from
* fuse_context.
*/
if (!fsel_fuse) {
struct fuse_context *cxt = fuse_get_context();
if (cxt)
fsel_fuse = cxt->fuse;
}
pthread_mutex_lock(&fsel_mutex);
if (ph != NULL) {
struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
if (oldph)
fsel_poll_notify_mask |= (1 << idx);
fsel_poll_handle[idx] = ph;
}
if (fsel_cnt[idx]) {
*reventsp |= POLLIN;
printf("POLL %X cnt=%u polled_zero=%u\n",
idx, fsel_cnt[idx], polled_zero);
polled_zero = 0;
} else
polled_zero++;
pthread_mutex_unlock(&fsel_mutex);
return 0;
}
static const struct fuse_operations fsel_oper = {
.destroy = fsel_destroy,
.getattr = fsel_getattr,
.readdir = fsel_readdir,
.open = fsel_open,
.release = fsel_release,
.read = fsel_read,
.poll = fsel_poll,
};
static void *fsel_producer(void *data)
{
const struct timespec interval = { 0, 250000000 };
unsigned idx = 0, nr = 1;
(void) data;
while (!fsel_stop) {
int i, t;
pthread_mutex_lock(&fsel_mutex);
/*
* This is the main producer loop which is executed
* ever 500ms. On each iteration, it fills one byte
* to 1, 2 or 4 files and sends poll notification if
* requested.
*/
for (i = 0, t = idx; i < nr;
i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
if (fsel_cnt[t] == FSEL_CNT_MAX)
continue;
fsel_cnt[t]++;
if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
struct fuse_pollhandle *ph;
printf("NOTIFY %X\n", t);
ph = fsel_poll_handle[t];
fuse_notify_poll(ph);
fsel_poll_notify_mask &= ~(1 << t);
fsel_poll_handle[t] = NULL;
}
}
idx = (idx + 1) % FSEL_FILES;
if (idx == 0)
nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
pthread_mutex_unlock(&fsel_mutex);
nanosleep(&interval, NULL);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_attr_t attr;
int ret;
errno = pthread_mutex_init(&fsel_mutex, NULL);
if (errno) {
perror("pthread_mutex_init");
return 1;
}
errno = pthread_attr_init(&attr);
if (errno) {
perror("pthread_attr_init");
return 1;
}
errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL);
if (errno) {
perror("pthread_create");
return 1;
}
ret = fuse_main(argc, argv, &fsel_oper, NULL);
return ret;
}
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
struct fuse * fuse
Definition fuse.h:862
uint32_t nonseekable
Definition fuse_common.h:78
uint32_t direct_io
Definition fuse_common.h:63
void(* destroy)(void *private_data)
Definition fuse.h:649

Definition in file poll.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2example_2poll_8c.html0000644000175000017500000007777515156613443021466 0ustar berndbernd libfuse: fuse-3.17.4/example/poll.c File Reference
libfuse
poll.c File Reference
#include <fuse.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <poll.h>
#include <stdbool.h>

Go to the source code of this file.

Detailed Description

This example illustrates how to write a FUSE file system that supports polling for changes that don't come through the kernel. It can be tested with the poll_client.c program.

Compile with:

gcc -Wall poll.c `pkg-config fuse3 --cflags --libs` -o poll

Source code

/*
FUSE fsel: FUSE select example
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <poll.h>
#include <stdbool.h>
/*
* fsel_open_mask is used to limit the number of opens to 1 per file.
* This is to use file index (0-F) as fh as poll support requires
* unique fh per open file. Lifting this would require proper open
* file management.
*/
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 up to 10 chars */
#define FSEL_FILES 16
static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
static _Atomic bool fsel_stop = false;
static pthread_t fsel_producer_thread;
static int fsel_path_index(const char *path)
{
char ch = path[1];
if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
return -1;
return ch <= '9' ? ch - '0' : ch - 'A' + 10;
}
static void fsel_destroy(void *private_data)
{
(void)private_data;
fsel_stop = true;
pthread_join(fsel_producer_thread, NULL);
}
static int fsel_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int idx;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0555;
stbuf->st_nlink = 2;
return 0;
}
idx = fsel_path_index(path);
if (idx < 0)
return -ENOENT;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = fsel_cnt[idx];
return 0;
}
static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
char name[2] = { };
int i;
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
for (i = 0; i < FSEL_FILES; i++) {
name[0] = fsel_hex_map[i];
filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
}
return 0;
}
static int fsel_open(const char *path, struct fuse_file_info *fi)
{
int idx = fsel_path_index(path);
if (idx < 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
if (fsel_open_mask & (1 << idx))
return -EBUSY;
fsel_open_mask |= (1 << idx);
/*
* fsel files are nonseekable somewhat pipe-like files which
* gets filled up periodically by producer thread and consumed
* on read. Tell FUSE as such.
*/
fi->fh = idx;
fi->direct_io = 1;
fi->nonseekable = 1;
return 0;
}
static int fsel_release(const char *path, struct fuse_file_info *fi)
{
int idx = fi->fh;
(void) path;
fsel_open_mask &= ~(1 << idx);
return 0;
}
static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int idx = fi->fh;
(void) path;
(void) offset;
pthread_mutex_lock(&fsel_mutex);
if (fsel_cnt[idx] < size)
size = fsel_cnt[idx];
printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
fsel_cnt[idx] -= size;
pthread_mutex_unlock(&fsel_mutex);
memset(buf, fsel_hex_map[idx], size);
return size;
}
static int fsel_poll(const char *path, struct fuse_file_info *fi,
struct fuse_pollhandle *ph, unsigned *reventsp)
{
static unsigned polled_zero;
int idx = fi->fh;
(void) path;
/*
* Poll notification requires pointer to struct fuse which
* can't be obtained when using fuse_main(). As notification
* happens only after poll is called, fill it here from
* fuse_context.
*/
if (!fsel_fuse) {
struct fuse_context *cxt = fuse_get_context();
if (cxt)
fsel_fuse = cxt->fuse;
}
pthread_mutex_lock(&fsel_mutex);
if (ph != NULL) {
struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
if (oldph)
fsel_poll_notify_mask |= (1 << idx);
fsel_poll_handle[idx] = ph;
}
if (fsel_cnt[idx]) {
*reventsp |= POLLIN;
printf("POLL %X cnt=%u polled_zero=%u\n",
idx, fsel_cnt[idx], polled_zero);
polled_zero = 0;
} else
polled_zero++;
pthread_mutex_unlock(&fsel_mutex);
return 0;
}
static const struct fuse_operations fsel_oper = {
.destroy = fsel_destroy,
.getattr = fsel_getattr,
.readdir = fsel_readdir,
.open = fsel_open,
.release = fsel_release,
.read = fsel_read,
.poll = fsel_poll,
};
static void *fsel_producer(void *data)
{
const struct timespec interval = { 0, 250000000 };
unsigned idx = 0, nr = 1;
(void) data;
while (!fsel_stop) {
int i, t;
pthread_mutex_lock(&fsel_mutex);
/*
* This is the main producer loop which is executed
* ever 500ms. On each iteration, it fills one byte
* to 1, 2 or 4 files and sends poll notification if
* requested.
*/
for (i = 0, t = idx; i < nr;
i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
if (fsel_cnt[t] == FSEL_CNT_MAX)
continue;
fsel_cnt[t]++;
if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
struct fuse_pollhandle *ph;
printf("NOTIFY %X\n", t);
ph = fsel_poll_handle[t];
fuse_notify_poll(ph);
fsel_poll_notify_mask &= ~(1 << t);
fsel_poll_handle[t] = NULL;
}
}
idx = (idx + 1) % FSEL_FILES;
if (idx == 0)
nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
pthread_mutex_unlock(&fsel_mutex);
nanosleep(&interval, NULL);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_attr_t attr;
int ret;
errno = pthread_mutex_init(&fsel_mutex, NULL);
if (errno) {
perror("pthread_mutex_init");
return 1;
}
errno = pthread_attr_init(&attr);
if (errno) {
perror("pthread_attr_init");
return 1;
}
errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL);
if (errno) {
perror("pthread_create");
return 1;
}
ret = fuse_main(argc, argv, &fsel_oper, NULL);
return ret;
}
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
struct fuse * fuse
Definition fuse.h:862
uint32_t nonseekable
Definition fuse_common.h:78
uint32_t direct_io
Definition fuse_common.h:63
void(* destroy)(void *private_data)
Definition fuse.h:649

Definition in file poll.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2poll_8c.html0000644000175000017500000007777515156613443021464 0ustar berndbernd libfuse: fuse-3.18.1/example/poll.c File Reference
libfuse
poll.c File Reference
#include <fuse.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <poll.h>
#include <stdbool.h>

Go to the source code of this file.

Detailed Description

This example illustrates how to write a FUSE file system that supports polling for changes that don't come through the kernel. It can be tested with the poll_client.c program.

Compile with:

gcc -Wall poll.c `pkg-config fuse3 --cflags --libs` -o poll

Source code

/*
FUSE fsel: FUSE select example
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <poll.h>
#include <stdbool.h>
/*
* fsel_open_mask is used to limit the number of opens to 1 per file.
* This is to use file index (0-F) as fh as poll support requires
* unique fh per open file. Lifting this would require proper open
* file management.
*/
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 up to 10 chars */
#define FSEL_FILES 16
static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
static _Atomic bool fsel_stop = false;
static pthread_t fsel_producer_thread;
static int fsel_path_index(const char *path)
{
char ch = path[1];
if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
return -1;
return ch <= '9' ? ch - '0' : ch - 'A' + 10;
}
static void fsel_destroy(void *private_data)
{
(void)private_data;
fsel_stop = true;
pthread_join(fsel_producer_thread, NULL);
}
static int fsel_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int idx;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0555;
stbuf->st_nlink = 2;
return 0;
}
idx = fsel_path_index(path);
if (idx < 0)
return -ENOENT;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = fsel_cnt[idx];
return 0;
}
static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
char name[2] = { };
int i;
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
for (i = 0; i < FSEL_FILES; i++) {
name[0] = fsel_hex_map[i];
filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
}
return 0;
}
static int fsel_open(const char *path, struct fuse_file_info *fi)
{
int idx = fsel_path_index(path);
if (idx < 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
if (fsel_open_mask & (1 << idx))
return -EBUSY;
fsel_open_mask |= (1 << idx);
/*
* fsel files are nonseekable somewhat pipe-like files which
* gets filled up periodically by producer thread and consumed
* on read. Tell FUSE as such.
*/
fi->fh = idx;
fi->direct_io = 1;
fi->nonseekable = 1;
return 0;
}
static int fsel_release(const char *path, struct fuse_file_info *fi)
{
int idx = fi->fh;
(void) path;
fsel_open_mask &= ~(1 << idx);
return 0;
}
static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int idx = fi->fh;
(void) path;
(void) offset;
pthread_mutex_lock(&fsel_mutex);
if (fsel_cnt[idx] < size)
size = fsel_cnt[idx];
printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
fsel_cnt[idx] -= size;
pthread_mutex_unlock(&fsel_mutex);
memset(buf, fsel_hex_map[idx], size);
return size;
}
static int fsel_poll(const char *path, struct fuse_file_info *fi,
struct fuse_pollhandle *ph, unsigned *reventsp)
{
static unsigned polled_zero;
int idx = fi->fh;
(void) path;
/*
* Poll notification requires pointer to struct fuse which
* can't be obtained when using fuse_main(). As notification
* happens only after poll is called, fill it here from
* fuse_context.
*/
if (!fsel_fuse) {
struct fuse_context *cxt = fuse_get_context();
if (cxt)
fsel_fuse = cxt->fuse;
}
pthread_mutex_lock(&fsel_mutex);
if (ph != NULL) {
struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
if (oldph)
fsel_poll_notify_mask |= (1 << idx);
fsel_poll_handle[idx] = ph;
}
if (fsel_cnt[idx]) {
*reventsp |= POLLIN;
printf("POLL %X cnt=%u polled_zero=%u\n",
idx, fsel_cnt[idx], polled_zero);
polled_zero = 0;
} else
polled_zero++;
pthread_mutex_unlock(&fsel_mutex);
return 0;
}
static const struct fuse_operations fsel_oper = {
.destroy = fsel_destroy,
.getattr = fsel_getattr,
.readdir = fsel_readdir,
.open = fsel_open,
.release = fsel_release,
.read = fsel_read,
.poll = fsel_poll,
};
static void *fsel_producer(void *data)
{
const struct timespec interval = { 0, 250000000 };
unsigned idx = 0, nr = 1;
(void) data;
while (!fsel_stop) {
int i, t;
pthread_mutex_lock(&fsel_mutex);
/*
* This is the main producer loop which is executed
* ever 500ms. On each iteration, it fills one byte
* to 1, 2 or 4 files and sends poll notification if
* requested.
*/
for (i = 0, t = idx; i < nr;
i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
if (fsel_cnt[t] == FSEL_CNT_MAX)
continue;
fsel_cnt[t]++;
if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
struct fuse_pollhandle *ph;
printf("NOTIFY %X\n", t);
ph = fsel_poll_handle[t];
fuse_notify_poll(ph);
fsel_poll_notify_mask &= ~(1 << t);
fsel_poll_handle[t] = NULL;
}
}
idx = (idx + 1) % FSEL_FILES;
if (idx == 0)
nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
pthread_mutex_unlock(&fsel_mutex);
nanosleep(&interval, NULL);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_attr_t attr;
int ret;
errno = pthread_mutex_init(&fsel_mutex, NULL);
if (errno) {
perror("pthread_mutex_init");
return 1;
}
errno = pthread_attr_init(&attr);
if (errno) {
perror("pthread_attr_init");
return 1;
}
errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL);
if (errno) {
perror("pthread_create");
return 1;
}
ret = fuse_main(argc, argv, &fsel_oper, NULL);
return ret;
}
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
struct fuse * fuse
Definition fuse.h:862
uint32_t nonseekable
Definition fuse_common.h:78
uint32_t direct_io
Definition fuse_common.h:63
void(* destroy)(void *private_data)
Definition fuse.h:649

Definition in file poll.c.

fuse-3.18.2/doc/html/example_2poll__client_8c.html0000644000175000017500000002110415156613443020754 0ustar berndbernd libfuse: example/poll_client.c File Reference
libfuse
poll_client.c File Reference
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

Go to the source code of this file.

Detailed Description

This program tests the poll.c example file systsem.

Compile with:

 gcc -Wall poll_client.c -o poll_client

Source code

/*
FUSE fselclient: FUSE select example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define FSEL_FILES 16
int main(void)
{
static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
int fds[FSEL_FILES];
int i, nfds, tries;
for (i = 0; i < FSEL_FILES; i++) {
char name[] = { hex_map[i], '\0' };
fds[i] = open(name, O_RDONLY);
if (fds[i] < 0) {
perror("open");
return 1;
}
}
nfds = fds[FSEL_FILES - 1] + 1;
for(tries=0; tries < 16; tries++) {
static char buf[4096];
fd_set rfds;
int rc;
FD_ZERO(&rfds);
for (i = 0; i < FSEL_FILES; i++)
FD_SET(fds[i], &rfds);
rc = select(nfds, &rfds, NULL, NULL, NULL);
if (rc < 0) {
perror("select");
return 1;
}
for (i = 0; i < FSEL_FILES; i++) {
if (!FD_ISSET(fds[i], &rfds)) {
printf("_: ");
continue;
}
printf("%X:", i);
rc = read(fds[i], buf, sizeof(buf));
if (rc < 0) {
perror("read");
return 1;
}
printf("%02d ", rc);
}
printf("\n");
}
return 0;
}

Definition in file poll_client.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2example_2poll__client_8c.html0000644000175000017500000002132515156613443023137 0ustar berndbernd libfuse: fuse-3.17.4/example/poll_client.c File Reference
libfuse
poll_client.c File Reference
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

Go to the source code of this file.

Detailed Description

This program tests the poll.c example file systsem.

Compile with:

 gcc -Wall poll_client.c -o poll_client

Source code

/*
FUSE fselclient: FUSE select example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define FSEL_FILES 16
int main(void)
{
static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
int fds[FSEL_FILES];
int i, nfds, tries;
for (i = 0; i < FSEL_FILES; i++) {
char name[] = { hex_map[i], '\0' };
fds[i] = open(name, O_RDONLY);
if (fds[i] < 0) {
perror("open");
return 1;
}
}
nfds = fds[FSEL_FILES - 1] + 1;
for(tries=0; tries < 16; tries++) {
static char buf[4096];
fd_set rfds;
int rc;
FD_ZERO(&rfds);
for (i = 0; i < FSEL_FILES; i++)
FD_SET(fds[i], &rfds);
rc = select(nfds, &rfds, NULL, NULL, NULL);
if (rc < 0) {
perror("select");
return 1;
}
for (i = 0; i < FSEL_FILES; i++) {
if (!FD_ISSET(fds[i], &rfds)) {
printf("_: ");
continue;
}
printf("%X:", i);
rc = read(fds[i], buf, sizeof(buf));
if (rc < 0) {
perror("read");
return 1;
}
printf("%02d ", rc);
}
printf("\n");
}
return 0;
}

Definition in file poll_client.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2poll__client_8c.html0000644000175000017500000002132515156613443023135 0ustar berndbernd libfuse: fuse-3.18.1/example/poll_client.c File Reference
libfuse
poll_client.c File Reference
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

Go to the source code of this file.

Detailed Description

This program tests the poll.c example file systsem.

Compile with:

 gcc -Wall poll_client.c -o poll_client

Source code

/*
FUSE fselclient: FUSE select example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define FSEL_FILES 16
int main(void)
{
static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
int fds[FSEL_FILES];
int i, nfds, tries;
for (i = 0; i < FSEL_FILES; i++) {
char name[] = { hex_map[i], '\0' };
fds[i] = open(name, O_RDONLY);
if (fds[i] < 0) {
perror("open");
return 1;
}
}
nfds = fds[FSEL_FILES - 1] + 1;
for(tries=0; tries < 16; tries++) {
static char buf[4096];
fd_set rfds;
int rc;
FD_ZERO(&rfds);
for (i = 0; i < FSEL_FILES; i++)
FD_SET(fds[i], &rfds);
rc = select(nfds, &rfds, NULL, NULL, NULL);
if (rc < 0) {
perror("select");
return 1;
}
for (i = 0; i < FSEL_FILES; i++) {
if (!FD_ISSET(fds[i], &rfds)) {
printf("_: ");
continue;
}
printf("%X:", i);
rc = read(fds[i], buf, sizeof(buf));
if (rc < 0) {
perror("read");
return 1;
}
printf("%02d ", rc);
}
printf("\n");
}
return 0;
}

Definition in file poll_client.c.

fuse-3.18.2/doc/html/example_2printcap_8c.html0000644000175000017500000011451615156613443020143 0ustar berndbernd libfuse: example/printcap.c File Reference
libfuse
printcap.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem that prints out all capabilities supported by the kernel and then exits.

Compile with:

gcc -Wall printcap.c `pkg-config fuse3 --cflags --libs` -o printcap

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2017 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct fuse_session *se;
// Define a structure to hold capability information
struct cap_info {
uint64_t flag;
const char *name;
};
// Define an array of all capabilities
static const struct cap_info capabilities[] = {
{FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"},
{FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"},
{FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"},
{FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"},
{FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"},
{FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"},
{FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"},
{FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"},
{FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"},
{FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"},
{FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"},
{FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"},
{FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"},
{FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"},
{FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"},
{FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"},
{FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"},
{FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"},
{FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"},
{FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"},
{FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"},
{FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"},
{FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"},
{FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"},
{FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"},
{FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"},
{FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"},
{FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"},
// Add any new capabilities here
{0, NULL} // Sentinel to mark the end of the array
};
static void print_capabilities(struct fuse_conn_info *conn)
{
printf("Capabilities:\n");
for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) {
if (fuse_get_feature_flag(conn, cap->flag)) {
printf("\t%s\n", cap->name);
}
}
}
static void pc_init(void *userdata, struct fuse_conn_info *conn)
{
(void) userdata;
printf("Protocol version: %d.%d\n", conn->proto_major,
conn->proto_minor);
print_capabilities(conn);
}
static const struct fuse_lowlevel_ops pc_oper = {
.init = pc_init,
};
int main(int argc, char **argv)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
char *mountpoint;
int ret = -1;
mountpoint = strdup("/tmp/fuse_printcap_XXXXXX");
if(mkdtemp(mountpoint) == NULL) {
perror("mkdtemp");
return 1;
}
printf("FUSE library version %s\n", fuse_pkgversion());
se = fuse_session_new(&args, &pc_oper,
sizeof(pc_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, mountpoint) != 0)
goto err_out3;
ret = fuse_session_loop(se);
err_out3:
err_out2:
err_out1:
rmdir(mountpoint);
free(mountpoint);
return ret ? 1 : 0;
}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t proto_major
uint32_t proto_minor
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file printcap.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2example_2printcap_8c.html0000644000175000017500000011473715156613443022326 0ustar berndbernd libfuse: fuse-3.17.4/example/printcap.c File Reference
libfuse
printcap.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem that prints out all capabilities supported by the kernel and then exits.

Compile with:

gcc -Wall printcap.c `pkg-config fuse3 --cflags --libs` -o printcap

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2017 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct fuse_session *se;
// Define a structure to hold capability information
struct cap_info {
uint64_t flag;
const char *name;
};
// Define an array of all capabilities
static const struct cap_info capabilities[] = {
{FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"},
{FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"},
{FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"},
{FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"},
{FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"},
{FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"},
{FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"},
{FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"},
{FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"},
{FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"},
{FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"},
{FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"},
{FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"},
{FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"},
{FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"},
{FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"},
{FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"},
{FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"},
{FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"},
{FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"},
{FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"},
{FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"},
{FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"},
{FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"},
{FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"},
{FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"},
{FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"},
{FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"},
// Add any new capabilities here
{0, NULL} // Sentinel to mark the end of the array
};
static void print_capabilities(struct fuse_conn_info *conn)
{
printf("Capabilities:\n");
for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) {
if (fuse_get_feature_flag(conn, cap->flag)) {
printf("\t%s\n", cap->name);
}
}
}
static void pc_init(void *userdata, struct fuse_conn_info *conn)
{
(void) userdata;
printf("Protocol version: %d.%d\n", conn->proto_major,
conn->proto_minor);
print_capabilities(conn);
}
static const struct fuse_lowlevel_ops pc_oper = {
.init = pc_init,
};
int main(int argc, char **argv)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
char *mountpoint;
int ret = -1;
mountpoint = strdup("/tmp/fuse_printcap_XXXXXX");
if(mkdtemp(mountpoint) == NULL) {
perror("mkdtemp");
return 1;
}
printf("FUSE library version %s\n", fuse_pkgversion());
se = fuse_session_new(&args, &pc_oper,
sizeof(pc_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, mountpoint) != 0)
goto err_out3;
ret = fuse_session_loop(se);
err_out3:
err_out2:
err_out1:
rmdir(mountpoint);
free(mountpoint);
return ret ? 1 : 0;
}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t proto_major
uint32_t proto_minor
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file printcap.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2example_2printcap_8c.html0000644000175000017500000011473715156613443022324 0ustar berndbernd libfuse: fuse-3.18.1/example/printcap.c File Reference
libfuse
printcap.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem that prints out all capabilities supported by the kernel and then exits.

Compile with:

gcc -Wall printcap.c `pkg-config fuse3 --cflags --libs` -o printcap

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2017 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct fuse_session *se;
// Define a structure to hold capability information
struct cap_info {
uint64_t flag;
const char *name;
};
// Define an array of all capabilities
static const struct cap_info capabilities[] = {
{FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"},
{FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"},
{FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"},
{FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"},
{FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"},
{FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"},
{FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"},
{FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"},
{FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"},
{FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"},
{FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"},
{FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"},
{FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"},
{FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"},
{FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"},
{FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"},
{FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"},
{FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"},
{FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"},
{FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"},
{FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"},
{FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"},
{FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"},
{FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"},
{FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"},
{FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"},
{FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"},
{FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"},
// Add any new capabilities here
{0, NULL} // Sentinel to mark the end of the array
};
static void print_capabilities(struct fuse_conn_info *conn)
{
printf("Capabilities:\n");
for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) {
if (fuse_get_feature_flag(conn, cap->flag)) {
printf("\t%s\n", cap->name);
}
}
}
static void pc_init(void *userdata, struct fuse_conn_info *conn)
{
(void) userdata;
printf("Protocol version: %d.%d\n", conn->proto_major,
conn->proto_minor);
print_capabilities(conn);
}
static const struct fuse_lowlevel_ops pc_oper = {
.init = pc_init,
};
int main(int argc, char **argv)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
char *mountpoint;
int ret = -1;
mountpoint = strdup("/tmp/fuse_printcap_XXXXXX");
if(mkdtemp(mountpoint) == NULL) {
perror("mkdtemp");
return 1;
}
printf("FUSE library version %s\n", fuse_pkgversion());
se = fuse_session_new(&args, &pc_oper,
sizeof(pc_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, mountpoint) != 0)
goto err_out3;
ret = fuse_session_loop(se);
err_out3:
err_out2:
err_out1:
rmdir(mountpoint);
free(mountpoint);
return ret ? 1 : 0;
}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t proto_major
uint32_t proto_minor
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file printcap.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse_8h.html0000644000175000017500000013537115156613443021442 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse.h File Reference
libfuse
fuse.h File Reference
#include "fuse_common.h"
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>

Go to the source code of this file.

Data Structures

struct  fuse_config
 
struct  fuse_operations
 
struct  fuse_context
 

Macros

#define FUSE_REGISTER_MODULE(name_, factory_)    fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
 

Typedefs

typedef int(* fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
 
typedef struct fuse_fs *(* fuse_module_factory_t) (struct fuse_args *args, struct fuse_fs *fs[])
 

Enumerations

enum  fuse_readdir_flags { FUSE_READDIR_DEFAULTS = 0 , FUSE_READDIR_PLUS = (1 << 0) }
 
enum  fuse_fill_dir_flags { FUSE_FILL_DIR_DEFAULTS = 0 , FUSE_FILL_DIR_PLUS = (1 << 1) }
 

Functions

int fuse_main_real_versioned (int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
 
void fuse_lib_help (struct fuse_args *args)
 
int fuse_mount (struct fuse *f, const char *mountpoint)
 
void fuse_unmount (struct fuse *f)
 
void fuse_destroy (struct fuse *f)
 
int fuse_loop (struct fuse *f)
 
void fuse_exit (struct fuse *f)
 
struct fuse_contextfuse_get_context (void)
 
int fuse_getgroups (int size, gid_t list[])
 
int fuse_interrupted (void)
 
int fuse_invalidate_path (struct fuse *f, const char *path)
 
int fuse_start_cleanup_thread (struct fuse *fuse)
 
void fuse_stop_cleanup_thread (struct fuse *fuse)
 
int fuse_clean_cache (struct fuse *fuse)
 
struct fuse_fs * fuse_fs_new (const struct fuse_operations *op, size_t op_size, void *private_data)
 
struct fuse_session * fuse_get_session (struct fuse *f)
 
int fuse_open_channel (const char *mountpoint, const char *options)
 

Detailed Description

This file defines the library interface of FUSE

IMPORTANT: you should define FUSE_USE_VERSION before including this header.

Definition in file fuse.h.

Macro Definition Documentation

◆ FUSE_REGISTER_MODULE

#define FUSE_REGISTER_MODULE (   name_,
  factory_ 
)     fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_

Register filesystem module

If the "-omodules=*name*_:..." option is present, filesystem objects are created and pushed onto the stack with the factory_ function.

Parameters
name_the name of this filesystem module
factory_the factory function for this filesystem module

Definition at line 1395 of file fuse.h.

Typedef Documentation

◆ fuse_fill_dir_t

typedef int(* fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)

Function to add an entry in a readdir() operation

The off parameter can be any non-zero value that enables the filesystem to identify the current point in the directory stream. It does not need to be the actual physical position. A value of zero is reserved to indicate that seeking in directories is not supported.

Parameters
bufthe buffer passed to the readdir() operation
namethe file name of the directory entry
stbuffile attributes, can be NULL
offoffset of the next entry or zero
flagsfill flags
Returns
1 if buffer is full, zero otherwise

Definition at line 87 of file fuse.h.

◆ fuse_module_factory_t

typedef struct fuse_fs *(* fuse_module_factory_t) (struct fuse_args *args, struct fuse_fs *fs[])

Factory for creating filesystem objects

The function may use and remove options from 'args' that belong to this module.

For now the 'fs' vector always contains exactly one filesystem. This is the filesystem which will be below the newly created filesystem in the stack.

Parameters
argsthe command line arguments
fsNULL terminated filesystem object vector
Returns
the new filesystem object

Definition at line 1366 of file fuse.h.

Enumeration Type Documentation

◆ fuse_fill_dir_flags

Readdir flags, passed to fuse_fill_dir_t callback.

Enumerator
FUSE_FILL_DIR_DEFAULTS 

"Plus" mode: all file attributes are valid

The attributes are used by the kernel to prefill the inode cache during a readdir.

It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set and vice versa.

Definition at line 58 of file fuse.h.

◆ fuse_readdir_flags

Readdir flags, passed to ->readdir()

Enumerator
FUSE_READDIR_DEFAULTS 

"Plus" mode.

The kernel wants to prefill the inode cache during readdir. The filesystem may honour this by filling in the attributes and setting FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also just ignore this flag completely.

Definition at line 42 of file fuse.h.

Function Documentation

◆ fuse_clean_cache()

int fuse_clean_cache ( struct fuse *  fuse)

Iterate over cache removing stale entries use in conjunction with "-oremember"

NOTE: This is already done for the standard sessions

Parameters
fusestruct fuse pointer for fuse instance
Returns
the number of seconds until the next cleanup

Definition at line 4433 of file fuse.c.

◆ fuse_destroy()

void fuse_destroy ( struct fuse *  f)

Destroy the FUSE handle.

NOTE: This function does not unmount the filesystem. If this is needed, call fuse_unmount() before calling this function.

Parameters
fthe FUSE handle

Definition at line 5146 of file fuse.c.

◆ fuse_exit()

void fuse_exit ( struct fuse *  f)

Flag session as terminated

This function will cause any running event loops to exit on the next opportunity.

Parameters
fthe FUSE handle

Definition at line 4639 of file fuse.c.

◆ fuse_fs_new()

struct fuse_fs * fuse_fs_new ( const struct fuse_operations op,
size_t  op_size,
void *  private_data 
)

Create a new fuse filesystem object

This is usually called from the factory of a fuse module to create a new instance of a filesystem.

Parameters
opthe filesystem operations
op_sizethe size of the fuse_operations structure
private_dataInitial value for the private_data field of struct fuse_context. May be overridden by the struct fuse_operations.init handler.
Returns
a new filesystem object

Definition at line 4854 of file fuse.c.

◆ fuse_get_context()

struct fuse_context * fuse_get_context ( void  )

Get the current context

The context is only valid for the duration of a filesystem operation, and thus must not be stored and used later.

Returns
the context

Definition at line 4644 of file fuse.c.

◆ fuse_get_session()

struct fuse_session * fuse_get_session ( struct fuse *  f)

Get session from fuse object

Definition at line 4520 of file fuse.c.

◆ fuse_getgroups()

int fuse_getgroups ( int  size,
gid_t  list[] 
)

Get the current supplementary group IDs for the current request

Similar to the getgroups(2) system call, except the return value is always the total number of group IDs, even if it is larger than the specified size.

The current fuse kernel module in linux (as of 2.6.30) doesn't pass the group list to userspace, hence this function needs to parse "/proc/$TID/task/$TID/status" to get the group IDs.

This feature may not be supported on all operating systems. In such a case this function will return -ENOSYS.

Parameters
sizesize of given array
listarray of group IDs to be filled in
Returns
the total number of supplementary group IDs or -errno on failure

Definition at line 4654 of file fuse.c.

◆ fuse_interrupted()

int fuse_interrupted ( void  )

Check if the current request has already been interrupted

Returns
1 if the request has been interrupted, 0 otherwise

Definition at line 4663 of file fuse.c.

◆ fuse_invalidate_path()

int fuse_invalidate_path ( struct fuse *  f,
const char *  path 
)

Invalidates cache for the given path.

This calls fuse_lowlevel_notify_inval_inode internally.

Returns
0 on successful invalidation, negative error value otherwise. This routine may return -ENOENT to indicate that there was no entry to be invalidated, e.g., because the path has not been seen before or has been forgotten; this should not be considered to be an error.

Definition at line 4673 of file fuse.c.

◆ fuse_lib_help()

void fuse_lib_help ( struct fuse_args args)

Print available options (high- and low-level) to stdout. This is not an exhaustive list, but includes only those options that may be of interest to an end-user of a file system.

The function looks at the argument vector only to determine if there are additional modules to be loaded (module=foo option), and attempts to call their help functions as well.

Parameters
argsthe argument vector.

Definition at line 4744 of file fuse.c.

◆ fuse_loop()

int fuse_loop ( struct fuse *  f)

FUSE event loop.

Requests from the kernel are processed, and the appropriate operations are called.

For a description of the return value and the conditions when the event loop exits, refer to the documentation of fuse_session_loop().

Parameters
fthe FUSE handle
Returns
see fuse_session_loop()

See also: fuse_loop_mt()

Definition at line 4577 of file fuse.c.

◆ fuse_main_real_versioned()

int fuse_main_real_versioned ( int  argc,
char *  argv[],
const struct fuse_operations op,
size_t  op_size,
struct libfuse_version version,
void *  user_data 
)

The real main function

Do not call this directly, use fuse_main()

Definition at line 307 of file helper.c.

◆ fuse_mount()

int fuse_mount ( struct fuse *  f,
const char *  mountpoint 
)

Mount a FUSE file system.

Parameters
mountpointthe mount point path
fthe FUSE handle
Returns
0 on success, -1 on failure.

Definition at line 5197 of file fuse.c.

◆ fuse_open_channel()

int fuse_open_channel ( const char *  mountpoint,
const char *  options 
)

Open a FUSE file descriptor and set up the mount for the given mountpoint and flags.

Parameters
mountpointreference to the mount in the file system
optionsmount options
Returns
the FUSE file descriptor or -1 upon error

Definition at line 482 of file helper.c.

◆ fuse_start_cleanup_thread()

int fuse_start_cleanup_thread ( struct fuse *  fuse)

Start the cleanup thread when using option "remember".

This is done automatically by fuse_loop_mt()

Parameters
fusestruct fuse pointer for fuse instance
Returns
0 on success and -1 on error

Definition at line 4906 of file fuse.c.

◆ fuse_stop_cleanup_thread()

void fuse_stop_cleanup_thread ( struct fuse *  fuse)

Stop the cleanup thread when using option "remember".

This is done automatically by fuse_loop_mt()

Parameters
fusestruct fuse pointer for fuse instance

Definition at line 4914 of file fuse.c.

◆ fuse_unmount()

void fuse_unmount ( struct fuse *  f)

Unmount a FUSE file system.

See fuse_session_unmount() for additional information.

Parameters
fthe FUSE handle

Definition at line 5202 of file fuse.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse_8h.html0000644000175000017500000013554515156613443021443 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse.h File Reference
libfuse
fuse.h File Reference
#include "fuse_common.h"
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>

Go to the source code of this file.

Data Structures

struct  fuse_config
 
struct  fuse_operations
 
struct  fuse_context
 

Macros

#define FUSE_REGISTER_MODULE(name_, factory_)    fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
 

Typedefs

typedef int(* fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
 
typedef struct fuse_fs *(* fuse_module_factory_t) (struct fuse_args *args, struct fuse_fs *fs[])
 

Enumerations

enum  fuse_readdir_flags { FUSE_READDIR_DEFAULTS = 0 , FUSE_READDIR_PLUS = (1 << 0) }
 
enum  fuse_fill_dir_flags { FUSE_FILL_DIR_DEFAULTS = 0 , FUSE_FILL_DIR_PLUS = (1 << 1) }
 

Functions

int fuse_main_real_versioned (int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
 
void fuse_lib_help (struct fuse_args *args)
 
int fuse_mount (struct fuse *f, const char *mountpoint)
 
void fuse_unmount (struct fuse *f)
 
void fuse_destroy (struct fuse *f)
 
int fuse_loop (struct fuse *f)
 
void fuse_exit (struct fuse *f)
 
struct fuse_contextfuse_get_context (void)
 
int fuse_getgroups (int size, gid_t list[])
 
int fuse_interrupted (void)
 
int fuse_invalidate_path (struct fuse *f, const char *path)
 
int fuse_start_cleanup_thread (struct fuse *fuse)
 
void fuse_stop_cleanup_thread (struct fuse *fuse)
 
int fuse_clean_cache (struct fuse *fuse)
 
struct fuse_fs * fuse_fs_new (const struct fuse_operations *op, size_t op_size, void *private_data)
 
struct fuse_session * fuse_get_session (struct fuse *f)
 
int fuse_open_channel (const char *mountpoint, const char *options)
 

Detailed Description

This file defines the library interface of FUSE

IMPORTANT: you should define FUSE_USE_VERSION before including this header.

Definition in file fuse.h.

Macro Definition Documentation

◆ FUSE_REGISTER_MODULE

#define FUSE_REGISTER_MODULE (   name_,
  factory_ 
)     fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_

Register filesystem module

If the "-omodules=*name*_:..." option is present, filesystem objects are created and pushed onto the stack with the factory_ function.

Parameters
name_the name of this filesystem module
factory_the factory function for this filesystem module

Definition at line 1414 of file fuse.h.

Typedef Documentation

◆ fuse_fill_dir_t

typedef int(* fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)

Function to add an entry in a readdir() operation

The off parameter can be any non-zero value that enables the filesystem to identify the current point in the directory stream. It does not need to be the actual physical position. A value of zero is reserved to indicate that seeking in directories is not supported.

Parameters
bufthe buffer passed to the readdir() operation
namethe file name of the directory entry
stbuffile attributes, can be NULL
offoffset of the next entry or zero
flagsfill flags
Returns
1 if buffer is full, zero otherwise

Definition at line 93 of file fuse.h.

◆ fuse_module_factory_t

typedef struct fuse_fs *(* fuse_module_factory_t) (struct fuse_args *args, struct fuse_fs *fs[])

Factory for creating filesystem objects

The function may use and remove options from 'args' that belong to this module.

For now the 'fs' vector always contains exactly one filesystem. This is the filesystem which will be below the newly created filesystem in the stack.

Parameters
argsthe command line arguments
fsNULL terminated filesystem object vector
Returns
the new filesystem object

Definition at line 1385 of file fuse.h.

Enumeration Type Documentation

◆ fuse_fill_dir_flags

Readdir flags, passed to fuse_fill_dir_t callback.

Enumerator
FUSE_FILL_DIR_DEFAULTS 

"Plus" mode: file attributes are valid

The attributes are used by the kernel to prefill the inode cache during a readdir.

It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set and vice versa.

This does not make libfuse honor the 'st_ino' field. That is controlled by the 'use_ino' option instead.

Definition at line 61 of file fuse.h.

◆ fuse_readdir_flags

Readdir flags, passed to ->readdir()

Enumerator
FUSE_READDIR_DEFAULTS 

"Plus" mode.

The kernel wants to prefill the inode cache during readdir. The filesystem may honour this by filling in the attributes and setting FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also just ignore this flag completely.

Definition at line 45 of file fuse.h.

Function Documentation

◆ fuse_clean_cache()

int fuse_clean_cache ( struct fuse *  fuse)

Iterate over cache removing stale entries use in conjunction with "-oremember"

NOTE: This is already done for the standard sessions

Parameters
fusestruct fuse pointer for fuse instance
Returns
the number of seconds until the next cleanup

Definition at line 4433 of file fuse.c.

◆ fuse_destroy()

void fuse_destroy ( struct fuse *  f)

Destroy the FUSE handle.

NOTE: This function does not unmount the filesystem. If this is needed, call fuse_unmount() before calling this function.

Parameters
fthe FUSE handle

Definition at line 5146 of file fuse.c.

◆ fuse_exit()

void fuse_exit ( struct fuse *  f)

Flag session as terminated

This function will cause any running event loops to exit on the next opportunity.

Parameters
fthe FUSE handle

Definition at line 4639 of file fuse.c.

◆ fuse_fs_new()

struct fuse_fs * fuse_fs_new ( const struct fuse_operations op,
size_t  op_size,
void *  private_data 
)

Create a new fuse filesystem object

This is usually called from the factory of a fuse module to create a new instance of a filesystem.

Parameters
opthe filesystem operations
op_sizethe size of the fuse_operations structure
private_dataInitial value for the private_data field of struct fuse_context. May be overridden by the struct fuse_operations.init handler.
Returns
a new filesystem object

Definition at line 4854 of file fuse.c.

◆ fuse_get_context()

struct fuse_context * fuse_get_context ( void  )

Get the current context

The context is only valid for the duration of a filesystem operation, and thus must not be stored and used later.

Returns
the context

Definition at line 4644 of file fuse.c.

◆ fuse_get_session()

struct fuse_session * fuse_get_session ( struct fuse *  f)

Get session from fuse object

Definition at line 4520 of file fuse.c.

◆ fuse_getgroups()

int fuse_getgroups ( int  size,
gid_t  list[] 
)

Get the current supplementary group IDs for the current request

Similar to the getgroups(2) system call, except the return value is always the total number of group IDs, even if it is larger than the specified size.

The current fuse kernel module in linux (as of 2.6.30) doesn't pass the group list to userspace, hence this function needs to parse "/proc/$TID/task/$TID/status" to get the group IDs.

This feature may not be supported on all operating systems. In such a case this function will return -ENOSYS.

Parameters
sizesize of given array
listarray of group IDs to be filled in
Returns
the total number of supplementary group IDs or -errno on failure

Definition at line 4654 of file fuse.c.

◆ fuse_interrupted()

int fuse_interrupted ( void  )

Check if the current request has already been interrupted

Returns
1 if the request has been interrupted, 0 otherwise

Definition at line 4663 of file fuse.c.

◆ fuse_invalidate_path()

int fuse_invalidate_path ( struct fuse *  f,
const char *  path 
)

Invalidates cache for the given path.

This calls fuse_lowlevel_notify_inval_inode internally.

Returns
0 on successful invalidation, negative error value otherwise. This routine may return -ENOENT to indicate that there was no entry to be invalidated, e.g., because the path has not been seen before or has been forgotten; this should not be considered to be an error.

Definition at line 4673 of file fuse.c.

◆ fuse_lib_help()

void fuse_lib_help ( struct fuse_args args)

Print available options (high- and low-level) to stdout. This is not an exhaustive list, but includes only those options that may be of interest to an end-user of a file system.

The function looks at the argument vector only to determine if there are additional modules to be loaded (module=foo option), and attempts to call their help functions as well.

Parameters
argsthe argument vector.

Definition at line 4744 of file fuse.c.

◆ fuse_loop()

int fuse_loop ( struct fuse *  f)

FUSE event loop.

Requests from the kernel are processed, and the appropriate operations are called.

For a description of the return value and the conditions when the event loop exits, refer to the documentation of fuse_session_loop().

Parameters
fthe FUSE handle
Returns
see fuse_session_loop()

See also: fuse_loop_mt()

Definition at line 4577 of file fuse.c.

◆ fuse_main_real_versioned()

int fuse_main_real_versioned ( int  argc,
char *  argv[],
const struct fuse_operations op,
size_t  op_size,
struct libfuse_version version,
void *  user_data 
)

The real main function

Do not call this directly, use fuse_main()

Definition at line 307 of file helper.c.

◆ fuse_mount()

int fuse_mount ( struct fuse *  f,
const char *  mountpoint 
)

Mount a FUSE file system.

Parameters
mountpointthe mount point path
fthe FUSE handle
Returns
0 on success, -1 on failure.

Definition at line 5197 of file fuse.c.

◆ fuse_open_channel()

int fuse_open_channel ( const char *  mountpoint,
const char *  options 
)

Open a FUSE file descriptor and set up the mount for the given mountpoint and flags.

Parameters
mountpointreference to the mount in the file system
optionsmount options
Returns
the FUSE file descriptor or -1 upon error

Definition at line 482 of file helper.c.

◆ fuse_start_cleanup_thread()

int fuse_start_cleanup_thread ( struct fuse *  fuse)

Start the cleanup thread when using option "remember".

This is done automatically by fuse_loop_mt()

Parameters
fusestruct fuse pointer for fuse instance
Returns
0 on success and -1 on error

Definition at line 4906 of file fuse.c.

◆ fuse_stop_cleanup_thread()

void fuse_stop_cleanup_thread ( struct fuse *  fuse)

Stop the cleanup thread when using option "remember".

This is done automatically by fuse_loop_mt()

Parameters
fusestruct fuse pointer for fuse instance

Definition at line 4914 of file fuse.c.

◆ fuse_unmount()

void fuse_unmount ( struct fuse *  f)

Unmount a FUSE file system.

See fuse_session_unmount() for additional information.

Parameters
fthe FUSE handle

Definition at line 5202 of file fuse.c.

fuse-3.18.2/doc/html/include_2fuse_8h.html0000644000175000017500000013430715156613443017262 0ustar berndbernd libfuse: include/fuse.h File Reference
libfuse
fuse.h File Reference
#include "fuse_common.h"
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>

Go to the source code of this file.

Data Structures

struct  fuse_config
 
struct  fuse_operations
 
struct  fuse_context
 

Macros

#define FUSE_REGISTER_MODULE(name_, factory_)    fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
 

Typedefs

typedef int(* fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
 
typedef struct fuse_fs *(* fuse_module_factory_t) (struct fuse_args *args, struct fuse_fs *fs[])
 

Enumerations

enum  fuse_readdir_flags { FUSE_READDIR_DEFAULTS = 0 , FUSE_READDIR_PLUS = (1 << 0) }
 
enum  fuse_fill_dir_flags { FUSE_FILL_DIR_DEFAULTS = 0 , FUSE_FILL_DIR_PLUS = (1 << 1) }
 

Functions

int fuse_main_real_versioned (int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
 
void fuse_lib_help (struct fuse_args *args)
 
int fuse_mount (struct fuse *f, const char *mountpoint)
 
void fuse_unmount (struct fuse *f)
 
void fuse_destroy (struct fuse *f)
 
int fuse_loop (struct fuse *f)
 
void fuse_exit (struct fuse *f)
 
struct fuse_contextfuse_get_context (void)
 
int fuse_getgroups (int size, gid_t list[])
 
int fuse_interrupted (void)
 
int fuse_invalidate_path (struct fuse *f, const char *path)
 
int fuse_start_cleanup_thread (struct fuse *fuse)
 
void fuse_stop_cleanup_thread (struct fuse *fuse)
 
int fuse_clean_cache (struct fuse *fuse)
 
struct fuse_fs * fuse_fs_new (const struct fuse_operations *op, size_t op_size, void *private_data)
 
struct fuse_session * fuse_get_session (struct fuse *f)
 
int fuse_open_channel (const char *mountpoint, const char *options)
 

Detailed Description

This file defines the library interface of FUSE

IMPORTANT: you should define FUSE_USE_VERSION before including this header.

Definition in file fuse.h.

Macro Definition Documentation

◆ FUSE_REGISTER_MODULE

#define FUSE_REGISTER_MODULE (   name_,
  factory_ 
)     fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_

Register filesystem module

If the "-omodules=*name*_:..." option is present, filesystem objects are created and pushed onto the stack with the factory_ function.

Parameters
name_the name of this filesystem module
factory_the factory function for this filesystem module

Definition at line 1414 of file fuse.h.

Typedef Documentation

◆ fuse_fill_dir_t

typedef int(* fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)

Function to add an entry in a readdir() operation

The off parameter can be any non-zero value that enables the filesystem to identify the current point in the directory stream. It does not need to be the actual physical position. A value of zero is reserved to indicate that seeking in directories is not supported.

Parameters
bufthe buffer passed to the readdir() operation
namethe file name of the directory entry
stbuffile attributes, can be NULL
offoffset of the next entry or zero
flagsfill flags
Returns
1 if buffer is full, zero otherwise

Definition at line 93 of file fuse.h.

◆ fuse_module_factory_t

typedef struct fuse_fs *(* fuse_module_factory_t) (struct fuse_args *args, struct fuse_fs *fs[])

Factory for creating filesystem objects

The function may use and remove options from 'args' that belong to this module.

For now the 'fs' vector always contains exactly one filesystem. This is the filesystem which will be below the newly created filesystem in the stack.

Parameters
argsthe command line arguments
fsNULL terminated filesystem object vector
Returns
the new filesystem object

Definition at line 1385 of file fuse.h.

Enumeration Type Documentation

◆ fuse_fill_dir_flags

Readdir flags, passed to fuse_fill_dir_t callback.

Enumerator
FUSE_FILL_DIR_DEFAULTS 

"Plus" mode: file attributes are valid

The attributes are used by the kernel to prefill the inode cache during a readdir.

It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set and vice versa.

This does not make libfuse honor the 'st_ino' field. That is controlled by the 'use_ino' option instead.

Definition at line 61 of file fuse.h.

◆ fuse_readdir_flags

Readdir flags, passed to ->readdir()

Enumerator
FUSE_READDIR_DEFAULTS 

"Plus" mode.

The kernel wants to prefill the inode cache during readdir. The filesystem may honour this by filling in the attributes and setting FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also just ignore this flag completely.

Definition at line 45 of file fuse.h.

Function Documentation

◆ fuse_clean_cache()

int fuse_clean_cache ( struct fuse *  fuse)

Iterate over cache removing stale entries use in conjunction with "-oremember"

NOTE: This is already done for the standard sessions

Parameters
fusestruct fuse pointer for fuse instance
Returns
the number of seconds until the next cleanup

Definition at line 4433 of file fuse.c.

◆ fuse_destroy()

void fuse_destroy ( struct fuse *  f)

Destroy the FUSE handle.

NOTE: This function does not unmount the filesystem. If this is needed, call fuse_unmount() before calling this function.

Parameters
fthe FUSE handle

Definition at line 5146 of file fuse.c.

◆ fuse_exit()

void fuse_exit ( struct fuse *  f)

Flag session as terminated

This function will cause any running event loops to exit on the next opportunity.

Parameters
fthe FUSE handle

Definition at line 4639 of file fuse.c.

◆ fuse_fs_new()

struct fuse_fs * fuse_fs_new ( const struct fuse_operations op,
size_t  op_size,
void *  private_data 
)

Create a new fuse filesystem object

This is usually called from the factory of a fuse module to create a new instance of a filesystem.

Parameters
opthe filesystem operations
op_sizethe size of the fuse_operations structure
private_dataInitial value for the private_data field of struct fuse_context. May be overridden by the struct fuse_operations.init handler.
Returns
a new filesystem object

Definition at line 4854 of file fuse.c.

◆ fuse_get_context()

struct fuse_context * fuse_get_context ( void  )

Get the current context

The context is only valid for the duration of a filesystem operation, and thus must not be stored and used later.

Returns
the context

Definition at line 4644 of file fuse.c.

◆ fuse_get_session()

struct fuse_session * fuse_get_session ( struct fuse *  f)

Get session from fuse object

Definition at line 4520 of file fuse.c.

◆ fuse_getgroups()

int fuse_getgroups ( int  size,
gid_t  list[] 
)

Get the current supplementary group IDs for the current request

Similar to the getgroups(2) system call, except the return value is always the total number of group IDs, even if it is larger than the specified size.

The current fuse kernel module in linux (as of 2.6.30) doesn't pass the group list to userspace, hence this function needs to parse "/proc/$TID/task/$TID/status" to get the group IDs.

This feature may not be supported on all operating systems. In such a case this function will return -ENOSYS.

Parameters
sizesize of given array
listarray of group IDs to be filled in
Returns
the total number of supplementary group IDs or -errno on failure

Definition at line 4654 of file fuse.c.

◆ fuse_interrupted()

int fuse_interrupted ( void  )

Check if the current request has already been interrupted

Returns
1 if the request has been interrupted, 0 otherwise

Definition at line 4663 of file fuse.c.

◆ fuse_invalidate_path()

int fuse_invalidate_path ( struct fuse *  f,
const char *  path 
)

Invalidates cache for the given path.

This calls fuse_lowlevel_notify_inval_inode internally.

Returns
0 on successful invalidation, negative error value otherwise. This routine may return -ENOENT to indicate that there was no entry to be invalidated, e.g., because the path has not been seen before or has been forgotten; this should not be considered to be an error.

Definition at line 4673 of file fuse.c.

◆ fuse_lib_help()

void fuse_lib_help ( struct fuse_args args)

Print available options (high- and low-level) to stdout. This is not an exhaustive list, but includes only those options that may be of interest to an end-user of a file system.

The function looks at the argument vector only to determine if there are additional modules to be loaded (module=foo option), and attempts to call their help functions as well.

Parameters
argsthe argument vector.

Definition at line 4744 of file fuse.c.

◆ fuse_loop()

int fuse_loop ( struct fuse *  f)

FUSE event loop.

Requests from the kernel are processed, and the appropriate operations are called.

For a description of the return value and the conditions when the event loop exits, refer to the documentation of fuse_session_loop().

Parameters
fthe FUSE handle
Returns
see fuse_session_loop()

See also: fuse_loop_mt()

Definition at line 4577 of file fuse.c.

◆ fuse_main_real_versioned()

int fuse_main_real_versioned ( int  argc,
char *  argv[],
const struct fuse_operations op,
size_t  op_size,
struct libfuse_version version,
void *  user_data 
)

The real main function

Do not call this directly, use fuse_main()

Definition at line 307 of file helper.c.

◆ fuse_mount()

int fuse_mount ( struct fuse *  f,
const char *  mountpoint 
)

Mount a FUSE file system.

Parameters
mountpointthe mount point path
fthe FUSE handle
Returns
0 on success, -1 on failure.

Definition at line 5197 of file fuse.c.

◆ fuse_open_channel()

int fuse_open_channel ( const char *  mountpoint,
const char *  options 
)

Open a FUSE file descriptor and set up the mount for the given mountpoint and flags.

Parameters
mountpointreference to the mount in the file system
optionsmount options
Returns
the FUSE file descriptor or -1 upon error

Definition at line 482 of file helper.c.

◆ fuse_start_cleanup_thread()

int fuse_start_cleanup_thread ( struct fuse *  fuse)

Start the cleanup thread when using option "remember".

This is done automatically by fuse_loop_mt()

Parameters
fusestruct fuse pointer for fuse instance
Returns
0 on success and -1 on error

Definition at line 4906 of file fuse.c.

◆ fuse_stop_cleanup_thread()

void fuse_stop_cleanup_thread ( struct fuse *  fuse)

Stop the cleanup thread when using option "remember".

This is done automatically by fuse_loop_mt()

Parameters
fusestruct fuse pointer for fuse instance

Definition at line 4914 of file fuse.c.

◆ fuse_unmount()

void fuse_unmount ( struct fuse *  f)

Unmount a FUSE file system.

See fuse_session_unmount() for additional information.

Parameters
fthe FUSE handle

Definition at line 5202 of file fuse.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__common_8h.html0000644000175000017500000026531015156613443023146 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_common.h File Reference
libfuse
fuse_common.h File Reference
#include <stdbool.h>
#include "libfuse_config.h"
#include "fuse_opt.h"
#include "fuse_log.h"
#include <stdint.h>
#include <sys/types.h>
#include <assert.h>

Go to the source code of this file.

Data Structures

struct  fuse_file_info
 
struct  fuse_loop_config
 
struct  fuse_conn_info
 
struct  fuse_buf
 
struct  fuse_bufvec
 
struct  libfuse_version
 

Macros

#define FUSE_CAP_ASYNC_READ   (1 << 0)
 
#define FUSE_CAP_POSIX_LOCKS   (1 << 1)
 
#define FUSE_CAP_ATOMIC_O_TRUNC   (1 << 3)
 
#define FUSE_CAP_EXPORT_SUPPORT   (1 << 4)
 
#define FUSE_CAP_DONT_MASK   (1 << 6)
 
#define FUSE_CAP_SPLICE_WRITE   (1 << 7)
 
#define FUSE_CAP_SPLICE_MOVE   (1 << 8)
 
#define FUSE_CAP_SPLICE_READ   (1 << 9)
 
#define FUSE_CAP_FLOCK_LOCKS   (1 << 10)
 
#define FUSE_CAP_IOCTL_DIR   (1 << 11)
 
#define FUSE_CAP_AUTO_INVAL_DATA   (1 << 12)
 
#define FUSE_CAP_READDIRPLUS   (1 << 13)
 
#define FUSE_CAP_READDIRPLUS_AUTO   (1 << 14)
 
#define FUSE_CAP_ASYNC_DIO   (1 << 15)
 
#define FUSE_CAP_WRITEBACK_CACHE   (1 << 16)
 
#define FUSE_CAP_NO_OPEN_SUPPORT   (1 << 17)
 
#define FUSE_CAP_PARALLEL_DIROPS   (1 << 18)
 
#define FUSE_CAP_POSIX_ACL   (1 << 19)
 
#define FUSE_CAP_HANDLE_KILLPRIV   (1 << 20)
 
#define FUSE_CAP_HANDLE_KILLPRIV_V2   (1 << 21)
 
#define FUSE_CAP_CACHE_SYMLINKS   (1 << 23)
 
#define FUSE_CAP_NO_OPENDIR_SUPPORT   (1 << 24)
 
#define FUSE_CAP_EXPLICIT_INVAL_DATA   (1 << 25)
 
#define FUSE_CAP_EXPIRE_ONLY   (1 << 26)
 
#define FUSE_CAP_SETXATTR_EXT   (1 << 27)
 
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP   (1 << 28)
 
#define FUSE_CAP_PASSTHROUGH   (1 << 29)
 
#define FUSE_CAP_NO_EXPORT_SUPPORT   (1 << 30)
 
#define FUSE_IOCTL_COMPAT   (1 << 0)
 
#define FUSE_BACKING_STACKED_UNDER   (0)
 

Enumerations

enum  fuse_buf_flags { FUSE_BUF_IS_FD = (1 << 1) , FUSE_BUF_FD_SEEK = (1 << 2) , FUSE_BUF_FD_RETRY = (1 << 3) }
 
enum  fuse_buf_copy_flags { FUSE_BUF_NO_SPLICE = (1 << 1) , FUSE_BUF_FORCE_SPLICE = (1 << 2) , FUSE_BUF_SPLICE_MOVE = (1 << 3) , FUSE_BUF_SPLICE_NONBLOCK = (1 << 4) }
 

Functions

struct fuse_conn_info_opts * fuse_parse_conn_info_opts (struct fuse_args *args)
 
void fuse_apply_conn_info_opts (struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
 
int fuse_daemonize (int foreground)
 
int fuse_version (void)
 
const char * fuse_pkgversion (void)
 
void fuse_pollhandle_destroy (struct fuse_pollhandle *ph)
 
size_t fuse_buf_size (const struct fuse_bufvec *bufv)
 
ssize_t fuse_buf_copy (struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
 
int fuse_set_signal_handlers (struct fuse_session *se)
 
int fuse_set_fail_signal_handlers (struct fuse_session *se)
 
void fuse_remove_signal_handlers (struct fuse_session *se)
 
bool fuse_set_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
void fuse_unset_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
bool fuse_get_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
int fuse_convert_to_conn_want_ext (struct fuse_conn_info *conn)
 

Macro Definition Documentation

◆ FUSE_BACKING_STACKED_UNDER

#define FUSE_BACKING_STACKED_UNDER   (0)

When FUSE_CAP_PASSTHROUGH is enabled, this is the maximum allowed stacking depth of the backing files. In current kernel, the maximum allowed stack depth if FILESYSTEM_MAX_STACK_DEPTH (2), which includes the FUSE passthrough layer, so the maximum stacking depth for backing files is 1.

The default is FUSE_BACKING_STACKED_UNDER (0), meaning that the backing files cannot be on a stacked filesystem, but another stacked filesystem can be stacked over this FUSE passthrough filesystem.

Set this to FUSE_BACKING_STACKED_OVER (1) if backing files may be on a stacked filesystem, such as overlayfs or another FUSE passthrough. In this configuration, another stacked filesystem cannot be stacked over this FUSE passthrough filesystem.

Definition at line 665 of file fuse_common.h.

◆ FUSE_CAP_ASYNC_DIO

#define FUSE_CAP_ASYNC_DIO   (1 << 15)

Indicates that the filesystem supports asynchronous direct I/O submission.

If this capability is not requested/available, the kernel will ensure that there is at most one pending read and one pending write request per direct I/O file-handle at any time.

This feature is enabled by default when supported by the kernel.

Definition at line 328 of file fuse_common.h.

◆ FUSE_CAP_ASYNC_READ

#define FUSE_CAP_ASYNC_READ   (1 << 0)

Indicates that the filesystem supports asynchronous read requests.

If this capability is not requested/available, the kernel will ensure that there is at most one pending read request per file-handle at any time, and will attempt to order read requests by increasing offset.

This feature is enabled by default when supported by the kernel.

Definition at line 177 of file fuse_common.h.

◆ FUSE_CAP_ATOMIC_O_TRUNC

#define FUSE_CAP_ATOMIC_O_TRUNC   (1 << 3)

Indicates that the filesystem supports the O_TRUNC open flag. If disabled, and an application specifies O_TRUNC, fuse first calls truncate() and then open() with O_TRUNC filtered out.

This feature is enabled by default when supported by the kernel.

Definition at line 194 of file fuse_common.h.

◆ FUSE_CAP_AUTO_INVAL_DATA

#define FUSE_CAP_AUTO_INVAL_DATA   (1 << 12)

Traditionally, while a file is open the FUSE kernel module only asks the filesystem for an update of the file's attributes when a client attempts to read beyond EOF. This is unsuitable for e.g. network filesystems, where the file contents may change without the kernel knowing about it.

If this flag is set, FUSE will check the validity of the attributes on every read. If the attributes are no longer valid (i.e., if the attr_timeout passed to fuse_reply_attr() or set in struct fuse_entry_param has passed), it will first issue a getattr request. If the new mtime differs from the previous value, any cached file contents will be invalidated as well.

This flag should always be set when available. If all file changes go through the kernel, attr_timeout should be set to a very large number to avoid unnecessary getattr() calls.

This feature is enabled by default when supported by the kernel.

Definition at line 281 of file fuse_common.h.

◆ FUSE_CAP_CACHE_SYMLINKS

#define FUSE_CAP_CACHE_SYMLINKS   (1 << 23)

Indicates that the kernel supports caching symlinks in its page cache.

When this feature is enabled, symlink targets are saved in the page cache. You can invalidate a cached link by calling: fuse_lowlevel_notify_inval_inode(se, ino, 0, 0);

This feature is disabled by default. If the kernel supports it (>= 4.20), you can enable this feature by setting this flag in the want field of the fuse_conn_info structure.

Definition at line 418 of file fuse_common.h.

◆ FUSE_CAP_DIRECT_IO_ALLOW_MMAP

#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP   (1 << 28)

Files opened with FUSE_DIRECT_IO do not support MAP_SHARED mmap. This restriction is relaxed through FUSE_CAP_DIRECT_IO_RELAX (kernel flag: FUSE_DIRECT_IO_RELAX). MAP_SHARED is disabled by default for FUSE_DIRECT_IO, as this flag can be used to ensure coherency between mount points (or network clients) and with kernel page cache as enforced by mmap that cannot be guaranteed anymore.

Definition at line 488 of file fuse_common.h.

◆ FUSE_CAP_DONT_MASK

#define FUSE_CAP_DONT_MASK   (1 << 6)

Indicates that the kernel should not apply the umask to the file mode on create operations.

This feature is disabled by default.

Definition at line 214 of file fuse_common.h.

◆ FUSE_CAP_EXPIRE_ONLY

#define FUSE_CAP_EXPIRE_ONLY   (1 << 26)

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.

Definition at line 472 of file fuse_common.h.

◆ FUSE_CAP_EXPLICIT_INVAL_DATA

#define FUSE_CAP_EXPLICIT_INVAL_DATA   (1 << 25)

Indicates support for invalidating cached pages only on explicit request.

If this flag is set in the capable field of the fuse_conn_info structure, then the FUSE kernel module supports invalidating cached pages only on explicit request by the filesystem through fuse_lowlevel_notify_inval_inode() or fuse_invalidate_path().

By setting this flag in the want field of the fuse_conn_info structure, the filesystem is responsible for invalidating cached pages through explicit requests to the kernel.

Note that setting this flag does not prevent the cached pages from being flushed by OS itself and/or through user actions.

Note that if both FUSE_CAP_EXPLICIT_INVAL_DATA and FUSE_CAP_AUTO_INVAL_DATA are set in the capable field of the fuse_conn_info structure then FUSE_CAP_AUTO_INVAL_DATA takes precedence.

This feature is disabled by default.

Definition at line 456 of file fuse_common.h.

◆ FUSE_CAP_EXPORT_SUPPORT

#define FUSE_CAP_EXPORT_SUPPORT   (1 << 4)

Indicates that the filesystem supports lookups of "." and "..".

When this flag is set, the filesystem must be prepared to receive requests for invalid inodes (i.e., for which a FORGET request was received or which have been used in a previous instance of the filesystem daemon) and must not reuse node-ids (even when setting generation numbers).

This feature is disabled by default.

Definition at line 206 of file fuse_common.h.

◆ FUSE_CAP_FLOCK_LOCKS

#define FUSE_CAP_FLOCK_LOCKS   (1 << 10)

If set, the calls to flock(2) will be emulated using POSIX locks and must then be handled by the filesystem's setlock() handler.

If not set, flock(2) calls will be handled by the FUSE kernel module internally (so any access that does not go through the kernel cannot be taken into account).

This feature is enabled by default when supported by the kernel and if the filesystem implements a flock() handler.

Definition at line 252 of file fuse_common.h.

◆ FUSE_CAP_HANDLE_KILLPRIV

#define FUSE_CAP_HANDLE_KILLPRIV   (1 << 20)

Indicates that the filesystem is responsible for unsetting setuid and setgid bits when a file is written, truncated, or its owner is changed.

This feature is disabled by default.

Definition at line 388 of file fuse_common.h.

◆ FUSE_CAP_HANDLE_KILLPRIV_V2

#define FUSE_CAP_HANDLE_KILLPRIV_V2   (1 << 21)

Indicates that the filesystem is responsible for unsetting setuid and setgid bit and additionally cap (stored as xattr) when a file is written, truncated, or its owner is changed. 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). KILLPRIV_V2 requires handling of

  • FUSE_OPEN_KILL_SUIDGID (set in struct fuse_create_in::open_flags)
  • FATTR_KILL_SUIDGID (set in struct fuse_setattr_in::valid)
  • FUSE_WRITE_KILL_SUIDGID (set in struct fuse_write_in::write_flags)

This feature is disabled by default.

Definition at line 405 of file fuse_common.h.

◆ FUSE_CAP_IOCTL_DIR

#define FUSE_CAP_IOCTL_DIR   (1 << 11)

Indicates that the filesystem supports ioctl's on directories.

This feature is enabled by default when supported by the kernel.

Definition at line 259 of file fuse_common.h.

◆ FUSE_CAP_NO_EXPORT_SUPPORT

#define FUSE_CAP_NO_EXPORT_SUPPORT   (1 << 30)

Indicates that the file system cannot handle NFS export

If this flag is set NFS export and name_to_handle_at is not going to work at all and will fail with EOPNOTSUPP.

Definition at line 508 of file fuse_common.h.

◆ FUSE_CAP_NO_OPEN_SUPPORT

#define FUSE_CAP_NO_OPEN_SUPPORT   (1 << 17)

Indicates support for zero-message opens. If this flag is set in the capable field of the fuse_conn_info structure, then the filesystem may return ENOSYS from the open() handler to indicate success. Further attempts to open files will be handled in the kernel. (If this flag is not set, returning ENOSYS will be treated as an error and signaled to the caller).

Setting this flag in the want field enables this behavior automatically within libfuse for low level API users. If non-low level users wish to have this behavior you must return ENOSYS from the open() handler on supporting kernels.

Definition at line 352 of file fuse_common.h.

◆ FUSE_CAP_NO_OPENDIR_SUPPORT

#define FUSE_CAP_NO_OPENDIR_SUPPORT   (1 << 24)

Indicates support for zero-message opendirs. If this flag is set in the capable field of the fuse_conn_info structure, then the filesystem may return ENOSYS from the opendir() handler to indicate success. Further opendir and releasedir messages will be handled in the kernel. (If this flag is not set, returning ENOSYS will be treated as an error and signalled to the caller.)

Setting this flag in the want field enables this behavior automatically within libfuse for low level API users. If non-low level users with to have this behavior you must return ENOSYS from the opendir() handler on supporting kernels.

Definition at line 433 of file fuse_common.h.

◆ FUSE_CAP_PARALLEL_DIROPS

#define FUSE_CAP_PARALLEL_DIROPS   (1 << 18)

Indicates support for parallel directory operations. If this flag is unset, the FUSE kernel module will ensure that lookup() and readdir() requests are never issued concurrently for the same directory.

Definition at line 360 of file fuse_common.h.

◆ FUSE_CAP_PASSTHROUGH

#define FUSE_CAP_PASSTHROUGH   (1 << 29)

Indicates support for passthrough mode access for read/write operations.

If this flag is set in the capable field of the fuse_conn_info structure, then the FUSE kernel module supports redirecting read/write operations to the backing file instead of letting them to be handled by the FUSE daemon.

This feature is disabled by default.

Definition at line 500 of file fuse_common.h.

◆ FUSE_CAP_POSIX_ACL

#define FUSE_CAP_POSIX_ACL   (1 << 19)

Indicates support for POSIX ACLs.

If this feature is enabled, the kernel will cache and have responsibility for enforcing ACLs. ACL will be stored as xattrs and passed to userspace, which is responsible for updating the ACLs in the filesystem, keeping the file mode in sync with the ACL, and ensuring inheritance of default ACLs when new filesystem nodes are created. Note that this requires that the file system is able to parse and interpret the xattr representation of ACLs.

Enabling this feature implicitly turns on the default_permissions mount option (even if it was not passed to mount(2)).

This feature is disabled by default.

Definition at line 379 of file fuse_common.h.

◆ FUSE_CAP_POSIX_LOCKS

#define FUSE_CAP_POSIX_LOCKS   (1 << 1)

Indicates that the filesystem supports "remote" locking.

This feature is enabled by default when supported by the kernel, and if getlk() and setlk() handlers are implemented.

Definition at line 185 of file fuse_common.h.

◆ FUSE_CAP_READDIRPLUS

#define FUSE_CAP_READDIRPLUS   (1 << 13)

Indicates that the filesystem supports readdirplus.

This feature is enabled by default when supported by the kernel and if the filesystem implements a readdirplus() handler.

Definition at line 289 of file fuse_common.h.

◆ FUSE_CAP_READDIRPLUS_AUTO

#define FUSE_CAP_READDIRPLUS_AUTO   (1 << 14)

Indicates that the filesystem supports adaptive readdirplus.

If FUSE_CAP_READDIRPLUS is not set, this flag has no effect.

If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel will always issue readdirplus() requests to retrieve directory contents.

If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel will issue both readdir() and readdirplus() requests, depending on how much information is expected to be required.

As of Linux 4.20, the algorithm is as follows: when userspace starts to read directory entries, issue a READDIRPLUS request to the filesystem. If any entry attributes have been looked up by the time userspace requests the next batch of entries continue with READDIRPLUS, otherwise switch to plain READDIR. This will reasult in eg plain "ls" triggering READDIRPLUS first then READDIR after that because it doesn't do lookups. "ls -l" should result in all READDIRPLUS, except if dentries are already cached.

This feature is enabled by default when supported by the kernel and if the filesystem implements both a readdirplus() and a readdir() handler.

Definition at line 317 of file fuse_common.h.

◆ FUSE_CAP_SETXATTR_EXT

#define FUSE_CAP_SETXATTR_EXT   (1 << 27)

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.

Definition at line 479 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_MOVE

#define FUSE_CAP_SPLICE_MOVE   (1 << 8)

Indicates that libfuse should try to move pages instead of copying when writing to / reading from the fuse device. This may improve performance.

This feature is disabled by default.

Definition at line 230 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_READ

#define FUSE_CAP_SPLICE_READ   (1 << 9)

Indicates that libfuse should try to use splice() when reading from the fuse device. This may improve performance.

This feature is enabled by default when supported by the kernel and if the filesystem implements a write_buf() handler.

Definition at line 239 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_WRITE

#define FUSE_CAP_SPLICE_WRITE   (1 << 7)

Indicates that libfuse should try to use splice() when writing to the fuse device. This may improve performance.

This feature is disabled by default.

Definition at line 222 of file fuse_common.h.

◆ FUSE_CAP_WRITEBACK_CACHE

#define FUSE_CAP_WRITEBACK_CACHE   (1 << 16)

Indicates that writeback caching should be enabled. This means that individual write request may be buffered and merged in the kernel before they are send to the filesystem.

This feature is disabled by default.

Definition at line 337 of file fuse_common.h.

◆ FUSE_IOCTL_COMPAT

#define FUSE_IOCTL_COMPAT   (1 << 0)

Ioctl flags

FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed FUSE_IOCTL_RETRY: retry with new iovecs FUSE_IOCTL_DIR: is a directory

FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs

Definition at line 520 of file fuse_common.h.

Enumeration Type Documentation

◆ fuse_buf_copy_flags

Buffer copy flags

Enumerator
FUSE_BUF_NO_SPLICE 

Don't use splice(2)

Always fall back to using read and write instead of splice(2) to copy data from one file descriptor to another.

If this flag is not set, then only fall back if splice is unavailable.

FUSE_BUF_FORCE_SPLICE 

Force splice

Always use splice(2) to copy data from one file descriptor to another. If splice is not available, return -EINVAL.

FUSE_BUF_SPLICE_MOVE 

Try to move data with splice.

If splice is used, try to move pages from the source to the destination instead of copying. See documentation of SPLICE_F_MOVE in splice(2) man page.

FUSE_BUF_SPLICE_NONBLOCK 

Don't block on the pipe when copying data with splice

Makes the operations on the pipe non-blocking (if the pipe is full or empty). See SPLICE_F_NONBLOCK in the splice(2) man page.

Definition at line 829 of file fuse_common.h.

◆ fuse_buf_flags

Buffer flags

Enumerator
FUSE_BUF_IS_FD 

Buffer contains a file descriptor

If this flag is set, the .fd field is valid, otherwise the .mem fields is valid.

FUSE_BUF_FD_SEEK 

Seek on the file descriptor

If this flag is set then the .pos field is valid and is used to seek to the given offset before performing operation on file descriptor.

FUSE_BUF_FD_RETRY 

Retry operation on file descriptor

If this flag is set then retry operation on file descriptor until .size bytes have been copied or an error or EOF is detected.

Definition at line 798 of file fuse_common.h.

Function Documentation

◆ fuse_apply_conn_info_opts()

void fuse_apply_conn_info_opts ( struct fuse_conn_info_opts *  opts,
struct fuse_conn_info conn 
)

This function applies the (parsed) parameters in opts to the conn pointer. It may modify the following fields: wants, max_write, max_readahead, congestion_threshold, max_background, time_gran. A field is only set (or unset) if the corresponding option has been explicitly set.

Definition at line 412 of file helper.c.

◆ fuse_buf_copy()

ssize_t fuse_buf_copy ( struct fuse_bufvec dst,
struct fuse_bufvec src,
enum fuse_buf_copy_flags  flags 
)

Copy data from one buffer vector to another

Parameters
dstdestination buffer vector
srcsource buffer vector
flagsflags controlling the copy
Returns
actual number of bytes copied or -errno on error

Definition at line 284 of file buffer.c.

◆ fuse_buf_size()

size_t fuse_buf_size ( const struct fuse_bufvec bufv)

Get total size of data in a fuse buffer vector

Parameters
bufvbuffer vector
Returns
size of data

Definition at line 22 of file buffer.c.

◆ fuse_convert_to_conn_want_ext()

int fuse_convert_to_conn_want_ext ( struct fuse_conn_info conn)

Get the wanted capability flags, converting from old format if necessary

Definition at line 2000 of file fuse_lowlevel.c.

◆ fuse_daemonize()

int fuse_daemonize ( int  foreground)

Go into the background

Parameters
foregroundif true, stay in the foreground
Returns
0 on success, -1 on failure

Definition at line 253 of file helper.c.

◆ fuse_get_feature_flag()

bool fuse_get_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Get the value of a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be checked
Returns
true if the flag is set, false otherwise

Definition at line 2061 of file fuse_lowlevel.c.

◆ fuse_parse_conn_info_opts()

struct fuse_conn_info_opts * fuse_parse_conn_info_opts ( struct fuse_args args)

This function parses several command-line options that can be used to override elements of struct fuse_conn_info. The pointer returned by this function should be passed to the fuse_apply_conn_info_opts() method by the file system's init() handler.

Before using this function, think twice if you really want these parameters to be adjustable from the command line. In most cases, they should be determined by the file system internally.

The following options are recognized:

-o max_write=N sets conn->max_write -o max_readahead=N sets conn->max_readahead -o max_background=N sets conn->max_background -o congestion_threshold=N sets conn->congestion_threshold -o async_read sets FUSE_CAP_ASYNC_READ in conn->want -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets FUSE_CAP_READDIRPLUS_AUTO in conn->want -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and FUSE_CAP_READDIRPLUS_AUTO in conn->want -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want -o time_gran=N sets conn->time_gran

Known options will be removed from args, unknown options will be passed through unchanged.

Parameters
argsargument vector (input+output)
Returns
parsed options

Definition at line 466 of file helper.c.

◆ fuse_pkgversion()

const char * fuse_pkgversion ( void  )

Get the full package version string of the library

Returns
the package version

Definition at line 5211 of file fuse.c.

◆ fuse_pollhandle_destroy()

void fuse_pollhandle_destroy ( struct fuse_pollhandle *  ph)

Destroy poll handle

Parameters
phthe poll handle

Definition at line 1903 of file fuse_lowlevel.c.

◆ fuse_remove_signal_handlers()

void fuse_remove_signal_handlers ( struct fuse_session *  se)

Restore default signal handlers

Resets global session. After this fuse_set_signal_handlers() may be called again.

Parameters
sethe same session as given in fuse_set_signal_handlers()

See also: fuse_set_signal_handlers()

Definition at line 187 of file fuse_signals.c.

◆ fuse_set_fail_signal_handlers()

int fuse_set_fail_signal_handlers ( struct fuse_session *  se)

Print a stack backtrace diagnostic on critical signals ()

Stores session in a global variable. May only be called once per process until fuse_remove_signal_handlers() is called.

Once either of the POSIX signals arrives, the signal handler calls fuse_session_exit().

Parameters
sethe session to exit
Returns
0 on success, -1 on failure

See also: fuse_remove_signal_handlers()

Definition at line 165 of file fuse_signals.c.

◆ fuse_set_feature_flag()

bool fuse_set_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Config operations. These APIs are introduced in version 312 (FUSE_MAKE_VERSION(3, 12)). Using them in earlier versions will result in errors. Set a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be set
Returns
true if the flag was set, false if the flag is not supported

Definition at line 2035 of file fuse_lowlevel.c.

◆ fuse_set_signal_handlers()

int fuse_set_signal_handlers ( struct fuse_session *  se)

Exit session on HUP, TERM and INT signals and ignore PIPE signal

Stores session in a global variable. May only be called once per process until fuse_remove_signal_handlers() is called.

Once either of the POSIX signals arrives, the signal handler calls fuse_session_exit().

Parameters
sethe session to exit
Returns
0 on success, -1 on failure

See also: fuse_remove_signal_handlers()

Definition at line 140 of file fuse_signals.c.

◆ fuse_unset_feature_flag()

void fuse_unset_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Unset a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be unset

Definition at line 2050 of file fuse_lowlevel.c.

◆ fuse_version()

int fuse_version ( void  )

Get the version of the library

Returns
the version

Definition at line 5206 of file fuse.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__common_8h.html0000644000175000017500000027007715156613443023152 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_common.h File Reference
libfuse
fuse_common.h File Reference
#include "libfuse_config.h"
#include "fuse_opt.h"
#include "fuse_log.h"
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <assert.h>

Go to the source code of this file.

Data Structures

struct  fuse_file_info
 
struct  fuse_loop_config
 
struct  fuse_conn_info
 
struct  fuse_buf
 
struct  fuse_bufvec
 
struct  libfuse_version
 

Macros

#define FUSE_CAP_ASYNC_READ   (1UL << 0)
 
#define FUSE_CAP_POSIX_LOCKS   (1UL << 1)
 
#define FUSE_CAP_ATOMIC_O_TRUNC   (1UL << 3)
 
#define FUSE_CAP_EXPORT_SUPPORT   (1UL << 4)
 
#define FUSE_CAP_DONT_MASK   (1UL << 6)
 
#define FUSE_CAP_SPLICE_WRITE   (1UL << 7)
 
#define FUSE_CAP_SPLICE_MOVE   (1UL << 8)
 
#define FUSE_CAP_SPLICE_READ   (1UL << 9)
 
#define FUSE_CAP_FLOCK_LOCKS   (1UL << 10)
 
#define FUSE_CAP_IOCTL_DIR   (1UL << 11)
 
#define FUSE_CAP_AUTO_INVAL_DATA   (1UL << 12)
 
#define FUSE_CAP_READDIRPLUS   (1UL << 13)
 
#define FUSE_CAP_READDIRPLUS_AUTO   (1UL << 14)
 
#define FUSE_CAP_ASYNC_DIO   (1UL << 15)
 
#define FUSE_CAP_WRITEBACK_CACHE   (1UL << 16)
 
#define FUSE_CAP_NO_OPEN_SUPPORT   (1UL << 17)
 
#define FUSE_CAP_PARALLEL_DIROPS   (1UL << 18)
 
#define FUSE_CAP_POSIX_ACL   (1UL << 19)
 
#define FUSE_CAP_HANDLE_KILLPRIV   (1UL << 20)
 
#define FUSE_CAP_HANDLE_KILLPRIV_V2   (1UL << 21)
 
#define FUSE_CAP_CACHE_SYMLINKS   (1UL << 23)
 
#define FUSE_CAP_NO_OPENDIR_SUPPORT   (1UL << 24)
 
#define FUSE_CAP_EXPLICIT_INVAL_DATA   (1UL << 25)
 
#define FUSE_CAP_EXPIRE_ONLY   (1UL << 26)
 
#define FUSE_CAP_SETXATTR_EXT   (1UL << 27)
 
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP   (1UL << 28)
 
#define FUSE_CAP_PASSTHROUGH   (1UL << 29)
 
#define FUSE_CAP_NO_EXPORT_SUPPORT   (1UL << 30)
 
#define FUSE_CAP_OVER_IO_URING   (1UL << 31)
 
#define FUSE_IOCTL_COMPAT   (1 << 0)
 
#define FUSE_BACKING_STACKED_UNDER   (0)
 

Enumerations

enum  fuse_buf_flags { FUSE_BUF_IS_FD = (1 << 1) , FUSE_BUF_FD_SEEK = (1 << 2) , FUSE_BUF_FD_RETRY = (1 << 3) }
 
enum  fuse_buf_copy_flags { FUSE_BUF_NO_SPLICE = (1 << 1) , FUSE_BUF_FORCE_SPLICE = (1 << 2) , FUSE_BUF_SPLICE_MOVE = (1 << 3) , FUSE_BUF_SPLICE_NONBLOCK = (1 << 4) }
 

Functions

struct fuse_conn_info_opts * fuse_parse_conn_info_opts (struct fuse_args *args)
 
void fuse_apply_conn_info_opts (struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
 
int fuse_daemonize (int foreground)
 
int fuse_version (void)
 
const char * fuse_pkgversion (void)
 
void fuse_pollhandle_destroy (struct fuse_pollhandle *ph)
 
size_t fuse_buf_size (const struct fuse_bufvec *bufv)
 
ssize_t fuse_buf_copy (struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
 
int fuse_set_signal_handlers (struct fuse_session *se)
 
int fuse_set_fail_signal_handlers (struct fuse_session *se)
 
void fuse_remove_signal_handlers (struct fuse_session *se)
 
bool fuse_set_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
void fuse_unset_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
bool fuse_get_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
int fuse_convert_to_conn_want_ext (struct fuse_conn_info *conn)
 

Macro Definition Documentation

◆ FUSE_BACKING_STACKED_UNDER

#define FUSE_BACKING_STACKED_UNDER   (0)

When FUSE_CAP_PASSTHROUGH is enabled, this is the maximum allowed stacking depth of the backing files. In current kernel, the maximum allowed stack depth if FILESYSTEM_MAX_STACK_DEPTH (2), which includes the FUSE passthrough layer, so the maximum stacking depth for backing files is 1.

The default is FUSE_BACKING_STACKED_UNDER (0), meaning that the backing files cannot be on a stacked filesystem, but another stacked filesystem can be stacked over this FUSE passthrough filesystem.

Set this to FUSE_BACKING_STACKED_OVER (1) if backing files may be on a stacked filesystem, such as overlayfs or another FUSE passthrough. In this configuration, another stacked filesystem cannot be stacked over this FUSE passthrough filesystem.

Definition at line 670 of file fuse_common.h.

◆ FUSE_CAP_ASYNC_DIO

#define FUSE_CAP_ASYNC_DIO   (1UL << 15)

Indicates that the filesystem supports asynchronous direct I/O submission.

If this capability is not requested/available, the kernel will ensure that there is at most one pending read and one pending write request per direct I/O file-handle at any time.

This feature is enabled by default when supported by the kernel.

Definition at line 328 of file fuse_common.h.

◆ FUSE_CAP_ASYNC_READ

#define FUSE_CAP_ASYNC_READ   (1UL << 0)

Indicates that the filesystem supports asynchronous read requests.

If this capability is not requested/available, the kernel will ensure that there is at most one pending read request per file-handle at any time, and will attempt to order read requests by increasing offset.

This feature is enabled by default when supported by the kernel.

Definition at line 177 of file fuse_common.h.

◆ FUSE_CAP_ATOMIC_O_TRUNC

#define FUSE_CAP_ATOMIC_O_TRUNC   (1UL << 3)

Indicates that the filesystem supports the O_TRUNC open flag. If disabled, and an application specifies O_TRUNC, fuse first calls truncate() and then open() with O_TRUNC filtered out.

This feature is enabled by default when supported by the kernel.

Definition at line 194 of file fuse_common.h.

◆ FUSE_CAP_AUTO_INVAL_DATA

#define FUSE_CAP_AUTO_INVAL_DATA   (1UL << 12)

Traditionally, while a file is open the FUSE kernel module only asks the filesystem for an update of the file's attributes when a client attempts to read beyond EOF. This is unsuitable for e.g. network filesystems, where the file contents may change without the kernel knowing about it.

If this flag is set, FUSE will check the validity of the attributes on every read. If the attributes are no longer valid (i.e., if the attr_timeout passed to fuse_reply_attr() or set in struct fuse_entry_param has passed), it will first issue a getattr request. If the new mtime differs from the previous value, any cached file contents will be invalidated as well.

This flag should always be set when available. If all file changes go through the kernel, attr_timeout should be set to a very large number to avoid unnecessary getattr() calls.

This feature is enabled by default when supported by the kernel.

Definition at line 281 of file fuse_common.h.

◆ FUSE_CAP_CACHE_SYMLINKS

#define FUSE_CAP_CACHE_SYMLINKS   (1UL << 23)

Indicates that the kernel supports caching symlinks in its page cache.

When this feature is enabled, symlink targets are saved in the page cache. You can invalidate a cached link by calling: fuse_lowlevel_notify_inval_inode(se, ino, 0, 0);

This feature is disabled by default. If the kernel supports it (>= 4.20), you can enable this feature by setting this flag in the want field of the fuse_conn_info structure.

Definition at line 418 of file fuse_common.h.

◆ FUSE_CAP_DIRECT_IO_ALLOW_MMAP

#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP   (1UL << 28)

Files opened with FUSE_DIRECT_IO do not support MAP_SHARED mmap. This restriction is relaxed through FUSE_CAP_DIRECT_IO_RELAX (kernel flag: FUSE_DIRECT_IO_RELAX). MAP_SHARED is disabled by default for FUSE_DIRECT_IO, as this flag can be used to ensure coherency between mount points (or network clients) and with kernel page cache as enforced by mmap that cannot be guaranteed anymore.

Definition at line 488 of file fuse_common.h.

◆ FUSE_CAP_DONT_MASK

#define FUSE_CAP_DONT_MASK   (1UL << 6)

Indicates that the kernel should not apply the umask to the file mode on create operations.

This feature is disabled by default.

Definition at line 214 of file fuse_common.h.

◆ FUSE_CAP_EXPIRE_ONLY

#define FUSE_CAP_EXPIRE_ONLY   (1UL << 26)

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.

Definition at line 472 of file fuse_common.h.

◆ FUSE_CAP_EXPLICIT_INVAL_DATA

#define FUSE_CAP_EXPLICIT_INVAL_DATA   (1UL << 25)

Indicates support for invalidating cached pages only on explicit request.

If this flag is set in the capable field of the fuse_conn_info structure, then the FUSE kernel module supports invalidating cached pages only on explicit request by the filesystem through fuse_lowlevel_notify_inval_inode() or fuse_invalidate_path().

By setting this flag in the want field of the fuse_conn_info structure, the filesystem is responsible for invalidating cached pages through explicit requests to the kernel.

Note that setting this flag does not prevent the cached pages from being flushed by OS itself and/or through user actions.

Note that if both FUSE_CAP_EXPLICIT_INVAL_DATA and FUSE_CAP_AUTO_INVAL_DATA are set in the capable field of the fuse_conn_info structure then FUSE_CAP_AUTO_INVAL_DATA takes precedence.

This feature is disabled by default.

Definition at line 456 of file fuse_common.h.

◆ FUSE_CAP_EXPORT_SUPPORT

#define FUSE_CAP_EXPORT_SUPPORT   (1UL << 4)

Indicates that the filesystem supports lookups of "." and "..".

When this flag is set, the filesystem must be prepared to receive requests for invalid inodes (i.e., for which a FORGET request was received or which have been used in a previous instance of the filesystem daemon) and must not reuse node-ids (even when setting generation numbers).

This feature is disabled by default.

Definition at line 206 of file fuse_common.h.

◆ FUSE_CAP_FLOCK_LOCKS

#define FUSE_CAP_FLOCK_LOCKS   (1UL << 10)

If set, the calls to flock(2) will be emulated using POSIX locks and must then be handled by the filesystem's setlock() handler.

If not set, flock(2) calls will be handled by the FUSE kernel module internally (so any access that does not go through the kernel cannot be taken into account).

This feature is enabled by default when supported by the kernel and if the filesystem implements a flock() handler.

Definition at line 252 of file fuse_common.h.

◆ FUSE_CAP_HANDLE_KILLPRIV

#define FUSE_CAP_HANDLE_KILLPRIV   (1UL << 20)

Indicates that the filesystem is responsible for unsetting setuid and setgid bits when a file is written, truncated, or its owner is changed.

This feature is disabled by default.

Definition at line 388 of file fuse_common.h.

◆ FUSE_CAP_HANDLE_KILLPRIV_V2

#define FUSE_CAP_HANDLE_KILLPRIV_V2   (1UL << 21)

Indicates that the filesystem is responsible for unsetting setuid and setgid bit and additionally cap (stored as xattr) when a file is written, truncated, or its owner is changed. 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). KILLPRIV_V2 requires handling of

  • FUSE_OPEN_KILL_SUIDGID (set in struct fuse_create_in::open_flags)
  • FATTR_KILL_SUIDGID (set in struct fuse_setattr_in::valid)
  • FUSE_WRITE_KILL_SUIDGID (set in struct fuse_write_in::write_flags)

This feature is disabled by default.

Definition at line 405 of file fuse_common.h.

◆ FUSE_CAP_IOCTL_DIR

#define FUSE_CAP_IOCTL_DIR   (1UL << 11)

Indicates that the filesystem supports ioctl's on directories.

This feature is enabled by default when supported by the kernel.

Definition at line 259 of file fuse_common.h.

◆ FUSE_CAP_NO_EXPORT_SUPPORT

#define FUSE_CAP_NO_EXPORT_SUPPORT   (1UL << 30)

Indicates that the file system cannot handle NFS export

If this flag is set NFS export and name_to_handle_at is not going to work at all and will fail with EOPNOTSUPP.

Definition at line 508 of file fuse_common.h.

◆ FUSE_CAP_NO_OPEN_SUPPORT

#define FUSE_CAP_NO_OPEN_SUPPORT   (1UL << 17)

Indicates support for zero-message opens. If this flag is set in the capable field of the fuse_conn_info structure, then the filesystem may return ENOSYS from the open() handler to indicate success. Further attempts to open files will be handled in the kernel. (If this flag is not set, returning ENOSYS will be treated as an error and signaled to the caller).

Setting this flag in the want field enables this behavior automatically within libfuse for low level API users. If non-low level users wish to have this behavior you must return ENOSYS from the open() handler on supporting kernels.

Definition at line 352 of file fuse_common.h.

◆ FUSE_CAP_NO_OPENDIR_SUPPORT

#define FUSE_CAP_NO_OPENDIR_SUPPORT   (1UL << 24)

Indicates support for zero-message opendirs. If this flag is set in the capable field of the fuse_conn_info structure, then the filesystem may return ENOSYS from the opendir() handler to indicate success. Further opendir and releasedir messages will be handled in the kernel. (If this flag is not set, returning ENOSYS will be treated as an error and signalled to the caller.)

Setting this flag in the want field enables this behavior automatically within libfuse for low level API users. If non-low level users with to have this behavior you must return ENOSYS from the opendir() handler on supporting kernels.

Definition at line 433 of file fuse_common.h.

◆ FUSE_CAP_OVER_IO_URING

#define FUSE_CAP_OVER_IO_URING   (1UL << 31)

Indicates support for io-uring between fuse-server and fuse-client

Definition at line 513 of file fuse_common.h.

◆ FUSE_CAP_PARALLEL_DIROPS

#define FUSE_CAP_PARALLEL_DIROPS   (1UL << 18)

Indicates support for parallel directory operations. If this flag is unset, the FUSE kernel module will ensure that lookup() and readdir() requests are never issued concurrently for the same directory.

Definition at line 360 of file fuse_common.h.

◆ FUSE_CAP_PASSTHROUGH

#define FUSE_CAP_PASSTHROUGH   (1UL << 29)

Indicates support for passthrough mode access for read/write operations.

If this flag is set in the capable field of the fuse_conn_info structure, then the FUSE kernel module supports redirecting read/write operations to the backing file instead of letting them to be handled by the FUSE daemon.

This feature is disabled by default.

Definition at line 500 of file fuse_common.h.

◆ FUSE_CAP_POSIX_ACL

#define FUSE_CAP_POSIX_ACL   (1UL << 19)

Indicates support for POSIX ACLs.

If this feature is enabled, the kernel will cache and have responsibility for enforcing ACLs. ACL will be stored as xattrs and passed to userspace, which is responsible for updating the ACLs in the filesystem, keeping the file mode in sync with the ACL, and ensuring inheritance of default ACLs when new filesystem nodes are created. Note that this requires that the file system is able to parse and interpret the xattr representation of ACLs.

Enabling this feature implicitly turns on the default_permissions mount option (even if it was not passed to mount(2)).

This feature is disabled by default.

Definition at line 379 of file fuse_common.h.

◆ FUSE_CAP_POSIX_LOCKS

#define FUSE_CAP_POSIX_LOCKS   (1UL << 1)

Indicates that the filesystem supports "remote" locking.

This feature is enabled by default when supported by the kernel, and if getlk() and setlk() handlers are implemented.

Definition at line 185 of file fuse_common.h.

◆ FUSE_CAP_READDIRPLUS

#define FUSE_CAP_READDIRPLUS   (1UL << 13)

Indicates that the filesystem supports readdirplus.

This feature is enabled by default when supported by the kernel and if the filesystem implements a readdirplus() handler.

Definition at line 289 of file fuse_common.h.

◆ FUSE_CAP_READDIRPLUS_AUTO

#define FUSE_CAP_READDIRPLUS_AUTO   (1UL << 14)

Indicates that the filesystem supports adaptive readdirplus.

If FUSE_CAP_READDIRPLUS is not set, this flag has no effect.

If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel will always issue readdirplus() requests to retrieve directory contents.

If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel will issue both readdir() and readdirplus() requests, depending on how much information is expected to be required.

As of Linux 4.20, the algorithm is as follows: when userspace starts to read directory entries, issue a READDIRPLUS request to the filesystem. If any entry attributes have been looked up by the time userspace requests the next batch of entries continue with READDIRPLUS, otherwise switch to plain READDIR. This will reasult in eg plain "ls" triggering READDIRPLUS first then READDIR after that because it doesn't do lookups. "ls -l" should result in all READDIRPLUS, except if dentries are already cached.

This feature is enabled by default when supported by the kernel and if the filesystem implements both a readdirplus() and a readdir() handler.

Definition at line 317 of file fuse_common.h.

◆ FUSE_CAP_SETXATTR_EXT

#define FUSE_CAP_SETXATTR_EXT   (1UL << 27)

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.

Definition at line 479 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_MOVE

#define FUSE_CAP_SPLICE_MOVE   (1UL << 8)

Indicates that libfuse should try to move pages instead of copying when writing to / reading from the fuse device. This may improve performance.

This feature is disabled by default.

Definition at line 230 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_READ

#define FUSE_CAP_SPLICE_READ   (1UL << 9)

Indicates that libfuse should try to use splice() when reading from the fuse device. This may improve performance.

This feature is enabled by default when supported by the kernel and if the filesystem implements a write_buf() handler.

Definition at line 239 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_WRITE

#define FUSE_CAP_SPLICE_WRITE   (1UL << 7)

Indicates that libfuse should try to use splice() when writing to the fuse device. This may improve performance.

This feature is disabled by default.

Definition at line 222 of file fuse_common.h.

◆ FUSE_CAP_WRITEBACK_CACHE

#define FUSE_CAP_WRITEBACK_CACHE   (1UL << 16)

Indicates that writeback caching should be enabled. This means that individual write request may be buffered and merged in the kernel before they are send to the filesystem.

This feature is disabled by default.

Definition at line 337 of file fuse_common.h.

◆ FUSE_IOCTL_COMPAT

#define FUSE_IOCTL_COMPAT   (1 << 0)

Ioctl flags

FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed FUSE_IOCTL_RETRY: retry with new iovecs FUSE_IOCTL_DIR: is a directory

FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs

Definition at line 525 of file fuse_common.h.

Enumeration Type Documentation

◆ fuse_buf_copy_flags

Buffer copy flags

Enumerator
FUSE_BUF_NO_SPLICE 

Don't use splice(2)

Always fall back to using read and write instead of splice(2) to copy data from one file descriptor to another.

If this flag is not set, then only fall back if splice is unavailable.

FUSE_BUF_FORCE_SPLICE 

Force splice

Always use splice(2) to copy data from one file descriptor to another. If splice is not available, return -EINVAL.

FUSE_BUF_SPLICE_MOVE 

Try to move data with splice.

If splice is used, try to move pages from the source to the destination instead of copying. See documentation of SPLICE_F_MOVE in splice(2) man page.

FUSE_BUF_SPLICE_NONBLOCK 

Don't block on the pipe when copying data with splice

Makes the operations on the pipe non-blocking (if the pipe is full or empty). See SPLICE_F_NONBLOCK in the splice(2) man page.

Definition at line 840 of file fuse_common.h.

◆ fuse_buf_flags

Buffer flags

Enumerator
FUSE_BUF_IS_FD 

Buffer contains a file descriptor

If this flag is set, the .fd field is valid, otherwise the .mem fields is valid.

FUSE_BUF_FD_SEEK 

Seek on the file descriptor

If this flag is set then the .pos field is valid and is used to seek to the given offset before performing operation on file descriptor.

FUSE_BUF_FD_RETRY 

Retry operation on file descriptor

If this flag is set then retry operation on file descriptor until .size bytes have been copied or an error or EOF is detected.

Definition at line 809 of file fuse_common.h.

Function Documentation

◆ fuse_apply_conn_info_opts()

void fuse_apply_conn_info_opts ( struct fuse_conn_info_opts *  opts,
struct fuse_conn_info conn 
)

This function applies the (parsed) parameters in opts to the conn pointer. It may modify the following fields: wants, max_write, max_readahead, congestion_threshold, max_background, time_gran. A field is only set (or unset) if the corresponding option has been explicitly set.

Definition at line 412 of file helper.c.

◆ fuse_buf_copy()

ssize_t fuse_buf_copy ( struct fuse_bufvec dst,
struct fuse_bufvec src,
enum fuse_buf_copy_flags  flags 
)

Copy data from one buffer vector to another

Parameters
dstdestination buffer vector
srcsource buffer vector
flagsflags controlling the copy
Returns
actual number of bytes copied or -errno on error

Definition at line 284 of file buffer.c.

◆ fuse_buf_size()

size_t fuse_buf_size ( const struct fuse_bufvec bufv)

Get total size of data in a fuse buffer vector

Parameters
bufvbuffer vector
Returns
size of data

Definition at line 22 of file buffer.c.

◆ fuse_convert_to_conn_want_ext()

int fuse_convert_to_conn_want_ext ( struct fuse_conn_info conn)

Get the wanted capability flags, converting from old format if necessary

Definition at line 2000 of file fuse_lowlevel.c.

◆ fuse_daemonize()

int fuse_daemonize ( int  foreground)

Go into the background

Parameters
foregroundif true, stay in the foreground
Returns
0 on success, -1 on failure

Definition at line 253 of file helper.c.

◆ fuse_get_feature_flag()

bool fuse_get_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Get the value of a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be checked
Returns
true if the flag is set, false otherwise

Definition at line 2061 of file fuse_lowlevel.c.

◆ fuse_parse_conn_info_opts()

struct fuse_conn_info_opts * fuse_parse_conn_info_opts ( struct fuse_args args)

This function parses several command-line options that can be used to override elements of struct fuse_conn_info. The pointer returned by this function should be passed to the fuse_apply_conn_info_opts() method by the file system's init() handler.

Before using this function, think twice if you really want these parameters to be adjustable from the command line. In most cases, they should be determined by the file system internally.

The following options are recognized:

-o max_write=N sets conn->max_write -o max_readahead=N sets conn->max_readahead -o max_background=N sets conn->max_background -o congestion_threshold=N sets conn->congestion_threshold -o async_read sets FUSE_CAP_ASYNC_READ in conn->want -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets FUSE_CAP_READDIRPLUS_AUTO in conn->want -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and FUSE_CAP_READDIRPLUS_AUTO in conn->want -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want -o time_gran=N sets conn->time_gran

Known options will be removed from args, unknown options will be passed through unchanged.

Parameters
argsargument vector (input+output)
Returns
parsed options

Definition at line 466 of file helper.c.

◆ fuse_pkgversion()

const char * fuse_pkgversion ( void  )

Get the full package version string of the library

Returns
the package version

Definition at line 5211 of file fuse.c.

◆ fuse_pollhandle_destroy()

void fuse_pollhandle_destroy ( struct fuse_pollhandle *  ph)

Destroy poll handle

Parameters
phthe poll handle

Definition at line 1903 of file fuse_lowlevel.c.

◆ fuse_remove_signal_handlers()

void fuse_remove_signal_handlers ( struct fuse_session *  se)

Restore default signal handlers

Resets global session. After this fuse_set_signal_handlers() may be called again.

Parameters
sethe same session as given in fuse_set_signal_handlers()

See also: fuse_set_signal_handlers()

Definition at line 187 of file fuse_signals.c.

◆ fuse_set_fail_signal_handlers()

int fuse_set_fail_signal_handlers ( struct fuse_session *  se)

Print a stack backtrace diagnostic on critical signals ()

Stores session in a global variable. May only be called once per process until fuse_remove_signal_handlers() is called.

Once either of the POSIX signals arrives, the signal handler calls fuse_session_exit().

Parameters
sethe session to exit
Returns
0 on success, -1 on failure

See also: fuse_remove_signal_handlers()

Definition at line 165 of file fuse_signals.c.

◆ fuse_set_feature_flag()

bool fuse_set_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Config operations. These APIs are introduced in version 312 (FUSE_MAKE_VERSION(3, 12)). Using them in earlier versions will result in errors. Set a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be set
Returns
true if the flag was set, false if the flag is not supported

Definition at line 2035 of file fuse_lowlevel.c.

◆ fuse_set_signal_handlers()

int fuse_set_signal_handlers ( struct fuse_session *  se)

Exit session on HUP, TERM and INT signals and ignore PIPE signal

Stores session in a global variable. May only be called once per process until fuse_remove_signal_handlers() is called.

Once either of the POSIX signals arrives, the signal handler calls fuse_session_exit().

Parameters
sethe session to exit
Returns
0 on success, -1 on failure

See also: fuse_remove_signal_handlers()

Definition at line 140 of file fuse_signals.c.

◆ fuse_unset_feature_flag()

void fuse_unset_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Unset a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be unset

Definition at line 2050 of file fuse_lowlevel.c.

◆ fuse_version()

int fuse_version ( void  )

Get the version of the library

Returns
the version

Definition at line 5206 of file fuse.c.

fuse-3.18.2/doc/html/include_2fuse__common_8h.html0000644000175000017500000026421015156613443020766 0ustar berndbernd libfuse: include/fuse_common.h File Reference
libfuse
fuse_common.h File Reference
#include "libfuse_config.h"
#include "fuse_opt.h"
#include "fuse_log.h"
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <assert.h>

Go to the source code of this file.

Data Structures

struct  fuse_file_info
 
struct  fuse_loop_config
 
struct  fuse_conn_info
 
struct  fuse_buf
 
struct  fuse_bufvec
 
struct  libfuse_version
 

Macros

#define FUSE_CAP_ASYNC_READ   (1UL << 0)
 
#define FUSE_CAP_POSIX_LOCKS   (1UL << 1)
 
#define FUSE_CAP_ATOMIC_O_TRUNC   (1UL << 3)
 
#define FUSE_CAP_EXPORT_SUPPORT   (1UL << 4)
 
#define FUSE_CAP_DONT_MASK   (1UL << 6)
 
#define FUSE_CAP_SPLICE_WRITE   (1UL << 7)
 
#define FUSE_CAP_SPLICE_MOVE   (1UL << 8)
 
#define FUSE_CAP_SPLICE_READ   (1UL << 9)
 
#define FUSE_CAP_FLOCK_LOCKS   (1UL << 10)
 
#define FUSE_CAP_IOCTL_DIR   (1UL << 11)
 
#define FUSE_CAP_AUTO_INVAL_DATA   (1UL << 12)
 
#define FUSE_CAP_READDIRPLUS   (1UL << 13)
 
#define FUSE_CAP_READDIRPLUS_AUTO   (1UL << 14)
 
#define FUSE_CAP_ASYNC_DIO   (1UL << 15)
 
#define FUSE_CAP_WRITEBACK_CACHE   (1UL << 16)
 
#define FUSE_CAP_NO_OPEN_SUPPORT   (1UL << 17)
 
#define FUSE_CAP_PARALLEL_DIROPS   (1UL << 18)
 
#define FUSE_CAP_POSIX_ACL   (1UL << 19)
 
#define FUSE_CAP_HANDLE_KILLPRIV   (1UL << 20)
 
#define FUSE_CAP_HANDLE_KILLPRIV_V2   (1UL << 21)
 
#define FUSE_CAP_CACHE_SYMLINKS   (1UL << 23)
 
#define FUSE_CAP_NO_OPENDIR_SUPPORT   (1UL << 24)
 
#define FUSE_CAP_EXPLICIT_INVAL_DATA   (1UL << 25)
 
#define FUSE_CAP_EXPIRE_ONLY   (1UL << 26)
 
#define FUSE_CAP_SETXATTR_EXT   (1UL << 27)
 
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP   (1UL << 28)
 
#define FUSE_CAP_PASSTHROUGH   (1UL << 29)
 
#define FUSE_CAP_NO_EXPORT_SUPPORT   (1UL << 30)
 
#define FUSE_CAP_OVER_IO_URING   (1UL << 31)
 
#define FUSE_IOCTL_COMPAT   (1 << 0)
 
#define FUSE_BACKING_STACKED_UNDER   (0)
 

Enumerations

enum  fuse_buf_flags { FUSE_BUF_IS_FD = (1 << 1) , FUSE_BUF_FD_SEEK = (1 << 2) , FUSE_BUF_FD_RETRY = (1 << 3) }
 
enum  fuse_buf_copy_flags { FUSE_BUF_NO_SPLICE = (1 << 1) , FUSE_BUF_FORCE_SPLICE = (1 << 2) , FUSE_BUF_SPLICE_MOVE = (1 << 3) , FUSE_BUF_SPLICE_NONBLOCK = (1 << 4) }
 

Functions

struct fuse_conn_info_opts * fuse_parse_conn_info_opts (struct fuse_args *args)
 
void fuse_apply_conn_info_opts (struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
 
int fuse_daemonize (int foreground)
 
int fuse_version (void)
 
const char * fuse_pkgversion (void)
 
void fuse_pollhandle_destroy (struct fuse_pollhandle *ph)
 
size_t fuse_buf_size (const struct fuse_bufvec *bufv)
 
ssize_t fuse_buf_copy (struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
 
int fuse_set_signal_handlers (struct fuse_session *se)
 
int fuse_set_fail_signal_handlers (struct fuse_session *se)
 
void fuse_remove_signal_handlers (struct fuse_session *se)
 
bool fuse_set_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
void fuse_unset_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
bool fuse_get_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
int fuse_convert_to_conn_want_ext (struct fuse_conn_info *conn)
 

Macro Definition Documentation

◆ FUSE_BACKING_STACKED_UNDER

#define FUSE_BACKING_STACKED_UNDER   (0)

When FUSE_CAP_PASSTHROUGH is enabled, this is the maximum allowed stacking depth of the backing files. In current kernel, the maximum allowed stack depth if FILESYSTEM_MAX_STACK_DEPTH (2), which includes the FUSE passthrough layer, so the maximum stacking depth for backing files is 1.

The default is FUSE_BACKING_STACKED_UNDER (0), meaning that the backing files cannot be on a stacked filesystem, but another stacked filesystem can be stacked over this FUSE passthrough filesystem.

Set this to FUSE_BACKING_STACKED_OVER (1) if backing files may be on a stacked filesystem, such as overlayfs or another FUSE passthrough. In this configuration, another stacked filesystem cannot be stacked over this FUSE passthrough filesystem.

Definition at line 670 of file fuse_common.h.

◆ FUSE_CAP_ASYNC_DIO

#define FUSE_CAP_ASYNC_DIO   (1UL << 15)

Indicates that the filesystem supports asynchronous direct I/O submission.

If this capability is not requested/available, the kernel will ensure that there is at most one pending read and one pending write request per direct I/O file-handle at any time.

This feature is enabled by default when supported by the kernel.

Definition at line 328 of file fuse_common.h.

◆ FUSE_CAP_ASYNC_READ

#define FUSE_CAP_ASYNC_READ   (1UL << 0)

Indicates that the filesystem supports asynchronous read requests.

If this capability is not requested/available, the kernel will ensure that there is at most one pending read request per file-handle at any time, and will attempt to order read requests by increasing offset.

This feature is enabled by default when supported by the kernel.

Definition at line 177 of file fuse_common.h.

◆ FUSE_CAP_ATOMIC_O_TRUNC

#define FUSE_CAP_ATOMIC_O_TRUNC   (1UL << 3)

Indicates that the filesystem supports the O_TRUNC open flag. If disabled, and an application specifies O_TRUNC, fuse first calls truncate() and then open() with O_TRUNC filtered out.

This feature is enabled by default when supported by the kernel.

Definition at line 194 of file fuse_common.h.

◆ FUSE_CAP_AUTO_INVAL_DATA

#define FUSE_CAP_AUTO_INVAL_DATA   (1UL << 12)

Traditionally, while a file is open the FUSE kernel module only asks the filesystem for an update of the file's attributes when a client attempts to read beyond EOF. This is unsuitable for e.g. network filesystems, where the file contents may change without the kernel knowing about it.

If this flag is set, FUSE will check the validity of the attributes on every read. If the attributes are no longer valid (i.e., if the attr_timeout passed to fuse_reply_attr() or set in struct fuse_entry_param has passed), it will first issue a getattr request. If the new mtime differs from the previous value, any cached file contents will be invalidated as well.

This flag should always be set when available. If all file changes go through the kernel, attr_timeout should be set to a very large number to avoid unnecessary getattr() calls.

This feature is enabled by default when supported by the kernel.

Definition at line 281 of file fuse_common.h.

◆ FUSE_CAP_CACHE_SYMLINKS

#define FUSE_CAP_CACHE_SYMLINKS   (1UL << 23)

Indicates that the kernel supports caching symlinks in its page cache.

When this feature is enabled, symlink targets are saved in the page cache. You can invalidate a cached link by calling: fuse_lowlevel_notify_inval_inode(se, ino, 0, 0);

This feature is disabled by default. If the kernel supports it (>= 4.20), you can enable this feature by setting this flag in the want field of the fuse_conn_info structure.

Definition at line 418 of file fuse_common.h.

◆ FUSE_CAP_DIRECT_IO_ALLOW_MMAP

#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP   (1UL << 28)

Files opened with FUSE_DIRECT_IO do not support MAP_SHARED mmap. This restriction is relaxed through FUSE_CAP_DIRECT_IO_RELAX (kernel flag: FUSE_DIRECT_IO_RELAX). MAP_SHARED is disabled by default for FUSE_DIRECT_IO, as this flag can be used to ensure coherency between mount points (or network clients) and with kernel page cache as enforced by mmap that cannot be guaranteed anymore.

Definition at line 488 of file fuse_common.h.

◆ FUSE_CAP_DONT_MASK

#define FUSE_CAP_DONT_MASK   (1UL << 6)

Indicates that the kernel should not apply the umask to the file mode on create operations.

This feature is disabled by default.

Definition at line 214 of file fuse_common.h.

◆ FUSE_CAP_EXPIRE_ONLY

#define FUSE_CAP_EXPIRE_ONLY   (1UL << 26)

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.

Definition at line 472 of file fuse_common.h.

◆ FUSE_CAP_EXPLICIT_INVAL_DATA

#define FUSE_CAP_EXPLICIT_INVAL_DATA   (1UL << 25)

Indicates support for invalidating cached pages only on explicit request.

If this flag is set in the capable field of the fuse_conn_info structure, then the FUSE kernel module supports invalidating cached pages only on explicit request by the filesystem through fuse_lowlevel_notify_inval_inode() or fuse_invalidate_path().

By setting this flag in the want field of the fuse_conn_info structure, the filesystem is responsible for invalidating cached pages through explicit requests to the kernel.

Note that setting this flag does not prevent the cached pages from being flushed by OS itself and/or through user actions.

Note that if both FUSE_CAP_EXPLICIT_INVAL_DATA and FUSE_CAP_AUTO_INVAL_DATA are set in the capable field of the fuse_conn_info structure then FUSE_CAP_AUTO_INVAL_DATA takes precedence.

This feature is disabled by default.

Definition at line 456 of file fuse_common.h.

◆ FUSE_CAP_EXPORT_SUPPORT

#define FUSE_CAP_EXPORT_SUPPORT   (1UL << 4)

Indicates that the filesystem supports lookups of "." and "..".

When this flag is set, the filesystem must be prepared to receive requests for invalid inodes (i.e., for which a FORGET request was received or which have been used in a previous instance of the filesystem daemon) and must not reuse node-ids (even when setting generation numbers).

This feature is disabled by default.

Definition at line 206 of file fuse_common.h.

◆ FUSE_CAP_FLOCK_LOCKS

#define FUSE_CAP_FLOCK_LOCKS   (1UL << 10)

If set, the calls to flock(2) will be emulated using POSIX locks and must then be handled by the filesystem's setlock() handler.

If not set, flock(2) calls will be handled by the FUSE kernel module internally (so any access that does not go through the kernel cannot be taken into account).

This feature is enabled by default when supported by the kernel and if the filesystem implements a flock() handler.

Definition at line 252 of file fuse_common.h.

◆ FUSE_CAP_HANDLE_KILLPRIV

#define FUSE_CAP_HANDLE_KILLPRIV   (1UL << 20)

Indicates that the filesystem is responsible for unsetting setuid and setgid bits when a file is written, truncated, or its owner is changed.

This feature is disabled by default.

Definition at line 388 of file fuse_common.h.

◆ FUSE_CAP_HANDLE_KILLPRIV_V2

#define FUSE_CAP_HANDLE_KILLPRIV_V2   (1UL << 21)

Indicates that the filesystem is responsible for unsetting setuid and setgid bit and additionally cap (stored as xattr) when a file is written, truncated, or its owner is changed. 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). KILLPRIV_V2 requires handling of

  • FUSE_OPEN_KILL_SUIDGID (set in struct fuse_create_in::open_flags)
  • FATTR_KILL_SUIDGID (set in struct fuse_setattr_in::valid)
  • FUSE_WRITE_KILL_SUIDGID (set in struct fuse_write_in::write_flags)

This feature is disabled by default.

Definition at line 405 of file fuse_common.h.

◆ FUSE_CAP_IOCTL_DIR

#define FUSE_CAP_IOCTL_DIR   (1UL << 11)

Indicates that the filesystem supports ioctl's on directories.

This feature is enabled by default when supported by the kernel.

Definition at line 259 of file fuse_common.h.

◆ FUSE_CAP_NO_EXPORT_SUPPORT

#define FUSE_CAP_NO_EXPORT_SUPPORT   (1UL << 30)

Indicates that the file system cannot handle NFS export

If this flag is set NFS export and name_to_handle_at is not going to work at all and will fail with EOPNOTSUPP.

Definition at line 508 of file fuse_common.h.

◆ FUSE_CAP_NO_OPEN_SUPPORT

#define FUSE_CAP_NO_OPEN_SUPPORT   (1UL << 17)

Indicates support for zero-message opens. If this flag is set in the capable field of the fuse_conn_info structure, then the filesystem may return ENOSYS from the open() handler to indicate success. Further attempts to open files will be handled in the kernel. (If this flag is not set, returning ENOSYS will be treated as an error and signaled to the caller).

Setting this flag in the want field enables this behavior automatically within libfuse for low level API users. If non-low level users wish to have this behavior you must return ENOSYS from the open() handler on supporting kernels.

Definition at line 352 of file fuse_common.h.

◆ FUSE_CAP_NO_OPENDIR_SUPPORT

#define FUSE_CAP_NO_OPENDIR_SUPPORT   (1UL << 24)

Indicates support for zero-message opendirs. If this flag is set in the capable field of the fuse_conn_info structure, then the filesystem may return ENOSYS from the opendir() handler to indicate success. Further opendir and releasedir messages will be handled in the kernel. (If this flag is not set, returning ENOSYS will be treated as an error and signalled to the caller.)

Setting this flag in the want field enables this behavior automatically within libfuse for low level API users. If non-low level users with to have this behavior you must return ENOSYS from the opendir() handler on supporting kernels.

Definition at line 433 of file fuse_common.h.

◆ FUSE_CAP_OVER_IO_URING

#define FUSE_CAP_OVER_IO_URING   (1UL << 31)

Indicates support for io-uring between fuse-server and fuse-client

Definition at line 513 of file fuse_common.h.

◆ FUSE_CAP_PARALLEL_DIROPS

#define FUSE_CAP_PARALLEL_DIROPS   (1UL << 18)

Indicates support for parallel directory operations. If this flag is unset, the FUSE kernel module will ensure that lookup() and readdir() requests are never issued concurrently for the same directory.

Definition at line 360 of file fuse_common.h.

◆ FUSE_CAP_PASSTHROUGH

#define FUSE_CAP_PASSTHROUGH   (1UL << 29)

Indicates support for passthrough mode access for read/write operations.

If this flag is set in the capable field of the fuse_conn_info structure, then the FUSE kernel module supports redirecting read/write operations to the backing file instead of letting them to be handled by the FUSE daemon.

This feature is disabled by default.

Definition at line 500 of file fuse_common.h.

◆ FUSE_CAP_POSIX_ACL

#define FUSE_CAP_POSIX_ACL   (1UL << 19)

Indicates support for POSIX ACLs.

If this feature is enabled, the kernel will cache and have responsibility for enforcing ACLs. ACL will be stored as xattrs and passed to userspace, which is responsible for updating the ACLs in the filesystem, keeping the file mode in sync with the ACL, and ensuring inheritance of default ACLs when new filesystem nodes are created. Note that this requires that the file system is able to parse and interpret the xattr representation of ACLs.

Enabling this feature implicitly turns on the default_permissions mount option (even if it was not passed to mount(2)).

This feature is disabled by default.

Definition at line 379 of file fuse_common.h.

◆ FUSE_CAP_POSIX_LOCKS

#define FUSE_CAP_POSIX_LOCKS   (1UL << 1)

Indicates that the filesystem supports "remote" locking.

This feature is enabled by default when supported by the kernel, and if getlk() and setlk() handlers are implemented.

Definition at line 185 of file fuse_common.h.

◆ FUSE_CAP_READDIRPLUS

#define FUSE_CAP_READDIRPLUS   (1UL << 13)

Indicates that the filesystem supports readdirplus.

This feature is enabled by default when supported by the kernel and if the filesystem implements a readdirplus() handler.

Definition at line 289 of file fuse_common.h.

◆ FUSE_CAP_READDIRPLUS_AUTO

#define FUSE_CAP_READDIRPLUS_AUTO   (1UL << 14)

Indicates that the filesystem supports adaptive readdirplus.

If FUSE_CAP_READDIRPLUS is not set, this flag has no effect.

If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel will always issue readdirplus() requests to retrieve directory contents.

If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel will issue both readdir() and readdirplus() requests, depending on how much information is expected to be required.

As of Linux 4.20, the algorithm is as follows: when userspace starts to read directory entries, issue a READDIRPLUS request to the filesystem. If any entry attributes have been looked up by the time userspace requests the next batch of entries continue with READDIRPLUS, otherwise switch to plain READDIR. This will reasult in eg plain "ls" triggering READDIRPLUS first then READDIR after that because it doesn't do lookups. "ls -l" should result in all READDIRPLUS, except if dentries are already cached.

This feature is enabled by default when supported by the kernel and if the filesystem implements both a readdirplus() and a readdir() handler.

Definition at line 317 of file fuse_common.h.

◆ FUSE_CAP_SETXATTR_EXT

#define FUSE_CAP_SETXATTR_EXT   (1UL << 27)

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.

Definition at line 479 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_MOVE

#define FUSE_CAP_SPLICE_MOVE   (1UL << 8)

Indicates that libfuse should try to move pages instead of copying when writing to / reading from the fuse device. This may improve performance.

This feature is disabled by default.

Definition at line 230 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_READ

#define FUSE_CAP_SPLICE_READ   (1UL << 9)

Indicates that libfuse should try to use splice() when reading from the fuse device. This may improve performance.

This feature is enabled by default when supported by the kernel and if the filesystem implements a write_buf() handler.

Definition at line 239 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_WRITE

#define FUSE_CAP_SPLICE_WRITE   (1UL << 7)

Indicates that libfuse should try to use splice() when writing to the fuse device. This may improve performance.

This feature is disabled by default.

Definition at line 222 of file fuse_common.h.

◆ FUSE_CAP_WRITEBACK_CACHE

#define FUSE_CAP_WRITEBACK_CACHE   (1UL << 16)

Indicates that writeback caching should be enabled. This means that individual write request may be buffered and merged in the kernel before they are send to the filesystem.

This feature is disabled by default.

Definition at line 337 of file fuse_common.h.

◆ FUSE_IOCTL_COMPAT

#define FUSE_IOCTL_COMPAT   (1 << 0)

Ioctl flags

FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed FUSE_IOCTL_RETRY: retry with new iovecs FUSE_IOCTL_DIR: is a directory

FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs

Definition at line 525 of file fuse_common.h.

Enumeration Type Documentation

◆ fuse_buf_copy_flags

Buffer copy flags

Enumerator
FUSE_BUF_NO_SPLICE 

Don't use splice(2)

Always fall back to using read and write instead of splice(2) to copy data from one file descriptor to another.

If this flag is not set, then only fall back if splice is unavailable.

FUSE_BUF_FORCE_SPLICE 

Force splice

Always use splice(2) to copy data from one file descriptor to another. If splice is not available, return -EINVAL.

FUSE_BUF_SPLICE_MOVE 

Try to move data with splice.

If splice is used, try to move pages from the source to the destination instead of copying. See documentation of SPLICE_F_MOVE in splice(2) man page.

FUSE_BUF_SPLICE_NONBLOCK 

Don't block on the pipe when copying data with splice

Makes the operations on the pipe non-blocking (if the pipe is full or empty). See SPLICE_F_NONBLOCK in the splice(2) man page.

Definition at line 840 of file fuse_common.h.

◆ fuse_buf_flags

Buffer flags

Enumerator
FUSE_BUF_IS_FD 

Buffer contains a file descriptor

If this flag is set, the .fd field is valid, otherwise the .mem fields is valid.

FUSE_BUF_FD_SEEK 

Seek on the file descriptor

If this flag is set then the .pos field is valid and is used to seek to the given offset before performing operation on file descriptor.

FUSE_BUF_FD_RETRY 

Retry operation on file descriptor

If this flag is set then retry operation on file descriptor until .size bytes have been copied or an error or EOF is detected.

Definition at line 809 of file fuse_common.h.

Function Documentation

◆ fuse_apply_conn_info_opts()

void fuse_apply_conn_info_opts ( struct fuse_conn_info_opts *  opts,
struct fuse_conn_info conn 
)

This function applies the (parsed) parameters in opts to the conn pointer. It may modify the following fields: wants, max_write, max_readahead, congestion_threshold, max_background, time_gran. A field is only set (or unset) if the corresponding option has been explicitly set.

Definition at line 412 of file helper.c.

◆ fuse_buf_copy()

ssize_t fuse_buf_copy ( struct fuse_bufvec dst,
struct fuse_bufvec src,
enum fuse_buf_copy_flags  flags 
)

Copy data from one buffer vector to another

Parameters
dstdestination buffer vector
srcsource buffer vector
flagsflags controlling the copy
Returns
actual number of bytes copied or -errno on error

Definition at line 284 of file buffer.c.

◆ fuse_buf_size()

size_t fuse_buf_size ( const struct fuse_bufvec bufv)

Get total size of data in a fuse buffer vector

Parameters
bufvbuffer vector
Returns
size of data

Definition at line 22 of file buffer.c.

◆ fuse_convert_to_conn_want_ext()

int fuse_convert_to_conn_want_ext ( struct fuse_conn_info conn)

Get the wanted capability flags, converting from old format if necessary

Definition at line 2000 of file fuse_lowlevel.c.

◆ fuse_daemonize()

int fuse_daemonize ( int  foreground)

Go into the background

Parameters
foregroundif true, stay in the foreground
Returns
0 on success, -1 on failure

Definition at line 253 of file helper.c.

◆ fuse_get_feature_flag()

bool fuse_get_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Get the value of a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be checked
Returns
true if the flag is set, false otherwise

Definition at line 2061 of file fuse_lowlevel.c.

◆ fuse_parse_conn_info_opts()

struct fuse_conn_info_opts * fuse_parse_conn_info_opts ( struct fuse_args args)

This function parses several command-line options that can be used to override elements of struct fuse_conn_info. The pointer returned by this function should be passed to the fuse_apply_conn_info_opts() method by the file system's init() handler.

Before using this function, think twice if you really want these parameters to be adjustable from the command line. In most cases, they should be determined by the file system internally.

The following options are recognized:

-o max_write=N sets conn->max_write -o max_readahead=N sets conn->max_readahead -o max_background=N sets conn->max_background -o congestion_threshold=N sets conn->congestion_threshold -o async_read sets FUSE_CAP_ASYNC_READ in conn->want -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets FUSE_CAP_READDIRPLUS_AUTO in conn->want -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and FUSE_CAP_READDIRPLUS_AUTO in conn->want -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want -o time_gran=N sets conn->time_gran

Known options will be removed from args, unknown options will be passed through unchanged.

Parameters
argsargument vector (input+output)
Returns
parsed options

Definition at line 466 of file helper.c.

◆ fuse_pkgversion()

const char * fuse_pkgversion ( void  )

Get the full package version string of the library

Returns
the package version

Definition at line 5211 of file fuse.c.

◆ fuse_pollhandle_destroy()

void fuse_pollhandle_destroy ( struct fuse_pollhandle *  ph)

Destroy poll handle

Parameters
phthe poll handle

Definition at line 1903 of file fuse_lowlevel.c.

◆ fuse_remove_signal_handlers()

void fuse_remove_signal_handlers ( struct fuse_session *  se)

Restore default signal handlers

Resets global session. After this fuse_set_signal_handlers() may be called again.

Parameters
sethe same session as given in fuse_set_signal_handlers()

See also: fuse_set_signal_handlers()

Definition at line 187 of file fuse_signals.c.

◆ fuse_set_fail_signal_handlers()

int fuse_set_fail_signal_handlers ( struct fuse_session *  se)

Print a stack backtrace diagnostic on critical signals ()

Stores session in a global variable. May only be called once per process until fuse_remove_signal_handlers() is called.

Once either of the POSIX signals arrives, the signal handler calls fuse_session_exit().

Parameters
sethe session to exit
Returns
0 on success, -1 on failure

See also: fuse_remove_signal_handlers()

Definition at line 165 of file fuse_signals.c.

◆ fuse_set_feature_flag()

bool fuse_set_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Config operations. These APIs are introduced in version 312 (FUSE_MAKE_VERSION(3, 12)). Using them in earlier versions will result in errors. Set a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be set
Returns
true if the flag was set, false if the flag is not supported

Definition at line 2035 of file fuse_lowlevel.c.

◆ fuse_set_signal_handlers()

int fuse_set_signal_handlers ( struct fuse_session *  se)

Exit session on HUP, TERM and INT signals and ignore PIPE signal

Stores session in a global variable. May only be called once per process until fuse_remove_signal_handlers() is called.

Once either of the POSIX signals arrives, the signal handler calls fuse_session_exit().

Parameters
sethe session to exit
Returns
0 on success, -1 on failure

See also: fuse_remove_signal_handlers()

Definition at line 140 of file fuse_signals.c.

◆ fuse_unset_feature_flag()

void fuse_unset_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Unset a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be unset

Definition at line 2050 of file fuse_lowlevel.c.

◆ fuse_version()

int fuse_version ( void  )

Get the version of the library

Returns
the version

Definition at line 5206 of file fuse.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__log_8h.html0000644000175000017500000003426315156613443022440 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_log.h File Reference
libfuse
fuse_log.h File Reference
#include <stdarg.h>

Go to the source code of this file.

Typedefs

typedef void(* fuse_log_func_t) (enum fuse_log_level level, const char *fmt, va_list ap)
 

Enumerations

enum  fuse_log_level
 

Functions

void fuse_set_log_func (fuse_log_func_t func)
 
void fuse_log (enum fuse_log_level level, const char *fmt,...)
 
void fuse_log_enable_syslog (const char *ident, int option, int facility)
 
void fuse_log_close_syslog (void)
 

Detailed Description

This file defines the logging interface of FUSE

Definition in file fuse_log.h.

Typedef Documentation

◆ fuse_log_func_t

typedef void(* fuse_log_func_t) (enum fuse_log_level level, const char *fmt, va_list ap)

Log message handler function.

This function must be thread-safe. It may be called from any libfuse function, including fuse_parse_cmdline() and other functions invoked before a FUSE filesystem is created.

Install a custom log message handler function using fuse_set_log_func().

Parameters
levellog severity level
fmtsprintf-style format string including newline
apformat string arguments

Definition at line 52 of file fuse_log.h.

Enumeration Type Documentation

◆ fuse_log_level

Log severity level

These levels correspond to syslog(2) log levels since they are widely used.

Definition at line 28 of file fuse_log.h.

Function Documentation

◆ fuse_log()

void fuse_log ( enum fuse_log_level  level,
const char *  fmt,
  ... 
)

Emit a log message

Parameters
levelseverity level (FUSE_LOG_ERR, FUSE_LOG_DEBUG, etc)
fmtsprintf-style format string including newline

Definition at line 77 of file fuse_log.c.

◆ fuse_log_close_syslog()

void fuse_log_close_syslog ( void  )

To be called at teardown to close syslog.

Definition at line 93 of file fuse_log.c.

◆ fuse_log_enable_syslog()

void fuse_log_enable_syslog ( const char *  ident,
int  option,
int  facility 
)

Switch default log handler from stderr to syslog

Passed options are according to 'man 3 openlog'

Definition at line 86 of file fuse_log.c.

◆ fuse_set_log_func()

void fuse_set_log_func ( fuse_log_func_t  func)

Install a custom log handler function.

Log messages are emitted by libfuse functions to report errors and debug information. Messages are printed to stderr by default but this can be overridden by installing a custom log message handler function.

The log message handler function is global and affects all FUSE filesystems created within this process.

Parameters
funca custom log message handler function or NULL to revert to the default

Definition at line 69 of file fuse_log.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__log_8h.html0000644000175000017500000003433215156613443022433 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_log.h File Reference
libfuse
fuse_log.h File Reference
#include <stdarg.h>

Go to the source code of this file.

Typedefs

typedef void(* fuse_log_func_t) (enum fuse_log_level level, const char *fmt, va_list ap)
 

Enumerations

enum  fuse_log_level
 

Functions

void fuse_set_log_func (fuse_log_func_t func)
 
void fuse_log (enum fuse_log_level level, const char *fmt,...) __attribute__((format(printf
 
void void fuse_log_enable_syslog (const char *ident, int option, int facility)
 
void fuse_log_close_syslog (void)
 

Detailed Description

This file defines the logging interface of FUSE

Definition in file fuse_log.h.

Typedef Documentation

◆ fuse_log_func_t

typedef void(* fuse_log_func_t) (enum fuse_log_level level, const char *fmt, va_list ap)

Log message handler function.

This function must be thread-safe. It may be called from any libfuse function, including fuse_parse_cmdline() and other functions invoked before a FUSE filesystem is created.

Install a custom log message handler function using fuse_set_log_func().

Parameters
levellog severity level
fmtsprintf-style format string including newline
apformat string arguments

Definition at line 52 of file fuse_log.h.

Enumeration Type Documentation

◆ fuse_log_level

Log severity level

These levels correspond to syslog(2) log levels since they are widely used.

Definition at line 28 of file fuse_log.h.

Function Documentation

◆ fuse_log()

void fuse_log ( enum fuse_log_level  level,
const char *  fmt,
  ... 
)

Emit a log message

Parameters
levelseverity level (FUSE_LOG_ERR, FUSE_LOG_DEBUG, etc)
fmtsprintf-style format string including newline

Definition at line 76 of file fuse_log.c.

◆ fuse_log_close_syslog()

void fuse_log_close_syslog ( void  )

To be called at teardown to close syslog.

Definition at line 93 of file fuse_log.c.

◆ fuse_log_enable_syslog()

void void fuse_log_enable_syslog ( const char *  ident,
int  option,
int  facility 
)

Switch default log handler from stderr to syslog

Passed options are according to 'man 3 openlog'

Definition at line 86 of file fuse_log.c.

◆ fuse_set_log_func()

void fuse_set_log_func ( fuse_log_func_t  func)

Install a custom log handler function.

Log messages are emitted by libfuse functions to report errors and debug information. Messages are printed to stderr by default but this can be overridden by installing a custom log message handler function.

The log message handler function is global and affects all FUSE filesystems created within this process.

Parameters
funca custom log message handler function or NULL to revert to the default

Definition at line 69 of file fuse_log.c.

fuse-3.18.2/doc/html/include_2fuse__log_8h.html0000644000175000017500000003366315156613443020265 0ustar berndbernd libfuse: include/fuse_log.h File Reference
libfuse
fuse_log.h File Reference
#include <stdarg.h>

Go to the source code of this file.

Typedefs

typedef void(* fuse_log_func_t) (enum fuse_log_level level, const char *fmt, va_list ap)
 

Enumerations

enum  fuse_log_level
 

Functions

void fuse_set_log_func (fuse_log_func_t func)
 
void fuse_log (enum fuse_log_level level, const char *fmt,...) __attribute__((format(printf
 
void void fuse_log_enable_syslog (const char *ident, int option, int facility)
 
void fuse_log_close_syslog (void)
 

Detailed Description

This file defines the logging interface of FUSE

Definition in file fuse_log.h.

Typedef Documentation

◆ fuse_log_func_t

typedef void(* fuse_log_func_t) (enum fuse_log_level level, const char *fmt, va_list ap)

Log message handler function.

This function must be thread-safe. It may be called from any libfuse function, including fuse_parse_cmdline() and other functions invoked before a FUSE filesystem is created.

Install a custom log message handler function using fuse_set_log_func().

Parameters
levellog severity level
fmtsprintf-style format string including newline
apformat string arguments

Definition at line 52 of file fuse_log.h.

Enumeration Type Documentation

◆ fuse_log_level

Log severity level

These levels correspond to syslog(2) log levels since they are widely used.

Definition at line 28 of file fuse_log.h.

Function Documentation

◆ fuse_log()

void fuse_log ( enum fuse_log_level  level,
const char *  fmt,
  ... 
)

Emit a log message

Parameters
levelseverity level (FUSE_LOG_ERR, FUSE_LOG_DEBUG, etc)
fmtsprintf-style format string including newline

Definition at line 76 of file fuse_log.c.

◆ fuse_log_close_syslog()

void fuse_log_close_syslog ( void  )

To be called at teardown to close syslog.

Definition at line 93 of file fuse_log.c.

◆ fuse_log_enable_syslog()

void void fuse_log_enable_syslog ( const char *  ident,
int  option,
int  facility 
)

Switch default log handler from stderr to syslog

Passed options are according to 'man 3 openlog'

Definition at line 86 of file fuse_log.c.

◆ fuse_set_log_func()

void fuse_set_log_func ( fuse_log_func_t  func)

Install a custom log handler function.

Log messages are emitted by libfuse functions to report errors and debug information. Messages are printed to stderr by default but this can be overridden by installing a custom log message handler function.

The log message handler function is global and affects all FUSE filesystems created within this process.

Parameters
funca custom log message handler function or NULL to revert to the default

Definition at line 69 of file fuse_log.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__lowlevel_8h.html0000644000175000017500000041220515156613443023504 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_lowlevel.h File Reference
libfuse
fuse_lowlevel.h File Reference
#include "fuse_common.h"
#include <stddef.h>
#include <utime.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>

Go to the source code of this file.

Data Structures

struct  fuse_entry_param
 
struct  fuse_ctx
 
struct  fuse_lowlevel_ops
 
struct  fuse_cmdline_opts
 

Macros

#define FUSE_ROOT_ID   1
 

Typedefs

typedef uint64_t fuse_ino_t
 
typedef struct fuse_req * fuse_req_t
 
typedef void(* fuse_interrupt_func_t) (fuse_req_t req, void *data)
 

Enumerations

enum  fuse_notify_entry_flags
 

Functions

int fuse_reply_err (fuse_req_t req, int err)
 
void fuse_reply_none (fuse_req_t req)
 
int fuse_reply_entry (fuse_req_t req, const struct fuse_entry_param *e)
 
int fuse_reply_create (fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
 
int fuse_reply_attr (fuse_req_t req, const struct stat *attr, double attr_timeout)
 
int fuse_reply_readlink (fuse_req_t req, const char *link)
 
int fuse_passthrough_open (fuse_req_t req, int fd)
 
int fuse_reply_open (fuse_req_t req, const struct fuse_file_info *fi)
 
int fuse_reply_write (fuse_req_t req, size_t count)
 
int fuse_reply_buf (fuse_req_t req, const char *buf, size_t size)
 
int fuse_reply_data (fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
 
int fuse_reply_iov (fuse_req_t req, const struct iovec *iov, int count)
 
int fuse_reply_statfs (fuse_req_t req, const struct statvfs *stbuf)
 
int fuse_reply_xattr (fuse_req_t req, size_t count)
 
int fuse_reply_lock (fuse_req_t req, const struct flock *lock)
 
int fuse_reply_bmap (fuse_req_t req, uint64_t idx)
 
size_t fuse_add_direntry (fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
 
size_t fuse_add_direntry_plus (fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
 
int fuse_reply_ioctl_retry (fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
 
int fuse_reply_ioctl (fuse_req_t req, int result, const void *buf, size_t size)
 
int fuse_reply_ioctl_iov (fuse_req_t req, int result, const struct iovec *iov, int count)
 
int fuse_reply_poll (fuse_req_t req, unsigned revents)
 
int fuse_reply_lseek (fuse_req_t req, off_t off)
 
int fuse_lowlevel_notify_poll (struct fuse_pollhandle *ph)
 
int fuse_lowlevel_notify_inval_inode (struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
 
int fuse_lowlevel_notify_inval_entry (struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_expire_entry (struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_delete (struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_store (struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
 
int fuse_lowlevel_notify_retrieve (struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
 
void * fuse_req_userdata (fuse_req_t req)
 
const struct fuse_ctxfuse_req_ctx (fuse_req_t req)
 
int fuse_req_getgroups (fuse_req_t req, int size, gid_t list[])
 
void fuse_req_interrupt_func (fuse_req_t req, fuse_interrupt_func_t func, void *data)
 
int fuse_req_interrupted (fuse_req_t req)
 
void fuse_lowlevel_version (void)
 
void fuse_lowlevel_help (void)
 
void fuse_cmdline_help (void)
 
int fuse_parse_cmdline_30 (struct fuse_args *args, struct fuse_cmdline_opts *opts)
 
int fuse_session_mount (struct fuse_session *se, const char *mountpoint)
 
int fuse_session_loop (struct fuse_session *se)
 
void fuse_session_exit (struct fuse_session *se)
 
void fuse_session_reset (struct fuse_session *se)
 
int fuse_session_exited (struct fuse_session *se)
 
void fuse_session_unmount (struct fuse_session *se)
 
void fuse_session_destroy (struct fuse_session *se)
 
int fuse_session_fd (struct fuse_session *se)
 
void fuse_session_process_buf (struct fuse_session *se, const struct fuse_buf *buf)
 
int fuse_session_receive_buf (struct fuse_session *se, struct fuse_buf *buf)
 

Detailed Description

Low level API

IMPORTANT: you should define FUSE_USE_VERSION before including this header. To use the newest API define it to 35 (recommended for any new application).

Definition in file fuse_lowlevel.h.

Macro Definition Documentation

◆ FUSE_ROOT_ID

#define FUSE_ROOT_ID   1

The node ID of the root inode

Definition at line 44 of file fuse_lowlevel.h.

Typedef Documentation

◆ fuse_ino_t

typedef uint64_t fuse_ino_t

Inode number type

Definition at line 47 of file fuse_lowlevel.h.

◆ fuse_interrupt_func_t

typedef void(* fuse_interrupt_func_t) (fuse_req_t req, void *data)

Callback function for an interrupt

Parameters
reqinterrupted request
datauser data

Definition at line 1948 of file fuse_lowlevel.h.

◆ fuse_req_t

typedef struct fuse_req* fuse_req_t

Request pointer type

Definition at line 50 of file fuse_lowlevel.h.

Enumeration Type Documentation

◆ fuse_notify_entry_flags

Flags for fuse_lowlevel_notify_entry() 0 = invalidate entry FUSE_LL_EXPIRE_ONLY = expire entry

Definition at line 148 of file fuse_lowlevel.h.

Function Documentation

◆ fuse_add_direntry()

size_t fuse_add_direntry ( fuse_req_t  req,
char *  buf,
size_t  bufsize,
const char *  name,
const struct stat *  stbuf,
off_t  off 
)

Add a directory entry to the buffer

Buffer needs to be large enough to hold the entry. If it's not, then the entry is not filled in but the size of the entry is still returned. The caller can check this by comparing the bufsize parameter with the returned entry size. If the entry size is larger than the buffer size, the operation failed.

From the 'stbuf' argument the st_ino field and bits 12-15 of the st_mode field are used. The other fields are ignored.

off should be any non-zero value that the filesystem can use to identify the current point in the directory stream. It does not need to be the actual physical position. A value of zero is reserved to mean "from the beginning", and should therefore never be used (the first call to fuse_add_direntry should be passed the offset of the second directory entry).

Parameters
reqrequest handle
bufthe point where the new entry will be added to the buffer
bufsizeremaining size of the buffer
namethe name of the entry
stbufthe file attributes
offthe offset of the next entry
Returns
the space needed for the entry

Definition at line 286 of file fuse_lowlevel.c.

◆ fuse_add_direntry_plus()

size_t fuse_add_direntry_plus ( fuse_req_t  req,
char *  buf,
size_t  bufsize,
const char *  name,
const struct fuse_entry_param e,
off_t  off 
)

Add a directory entry to the buffer with the attributes

See documentation of fuse_add_direntry() for more details.

Parameters
reqrequest handle
bufthe point where the new entry will be added to the buffer
bufsizeremaining size of the buffer
namethe name of the entry
ethe directory entry
offthe offset of the next entry
Returns
the space needed for the entry

Definition at line 376 of file fuse_lowlevel.c.

◆ fuse_cmdline_help()

void fuse_cmdline_help ( void  )

Print available options for fuse_parse_cmdline().

Definition at line 130 of file helper.c.

◆ fuse_lowlevel_help()

void fuse_lowlevel_help ( void  )

Print available low-level options to stdout. This is not an exhaustive list, but includes only those options that may be of interest to an end-user of a file system.

Definition at line 3000 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_delete()

int fuse_lowlevel_notify_delete ( struct fuse_session *  se,
fuse_ino_t  parent,
fuse_ino_t  child,
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):

If the provided child inode matches the inode that is currently associated with the cached dentry, and if there are any inotify watches registered for the dentry, then the watchers are informed that the dentry has been deleted.

To avoid a deadlock this function must not be called while 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).

When called correctly, this function will never block.

Added in FUSE protocol version 7.18. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
parentinode number
childinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure

Definition at line 2562 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_expire_entry()

int fuse_lowlevel_notify_expire_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.

Parameters
sethe session object
parentinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure, -enosys if no kernel support

Definition at line 2549 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_inval_entry()

int fuse_lowlevel_notify_inval_entry ( struct fuse_session *  se,
fuse_ino_t  parent,
const char *  name,
size_t  namelen 
)

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 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() request for the parent, and a setattr(), unlink(), rmdir(), rename(), setxattr(), removexattr(), readdir() or readdirplus() request for the inode itself.

When called correctly, this function will never block.

Added in FUSE protocol version 7.12. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
parentinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure

Definition at line 2543 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_inval_inode()

int fuse_lowlevel_notify_inval_inode ( struct fuse_session *  se,
fuse_ino_t  ino,
off_t  off,
off_t  len 
)

Notify to invalidate cache for an inode.

Added in FUSE protocol version 7.12. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

If the filesystem has writeback caching enabled, invalidating an inode will first trigger a writeback of all dirty pages. The call will block until all writeback requests have completed and the inode has been invalidated. It will, however, not wait for completion of pending writeback requests that have been issued before.

If there are no dirty pages, this function will never block.

Parameters
sethe session object
inothe inode number
offthe offset in the inode where to start invalidating or negative to invalidate attributes only
lenthe amount of cache to invalidate or 0 for all
Returns
zero for success, -errno for failure

Definition at line 2475 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_poll()

int fuse_lowlevel_notify_poll ( struct fuse_pollhandle *  ph)

Notify IO readiness event

For more information, please read comment for poll operation.

Parameters
phpoll handle to notify IO readiness event for

Definition at line 2458 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_retrieve()

int fuse_lowlevel_notify_retrieve ( struct fuse_session *  se,
fuse_ino_t  ino,
size_t  size,
off_t  offset,
void *  cookie 
)

Retrieve data from the kernel buffers

Retrieve data in the kernel buffers belonging to the given inode. If successful then the retrieve_reply() method will be called with the returned data.

Only present pages are returned in the retrieve reply. Retrieving stops when it finds a non-present page and only data prior to that is returned.

If this function returns an error, then the retrieve will not be completed and no reply will be sent.

This function doesn't change the dirty state of pages in the kernel buffer. For dirty pages the write() method will be called regardless of having been retrieved previously.

Added in FUSE protocol version 7.15. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
inothe inode number
sizethe number of bytes to retrieve
offsetthe starting offset into the file to retrieve from
cookieuser data to supply to the reply callback
Returns
zero for success, -errno for failure

Definition at line 2668 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_store()

int fuse_lowlevel_notify_store ( struct fuse_session *  se,
fuse_ino_t  ino,
off_t  offset,
struct fuse_bufvec bufv,
enum fuse_buf_copy_flags  flags 
)

Store data to the kernel buffers

Synchronously store data in the kernel buffers belonging to the given inode. The stored data is marked up-to-date (no read will be performed against it, unless it's invalidated or evicted from the cache).

If the stored data overflows the current file size, then the size is extended, similarly to a write(2) on the filesystem.

If this function returns an error, then the store wasn't fully completed, but it may have been partially completed.

Added in FUSE protocol version 7.15. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
inothe inode number
offsetthe starting offset into the file to store to
bufvbuffer vector
flagsflags controlling the copy
Returns
zero for success, -errno for failure

Definition at line 2588 of file fuse_lowlevel.c.

◆ fuse_lowlevel_version()

void fuse_lowlevel_version ( void  )

Print low-level version information to stdout.

Definition at line 2993 of file fuse_lowlevel.c.

◆ fuse_parse_cmdline_30()

int fuse_parse_cmdline_30 ( struct fuse_args args,
struct fuse_cmdline_opts opts 
)

Utility function to parse common options for simple file systems using the low-level API. A help text that describes the available options can be printed with fuse_cmdline_help. A single non-option argument is treated as the mountpoint. Multiple non-option arguments will result in an error.

If neither -o subtype= or -o fsname= options are given, a new subtype option will be added and set to the basename of the program (the fsname will remain unset, and then defaults to "fuse").

Known options will be removed from args, unknown options will remain.

Parameters
argsargument vector (input+output)
optsoutput argument for parsed options
Returns
0 on success, -1 on failure

struct fuse_cmdline_opts got extended in libfuse-3.12

Definition at line 237 of file helper.c.

◆ fuse_passthrough_open()

int fuse_passthrough_open ( fuse_req_t  req,
int  fd 
)

Setup passthrough backing file for open reply

Currently there should be only one backing id per node / backing file.

Possible requests: open, opendir, create

Parameters
reqrequest handle
fdbacking file descriptor
Returns
positive backing id for success, 0 for failure

Definition at line 480 of file fuse_lowlevel.c.

◆ fuse_reply_attr()

int fuse_reply_attr ( fuse_req_t  req,
const struct stat *  attr,
double  attr_timeout 
)

Reply with attributes

Possible requests: getattr, setattr

Parameters
reqrequest handle
attrthe attributes
attr_timeoutvalidity timeout (in seconds) for the attributes
Returns
zero for success, -errno for failure to send reply

Definition at line 460 of file fuse_lowlevel.c.

◆ fuse_reply_bmap()

int fuse_reply_bmap ( fuse_req_t  req,
uint64_t  idx 
)

Reply with block index

Possible requests: bmap

Parameters
reqrequest handle
idxblock index within device
Returns
zero for success, -errno for failure to send reply

Definition at line 973 of file fuse_lowlevel.c.

◆ fuse_reply_buf()

int fuse_reply_buf ( fuse_req_t  req,
const char *  buf,
size_t  size 
)

Reply with data

Possible requests: read, readdir, getxattr, listxattr

Parameters
reqrequest handle
bufbuffer containing data
sizethe size of data in bytes
Returns
zero for success, -errno for failure to send reply

Definition at line 524 of file fuse_lowlevel.c.

◆ fuse_reply_create()

int fuse_reply_create ( fuse_req_t  req,
const struct fuse_entry_param e,
const struct fuse_file_info fi 
)

Reply with a directory entry and open parameters

currently the following members of 'fi' are used: fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, parallel_direct_writes

Possible requests: create

Side effects: increments the lookup count on success

Parameters
reqrequest handle
ethe entry parameters
fifile information
Returns
zero for success, -errno for failure to send reply

Definition at line 444 of file fuse_lowlevel.c.

◆ fuse_reply_data()

int fuse_reply_data ( fuse_req_t  req,
struct fuse_bufvec bufv,
enum fuse_buf_copy_flags  flags 
)

Reply with data copied/moved from buffer(s)

Zero copy data transfer ("splicing") will be used under the following circumstances:

  1. FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and
  2. the kernel supports splicing from the fuse device (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and
  3. flags does not contain FUSE_BUF_NO_SPLICE
  4. The amount of data that is provided in file-descriptor backed buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) is at least twice the page size.

In order for SPLICE_F_MOVE to be used, the following additional conditions have to be fulfilled:

  1. FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and
  2. the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.capable), and
  3. flags contains FUSE_BUF_SPLICE_MOVE

Note that, if splice is used, the data is actually spliced twice: once into a temporary pipe (to prepend header data), and then again into the kernel. If some of the provided buffers are memory-backed, the data in them is copied in step one and spliced in step two.

The FUSE_BUF_SPLICE_FORCE_SPLICE and FUSE_BUF_SPLICE_NONBLOCK flags are silently ignored.

Possible requests: read, readdir, getxattr, listxattr

Side effects: when used to return data from a readdirplus() (but not readdir()) call, increments the lookup count of each returned entry by one on success.

Parameters
reqrequest handle
bufvbuffer vector
flagsflags controlling the copy
Returns
zero for success, -errno for failure to send reply

Definition at line 912 of file fuse_lowlevel.c.

◆ fuse_reply_entry()

int fuse_reply_entry ( fuse_req_t  req,
const struct fuse_entry_param e 
)

Reply with a directory entry

Possible requests: lookup, mknod, mkdir, symlink, link

Side effects: increments the lookup count on success

Parameters
reqrequest handle
ethe entry parameters
Returns
zero for success, -errno for failure to send reply

Definition at line 428 of file fuse_lowlevel.c.

◆ fuse_reply_err()

int fuse_reply_err ( fuse_req_t  req,
int  err 
)

Reply with an error code or success.

Possible requests: all except forget, forget_multi, retrieve_reply

Wherever possible, error codes should be chosen from the list of documented error conditions in the corresponding system calls manpage.

An error code of ENOSYS is sometimes treated specially. This is indicated in the documentation of the affected handler functions.

The following requests may be answered with a zero error code: unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, removexattr, setlk.

Parameters
reqrequest handle
errthe positive error value, or zero for success
Returns
zero for success, -errno for failure to send reply

Definition at line 331 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl()

int fuse_reply_ioctl ( fuse_req_t  req,
int  result,
const void *  buf,
size_t  size 
)

Reply to finish ioctl

Possible requests: ioctl

Parameters
reqrequest handle
resultresult to be passed to the caller
bufbuffer containing output data
sizelength of output data

Definition at line 1071 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl_iov()

int fuse_reply_ioctl_iov ( fuse_req_t  req,
int  result,
const struct iovec *  iov,
int  count 
)

Reply to finish ioctl with iov buffer

Possible requests: ioctl

Parameters
reqrequest handle
resultresult to be passed to the caller
iovthe vector containing the data
countthe size of vector

Definition at line 1092 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl_retry()

int fuse_reply_ioctl_retry ( fuse_req_t  req,
const struct iovec *  in_iov,
size_t  in_count,
const struct iovec *  out_iov,
size_t  out_count 
)

Reply to ask for data fetch and output buffer preparation. ioctl will be retried with the specified input data fetched and output buffer prepared.

Possible requests: ioctl

Parameters
reqrequest handle
in_ioviovec specifying data to fetch from the caller
in_countnumber of entries in in_iov
out_ioviovec specifying addresses to write output to
out_countnumber of entries in out_iov
Returns
zero for success, -errno for failure to send reply

Definition at line 1001 of file fuse_lowlevel.c.

◆ fuse_reply_iov()

int fuse_reply_iov ( fuse_req_t  req,
const struct iovec *  iov,
int  count 
)

Reply with data vector

Possible requests: read, readdir, getxattr, listxattr

Parameters
reqrequest handle
iovthe vector containing the data
countthe size of vector
Returns
zero for success, -errno for failure to send reply

Definition at line 265 of file fuse_lowlevel.c.

◆ fuse_reply_lock()

int fuse_reply_lock ( fuse_req_t  req,
const struct flock *  lock 
)

Reply with file lock information

Possible requests: getlk

Parameters
reqrequest handle
lockthe lock information
Returns
zero for success, -errno for failure to send reply

Definition at line 956 of file fuse_lowlevel.c.

◆ fuse_reply_lseek()

int fuse_reply_lseek ( fuse_req_t  req,
off_t  off 
)

Reply with offset

Possible requests: lseek

Parameters
reqrequest handle
offoffset of next data or hole
Returns
zero for success, -errno for failure to send reply

Definition at line 1126 of file fuse_lowlevel.c.

◆ fuse_reply_none()

void fuse_reply_none ( fuse_req_t  req)

Don't send reply

Possible requests: forget forget_multi retrieve_reply

Parameters
reqrequest handle

Definition at line 336 of file fuse_lowlevel.c.

◆ fuse_reply_open()

int fuse_reply_open ( fuse_req_t  req,
const struct fuse_file_info fi 
)

Reply with open parameters

currently the following members of 'fi' are used: fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, parallel_direct_writes,

Possible requests: open, opendir

Parameters
reqrequest handle
fifile information
Returns
zero for success, -errno for failure to send reply

Definition at line 505 of file fuse_lowlevel.c.

◆ fuse_reply_poll()

int fuse_reply_poll ( fuse_req_t  req,
unsigned  revents 
)

Reply with poll result event mask

Parameters
reqrequest handle
reventspoll result event mask

Definition at line 1116 of file fuse_lowlevel.c.

◆ fuse_reply_readlink()

int fuse_reply_readlink ( fuse_req_t  req,
const char *  link 
)

Reply with the contents of a symbolic link

Possible requests: readlink

Parameters
reqrequest handle
linksymbolic link contents
Returns
zero for success, -errno for failure to send reply

Definition at line 475 of file fuse_lowlevel.c.

◆ fuse_reply_statfs()

int fuse_reply_statfs ( fuse_req_t  req,
const struct statvfs *  stbuf 
)

Reply with filesystem statistics

Possible requests: statfs

Parameters
reqrequest handle
stbuffilesystem statistics
Returns
zero for success, -errno for failure to send reply

Definition at line 934 of file fuse_lowlevel.c.

◆ fuse_reply_write()

int fuse_reply_write ( fuse_req_t  req,
size_t  count 
)

Reply with number of bytes written

Possible requests: write

Parameters
reqrequest handle
countthe number of bytes written
Returns
zero for success, -errno for failure to send reply

Definition at line 514 of file fuse_lowlevel.c.

◆ fuse_reply_xattr()

int fuse_reply_xattr ( fuse_req_t  req,
size_t  count 
)

Reply with needed buffer size

Possible requests: getxattr, listxattr

Parameters
reqrequest handle
countthe buffer size needed in bytes
Returns
zero for success, -errno for failure to send reply

Definition at line 946 of file fuse_lowlevel.c.

◆ fuse_req_ctx()

const struct fuse_ctx * fuse_req_ctx ( fuse_req_t  req)

Get the context from the request

The pointer returned by this function will only be valid for the request's lifetime

Parameters
reqrequest handle
Returns
the context structure

Definition at line 2718 of file fuse_lowlevel.c.

◆ fuse_req_getgroups()

int fuse_req_getgroups ( fuse_req_t  req,
int  size,
gid_t  list[] 
)

Get the current supplementary group IDs for the specified request

Similar to the getgroups(2) system call, except the return value is always the total number of group IDs, even if it is larger than the specified size.

The current fuse kernel module in linux (as of 2.6.30) doesn't pass the group list to userspace, hence this function needs to parse "/proc/$TID/task/$TID/status" to get the group IDs.

This feature may not be supported on all operating systems. In such a case this function will return -ENOSYS.

Parameters
reqrequest handle
sizesize of given array
listarray of group IDs to be filled in
Returns
the total number of supplementary group IDs or -errno on failure

Definition at line 3614 of file fuse_lowlevel.c.

◆ fuse_req_interrupt_func()

void fuse_req_interrupt_func ( fuse_req_t  req,
fuse_interrupt_func_t  func,
void *  data 
)

Register/unregister callback for an interrupt

If an interrupt has already happened, then the callback function is called from within this function, hence it's not possible for interrupts to be lost.

Parameters
reqrequest handle
functhe callback function or NULL for unregister
datauser data passed to the callback function

Definition at line 2723 of file fuse_lowlevel.c.

◆ fuse_req_interrupted()

int fuse_req_interrupted ( fuse_req_t  req)

Check if a request has already been interrupted

Parameters
reqrequest handle
Returns
1 if the request has been interrupted, 0 otherwise

Definition at line 2736 of file fuse_lowlevel.c.

◆ fuse_req_userdata()

void * fuse_req_userdata ( fuse_req_t  req)

Get the userdata from the request

Parameters
reqrequest handle
Returns
the user data passed to fuse_session_new()

Definition at line 2713 of file fuse_lowlevel.c.

◆ fuse_session_destroy()

void fuse_session_destroy ( struct fuse_session *  se)

Destroy a session

Parameters
sethe session

Definition at line 3010 of file fuse_lowlevel.c.

◆ fuse_session_exit()

void fuse_session_exit ( struct fuse_session *  se)

Flag a session as terminated.

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).

Parameters
sethe session

◆ fuse_session_exited()

int fuse_session_exited ( struct fuse_session *  se)

Query the terminated flag of a session

Parameters
sethe session
Returns
1 if exited, 0 if not exited

◆ fuse_session_fd()

int fuse_session_fd ( struct fuse_session *  se)

Return file descriptor for communication with kernel.

The file selector can be used to integrate FUSE with a custom event loop. Whenever data is available for reading on the provided fd, the event loop should call fuse_session_receive_buf followed by fuse_session_process_buf to process the request.

The returned file descriptor is valid until fuse_session_unmount is called.

Parameters
sethe session
Returns
a file descriptor

Definition at line 3534 of file fuse_lowlevel.c.

◆ fuse_session_loop()

int fuse_session_loop ( struct fuse_session *  se)

Enter a single threaded, blocking event loop.

When the event loop terminates because the connection to the FUSE kernel module has been closed, this function returns zero. This happens when the filesystem is unmounted regularly (by the filesystem owner or root running the umount(8) or fusermount(1) command), or if connection is explicitly severed by writing 1 to theabort file in /sys/fs/fuse/connections/NNN. The only way to distinguish between these two conditions is to check if the filesystem is still mounted after the session loop returns.

When some error occurs during request processing, the function returns a negated errno(3) value.

If the loop has been terminated because of a signal handler installed by fuse_set_signal_handlers(), this function returns the (positive) signal value that triggered the exit.

Parameters
sethe session
Returns
0, -errno, or a signal value

Definition at line 19 of file fuse_loop.c.

◆ fuse_session_mount()

int fuse_session_mount ( struct fuse_session *  se,
const char *  mountpoint 
)

Mount a FUSE file system.

Parameters
mountpointthe mount point path
sesession object
Returns
0 on success, -1 on failure.

Definition at line 3473 of file fuse_lowlevel.c.

◆ fuse_session_process_buf()

void fuse_session_process_buf ( struct fuse_session *  se,
const struct fuse_buf buf 
)

Process a raw request supplied in a generic buffer

The fuse_buf may contain a memory buffer or a pipe file descriptor.

Parameters
sethe session
bufthe fuse_buf containing the request

Definition at line 2830 of file fuse_lowlevel.c.

◆ fuse_session_receive_buf()

int fuse_session_receive_buf ( struct fuse_session *  se,
struct fuse_buf buf 
)

Read a raw request from the kernel into the supplied buffer.

Depending on file system options, system capabilities, and request size the request is either read into a memory buffer or spliced into a temporary pipe.

Parameters
sethe session
bufthe fuse_buf to store the request in
Returns
the actual size of the raw request, or -errno on error

Definition at line 3285 of file fuse_lowlevel.c.

◆ fuse_session_reset()

void fuse_session_reset ( struct fuse_session *  se)

Reset the terminated flag of a session

Parameters
sethe session

◆ fuse_session_unmount()

void fuse_session_unmount ( struct fuse_session *  se)

Ensure that file system is unmounted.

In regular operation, the file system is typically unmounted by the user calling umount(8) or fusermount(1), which then terminates the FUSE session loop. However, the session loop may also terminate as a result of an explicit call to fuse_session_exit() (e.g. by a signal handler installed by fuse_set_signal_handler()). In this case the filesystem remains mounted, but any attempt to access it will block (while the filesystem process is still running) or give an ESHUTDOWN error (after the filesystem process has terminated).

If the communication channel with the FUSE kernel module is still open (i.e., if the session loop was terminated by an explicit call to fuse_session_exit()), this function will close it and unmount the filesystem. If the communication channel has been closed by the kernel, this method will do (almost) nothing.

NOTE: The above semantics mean that if the connection to the kernel is terminated via the /sys/fs/fuse/connections/NNN/abort file, this method will not unmount the filesystem.

Parameters
sethe session

Definition at line 3539 of file fuse_lowlevel.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__lowlevel_8h.html0000644000175000017500000043450115156613443023505 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_lowlevel.h File Reference
libfuse
fuse_lowlevel.h File Reference
#include "fuse_common.h"
#include <stddef.h>
#include <utime.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>

Go to the source code of this file.

Data Structures

struct  fuse_entry_param
 
struct  fuse_ctx
 
struct  fuse_lowlevel_ops
 
struct  fuse_cmdline_opts
 

Macros

#define FUSE_ROOT_ID   1
 

Typedefs

typedef uint64_t fuse_ino_t
 
typedef struct fuse_req * fuse_req_t
 
typedef void(* fuse_interrupt_func_t) (fuse_req_t req, void *data)
 

Enumerations

enum  fuse_notify_entry_flags
 

Functions

int fuse_reply_err (fuse_req_t req, int err)
 
void fuse_reply_none (fuse_req_t req)
 
int fuse_reply_entry (fuse_req_t req, const struct fuse_entry_param *e)
 
int fuse_reply_create (fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
 
int fuse_reply_attr (fuse_req_t req, const struct stat *attr, double attr_timeout)
 
int fuse_reply_readlink (fuse_req_t req, const char *link)
 
int fuse_passthrough_open (fuse_req_t req, int fd)
 
int fuse_reply_open (fuse_req_t req, const struct fuse_file_info *fi)
 
int fuse_reply_write (fuse_req_t req, size_t count)
 
int fuse_reply_buf (fuse_req_t req, const char *buf, size_t size)
 
int fuse_reply_data (fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
 
int fuse_reply_iov (fuse_req_t req, const struct iovec *iov, int count)
 
int fuse_reply_statfs (fuse_req_t req, const struct statvfs *stbuf)
 
int fuse_reply_xattr (fuse_req_t req, size_t count)
 
int fuse_reply_lock (fuse_req_t req, const struct flock *lock)
 
int fuse_reply_bmap (fuse_req_t req, uint64_t idx)
 
size_t fuse_add_direntry (fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
 
size_t fuse_add_direntry_plus (fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
 
int fuse_reply_ioctl_retry (fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
 
int fuse_reply_ioctl (fuse_req_t req, int result, const void *buf, size_t size)
 
int fuse_reply_ioctl_iov (fuse_req_t req, int result, const struct iovec *iov, int count)
 
int fuse_reply_poll (fuse_req_t req, unsigned revents)
 
int fuse_reply_lseek (fuse_req_t req, off_t off)
 
int fuse_reply_statx (fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
 
int fuse_lowlevel_notify_poll (struct fuse_pollhandle *ph)
 
int fuse_lowlevel_notify_inval_inode (struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
 
int fuse_lowlevel_notify_increment_epoch (struct fuse_session *se)
 
int fuse_lowlevel_notify_inval_entry (struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_expire_entry (struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_delete (struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_store (struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
 
int fuse_lowlevel_notify_retrieve (struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
 
void * fuse_req_userdata (fuse_req_t req)
 
const struct fuse_ctxfuse_req_ctx (fuse_req_t req)
 
int fuse_req_getgroups (fuse_req_t req, int size, gid_t list[])
 
void fuse_req_interrupt_func (fuse_req_t req, fuse_interrupt_func_t func, void *data)
 
int fuse_req_interrupted (fuse_req_t req)
 
void fuse_lowlevel_version (void)
 
void fuse_lowlevel_help (void)
 
void fuse_cmdline_help (void)
 
int fuse_parse_cmdline_30 (struct fuse_args *args, struct fuse_cmdline_opts *opts)
 
int fuse_session_mount (struct fuse_session *se, const char *mountpoint)
 
int fuse_session_loop (struct fuse_session *se)
 
void fuse_session_exit (struct fuse_session *se)
 
void fuse_session_reset (struct fuse_session *se)
 
int fuse_session_exited (struct fuse_session *se)
 
void fuse_session_unmount (struct fuse_session *se)
 
void fuse_session_destroy (struct fuse_session *se)
 
int fuse_session_fd (struct fuse_session *se)
 
void fuse_session_process_buf (struct fuse_session *se, const struct fuse_buf *buf)
 
int fuse_session_receive_buf (struct fuse_session *se, struct fuse_buf *buf)
 
bool fuse_req_is_uring (fuse_req_t req)
 
int fuse_req_get_payload (fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
 

Detailed Description

Low level API

IMPORTANT: you should define FUSE_USE_VERSION before including this header. To use the newest API define it to 35 (recommended for any new application).

Definition in file fuse_lowlevel.h.

Macro Definition Documentation

◆ FUSE_ROOT_ID

#define FUSE_ROOT_ID   1

The node ID of the root inode

Definition at line 44 of file fuse_lowlevel.h.

Typedef Documentation

◆ fuse_ino_t

typedef uint64_t fuse_ino_t

Inode number type

Definition at line 47 of file fuse_lowlevel.h.

◆ fuse_interrupt_func_t

typedef void(* fuse_interrupt_func_t) (fuse_req_t req, void *data)

Callback function for an interrupt

Parameters
reqinterrupted request
datauser data

Definition at line 1992 of file fuse_lowlevel.h.

◆ fuse_req_t

typedef struct fuse_req* fuse_req_t

Request pointer type

Definition at line 50 of file fuse_lowlevel.h.

Enumeration Type Documentation

◆ fuse_notify_entry_flags

Flags for fuse_lowlevel_notify_entry() 0 = invalidate entry FUSE_LL_EXPIRE_ONLY = expire entry

Definition at line 151 of file fuse_lowlevel.h.

Function Documentation

◆ fuse_add_direntry()

size_t fuse_add_direntry ( fuse_req_t  req,
char *  buf,
size_t  bufsize,
const char *  name,
const struct stat *  stbuf,
off_t  off 
)

Add a directory entry to the buffer

Buffer needs to be large enough to hold the entry. If it's not, then the entry is not filled in but the size of the entry is still returned. The caller can check this by comparing the bufsize parameter with the returned entry size. If the entry size is larger than the buffer size, the operation failed.

From the 'stbuf' argument the st_ino field and bits 12-15 of the st_mode field are used. The other fields are ignored.

off should be any non-zero value that the filesystem can use to identify the current point in the directory stream. It does not need to be the actual physical position. A value of zero is reserved to mean "from the beginning", and should therefore never be used (the first call to fuse_add_direntry should be passed the offset of the second directory entry).

Parameters
reqrequest handle
bufthe point where the new entry will be added to the buffer
bufsizeremaining size of the buffer
namethe name of the entry
stbufthe file attributes
offthe offset of the next entry
Returns
the space needed for the entry

Definition at line 286 of file fuse_lowlevel.c.

◆ fuse_add_direntry_plus()

size_t fuse_add_direntry_plus ( fuse_req_t  req,
char *  buf,
size_t  bufsize,
const char *  name,
const struct fuse_entry_param e,
off_t  off 
)

Add a directory entry to the buffer with the attributes

See documentation of fuse_add_direntry() for more details.

Parameters
reqrequest handle
bufthe point where the new entry will be added to the buffer
bufsizeremaining size of the buffer
namethe name of the entry
ethe directory entry
offthe offset of the next entry
Returns
the space needed for the entry

Definition at line 376 of file fuse_lowlevel.c.

◆ fuse_cmdline_help()

void fuse_cmdline_help ( void  )

Print available options for fuse_parse_cmdline().

Definition at line 130 of file helper.c.

◆ fuse_lowlevel_help()

void fuse_lowlevel_help ( void  )

Print available low-level options to stdout. This is not an exhaustive list, but includes only those options that may be of interest to an end-user of a file system.

Definition at line 3000 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_delete()

int fuse_lowlevel_notify_delete ( struct fuse_session *  se,
fuse_ino_t  parent,
fuse_ino_t  child,
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):

If the provided child inode matches the inode that is currently associated with the cached dentry, and if there are any inotify watches registered for the dentry, then the watchers are informed that the dentry has been deleted.

To avoid a deadlock this function must not be called while 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).

When called correctly, this function will never block.

Added in FUSE protocol version 7.18. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
parentinode number
childinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure

Definition at line 2562 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_expire_entry()

int fuse_lowlevel_notify_expire_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.

Parameters
sethe session object
parentinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure, -enosys if no kernel support

Definition at line 2549 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_increment_epoch()

int fuse_lowlevel_notify_increment_epoch ( struct fuse_session *  se)

Notify to increment the epoch for the current

Each fuse connection has an 'epoch', which is initialized during INIT. Caching will then be validated against the epoch value: if the current epoch is higher than an object being revalidated, the object is invalid.

This function simply increment the current epoch value.

Parameters
sethe session object
Returns
zero for success, -errno for failure

Definition at line 3116 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_inval_entry()

int fuse_lowlevel_notify_inval_entry ( struct fuse_session *  se,
fuse_ino_t  parent,
const char *  name,
size_t  namelen 
)

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 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() request for the parent, and a setattr(), unlink(), rmdir(), rename(), setxattr(), removexattr(), readdir() or readdirplus() request for the inode itself.

When called correctly, this function will never block.

Added in FUSE protocol version 7.12. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
parentinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure

Definition at line 2543 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_inval_inode()

int fuse_lowlevel_notify_inval_inode ( struct fuse_session *  se,
fuse_ino_t  ino,
off_t  off,
off_t  len 
)

Notify to invalidate cache for an inode.

Added in FUSE protocol version 7.12. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

If the filesystem has writeback caching enabled, invalidating an inode will first trigger a writeback of all dirty pages. The call will block until all writeback requests have completed and the inode has been invalidated. It will, however, not wait for completion of pending writeback requests that have been issued before.

If there are no dirty pages, this function will never block.

Parameters
sethe session object
inothe inode number
offthe offset in the inode where to start invalidating or negative to invalidate attributes only
lenthe amount of cache to invalidate or 0 for all
Returns
zero for success, -errno for failure

Definition at line 2475 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_poll()

int fuse_lowlevel_notify_poll ( struct fuse_pollhandle *  ph)

Notify IO readiness event

For more information, please read comment for poll operation.

Parameters
phpoll handle to notify IO readiness event for

Definition at line 2458 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_retrieve()

int fuse_lowlevel_notify_retrieve ( struct fuse_session *  se,
fuse_ino_t  ino,
size_t  size,
off_t  offset,
void *  cookie 
)

Retrieve data from the kernel buffers

Retrieve data in the kernel buffers belonging to the given inode. If successful then the retrieve_reply() method will be called with the returned data.

Only present pages are returned in the retrieve reply. Retrieving stops when it finds a non-present page and only data prior to that is returned.

If this function returns an error, then the retrieve will not be completed and no reply will be sent.

This function doesn't change the dirty state of pages in the kernel buffer. For dirty pages the write() method will be called regardless of having been retrieved previously.

Added in FUSE protocol version 7.15. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
inothe inode number
sizethe number of bytes to retrieve
offsetthe starting offset into the file to retrieve from
cookieuser data to supply to the reply callback
Returns
zero for success, -errno for failure

Definition at line 2668 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_store()

int fuse_lowlevel_notify_store ( struct fuse_session *  se,
fuse_ino_t  ino,
off_t  offset,
struct fuse_bufvec bufv,
enum fuse_buf_copy_flags  flags 
)

Store data to the kernel buffers

Synchronously store data in the kernel buffers belonging to the given inode. The stored data is marked up-to-date (no read will be performed against it, unless it's invalidated or evicted from the cache).

If the stored data overflows the current file size, then the size is extended, similarly to a write(2) on the filesystem.

If this function returns an error, then the store wasn't fully completed, but it may have been partially completed.

Added in FUSE protocol version 7.15. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
inothe inode number
offsetthe starting offset into the file to store to
bufvbuffer vector
flagsflags controlling the copy
Returns
zero for success, -errno for failure

Definition at line 2588 of file fuse_lowlevel.c.

◆ fuse_lowlevel_version()

void fuse_lowlevel_version ( void  )

Print low-level version information to stdout.

Definition at line 2993 of file fuse_lowlevel.c.

◆ fuse_parse_cmdline_30()

int fuse_parse_cmdline_30 ( struct fuse_args args,
struct fuse_cmdline_opts opts 
)

Utility function to parse common options for simple file systems using the low-level API. A help text that describes the available options can be printed with fuse_cmdline_help. A single non-option argument is treated as the mountpoint. Multiple non-option arguments will result in an error.

If neither -o subtype= or -o fsname= options are given, a new subtype option will be added and set to the basename of the program (the fsname will remain unset, and then defaults to "fuse").

Known options will be removed from args, unknown options will remain.

Parameters
argsargument vector (input+output)
optsoutput argument for parsed options
Returns
0 on success, -1 on failure

struct fuse_cmdline_opts got extended in libfuse-3.12

Definition at line 237 of file helper.c.

◆ fuse_passthrough_open()

int fuse_passthrough_open ( fuse_req_t  req,
int  fd 
)

Setup passthrough backing file for open reply

Currently there should be only one backing id per node / backing file.

Possible requests: open, opendir, create

Parameters
reqrequest handle
fdbacking file descriptor
Returns
positive backing id for success, 0 for failure

Definition at line 480 of file fuse_lowlevel.c.

◆ fuse_reply_attr()

int fuse_reply_attr ( fuse_req_t  req,
const struct stat *  attr,
double  attr_timeout 
)

Reply with attributes

Possible requests: getattr, setattr

Parameters
reqrequest handle
attrthe attributes
attr_timeoutvalidity timeout (in seconds) for the attributes
Returns
zero for success, -errno for failure to send reply

Definition at line 460 of file fuse_lowlevel.c.

◆ fuse_reply_bmap()

int fuse_reply_bmap ( fuse_req_t  req,
uint64_t  idx 
)

Reply with block index

Possible requests: bmap

Parameters
reqrequest handle
idxblock index within device
Returns
zero for success, -errno for failure to send reply

Definition at line 973 of file fuse_lowlevel.c.

◆ fuse_reply_buf()

int fuse_reply_buf ( fuse_req_t  req,
const char *  buf,
size_t  size 
)

Reply with data

Possible requests: read, readdir, getxattr, listxattr

Parameters
reqrequest handle
bufbuffer containing data
sizethe size of data in bytes
Returns
zero for success, -errno for failure to send reply

Definition at line 524 of file fuse_lowlevel.c.

◆ fuse_reply_create()

int fuse_reply_create ( fuse_req_t  req,
const struct fuse_entry_param e,
const struct fuse_file_info fi 
)

Reply with a directory entry and open parameters

currently the following members of 'fi' are used: fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, parallel_direct_writes

Possible requests: create

Side effects: increments the lookup count on success

Parameters
reqrequest handle
ethe entry parameters
fifile information
Returns
zero for success, -errno for failure to send reply

Definition at line 444 of file fuse_lowlevel.c.

◆ fuse_reply_data()

int fuse_reply_data ( fuse_req_t  req,
struct fuse_bufvec bufv,
enum fuse_buf_copy_flags  flags 
)

Reply with data copied/moved from buffer(s)

Zero copy data transfer ("splicing") will be used under the following circumstances:

  1. FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and
  2. the kernel supports splicing from the fuse device (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and
  3. flags does not contain FUSE_BUF_NO_SPLICE
  4. The amount of data that is provided in file-descriptor backed buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) is at least twice the page size.

In order for SPLICE_F_MOVE to be used, the following additional conditions have to be fulfilled:

  1. FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and
  2. the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.capable), and
  3. flags contains FUSE_BUF_SPLICE_MOVE

Note that, if splice is used, the data is actually spliced twice: once into a temporary pipe (to prepend header data), and then again into the kernel. If some of the provided buffers are memory-backed, the data in them is copied in step one and spliced in step two.

The FUSE_BUF_SPLICE_FORCE_SPLICE and FUSE_BUF_SPLICE_NONBLOCK flags are silently ignored.

Possible requests: read, readdir, getxattr, listxattr

Side effects: when used to return data from a readdirplus() (but not readdir()) call, increments the lookup count of each returned entry by one on success.

Parameters
reqrequest handle
bufvbuffer vector
flagsflags controlling the copy
Returns
zero for success, -errno for failure to send reply

Definition at line 912 of file fuse_lowlevel.c.

◆ fuse_reply_entry()

int fuse_reply_entry ( fuse_req_t  req,
const struct fuse_entry_param e 
)

Reply with a directory entry

Possible requests: lookup, mknod, mkdir, symlink, link

Side effects: increments the lookup count on success

Parameters
reqrequest handle
ethe entry parameters
Returns
zero for success, -errno for failure to send reply

Definition at line 428 of file fuse_lowlevel.c.

◆ fuse_reply_err()

int fuse_reply_err ( fuse_req_t  req,
int  err 
)

Reply with an error code or success.

Possible requests: all except forget, forget_multi, retrieve_reply

Wherever possible, error codes should be chosen from the list of documented error conditions in the corresponding system calls manpage.

An error code of ENOSYS is sometimes treated specially. This is indicated in the documentation of the affected handler functions.

The following requests may be answered with a zero error code: unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, removexattr, setlk.

Parameters
reqrequest handle
errthe positive error value, or zero for success
Returns
zero for success, -errno for failure to send reply

Definition at line 331 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl()

int fuse_reply_ioctl ( fuse_req_t  req,
int  result,
const void *  buf,
size_t  size 
)

Reply to finish ioctl

Possible requests: ioctl

Parameters
reqrequest handle
resultresult to be passed to the caller
bufbuffer containing output data
sizelength of output data

Definition at line 1071 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl_iov()

int fuse_reply_ioctl_iov ( fuse_req_t  req,
int  result,
const struct iovec *  iov,
int  count 
)

Reply to finish ioctl with iov buffer

Possible requests: ioctl

Parameters
reqrequest handle
resultresult to be passed to the caller
iovthe vector containing the data
countthe size of vector

Definition at line 1092 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl_retry()

int fuse_reply_ioctl_retry ( fuse_req_t  req,
const struct iovec *  in_iov,
size_t  in_count,
const struct iovec *  out_iov,
size_t  out_count 
)

Reply to ask for data fetch and output buffer preparation. ioctl will be retried with the specified input data fetched and output buffer prepared.

Possible requests: ioctl

Parameters
reqrequest handle
in_ioviovec specifying data to fetch from the caller
in_countnumber of entries in in_iov
out_ioviovec specifying addresses to write output to
out_countnumber of entries in out_iov
Returns
zero for success, -errno for failure to send reply

Definition at line 1001 of file fuse_lowlevel.c.

◆ fuse_reply_iov()

int fuse_reply_iov ( fuse_req_t  req,
const struct iovec *  iov,
int  count 
)

Reply with data vector

Possible requests: read, readdir, getxattr, listxattr

Parameters
reqrequest handle
iovthe vector containing the data
countthe size of vector
Returns
zero for success, -errno for failure to send reply

Definition at line 265 of file fuse_lowlevel.c.

◆ fuse_reply_lock()

int fuse_reply_lock ( fuse_req_t  req,
const struct flock *  lock 
)

Reply with file lock information

Possible requests: getlk

Parameters
reqrequest handle
lockthe lock information
Returns
zero for success, -errno for failure to send reply

Definition at line 956 of file fuse_lowlevel.c.

◆ fuse_reply_lseek()

int fuse_reply_lseek ( fuse_req_t  req,
off_t  off 
)

Reply with offset

Possible requests: lseek

Parameters
reqrequest handle
offoffset of next data or hole
Returns
zero for success, -errno for failure to send reply

Definition at line 1126 of file fuse_lowlevel.c.

◆ fuse_reply_none()

void fuse_reply_none ( fuse_req_t  req)

Don't send reply

Possible requests: forget forget_multi retrieve_reply

Parameters
reqrequest handle

Definition at line 336 of file fuse_lowlevel.c.

◆ fuse_reply_open()

int fuse_reply_open ( fuse_req_t  req,
const struct fuse_file_info fi 
)

Reply with open parameters

currently the following members of 'fi' are used: fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, parallel_direct_writes,

Possible requests: open, opendir

Parameters
reqrequest handle
fifile information
Returns
zero for success, -errno for failure to send reply

Definition at line 505 of file fuse_lowlevel.c.

◆ fuse_reply_poll()

int fuse_reply_poll ( fuse_req_t  req,
unsigned  revents 
)

Reply with poll result event mask

Parameters
reqrequest handle
reventspoll result event mask

Definition at line 1116 of file fuse_lowlevel.c.

◆ fuse_reply_readlink()

int fuse_reply_readlink ( fuse_req_t  req,
const char *  link 
)

Reply with the contents of a symbolic link

Possible requests: readlink

Parameters
reqrequest handle
linksymbolic link contents
Returns
zero for success, -errno for failure to send reply

Definition at line 475 of file fuse_lowlevel.c.

◆ fuse_reply_statfs()

int fuse_reply_statfs ( fuse_req_t  req,
const struct statvfs *  stbuf 
)

Reply with filesystem statistics

Possible requests: statfs

Parameters
reqrequest handle
stbuffilesystem statistics
Returns
zero for success, -errno for failure to send reply

Definition at line 934 of file fuse_lowlevel.c.

◆ fuse_reply_statx()

int fuse_reply_statx ( fuse_req_t  req,
int  flags,
struct statx *  statx,
double  attr_timeout 
)

Reply with extended file attributes.

Possible requests: statx

Parameters
reqrequest handle
flagsstatx flags
statxthe attributes
attr_timeoutvalidity timeout (in seconds) for the attributes
Returns
zero for success, -errno for failure to send reply

Definition at line 1260 of file fuse_lowlevel.c.

◆ fuse_reply_write()

int fuse_reply_write ( fuse_req_t  req,
size_t  count 
)

Reply with number of bytes written

Possible requests: write

Parameters
reqrequest handle
countthe number of bytes written
Returns
zero for success, -errno for failure to send reply

Definition at line 514 of file fuse_lowlevel.c.

◆ fuse_reply_xattr()

int fuse_reply_xattr ( fuse_req_t  req,
size_t  count 
)

Reply with needed buffer size

Possible requests: getxattr, listxattr

Parameters
reqrequest handle
countthe buffer size needed in bytes
Returns
zero for success, -errno for failure to send reply

Definition at line 946 of file fuse_lowlevel.c.

◆ fuse_req_ctx()

const struct fuse_ctx * fuse_req_ctx ( fuse_req_t  req)

Get the context from the request

The pointer returned by this function will only be valid for the request's lifetime

Parameters
reqrequest handle
Returns
the context structure

Definition at line 2718 of file fuse_lowlevel.c.

◆ fuse_req_get_payload()

int fuse_req_get_payload ( fuse_req_t  req,
char **  payload,
size_t *  payload_sz,
void **  mr 
)

Get the payload of a request (for requests submitted through fuse-io-uring only)

This is useful for a file system that wants to write data directly to the request buffer. With io-uring the req is the buffer owner and the file system can write directly to the buffer and avoid extra copying. For example useful for network file systems.

Parameters
reqthe request
payloadpointer to the payload
payload_szsize of the payload
mrmemory registration handle, currently unused
Returns
0 on success, -errno on failure

Definition at line 3386 of file fuse_lowlevel.c.

◆ fuse_req_getgroups()

int fuse_req_getgroups ( fuse_req_t  req,
int  size,
gid_t  list[] 
)

Get the current supplementary group IDs for the specified request

Similar to the getgroups(2) system call, except the return value is always the total number of group IDs, even if it is larger than the specified size.

The current fuse kernel module in linux (as of 2.6.30) doesn't pass the group list to userspace, hence this function needs to parse "/proc/$TID/task/$TID/status" to get the group IDs.

This feature may not be supported on all operating systems. In such a case this function will return -ENOSYS.

Parameters
reqrequest handle
sizesize of given array
listarray of group IDs to be filled in
Returns
the total number of supplementary group IDs or -errno on failure

Definition at line 3614 of file fuse_lowlevel.c.

◆ fuse_req_interrupt_func()

void fuse_req_interrupt_func ( fuse_req_t  req,
fuse_interrupt_func_t  func,
void *  data 
)

Register/unregister callback for an interrupt

If an interrupt has already happened, then the callback function is called from within this function, hence it's not possible for interrupts to be lost.

Parameters
reqrequest handle
functhe callback function or NULL for unregister
datauser data passed to the callback function

Definition at line 2723 of file fuse_lowlevel.c.

◆ fuse_req_interrupted()

int fuse_req_interrupted ( fuse_req_t  req)

Check if a request has already been interrupted

Parameters
reqrequest handle
Returns
1 if the request has been interrupted, 0 otherwise

Definition at line 2736 of file fuse_lowlevel.c.

◆ fuse_req_is_uring()

bool fuse_req_is_uring ( fuse_req_t  req)

Check if the request is submitted through fuse-io-uring

Definition at line 3380 of file fuse_lowlevel.c.

◆ fuse_req_userdata()

void * fuse_req_userdata ( fuse_req_t  req)

Get the userdata from the request

Parameters
reqrequest handle
Returns
the user data passed to fuse_session_new()

Definition at line 2713 of file fuse_lowlevel.c.

◆ fuse_session_destroy()

void fuse_session_destroy ( struct fuse_session *  se)

Destroy a session

Parameters
sethe session

Definition at line 3010 of file fuse_lowlevel.c.

◆ fuse_session_exit()

void fuse_session_exit ( struct fuse_session *  se)

Flag a session as terminated.

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).

Parameters
sethe session

◆ fuse_session_exited()

int fuse_session_exited ( struct fuse_session *  se)

Query the terminated flag of a session

Parameters
sethe session
Returns
1 if exited, 0 if not exited

◆ fuse_session_fd()

int fuse_session_fd ( struct fuse_session *  se)

Return file descriptor for communication with kernel.

The file selector can be used to integrate FUSE with a custom event loop. Whenever data is available for reading on the provided fd, the event loop should call fuse_session_receive_buf followed by fuse_session_process_buf to process the request.

The returned file descriptor is valid until fuse_session_unmount is called.

Parameters
sethe session
Returns
a file descriptor

Definition at line 3534 of file fuse_lowlevel.c.

◆ fuse_session_loop()

int fuse_session_loop ( struct fuse_session *  se)

Enter a single threaded, blocking event loop.

When the event loop terminates because the connection to the FUSE kernel module has been closed, this function returns zero. This happens when the filesystem is unmounted regularly (by the filesystem owner or root running the umount(8) or fusermount(1) command), or if connection is explicitly severed by writing 1 to theabort file in /sys/fs/fuse/connections/NNN. The only way to distinguish between these two conditions is to check if the filesystem is still mounted after the session loop returns.

When some error occurs during request processing, the function returns a negated errno(3) value.

If the loop has been terminated because of a signal handler installed by fuse_set_signal_handlers(), this function returns the (positive) signal value that triggered the exit.

Parameters
sethe session
Returns
0, -errno, or a signal value

Definition at line 19 of file fuse_loop.c.

◆ fuse_session_mount()

int fuse_session_mount ( struct fuse_session *  se,
const char *  mountpoint 
)

Mount a FUSE file system.

Parameters
mountpointthe mount point path
sesession object
Returns
0 on success, -1 on failure.

Definition at line 3473 of file fuse_lowlevel.c.

◆ fuse_session_process_buf()

void fuse_session_process_buf ( struct fuse_session *  se,
const struct fuse_buf buf 
)

Process a raw request supplied in a generic buffer

The fuse_buf may contain a memory buffer or a pipe file descriptor.

Parameters
sethe session
bufthe fuse_buf containing the request

Definition at line 2830 of file fuse_lowlevel.c.

◆ fuse_session_receive_buf()

int fuse_session_receive_buf ( struct fuse_session *  se,
struct fuse_buf buf 
)

Read a raw request from the kernel into the supplied buffer.

Depending on file system options, system capabilities, and request size the request is either read into a memory buffer or spliced into a temporary pipe.

Parameters
sethe session
bufthe fuse_buf to store the request in
Returns
the actual size of the raw request, or -errno on error

Definition at line 3285 of file fuse_lowlevel.c.

◆ fuse_session_reset()

void fuse_session_reset ( struct fuse_session *  se)

Reset the terminated flag of a session

Parameters
sethe session

◆ fuse_session_unmount()

void fuse_session_unmount ( struct fuse_session *  se)

Ensure that file system is unmounted.

In regular operation, the file system is typically unmounted by the user calling umount(8) or fusermount(1), which then terminates the FUSE session loop. However, the session loop may also terminate as a result of an explicit call to fuse_session_exit() (e.g. by a signal handler installed by fuse_set_signal_handler()). In this case the filesystem remains mounted, but any attempt to access it will block (while the filesystem process is still running) or give an ESHUTDOWN error (after the filesystem process has terminated).

If the communication channel with the FUSE kernel module is still open (i.e., if the session loop was terminated by an explicit call to fuse_session_exit()), this function will close it and unmount the filesystem. If the communication channel has been closed by the kernel, this method will do (almost) nothing.

NOTE: The above semantics mean that if the connection to the kernel is terminated via the /sys/fs/fuse/connections/NNN/abort file, this method will not unmount the filesystem.

Parameters
sethe session

Definition at line 3539 of file fuse_lowlevel.c.

fuse-3.18.2/doc/html/include_2fuse__lowlevel_8h.html0000644000175000017500000043224515156613443021334 0ustar berndbernd libfuse: include/fuse_lowlevel.h File Reference
libfuse
fuse_lowlevel.h File Reference
#include "fuse_common.h"
#include <stddef.h>
#include <utime.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>

Go to the source code of this file.

Data Structures

struct  fuse_entry_param
 
struct  fuse_ctx
 
struct  fuse_lowlevel_ops
 
struct  fuse_cmdline_opts
 

Macros

#define FUSE_ROOT_ID   1
 

Typedefs

typedef uint64_t fuse_ino_t
 
typedef struct fuse_req * fuse_req_t
 
typedef void(* fuse_interrupt_func_t) (fuse_req_t req, void *data)
 

Enumerations

enum  fuse_notify_entry_flags
 

Functions

int fuse_reply_err (fuse_req_t req, int err)
 
void fuse_reply_none (fuse_req_t req)
 
int fuse_reply_entry (fuse_req_t req, const struct fuse_entry_param *e)
 
int fuse_reply_create (fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
 
int fuse_reply_attr (fuse_req_t req, const struct stat *attr, double attr_timeout)
 
int fuse_reply_readlink (fuse_req_t req, const char *link)
 
int fuse_passthrough_open (fuse_req_t req, int fd)
 
int fuse_reply_open (fuse_req_t req, const struct fuse_file_info *fi)
 
int fuse_reply_write (fuse_req_t req, size_t count)
 
int fuse_reply_buf (fuse_req_t req, const char *buf, size_t size)
 
int fuse_reply_data (fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
 
int fuse_reply_iov (fuse_req_t req, const struct iovec *iov, int count)
 
int fuse_reply_statfs (fuse_req_t req, const struct statvfs *stbuf)
 
int fuse_reply_xattr (fuse_req_t req, size_t count)
 
int fuse_reply_lock (fuse_req_t req, const struct flock *lock)
 
int fuse_reply_bmap (fuse_req_t req, uint64_t idx)
 
size_t fuse_add_direntry (fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
 
size_t fuse_add_direntry_plus (fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
 
int fuse_reply_ioctl_retry (fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
 
int fuse_reply_ioctl (fuse_req_t req, int result, const void *buf, size_t size)
 
int fuse_reply_ioctl_iov (fuse_req_t req, int result, const struct iovec *iov, int count)
 
int fuse_reply_poll (fuse_req_t req, unsigned revents)
 
int fuse_reply_lseek (fuse_req_t req, off_t off)
 
int fuse_reply_statx (fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
 
int fuse_lowlevel_notify_poll (struct fuse_pollhandle *ph)
 
int fuse_lowlevel_notify_inval_inode (struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
 
int fuse_lowlevel_notify_increment_epoch (struct fuse_session *se)
 
int fuse_lowlevel_notify_inval_entry (struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_expire_entry (struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_delete (struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_store (struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
 
int fuse_lowlevel_notify_retrieve (struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
 
void * fuse_req_userdata (fuse_req_t req)
 
const struct fuse_ctxfuse_req_ctx (fuse_req_t req)
 
int fuse_req_getgroups (fuse_req_t req, int size, gid_t list[])
 
void fuse_req_interrupt_func (fuse_req_t req, fuse_interrupt_func_t func, void *data)
 
int fuse_req_interrupted (fuse_req_t req)
 
void fuse_lowlevel_version (void)
 
void fuse_lowlevel_help (void)
 
void fuse_cmdline_help (void)
 
int fuse_parse_cmdline_30 (struct fuse_args *args, struct fuse_cmdline_opts *opts)
 
int fuse_session_mount (struct fuse_session *se, const char *mountpoint)
 
int fuse_session_loop (struct fuse_session *se)
 
void fuse_session_exit (struct fuse_session *se)
 
void fuse_session_reset (struct fuse_session *se)
 
int fuse_session_exited (struct fuse_session *se)
 
void fuse_session_unmount (struct fuse_session *se)
 
void fuse_session_destroy (struct fuse_session *se)
 
int fuse_session_fd (struct fuse_session *se)
 
void fuse_session_process_buf (struct fuse_session *se, const struct fuse_buf *buf)
 
int fuse_session_receive_buf (struct fuse_session *se, struct fuse_buf *buf)
 
bool fuse_req_is_uring (fuse_req_t req)
 
int fuse_req_get_payload (fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
 

Detailed Description

Low level API

IMPORTANT: you should define FUSE_USE_VERSION before including this header. To use the newest API define it to 35 (recommended for any new application).

Definition in file fuse_lowlevel.h.

Macro Definition Documentation

◆ FUSE_ROOT_ID

#define FUSE_ROOT_ID   1

The node ID of the root inode

Definition at line 44 of file fuse_lowlevel.h.

Typedef Documentation

◆ fuse_ino_t

typedef uint64_t fuse_ino_t

Inode number type

Definition at line 47 of file fuse_lowlevel.h.

◆ fuse_interrupt_func_t

typedef void(* fuse_interrupt_func_t) (fuse_req_t req, void *data)

Callback function for an interrupt

Parameters
reqinterrupted request
datauser data

Definition at line 1992 of file fuse_lowlevel.h.

◆ fuse_req_t

typedef struct fuse_req* fuse_req_t

Request pointer type

Definition at line 50 of file fuse_lowlevel.h.

Enumeration Type Documentation

◆ fuse_notify_entry_flags

Flags for fuse_lowlevel_notify_entry() 0 = invalidate entry FUSE_LL_EXPIRE_ONLY = expire entry

Definition at line 151 of file fuse_lowlevel.h.

Function Documentation

◆ fuse_add_direntry()

size_t fuse_add_direntry ( fuse_req_t  req,
char *  buf,
size_t  bufsize,
const char *  name,
const struct stat *  stbuf,
off_t  off 
)

Add a directory entry to the buffer

Buffer needs to be large enough to hold the entry. If it's not, then the entry is not filled in but the size of the entry is still returned. The caller can check this by comparing the bufsize parameter with the returned entry size. If the entry size is larger than the buffer size, the operation failed.

From the 'stbuf' argument the st_ino field and bits 12-15 of the st_mode field are used. The other fields are ignored.

off should be any non-zero value that the filesystem can use to identify the current point in the directory stream. It does not need to be the actual physical position. A value of zero is reserved to mean "from the beginning", and should therefore never be used (the first call to fuse_add_direntry should be passed the offset of the second directory entry).

Parameters
reqrequest handle
bufthe point where the new entry will be added to the buffer
bufsizeremaining size of the buffer
namethe name of the entry
stbufthe file attributes
offthe offset of the next entry
Returns
the space needed for the entry

Definition at line 286 of file fuse_lowlevel.c.

◆ fuse_add_direntry_plus()

size_t fuse_add_direntry_plus ( fuse_req_t  req,
char *  buf,
size_t  bufsize,
const char *  name,
const struct fuse_entry_param e,
off_t  off 
)

Add a directory entry to the buffer with the attributes

See documentation of fuse_add_direntry() for more details.

Parameters
reqrequest handle
bufthe point where the new entry will be added to the buffer
bufsizeremaining size of the buffer
namethe name of the entry
ethe directory entry
offthe offset of the next entry
Returns
the space needed for the entry

Definition at line 376 of file fuse_lowlevel.c.

◆ fuse_cmdline_help()

void fuse_cmdline_help ( void  )

Print available options for fuse_parse_cmdline().

Definition at line 130 of file helper.c.

◆ fuse_lowlevel_help()

void fuse_lowlevel_help ( void  )

Print available low-level options to stdout. This is not an exhaustive list, but includes only those options that may be of interest to an end-user of a file system.

Definition at line 3000 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_delete()

int fuse_lowlevel_notify_delete ( struct fuse_session *  se,
fuse_ino_t  parent,
fuse_ino_t  child,
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):

If the provided child inode matches the inode that is currently associated with the cached dentry, and if there are any inotify watches registered for the dentry, then the watchers are informed that the dentry has been deleted.

To avoid a deadlock this function must not be called while 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).

When called correctly, this function will never block.

Added in FUSE protocol version 7.18. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
parentinode number
childinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure

Definition at line 2562 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_expire_entry()

int fuse_lowlevel_notify_expire_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.

Parameters
sethe session object
parentinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure, -enosys if no kernel support

Definition at line 2549 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_increment_epoch()

int fuse_lowlevel_notify_increment_epoch ( struct fuse_session *  se)

Notify to increment the epoch for the current

Each fuse connection has an 'epoch', which is initialized during INIT. Caching will then be validated against the epoch value: if the current epoch is higher than an object being revalidated, the object is invalid.

This function simply increment the current epoch value.

Parameters
sethe session object
Returns
zero for success, -errno for failure

Definition at line 3116 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_inval_entry()

int fuse_lowlevel_notify_inval_entry ( struct fuse_session *  se,
fuse_ino_t  parent,
const char *  name,
size_t  namelen 
)

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 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() request for the parent, and a setattr(), unlink(), rmdir(), rename(), setxattr(), removexattr(), readdir() or readdirplus() request for the inode itself.

When called correctly, this function will never block.

Added in FUSE protocol version 7.12. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
parentinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure

Definition at line 2543 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_inval_inode()

int fuse_lowlevel_notify_inval_inode ( struct fuse_session *  se,
fuse_ino_t  ino,
off_t  off,
off_t  len 
)

Notify to invalidate cache for an inode.

Added in FUSE protocol version 7.12. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

If the filesystem has writeback caching enabled, invalidating an inode will first trigger a writeback of all dirty pages. The call will block until all writeback requests have completed and the inode has been invalidated. It will, however, not wait for completion of pending writeback requests that have been issued before.

If there are no dirty pages, this function will never block.

Parameters
sethe session object
inothe inode number
offthe offset in the inode where to start invalidating or negative to invalidate attributes only
lenthe amount of cache to invalidate or 0 for all
Returns
zero for success, -errno for failure

Definition at line 2475 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_poll()

int fuse_lowlevel_notify_poll ( struct fuse_pollhandle *  ph)

Notify IO readiness event

For more information, please read comment for poll operation.

Parameters
phpoll handle to notify IO readiness event for

Definition at line 2458 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_retrieve()

int fuse_lowlevel_notify_retrieve ( struct fuse_session *  se,
fuse_ino_t  ino,
size_t  size,
off_t  offset,
void *  cookie 
)

Retrieve data from the kernel buffers

Retrieve data in the kernel buffers belonging to the given inode. If successful then the retrieve_reply() method will be called with the returned data.

Only present pages are returned in the retrieve reply. Retrieving stops when it finds a non-present page and only data prior to that is returned.

If this function returns an error, then the retrieve will not be completed and no reply will be sent.

This function doesn't change the dirty state of pages in the kernel buffer. For dirty pages the write() method will be called regardless of having been retrieved previously.

Added in FUSE protocol version 7.15. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
inothe inode number
sizethe number of bytes to retrieve
offsetthe starting offset into the file to retrieve from
cookieuser data to supply to the reply callback
Returns
zero for success, -errno for failure

Definition at line 2668 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_store()

int fuse_lowlevel_notify_store ( struct fuse_session *  se,
fuse_ino_t  ino,
off_t  offset,
struct fuse_bufvec bufv,
enum fuse_buf_copy_flags  flags 
)

Store data to the kernel buffers

Synchronously store data in the kernel buffers belonging to the given inode. The stored data is marked up-to-date (no read will be performed against it, unless it's invalidated or evicted from the cache).

If the stored data overflows the current file size, then the size is extended, similarly to a write(2) on the filesystem.

If this function returns an error, then the store wasn't fully completed, but it may have been partially completed.

Added in FUSE protocol version 7.15. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
inothe inode number
offsetthe starting offset into the file to store to
bufvbuffer vector
flagsflags controlling the copy
Returns
zero for success, -errno for failure

Definition at line 2588 of file fuse_lowlevel.c.

◆ fuse_lowlevel_version()

void fuse_lowlevel_version ( void  )

Print low-level version information to stdout.

Definition at line 2993 of file fuse_lowlevel.c.

◆ fuse_parse_cmdline_30()

int fuse_parse_cmdline_30 ( struct fuse_args args,
struct fuse_cmdline_opts opts 
)

Utility function to parse common options for simple file systems using the low-level API. A help text that describes the available options can be printed with fuse_cmdline_help. A single non-option argument is treated as the mountpoint. Multiple non-option arguments will result in an error.

If neither -o subtype= or -o fsname= options are given, a new subtype option will be added and set to the basename of the program (the fsname will remain unset, and then defaults to "fuse").

Known options will be removed from args, unknown options will remain.

Parameters
argsargument vector (input+output)
optsoutput argument for parsed options
Returns
0 on success, -1 on failure

struct fuse_cmdline_opts got extended in libfuse-3.12

Definition at line 237 of file helper.c.

◆ fuse_passthrough_open()

int fuse_passthrough_open ( fuse_req_t  req,
int  fd 
)

Setup passthrough backing file for open reply

Currently there should be only one backing id per node / backing file.

Possible requests: open, opendir, create

Parameters
reqrequest handle
fdbacking file descriptor
Returns
positive backing id for success, 0 for failure

Definition at line 480 of file fuse_lowlevel.c.

◆ fuse_reply_attr()

int fuse_reply_attr ( fuse_req_t  req,
const struct stat *  attr,
double  attr_timeout 
)

Reply with attributes

Possible requests: getattr, setattr

Parameters
reqrequest handle
attrthe attributes
attr_timeoutvalidity timeout (in seconds) for the attributes
Returns
zero for success, -errno for failure to send reply

Definition at line 460 of file fuse_lowlevel.c.

◆ fuse_reply_bmap()

int fuse_reply_bmap ( fuse_req_t  req,
uint64_t  idx 
)

Reply with block index

Possible requests: bmap

Parameters
reqrequest handle
idxblock index within device
Returns
zero for success, -errno for failure to send reply

Definition at line 973 of file fuse_lowlevel.c.

◆ fuse_reply_buf()

int fuse_reply_buf ( fuse_req_t  req,
const char *  buf,
size_t  size 
)

Reply with data

Possible requests: read, readdir, getxattr, listxattr

Parameters
reqrequest handle
bufbuffer containing data
sizethe size of data in bytes
Returns
zero for success, -errno for failure to send reply

Definition at line 524 of file fuse_lowlevel.c.

◆ fuse_reply_create()

int fuse_reply_create ( fuse_req_t  req,
const struct fuse_entry_param e,
const struct fuse_file_info fi 
)

Reply with a directory entry and open parameters

currently the following members of 'fi' are used: fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, parallel_direct_writes

Possible requests: create

Side effects: increments the lookup count on success

Parameters
reqrequest handle
ethe entry parameters
fifile information
Returns
zero for success, -errno for failure to send reply

Definition at line 444 of file fuse_lowlevel.c.

◆ fuse_reply_data()

int fuse_reply_data ( fuse_req_t  req,
struct fuse_bufvec bufv,
enum fuse_buf_copy_flags  flags 
)

Reply with data copied/moved from buffer(s)

Zero copy data transfer ("splicing") will be used under the following circumstances:

  1. FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and
  2. the kernel supports splicing from the fuse device (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and
  3. flags does not contain FUSE_BUF_NO_SPLICE
  4. The amount of data that is provided in file-descriptor backed buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) is at least twice the page size.

In order for SPLICE_F_MOVE to be used, the following additional conditions have to be fulfilled:

  1. FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and
  2. the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.capable), and
  3. flags contains FUSE_BUF_SPLICE_MOVE

Note that, if splice is used, the data is actually spliced twice: once into a temporary pipe (to prepend header data), and then again into the kernel. If some of the provided buffers are memory-backed, the data in them is copied in step one and spliced in step two.

The FUSE_BUF_SPLICE_FORCE_SPLICE and FUSE_BUF_SPLICE_NONBLOCK flags are silently ignored.

Possible requests: read, readdir, getxattr, listxattr

Side effects: when used to return data from a readdirplus() (but not readdir()) call, increments the lookup count of each returned entry by one on success.

Parameters
reqrequest handle
bufvbuffer vector
flagsflags controlling the copy
Returns
zero for success, -errno for failure to send reply

Definition at line 912 of file fuse_lowlevel.c.

◆ fuse_reply_entry()

int fuse_reply_entry ( fuse_req_t  req,
const struct fuse_entry_param e 
)

Reply with a directory entry

Possible requests: lookup, mknod, mkdir, symlink, link

Side effects: increments the lookup count on success

Parameters
reqrequest handle
ethe entry parameters
Returns
zero for success, -errno for failure to send reply

Definition at line 428 of file fuse_lowlevel.c.

◆ fuse_reply_err()

int fuse_reply_err ( fuse_req_t  req,
int  err 
)

Reply with an error code or success.

Possible requests: all except forget, forget_multi, retrieve_reply

Wherever possible, error codes should be chosen from the list of documented error conditions in the corresponding system calls manpage.

An error code of ENOSYS is sometimes treated specially. This is indicated in the documentation of the affected handler functions.

The following requests may be answered with a zero error code: unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, removexattr, setlk.

Parameters
reqrequest handle
errthe positive error value, or zero for success
Returns
zero for success, -errno for failure to send reply

Definition at line 331 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl()

int fuse_reply_ioctl ( fuse_req_t  req,
int  result,
const void *  buf,
size_t  size 
)

Reply to finish ioctl

Possible requests: ioctl

Parameters
reqrequest handle
resultresult to be passed to the caller
bufbuffer containing output data
sizelength of output data

Definition at line 1071 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl_iov()

int fuse_reply_ioctl_iov ( fuse_req_t  req,
int  result,
const struct iovec *  iov,
int  count 
)

Reply to finish ioctl with iov buffer

Possible requests: ioctl

Parameters
reqrequest handle
resultresult to be passed to the caller
iovthe vector containing the data
countthe size of vector

Definition at line 1092 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl_retry()

int fuse_reply_ioctl_retry ( fuse_req_t  req,
const struct iovec *  in_iov,
size_t  in_count,
const struct iovec *  out_iov,
size_t  out_count 
)

Reply to ask for data fetch and output buffer preparation. ioctl will be retried with the specified input data fetched and output buffer prepared.

Possible requests: ioctl

Parameters
reqrequest handle
in_ioviovec specifying data to fetch from the caller
in_countnumber of entries in in_iov
out_ioviovec specifying addresses to write output to
out_countnumber of entries in out_iov
Returns
zero for success, -errno for failure to send reply

Definition at line 1001 of file fuse_lowlevel.c.

◆ fuse_reply_iov()

int fuse_reply_iov ( fuse_req_t  req,
const struct iovec *  iov,
int  count 
)

Reply with data vector

Possible requests: read, readdir, getxattr, listxattr

Parameters
reqrequest handle
iovthe vector containing the data
countthe size of vector
Returns
zero for success, -errno for failure to send reply

Definition at line 265 of file fuse_lowlevel.c.

◆ fuse_reply_lock()

int fuse_reply_lock ( fuse_req_t  req,
const struct flock *  lock 
)

Reply with file lock information

Possible requests: getlk

Parameters
reqrequest handle
lockthe lock information
Returns
zero for success, -errno for failure to send reply

Definition at line 956 of file fuse_lowlevel.c.

◆ fuse_reply_lseek()

int fuse_reply_lseek ( fuse_req_t  req,
off_t  off 
)

Reply with offset

Possible requests: lseek

Parameters
reqrequest handle
offoffset of next data or hole
Returns
zero for success, -errno for failure to send reply

Definition at line 1126 of file fuse_lowlevel.c.

◆ fuse_reply_none()

void fuse_reply_none ( fuse_req_t  req)

Don't send reply

Possible requests: forget forget_multi retrieve_reply

Parameters
reqrequest handle

Definition at line 336 of file fuse_lowlevel.c.

◆ fuse_reply_open()

int fuse_reply_open ( fuse_req_t  req,
const struct fuse_file_info fi 
)

Reply with open parameters

currently the following members of 'fi' are used: fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, parallel_direct_writes,

Possible requests: open, opendir

Parameters
reqrequest handle
fifile information
Returns
zero for success, -errno for failure to send reply

Definition at line 505 of file fuse_lowlevel.c.

◆ fuse_reply_poll()

int fuse_reply_poll ( fuse_req_t  req,
unsigned  revents 
)

Reply with poll result event mask

Parameters
reqrequest handle
reventspoll result event mask

Definition at line 1116 of file fuse_lowlevel.c.

◆ fuse_reply_readlink()

int fuse_reply_readlink ( fuse_req_t  req,
const char *  link 
)

Reply with the contents of a symbolic link

Possible requests: readlink

Parameters
reqrequest handle
linksymbolic link contents
Returns
zero for success, -errno for failure to send reply

Definition at line 475 of file fuse_lowlevel.c.

◆ fuse_reply_statfs()

int fuse_reply_statfs ( fuse_req_t  req,
const struct statvfs *  stbuf 
)

Reply with filesystem statistics

Possible requests: statfs

Parameters
reqrequest handle
stbuffilesystem statistics
Returns
zero for success, -errno for failure to send reply

Definition at line 934 of file fuse_lowlevel.c.

◆ fuse_reply_statx()

int fuse_reply_statx ( fuse_req_t  req,
int  flags,
struct statx *  statx,
double  attr_timeout 
)

Reply with extended file attributes.

Possible requests: statx

Parameters
reqrequest handle
flagsstatx flags
statxthe attributes
attr_timeoutvalidity timeout (in seconds) for the attributes
Returns
zero for success, -errno for failure to send reply

Definition at line 1260 of file fuse_lowlevel.c.

◆ fuse_reply_write()

int fuse_reply_write ( fuse_req_t  req,
size_t  count 
)

Reply with number of bytes written

Possible requests: write

Parameters
reqrequest handle
countthe number of bytes written
Returns
zero for success, -errno for failure to send reply

Definition at line 514 of file fuse_lowlevel.c.

◆ fuse_reply_xattr()

int fuse_reply_xattr ( fuse_req_t  req,
size_t  count 
)

Reply with needed buffer size

Possible requests: getxattr, listxattr

Parameters
reqrequest handle
countthe buffer size needed in bytes
Returns
zero for success, -errno for failure to send reply

Definition at line 946 of file fuse_lowlevel.c.

◆ fuse_req_ctx()

const struct fuse_ctx * fuse_req_ctx ( fuse_req_t  req)

Get the context from the request

The pointer returned by this function will only be valid for the request's lifetime

Parameters
reqrequest handle
Returns
the context structure

Definition at line 2718 of file fuse_lowlevel.c.

◆ fuse_req_get_payload()

int fuse_req_get_payload ( fuse_req_t  req,
char **  payload,
size_t *  payload_sz,
void **  mr 
)

Get the payload of a request (for requests submitted through fuse-io-uring only)

This is useful for a file system that wants to write data directly to the request buffer. With io-uring the req is the buffer owner and the file system can write directly to the buffer and avoid extra copying. For example useful for network file systems.

Parameters
reqthe request
payloadpointer to the payload
payload_szsize of the payload
mrmemory registration handle, currently unused
Returns
0 on success, -errno on failure

Definition at line 3386 of file fuse_lowlevel.c.

◆ fuse_req_getgroups()

int fuse_req_getgroups ( fuse_req_t  req,
int  size,
gid_t  list[] 
)

Get the current supplementary group IDs for the specified request

Similar to the getgroups(2) system call, except the return value is always the total number of group IDs, even if it is larger than the specified size.

The current fuse kernel module in linux (as of 2.6.30) doesn't pass the group list to userspace, hence this function needs to parse "/proc/$TID/task/$TID/status" to get the group IDs.

This feature may not be supported on all operating systems. In such a case this function will return -ENOSYS.

Parameters
reqrequest handle
sizesize of given array
listarray of group IDs to be filled in
Returns
the total number of supplementary group IDs or -errno on failure

Definition at line 3614 of file fuse_lowlevel.c.

◆ fuse_req_interrupt_func()

void fuse_req_interrupt_func ( fuse_req_t  req,
fuse_interrupt_func_t  func,
void *  data 
)

Register/unregister callback for an interrupt

If an interrupt has already happened, then the callback function is called from within this function, hence it's not possible for interrupts to be lost.

Parameters
reqrequest handle
functhe callback function or NULL for unregister
datauser data passed to the callback function

Definition at line 2723 of file fuse_lowlevel.c.

◆ fuse_req_interrupted()

int fuse_req_interrupted ( fuse_req_t  req)

Check if a request has already been interrupted

Parameters
reqrequest handle
Returns
1 if the request has been interrupted, 0 otherwise

Definition at line 2736 of file fuse_lowlevel.c.

◆ fuse_req_is_uring()

bool fuse_req_is_uring ( fuse_req_t  req)

Check if the request is submitted through fuse-io-uring

Definition at line 3380 of file fuse_lowlevel.c.

◆ fuse_req_userdata()

void * fuse_req_userdata ( fuse_req_t  req)

Get the userdata from the request

Parameters
reqrequest handle
Returns
the user data passed to fuse_session_new()

Definition at line 2713 of file fuse_lowlevel.c.

◆ fuse_session_destroy()

void fuse_session_destroy ( struct fuse_session *  se)

Destroy a session

Parameters
sethe session

Definition at line 3010 of file fuse_lowlevel.c.

◆ fuse_session_exit()

void fuse_session_exit ( struct fuse_session *  se)

Flag a session as terminated.

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).

Parameters
sethe session

◆ fuse_session_exited()

int fuse_session_exited ( struct fuse_session *  se)

Query the terminated flag of a session

Parameters
sethe session
Returns
1 if exited, 0 if not exited

◆ fuse_session_fd()

int fuse_session_fd ( struct fuse_session *  se)

Return file descriptor for communication with kernel.

The file selector can be used to integrate FUSE with a custom event loop. Whenever data is available for reading on the provided fd, the event loop should call fuse_session_receive_buf followed by fuse_session_process_buf to process the request.

The returned file descriptor is valid until fuse_session_unmount is called.

Parameters
sethe session
Returns
a file descriptor

Definition at line 3534 of file fuse_lowlevel.c.

◆ fuse_session_loop()

int fuse_session_loop ( struct fuse_session *  se)

Enter a single threaded, blocking event loop.

When the event loop terminates because the connection to the FUSE kernel module has been closed, this function returns zero. This happens when the filesystem is unmounted regularly (by the filesystem owner or root running the umount(8) or fusermount(1) command), or if connection is explicitly severed by writing 1 to theabort file in /sys/fs/fuse/connections/NNN. The only way to distinguish between these two conditions is to check if the filesystem is still mounted after the session loop returns.

When some error occurs during request processing, the function returns a negated errno(3) value.

If the loop has been terminated because of a signal handler installed by fuse_set_signal_handlers(), this function returns the (positive) signal value that triggered the exit.

Parameters
sethe session
Returns
0, -errno, or a signal value

Definition at line 19 of file fuse_loop.c.

◆ fuse_session_mount()

int fuse_session_mount ( struct fuse_session *  se,
const char *  mountpoint 
)

Mount a FUSE file system.

Parameters
mountpointthe mount point path
sesession object
Returns
0 on success, -1 on failure.

Definition at line 3473 of file fuse_lowlevel.c.

◆ fuse_session_process_buf()

void fuse_session_process_buf ( struct fuse_session *  se,
const struct fuse_buf buf 
)

Process a raw request supplied in a generic buffer

The fuse_buf may contain a memory buffer or a pipe file descriptor.

Parameters
sethe session
bufthe fuse_buf containing the request

Definition at line 2830 of file fuse_lowlevel.c.

◆ fuse_session_receive_buf()

int fuse_session_receive_buf ( struct fuse_session *  se,
struct fuse_buf buf 
)

Read a raw request from the kernel into the supplied buffer.

Depending on file system options, system capabilities, and request size the request is either read into a memory buffer or spliced into a temporary pipe.

Parameters
sethe session
bufthe fuse_buf to store the request in
Returns
the actual size of the raw request, or -errno on error

Definition at line 3285 of file fuse_lowlevel.c.

◆ fuse_session_reset()

void fuse_session_reset ( struct fuse_session *  se)

Reset the terminated flag of a session

Parameters
sethe session

◆ fuse_session_unmount()

void fuse_session_unmount ( struct fuse_session *  se)

Ensure that file system is unmounted.

In regular operation, the file system is typically unmounted by the user calling umount(8) or fusermount(1), which then terminates the FUSE session loop. However, the session loop may also terminate as a result of an explicit call to fuse_session_exit() (e.g. by a signal handler installed by fuse_set_signal_handler()). In this case the filesystem remains mounted, but any attempt to access it will block (while the filesystem process is still running) or give an ESHUTDOWN error (after the filesystem process has terminated).

If the communication channel with the FUSE kernel module is still open (i.e., if the session loop was terminated by an explicit call to fuse_session_exit()), this function will close it and unmount the filesystem. If the communication channel has been closed by the kernel, this method will do (almost) nothing.

NOTE: The above semantics mean that if the connection to the kernel is terminated via the /sys/fs/fuse/connections/NNN/abort file, this method will not unmount the filesystem.

Parameters
sethe session

Definition at line 3539 of file fuse_lowlevel.c.

fuse-3.18.2/doc/html/fuse-3_817_84_2include_2fuse__opt_8h.html0000644000175000017500000007717015156613443022465 0ustar berndbernd libfuse: fuse-3.17.4/include/fuse_opt.h File Reference
libfuse
fuse_opt.h File Reference

Go to the source code of this file.

Data Structures

struct  fuse_opt
 
struct  fuse_args
 

Macros

#define FUSE_OPT_KEY(templ, key)   { templ, -1U, key }
 
#define FUSE_OPT_END   { NULL, 0, 0 }
 
#define FUSE_ARGS_INIT(argc, argv)   { argc, argv, 0 }
 
#define FUSE_OPT_KEY_OPT   -1
 
#define FUSE_OPT_KEY_NONOPT   -2
 
#define FUSE_OPT_KEY_KEEP   -3
 
#define FUSE_OPT_KEY_DISCARD   -4
 

Typedefs

typedef int(* fuse_opt_proc_t) (void *data, const char *arg, int key, struct fuse_args *outargs)
 

Functions

int fuse_opt_parse (struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
 
int fuse_opt_add_opt (char **opts, const char *opt)
 
int fuse_opt_add_opt_escaped (char **opts, const char *opt)
 
int fuse_opt_add_arg (struct fuse_args *args, const char *arg)
 
int fuse_opt_insert_arg (struct fuse_args *args, int pos, const char *arg)
 
void fuse_opt_free_args (struct fuse_args *args)
 
int fuse_opt_match (const struct fuse_opt opts[], const char *opt)
 

Detailed Description

This file defines the option parsing interface of FUSE

Definition in file fuse_opt.h.

Macro Definition Documentation

◆ FUSE_ARGS_INIT

#define FUSE_ARGS_INIT (   argc,
  argv 
)    { argc, argv, 0 }

Initializer for 'struct fuse_args'

Definition at line 123 of file fuse_opt.h.

◆ FUSE_OPT_END

#define FUSE_OPT_END   { NULL, 0, 0 }

Last option. An array of 'struct fuse_opt' must end with a NULL template value

Definition at line 104 of file fuse_opt.h.

◆ FUSE_OPT_KEY

#define FUSE_OPT_KEY (   templ,
  key 
)    { templ, -1U, key }

Key option. In case of a match, the processing function will be called with the specified key.

Definition at line 98 of file fuse_opt.h.

◆ FUSE_OPT_KEY_DISCARD

#define FUSE_OPT_KEY_DISCARD   -4

Special key value for options to discard

Argument is not passed to processing function, but behave as if the processing function returned zero

Definition at line 153 of file fuse_opt.h.

◆ FUSE_OPT_KEY_KEEP

#define FUSE_OPT_KEY_KEEP   -3

Special key value for options to keep

Argument is not passed to processing function, but behave as if the processing function returned 1

Definition at line 145 of file fuse_opt.h.

◆ FUSE_OPT_KEY_NONOPT

#define FUSE_OPT_KEY_NONOPT   -2

Key value passed to the processing function for all non-options

Non-options are the arguments beginning with a character other than '-' or all arguments after the special '–' option

Definition at line 137 of file fuse_opt.h.

◆ FUSE_OPT_KEY_OPT

#define FUSE_OPT_KEY_OPT   -1

Key value passed to the processing function if an option did not match any template

Definition at line 129 of file fuse_opt.h.

Typedef Documentation

◆ fuse_opt_proc_t

typedef int(* fuse_opt_proc_t) (void *data, const char *arg, int key, struct fuse_args *outargs)

Processing function

This function is called if

  • option did not match any 'struct fuse_opt'
  • argument is a non-option
  • option did match and offset was set to -1

The 'arg' parameter will always contain the whole argument or option including the parameter if exists. A two-argument option ("-x foo") is always converted to single argument option of the form "-xfoo" before this function is called.

Options of the form '-ofoo' are passed to this function without the '-o' prefix.

The return value of this function determines whether this argument is to be inserted into the output argument vector, or discarded.

Parameters
datais the user data passed to the fuse_opt_parse() function
argis the whole argument or option
keydetermines why the processing function was called
outargsthe current output argument list
Returns
-1 on error, 0 if arg is to be discarded, 1 if arg should be kept

Definition at line 180 of file fuse_opt.h.

Function Documentation

◆ fuse_opt_add_arg()

int fuse_opt_add_arg ( struct fuse_args args,
const char *  arg 
)

Add an argument to a NULL terminated argument vector

Parameters
argsis the structure containing the current argument list
argis the new argument to add
Returns
-1 on allocation error, 0 on success

Definition at line 55 of file fuse_opt.c.

◆ fuse_opt_add_opt()

int fuse_opt_add_opt ( char **  opts,
const char *  opt 
)

Add an option to a comma separated option list

Parameters
optsis a pointer to an option list, may point to a NULL value
optis the option to add
Returns
-1 on allocation error, 0 on success

Definition at line 139 of file fuse_opt.c.

◆ fuse_opt_add_opt_escaped()

int fuse_opt_add_opt_escaped ( char **  opts,
const char *  opt 
)

Add an option, escaping commas, to a comma separated option list

Parameters
optsis a pointer to an option list, may point to a NULL value
optis the option to add
Returns
-1 on allocation error, 0 on success

Definition at line 144 of file fuse_opt.c.

◆ fuse_opt_free_args()

void fuse_opt_free_args ( struct fuse_args args)

Free the contents of argument list

The structure itself is not freed

Parameters
argsis the structure containing the argument list

Definition at line 34 of file fuse_opt.c.

◆ fuse_opt_insert_arg()

int fuse_opt_insert_arg ( struct fuse_args args,
int  pos,
const char *  arg 
)

Add an argument at the specified position in a NULL terminated argument vector

Adds the argument to the N-th position. This is useful for adding options at the beginning of the array which must not come after the special '–' option.

Parameters
argsis the structure containing the current argument list
posis the position at which to add the argument
argis the new argument to add
Returns
-1 on allocation error, 0 on success

Definition at line 95 of file fuse_opt.c.

◆ fuse_opt_match()

int fuse_opt_match ( const struct fuse_opt  opts[],
const char *  opt 
)

Check if an option matches

Parameters
optsis the option description array
optis the option to match
Returns
1 if a match is found, 0 if not

◆ fuse_opt_parse()

int fuse_opt_parse ( struct fuse_args args,
void *  data,
const struct fuse_opt  opts[],
fuse_opt_proc_t  proc 
)

Option parsing function

If 'args' was returned from a previous call to fuse_opt_parse() or it was constructed from

A NULL 'args' is equivalent to an empty argument vector

A NULL 'opts' is equivalent to an 'opts' array containing a single end marker

A NULL 'proc' is equivalent to a processing function always returning '1'

Parameters
argsis the input and output argument list
datais the user data
optsis the option description array
procis the processing function
Returns
-1 on error, 0 on success

Definition at line 398 of file fuse_opt.c.

fuse-3.18.2/doc/html/fuse-3_818_81_2include_2fuse__opt_8h.html0000644000175000017500000007717015156613443022463 0ustar berndbernd libfuse: fuse-3.18.1/include/fuse_opt.h File Reference
libfuse
fuse_opt.h File Reference

Go to the source code of this file.

Data Structures

struct  fuse_opt
 
struct  fuse_args
 

Macros

#define FUSE_OPT_KEY(templ, key)   { templ, -1U, key }
 
#define FUSE_OPT_END   { NULL, 0, 0 }
 
#define FUSE_ARGS_INIT(argc, argv)   { argc, argv, 0 }
 
#define FUSE_OPT_KEY_OPT   -1
 
#define FUSE_OPT_KEY_NONOPT   -2
 
#define FUSE_OPT_KEY_KEEP   -3
 
#define FUSE_OPT_KEY_DISCARD   -4
 

Typedefs

typedef int(* fuse_opt_proc_t) (void *data, const char *arg, int key, struct fuse_args *outargs)
 

Functions

int fuse_opt_parse (struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
 
int fuse_opt_add_opt (char **opts, const char *opt)
 
int fuse_opt_add_opt_escaped (char **opts, const char *opt)
 
int fuse_opt_add_arg (struct fuse_args *args, const char *arg)
 
int fuse_opt_insert_arg (struct fuse_args *args, int pos, const char *arg)
 
void fuse_opt_free_args (struct fuse_args *args)
 
int fuse_opt_match (const struct fuse_opt opts[], const char *opt)
 

Detailed Description

This file defines the option parsing interface of FUSE

Definition in file fuse_opt.h.

Macro Definition Documentation

◆ FUSE_ARGS_INIT

#define FUSE_ARGS_INIT (   argc,
  argv 
)    { argc, argv, 0 }

Initializer for 'struct fuse_args'

Definition at line 123 of file fuse_opt.h.

◆ FUSE_OPT_END

#define FUSE_OPT_END   { NULL, 0, 0 }

Last option. An array of 'struct fuse_opt' must end with a NULL template value

Definition at line 104 of file fuse_opt.h.

◆ FUSE_OPT_KEY

#define FUSE_OPT_KEY (   templ,
  key 
)    { templ, -1U, key }

Key option. In case of a match, the processing function will be called with the specified key.

Definition at line 98 of file fuse_opt.h.

◆ FUSE_OPT_KEY_DISCARD

#define FUSE_OPT_KEY_DISCARD   -4

Special key value for options to discard

Argument is not passed to processing function, but behave as if the processing function returned zero

Definition at line 153 of file fuse_opt.h.

◆ FUSE_OPT_KEY_KEEP

#define FUSE_OPT_KEY_KEEP   -3

Special key value for options to keep

Argument is not passed to processing function, but behave as if the processing function returned 1

Definition at line 145 of file fuse_opt.h.

◆ FUSE_OPT_KEY_NONOPT

#define FUSE_OPT_KEY_NONOPT   -2

Key value passed to the processing function for all non-options

Non-options are the arguments beginning with a character other than '-' or all arguments after the special '–' option

Definition at line 137 of file fuse_opt.h.

◆ FUSE_OPT_KEY_OPT

#define FUSE_OPT_KEY_OPT   -1

Key value passed to the processing function if an option did not match any template

Definition at line 129 of file fuse_opt.h.

Typedef Documentation

◆ fuse_opt_proc_t

typedef int(* fuse_opt_proc_t) (void *data, const char *arg, int key, struct fuse_args *outargs)

Processing function

This function is called if

  • option did not match any 'struct fuse_opt'
  • argument is a non-option
  • option did match and offset was set to -1

The 'arg' parameter will always contain the whole argument or option including the parameter if exists. A two-argument option ("-x foo") is always converted to single argument option of the form "-xfoo" before this function is called.

Options of the form '-ofoo' are passed to this function without the '-o' prefix.

The return value of this function determines whether this argument is to be inserted into the output argument vector, or discarded.

Parameters
datais the user data passed to the fuse_opt_parse() function
argis the whole argument or option
keydetermines why the processing function was called
outargsthe current output argument list
Returns
-1 on error, 0 if arg is to be discarded, 1 if arg should be kept

Definition at line 180 of file fuse_opt.h.

Function Documentation

◆ fuse_opt_add_arg()

int fuse_opt_add_arg ( struct fuse_args args,
const char *  arg 
)

Add an argument to a NULL terminated argument vector

Parameters
argsis the structure containing the current argument list
argis the new argument to add
Returns
-1 on allocation error, 0 on success

Definition at line 55 of file fuse_opt.c.

◆ fuse_opt_add_opt()

int fuse_opt_add_opt ( char **  opts,
const char *  opt 
)

Add an option to a comma separated option list

Parameters
optsis a pointer to an option list, may point to a NULL value
optis the option to add
Returns
-1 on allocation error, 0 on success

Definition at line 139 of file fuse_opt.c.

◆ fuse_opt_add_opt_escaped()

int fuse_opt_add_opt_escaped ( char **  opts,
const char *  opt 
)

Add an option, escaping commas, to a comma separated option list

Parameters
optsis a pointer to an option list, may point to a NULL value
optis the option to add
Returns
-1 on allocation error, 0 on success

Definition at line 144 of file fuse_opt.c.

◆ fuse_opt_free_args()

void fuse_opt_free_args ( struct fuse_args args)

Free the contents of argument list

The structure itself is not freed

Parameters
argsis the structure containing the argument list

Definition at line 34 of file fuse_opt.c.

◆ fuse_opt_insert_arg()

int fuse_opt_insert_arg ( struct fuse_args args,
int  pos,
const char *  arg 
)

Add an argument at the specified position in a NULL terminated argument vector

Adds the argument to the N-th position. This is useful for adding options at the beginning of the array which must not come after the special '–' option.

Parameters
argsis the structure containing the current argument list
posis the position at which to add the argument
argis the new argument to add
Returns
-1 on allocation error, 0 on success

Definition at line 95 of file fuse_opt.c.

◆ fuse_opt_match()

int fuse_opt_match ( const struct fuse_opt  opts[],
const char *  opt 
)

Check if an option matches

Parameters
optsis the option description array
optis the option to match
Returns
1 if a match is found, 0 if not

◆ fuse_opt_parse()

int fuse_opt_parse ( struct fuse_args args,
void *  data,
const struct fuse_opt  opts[],
fuse_opt_proc_t  proc 
)

Option parsing function

If 'args' was returned from a previous call to fuse_opt_parse() or it was constructed from

A NULL 'args' is equivalent to an empty argument vector

A NULL 'opts' is equivalent to an 'opts' array containing a single end marker

A NULL 'proc' is equivalent to a processing function always returning '1'

Parameters
argsis the input and output argument list
datais the user data
optsis the option description array
procis the processing function
Returns
-1 on error, 0 on success

Definition at line 398 of file fuse_opt.c.

fuse-3.18.2/doc/html/include_2fuse__opt_8h.html0000644000175000017500000007602615156613443020306 0ustar berndbernd libfuse: include/fuse_opt.h File Reference
libfuse
fuse_opt.h File Reference

Go to the source code of this file.

Data Structures

struct  fuse_opt
 
struct  fuse_args
 

Macros

#define FUSE_OPT_KEY(templ, key)   { templ, -1U, key }
 
#define FUSE_OPT_END   { NULL, 0, 0 }
 
#define FUSE_ARGS_INIT(argc, argv)   { argc, argv, 0 }
 
#define FUSE_OPT_KEY_OPT   -1
 
#define FUSE_OPT_KEY_NONOPT   -2
 
#define FUSE_OPT_KEY_KEEP   -3
 
#define FUSE_OPT_KEY_DISCARD   -4
 

Typedefs

typedef int(* fuse_opt_proc_t) (void *data, const char *arg, int key, struct fuse_args *outargs)
 

Functions

int fuse_opt_parse (struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
 
int fuse_opt_add_opt (char **opts, const char *opt)
 
int fuse_opt_add_opt_escaped (char **opts, const char *opt)
 
int fuse_opt_add_arg (struct fuse_args *args, const char *arg)
 
int fuse_opt_insert_arg (struct fuse_args *args, int pos, const char *arg)
 
void fuse_opt_free_args (struct fuse_args *args)
 
int fuse_opt_match (const struct fuse_opt opts[], const char *opt)
 

Detailed Description

This file defines the option parsing interface of FUSE

Definition in file fuse_opt.h.

Macro Definition Documentation

◆ FUSE_ARGS_INIT

#define FUSE_ARGS_INIT (   argc,
  argv 
)    { argc, argv, 0 }

Initializer for 'struct fuse_args'

Definition at line 123 of file fuse_opt.h.

◆ FUSE_OPT_END

#define FUSE_OPT_END   { NULL, 0, 0 }

Last option. An array of 'struct fuse_opt' must end with a NULL template value

Definition at line 104 of file fuse_opt.h.

◆ FUSE_OPT_KEY

#define FUSE_OPT_KEY (   templ,
  key 
)    { templ, -1U, key }

Key option. In case of a match, the processing function will be called with the specified key.

Definition at line 98 of file fuse_opt.h.

◆ FUSE_OPT_KEY_DISCARD

#define FUSE_OPT_KEY_DISCARD   -4

Special key value for options to discard

Argument is not passed to processing function, but behave as if the processing function returned zero

Definition at line 153 of file fuse_opt.h.

◆ FUSE_OPT_KEY_KEEP

#define FUSE_OPT_KEY_KEEP   -3

Special key value for options to keep

Argument is not passed to processing function, but behave as if the processing function returned 1

Definition at line 145 of file fuse_opt.h.

◆ FUSE_OPT_KEY_NONOPT

#define FUSE_OPT_KEY_NONOPT   -2

Key value passed to the processing function for all non-options

Non-options are the arguments beginning with a character other than '-' or all arguments after the special '–' option

Definition at line 137 of file fuse_opt.h.

◆ FUSE_OPT_KEY_OPT

#define FUSE_OPT_KEY_OPT   -1

Key value passed to the processing function if an option did not match any template

Definition at line 129 of file fuse_opt.h.

Typedef Documentation

◆ fuse_opt_proc_t

typedef int(* fuse_opt_proc_t) (void *data, const char *arg, int key, struct fuse_args *outargs)

Processing function

This function is called if

  • option did not match any 'struct fuse_opt'
  • argument is a non-option
  • option did match and offset was set to -1

The 'arg' parameter will always contain the whole argument or option including the parameter if exists. A two-argument option ("-x foo") is always converted to single argument option of the form "-xfoo" before this function is called.

Options of the form '-ofoo' are passed to this function without the '-o' prefix.

The return value of this function determines whether this argument is to be inserted into the output argument vector, or discarded.

Parameters
datais the user data passed to the fuse_opt_parse() function
argis the whole argument or option
keydetermines why the processing function was called
outargsthe current output argument list
Returns
-1 on error, 0 if arg is to be discarded, 1 if arg should be kept

Definition at line 180 of file fuse_opt.h.

Function Documentation

◆ fuse_opt_add_arg()

int fuse_opt_add_arg ( struct fuse_args args,
const char *  arg 
)

Add an argument to a NULL terminated argument vector

Parameters
argsis the structure containing the current argument list
argis the new argument to add
Returns
-1 on allocation error, 0 on success

Definition at line 55 of file fuse_opt.c.

◆ fuse_opt_add_opt()

int fuse_opt_add_opt ( char **  opts,
const char *  opt 
)

Add an option to a comma separated option list

Parameters
optsis a pointer to an option list, may point to a NULL value
optis the option to add
Returns
-1 on allocation error, 0 on success

Definition at line 139 of file fuse_opt.c.

◆ fuse_opt_add_opt_escaped()

int fuse_opt_add_opt_escaped ( char **  opts,
const char *  opt 
)

Add an option, escaping commas, to a comma separated option list

Parameters
optsis a pointer to an option list, may point to a NULL value
optis the option to add
Returns
-1 on allocation error, 0 on success

Definition at line 144 of file fuse_opt.c.

◆ fuse_opt_free_args()

void fuse_opt_free_args ( struct fuse_args args)

Free the contents of argument list

The structure itself is not freed

Parameters
argsis the structure containing the argument list

Definition at line 34 of file fuse_opt.c.

◆ fuse_opt_insert_arg()

int fuse_opt_insert_arg ( struct fuse_args args,
int  pos,
const char *  arg 
)

Add an argument at the specified position in a NULL terminated argument vector

Adds the argument to the N-th position. This is useful for adding options at the beginning of the array which must not come after the special '–' option.

Parameters
argsis the structure containing the current argument list
posis the position at which to add the argument
argis the new argument to add
Returns
-1 on allocation error, 0 on success

Definition at line 95 of file fuse_opt.c.

◆ fuse_opt_match()

int fuse_opt_match ( const struct fuse_opt  opts[],
const char *  opt 
)

Check if an option matches

Parameters
optsis the option description array
optis the option to match
Returns
1 if a match is found, 0 if not

◆ fuse_opt_parse()

int fuse_opt_parse ( struct fuse_args args,
void *  data,
const struct fuse_opt  opts[],
fuse_opt_proc_t  proc 
)

Option parsing function

If 'args' was returned from a previous call to fuse_opt_parse() or it was constructed from

A NULL 'args' is equivalent to an empty argument vector

A NULL 'opts' is equivalent to an 'opts' array containing a single end marker

A NULL 'proc' is equivalent to a processing function always returning '1'

Parameters
argsis the input and output argument list
datais the user data
optsis the option description array
procis the processing function
Returns
-1 on error, 0 on success

Definition at line 398 of file fuse_opt.c.

fuse-3.18.2/doc/html/structfuse__args.html0000644000175000017500000001432615156613443017514 0ustar berndbernd libfuse: fuse_args Struct Reference
libfuse
fuse_args Struct Reference

#include <fuse_opt.h>

Data Fields

int argc
 
char ** argv
 
int allocated
 

Detailed Description

Argument list

Definition at line 109 of file fuse_opt.h.

Field Documentation

◆ allocated

int fuse_args::allocated

Is 'argv' allocated?

Definition at line 117 of file fuse_opt.h.

◆ argc

int fuse_args::argc

Argument count

Definition at line 111 of file fuse_opt.h.

◆ argv

char ** fuse_args::argv

Argument vector. NULL terminated

Definition at line 114 of file fuse_opt.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__buf.html0000644000175000017500000002377015156613443017337 0ustar berndbernd libfuse: fuse_buf Struct Reference
libfuse
fuse_buf Struct Reference

#include <fuse_common.h>

Data Fields

size_t size
 
enum fuse_buf_flags flags
 
void * mem
 
int fd
 
off_t pos
 
size_t mem_size
 

Detailed Description

Single data buffer

Generic data buffer for I/O, extended attributes, etc... Data may be supplied as a memory pointer or as a file descriptor

Definition at line 874 of file fuse_common.h.

Field Documentation

◆ fd

int fuse_buf::fd

File descriptor

Used if FUSE_BUF_IS_FD flag is set.

Definition at line 897 of file fuse_common.h.

◆ flags

enum fuse_buf_flags fuse_buf::flags

Buffer flags

Definition at line 883 of file fuse_common.h.

◆ mem

void * fuse_buf::mem

Memory pointer

Used unless FUSE_BUF_IS_FD flag is set.

Definition at line 890 of file fuse_common.h.

◆ mem_size

size_t fuse_buf::mem_size

Size of memory pointer

Used only if mem was internally allocated. Not used if mem was user-provided.

Definition at line 912 of file fuse_common.h.

◆ pos

off_t fuse_buf::pos

File position

Used if FUSE_BUF_FD_SEEK flag is set.

Definition at line 904 of file fuse_common.h.

◆ size

size_t fuse_buf::size

Size of data in bytes

Definition at line 878 of file fuse_common.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__bufvec.html0000644000175000017500000001723215156613443020031 0ustar berndbernd libfuse: fuse_bufvec Struct Reference
libfuse
fuse_bufvec Struct Reference

#include <fuse_common.h>

Data Fields

size_t count
 
size_t idx
 
size_t off
 
struct fuse_buf buf [1]
 

Detailed Description

Data buffer vector

An array of data buffers, each containing a memory pointer or a file descriptor.

Allocate dynamically to add more than one buffer.

Definition at line 923 of file fuse_common.h.

Field Documentation

◆ buf

struct fuse_buf fuse_bufvec::buf

Array of buffers

Definition at line 942 of file fuse_common.h.

◆ count

size_t fuse_bufvec::count

Number of buffers in the array

Definition at line 927 of file fuse_common.h.

◆ idx

size_t fuse_bufvec::idx

Index of current buffer within the array

Definition at line 932 of file fuse_common.h.

◆ off

size_t fuse_bufvec::off

Current offset within the current buffer

Definition at line 937 of file fuse_common.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__cmdline__opts.html0000644000175000017500000000570615156613443021401 0ustar berndbernd libfuse: fuse_cmdline_opts Struct Reference
libfuse
fuse_cmdline_opts Struct Reference

#include <fuse_lowlevel.h>

Detailed Description

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.

Definition at line 2003 of file fuse_lowlevel.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__config.html0000644000175000017500000010102015156613443020011 0ustar berndbernd libfuse: fuse_config Struct Reference
libfuse
fuse_config Struct Reference

#include <fuse.h>

Data Fields

int32_t set_gid
 
int32_t set_uid
 
int32_t set_mode
 
double entry_timeout
 
double negative_timeout
 
double attr_timeout
 
int32_t intr
 
int32_t intr_signal
 
int32_t remember
 
int32_t hard_remove
 
int32_t use_ino
 
int32_t readdir_ino
 
int32_t direct_io
 
int32_t kernel_cache
 
int32_t auto_cache
 
int32_t nullpath_ok
 
int32_t show_help
 
uint32_t fmask
 
int32_t no_rofd_flush
 
int32_t parallel_direct_writes
 
uint32_t flags
 
uint64_t reserved [48]
 

Detailed Description

Configuration of the high-level API

This structure is initialized from the arguments passed to fuse_new(), and then passed to the file system's init() handler which should ensure that the configuration is compatible with the file system implementation.

Note: this data structure is ABI sensitive, new options have to be appended at the end of the structure

Definition at line 101 of file fuse.h.

Field Documentation

◆ attr_timeout

double fuse_config::attr_timeout

The timeout in seconds for which file/directory attributes (as returned by e.g. the getattr handler) are cached.

Definition at line 143 of file fuse.h.

◆ auto_cache

int32_t fuse_config::auto_cache

This option is an alternative to kernel_cache. Instead of unconditionally keeping cached data, the cached data is invalidated on open(2) if if the modification time or the size of the file has changed since it was last opened.

Definition at line 253 of file fuse.h.

◆ direct_io

int32_t fuse_config::direct_io

This option disables the use of page cache (file content cache) in the kernel for this filesystem. This has several affects:

  1. Each read(2) or write(2) system call will initiate one or more read or write operations, data will not be cached in the kernel.
  2. The return value of the read() and write() system calls will correspond to the return values of the read and write operations. This is useful for example if the file size is not known in advance (before reading it).

Internally, enabling this option causes fuse to set the direct_io field of struct fuse_file_info - overwriting any value that was put there by the file system.

Definition at line 226 of file fuse.h.

◆ entry_timeout

double fuse_config::entry_timeout

The timeout in seconds for which name lookups will be cached.

Definition at line 127 of file fuse.h.

◆ flags

uint32_t fuse_config::flags

Reserved for future use.

Definition at line 318 of file fuse.h.

◆ fmask

uint32_t fuse_config::fmask

fmask and dmask function the same way as umask, but apply to files and directories separately. If non-zero, fmask and dmask take precedence over the umask setting.

Definition at line 288 of file fuse.h.

◆ hard_remove

int32_t fuse_config::hard_remove

The default behavior is that if an open file is deleted, the file is renamed to a hidden file (.fuse_hiddenXXX), and only removed when the file is finally released. This relieves the filesystem implementation of having to deal with this problem. This option disables the hiding behavior, and files are removed immediately in an unlink operation (or in a rename operation which overwrites an existing file).

It is recommended that you not use the hard_remove option. When hard_remove is set, the following libc functions fail on unlinked files (returning errno of ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2), ftruncate(2), fstat(2), fchmod(2), fchown(2)

Definition at line 185 of file fuse.h.

◆ intr

int32_t fuse_config::intr

Allow requests to be interrupted

Definition at line 148 of file fuse.h.

◆ intr_signal

int32_t fuse_config::intr_signal

Specify which signal number to send to the filesystem when a request is interrupted. The default is hardcoded to USR1.

Definition at line 155 of file fuse.h.

◆ kernel_cache

int32_t fuse_config::kernel_cache

This option disables flushing the cache of the file contents on every open(2). This should only be enabled on filesystems where the file data is never changed externally (not through the mounted FUSE filesystem). Thus it is not suitable for network filesystems and other intermediate filesystems.

NOTE: if this option is not specified (and neither direct_io) data is still cached after the open(2), so a read(2) system call will not always initiate a read operation.

Internally, enabling this option causes fuse to set the keep_cache field of struct fuse_file_info - overwriting any value that was put there by the file system.

Definition at line 245 of file fuse.h.

◆ negative_timeout

double fuse_config::negative_timeout

The timeout in seconds for which a negative lookup will be cached. This means, that if file did not exist (lookup returned ENOENT), the lookup will only be redone after the timeout, and the file/directory will be assumed to not exist until then. A value of zero means that negative lookups are not cached.

Definition at line 137 of file fuse.h.

◆ no_rofd_flush

int32_t fuse_config::no_rofd_flush

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.

Definition at line 297 of file fuse.h.

◆ nullpath_ok

int32_t fuse_config::nullpath_ok

If this option is given the file-system handlers for the following operations will not receive path information: read, write, flush, release, fallocate, fsync, readdir, releasedir, fsyncdir, lock, ioctl and poll.

For the truncate, getattr, chmod, chown and utimens operations the path will be provided only if the struct fuse_file_info argument is NULL.

Definition at line 273 of file fuse.h.

◆ parallel_direct_writes

int32_t fuse_config::parallel_direct_writes

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).

Definition at line 312 of file fuse.h.

◆ readdir_ino

int32_t fuse_config::readdir_ino

If use_ino option is not given, still try to fill in the d_ino field in readdir(2). If the name was previously looked up, and is still in the cache, the inode number found there will be used. Otherwise it will be set to -1. If use_ino option is given, this option is ignored.

Definition at line 207 of file fuse.h.

◆ remember

int32_t fuse_config::remember

Normally, FUSE assigns inodes to paths only for as long as the kernel is aware of them. With this option inodes are instead remembered for at least this many seconds. This will require more memory, but may be necessary when using applications that make use of inode numbers.

A number of -1 means that inodes will be remembered for the entire life-time of the file-system process.

Definition at line 167 of file fuse.h.

◆ reserved

uint64_t fuse_config::reserved

Reserved for future use.

Definition at line 323 of file fuse.h.

◆ set_gid

int32_t fuse_config::set_gid

If set_gid is non-zero, the st_gid attribute of each file is overwritten with the value of gid.

Definition at line 106 of file fuse.h.

◆ set_mode

int32_t fuse_config::set_mode

If set_mode is non-zero, the any permissions bits set in umask are unset in the st_mode attribute of each file.

Definition at line 120 of file fuse.h.

◆ set_uid

int32_t fuse_config::set_uid

If set_uid is non-zero, the st_uid attribute of each file is overwritten with the value of uid.

Definition at line 113 of file fuse.h.

◆ show_help

int32_t fuse_config::show_help

These 3 options are used by libfuse internally and should not be touched.

Definition at line 279 of file fuse.h.

◆ use_ino

int32_t fuse_config::use_ino

Honor the st_ino field in the functions getattr() and fill_dir(). This value is used to fill in the st_ino field in the stat(2), lstat(2), fstat(2) functions and the d_ino field in the readdir(2) function. The filesystem does not have to guarantee uniqueness, however some applications rely on this value being unique for the whole filesystem.

Note that this does not affect the inode that libfuse and the kernel use internally (also called the "nodeid").

Definition at line 198 of file fuse.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__conn__info.html0000644000175000017500000006256615156613443020700 0ustar berndbernd libfuse: fuse_conn_info Struct Reference
libfuse
fuse_conn_info Struct Reference

#include <fuse_common.h>

Data Fields

uint32_t proto_major
 
uint32_t proto_minor
 
uint32_t max_write
 
uint32_t max_read
 
uint32_t max_readahead
 
uint32_t capable
 
uint32_t want
 
uint32_t max_background
 
uint32_t congestion_threshold
 
uint32_t time_gran
 
uint32_t no_interrupt: 1
 
uint64_t capable_ext
 
uint64_t want_ext
 
uint32_t reserved [16]
 
uint16_t request_timeout
 
uint16_t reserved [31]
 

Detailed Description

Connection information, passed to the ->init() method

Some of the elements are read-write, these can be changed to indicate the value requested by the filesystem. The requested value must usually be smaller than the indicated value.

Note: The capable and want fields are limited to 32 bits for ABI compatibility. For full 64-bit capability support, use the capable_ext and want_ext fields instead.

Definition at line 538 of file fuse_common.h.

Field Documentation

◆ capable

uint32_t fuse_conn_info::capable

Capability flags that the kernel supports (read-only)

Deprecated left over for ABI compatibility, use capable_ext

Definition at line 578 of file fuse_common.h.

◆ capable_ext

uint64_t fuse_conn_info::capable_ext

Extended capability flags that the kernel supports (read-only) This field provides full 64-bit capability support.

Definition at line 686 of file fuse_common.h.

◆ congestion_threshold

uint32_t fuse_conn_info::congestion_threshold

Kernel congestion threshold parameter. If the number of pending background requests exceeds this number, the FUSE kernel module will mark the filesystem as "congested". This instructs the kernel to expect that queued requests will take some time to complete, and to adjust its algorithms accordingly (e.g. by putting a waiting thread to sleep instead of using a busy-loop).

Definition at line 630 of file fuse_common.h.

◆ max_background

uint32_t fuse_conn_info::max_background

Maximum number of pending "background" requests. A background request is any type of request for which the total number is not limited by other means. As of kernel 4.8, only two types of requests fall into this category:

  1. Read-ahead requests
  2. Asynchronous direct I/O requests

Read-ahead requests are generated (if max_readahead is non-zero) by the kernel to preemptively fill its caches when it anticipates that userspace will soon read more data.

Asynchronous direct I/O requests are generated if FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large direct I/O request. In this case the kernel will internally split it up into multiple smaller requests and submit them to the filesystem concurrently.

Note that the following requests are not background requests: writeback requests (limited by the kernel's flusher algorithm), regular (i.e., synchronous and buffered) userspace read/write requests (limited to one per thread), asynchronous read requests (Linux's io_submit(2) call actually blocks, so these are also limited to one per thread).

Definition at line 620 of file fuse_common.h.

◆ max_read

uint32_t fuse_conn_info::max_read

Maximum size of read requests. A value of zero indicates no limit. However, even if the filesystem does not specify a limit, the maximum size of read requests will still be limited by the kernel.

NOTE: For the time being, the maximum size of read requests must be set both here and passed to fuse_session_new() using the -o max_read=<n> mount option. At some point in the future, specifying the mount option will no longer be necessary.

Definition at line 566 of file fuse_common.h.

◆ max_readahead

uint32_t fuse_conn_info::max_readahead

Maximum readahead

Definition at line 571 of file fuse_common.h.

◆ max_write

uint32_t fuse_conn_info::max_write

Maximum size of the write buffer

Definition at line 552 of file fuse_common.h.

◆ no_interrupt

uint32_t fuse_conn_info::no_interrupt

Disable FUSE_INTERRUPT requests.

Enable no_interrupt option to: 1) Avoid unnecessary locking operations and list operations, 2) Return ENOSYS for the reply of FUSE_INTERRUPT request to inform the kernel not to send the FUSE_INTERRUPT request.

Definition at line 677 of file fuse_common.h.

◆ proto_major

uint32_t fuse_conn_info::proto_major

Major version of the protocol (read-only)

Definition at line 542 of file fuse_common.h.

◆ proto_minor

uint32_t fuse_conn_info::proto_minor

Minor version of the protocol (read-only)

Definition at line 547 of file fuse_common.h.

◆ request_timeout

uint16_t fuse_conn_info::request_timeout

Request timeout (in seconds). If the request is not answered by this timeout, the connection will be aborted by the kernel.

Definition at line 707 of file fuse_common.h.

◆ reserved [1/2]

uint16_t fuse_conn_info::reserved

For future use.

Definition at line 701 of file fuse_common.h.

◆ reserved [2/2]

uint16_t fuse_conn_info::reserved[31]

For future use.

Definition at line 712 of file fuse_common.h.

◆ time_gran

uint32_t fuse_conn_info::time_gran

When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible for updating mtime and ctime when write requests are received. The updated values are passed to the filesystem with setattr() requests. However, if the filesystem does not support the full resolution of the kernel timestamps (nanoseconds), the mtime and ctime values used by kernel and filesystem will differ (and result in an apparent change of times after a cache flush).

To prevent this problem, this variable can be used to inform the kernel about the timestamp granularity supported by the file-system. The value should be power of 10. The default is 1, i.e. full nano-second resolution. Filesystems supporting only second resolution should set this to 1000000000.

Definition at line 647 of file fuse_common.h.

◆ want

uint32_t fuse_conn_info::want

Capability flags that the filesystem wants to enable.

libfuse attempts to initialize this field with reasonable default values before calling the init() handler.

Deprecated left over for ABI compatibility. Use want_ext with the helper functions fuse_set_feature_flag() / fuse_unset_feature_flag()

Definition at line 590 of file fuse_common.h.

◆ want_ext

uint64_t fuse_conn_info::want_ext

Extended capability flags that the filesystem wants to enable. This field provides full 64-bit capability support.

Don't set this field directly, but use the helper functions fuse_set_feature_flag() / fuse_unset_feature_flag()

Definition at line 696 of file fuse_common.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__context.html0000644000175000017500000002301015156613443020232 0ustar berndbernd libfuse: fuse_context Struct Reference
libfuse
fuse_context Struct Reference

#include <fuse.h>

Data Fields

struct fuse * fuse
 
uid_t uid
 
gid_t gid
 
pid_t pid
 
void * private_data
 
mode_t umask
 

Detailed Description

Extra context that may be needed by some filesystems

The uid, gid and pid fields are not filled in case of a writepage operation.

Definition at line 860 of file fuse.h.

Field Documentation

◆ fuse

struct fuse * fuse_context::fuse

Pointer to the fuse object

Definition at line 862 of file fuse.h.

◆ gid

gid_t fuse_context::gid

Group ID of the calling process

Definition at line 868 of file fuse.h.

◆ pid

pid_t fuse_context::pid

Process ID of the calling thread

Definition at line 871 of file fuse.h.

◆ private_data

void * fuse_context::private_data

Private filesystem data

Definition at line 874 of file fuse.h.

◆ uid

uid_t fuse_context::uid

User ID of the calling process

Definition at line 865 of file fuse.h.

◆ umask

mode_t fuse_context::umask

Umask of the calling process

Definition at line 877 of file fuse.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__ctx.html0000644000175000017500000001727315156613443017362 0ustar berndbernd libfuse: fuse_ctx Struct Reference
libfuse
fuse_ctx Struct Reference

#include <fuse_lowlevel.h>

Data Fields

uid_t uid
 
gid_t gid
 
pid_t pid
 
mode_t umask
 

Detailed Description

Additional context associated with requests.

Note that the reported client uid, gid and pid may be zero in some situations. For example, if the FUSE file system is running in a PID or user namespace but then accessed from outside the namespace, there is no valid uid/pid/gid that could be reported.

Definition at line 112 of file fuse_lowlevel.h.

Field Documentation

◆ gid

gid_t fuse_ctx::gid

Group ID of the calling process

Definition at line 117 of file fuse_lowlevel.h.

◆ pid

pid_t fuse_ctx::pid

Thread ID of the calling process

Definition at line 120 of file fuse_lowlevel.h.

◆ uid

uid_t fuse_ctx::uid

User ID of the calling process

Definition at line 114 of file fuse_lowlevel.h.

◆ umask

mode_t fuse_ctx::umask

Umask of the calling process

Definition at line 123 of file fuse_lowlevel.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__entry__param.html0000644000175000017500000002376615156613443021250 0ustar berndbernd libfuse: fuse_entry_param Struct Reference
libfuse
fuse_entry_param Struct Reference

#include <fuse_lowlevel.h>

Data Fields

fuse_ino_t ino
 
uint64_t generation
 
struct stat attr
 
double attr_timeout
 
double entry_timeout
 

Detailed Description

Directory entry parameters supplied to fuse_reply_entry()

Definition at line 60 of file fuse_lowlevel.h.

Field Documentation

◆ attr

struct stat fuse_entry_param::attr

Inode attributes.

Even if attr_timeout == 0, attr must be correct. For example, for open(), FUSE uses attr.st_size from lookup() to determine how many bytes to request. If this value is not correct, incorrect data will be returned.

Definition at line 89 of file fuse_lowlevel.h.

◆ attr_timeout

double fuse_entry_param::attr_timeout

Validity timeout (in seconds) for inode attributes. If attributes only change as a result of requests that come through the kernel, this should be set to a very large value.

Definition at line 95 of file fuse_lowlevel.h.

◆ entry_timeout

double fuse_entry_param::entry_timeout

Validity timeout (in seconds) for the name. If directory entries are changed/deleted only as a result of requests that come through the kernel, this should be set to a very large value.

Definition at line 101 of file fuse_lowlevel.h.

◆ generation

uint64_t fuse_entry_param::generation

Generation number for this entry.

If the file system will be exported over NFS, the ino/generation pairs need to be unique over the file system's lifetime (rather than just the mount time). So if the file system reuses an inode after it has been deleted, it must assign a new, previously unused generation number to the inode at the same time.

Definition at line 80 of file fuse_lowlevel.h.

◆ ino

fuse_ino_t fuse_entry_param::ino

Unique inode number

In lookup, zero means negative entry (from version 2.5) Returning ENOENT also means negative entry, but by setting zero ino the kernel may cache negative entries for entry_timeout seconds.

Definition at line 68 of file fuse_lowlevel.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__ext__header.html0000644000175000017500000000602015156613443021017 0ustar berndbernd libfuse: fuse_ext_header Struct Reference
libfuse
fuse_ext_header Struct Reference

#include <fuse_kernel.h>

Detailed Description

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

Definition at line 1174 of file fuse_kernel.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__file__info.html0000644000175000017500000005326115156613443020652 0ustar berndbernd libfuse: fuse_file_info Struct Reference
libfuse
fuse_file_info Struct Reference

#include <fuse_common.h>

Data Fields

int32_t flags
 
uint32_t writepage: 1
 
uint32_t direct_io: 1
 
uint32_t keep_cache: 1
 
uint32_t flush: 1
 
uint32_t nonseekable: 1
 
uint32_t cache_readdir: 1
 
uint32_t noflush: 1
 
uint32_t parallel_direct_writes: 1
 
uint32_t padding: 23
 
uint64_t fh
 
uint64_t lock_owner
 
uint32_t poll_events
 
int32_t backing_id
 
uint64_t compat_flags
 

Detailed Description

Information about an open file.

File Handles are created by the open, opendir, and create methods and closed by the release and releasedir methods. Multiple file handles may be concurrently open for the same file. Generally, a client will create one file handle per file descriptor, though in some cases multiple file descriptors can share a single file handle.

Note: This data structure is ABI sensitive, new parameters have to be added within padding/padding2 bits and always below existing parameters.

Definition at line 50 of file fuse_common.h.

Field Documentation

◆ backing_id

int32_t fuse_file_info::backing_id

Passthrough backing file id. May be filled in by filesystem in create and open. It is used to create a passthrough connection between FUSE file and backing file.

Definition at line 119 of file fuse_common.h.

◆ cache_readdir

uint32_t fuse_file_info::cache_readdir

Can be filled in by opendir. It signals the kernel to enable caching of entries returned by readdir(). Has no effect when set in other contexts (in particular it does nothing when set by open()).

Definition at line 89 of file fuse_common.h.

◆ compat_flags

uint64_t fuse_file_info::compat_flags

struct fuse_file_info api and abi flags

Definition at line 122 of file fuse_common.h.

◆ direct_io

uint32_t fuse_file_info::direct_io

Can be filled in by open/create, to use direct I/O on this file.

Definition at line 63 of file fuse_common.h.

◆ fh

uint64_t fuse_file_info::fh

File handle id. May be filled in by filesystem in create, open, and opendir(). Available in most other file operations on the same file handle.

Definition at line 107 of file fuse_common.h.

◆ flags

int32_t fuse_file_info::flags

Open flags. Available in open(), release() and create()

Definition at line 52 of file fuse_common.h.

◆ flush

uint32_t fuse_file_info::flush

Indicates a flush operation. Set in flush operation, also maybe set in highlevel lock operation and lowlevel release operation.

Definition at line 74 of file fuse_common.h.

◆ keep_cache

uint32_t fuse_file_info::keep_cache

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.

Definition at line 69 of file fuse_common.h.

◆ lock_owner

uint64_t fuse_file_info::lock_owner

Lock owner id. Available in locking operations and flush

Definition at line 110 of file fuse_common.h.

◆ noflush

uint32_t fuse_file_info::noflush

Can be filled in by open, to indicate that flush is not needed on close.

Definition at line 93 of file fuse_common.h.

◆ nonseekable

uint32_t fuse_file_info::nonseekable

Can be filled in by open, to indicate that the file is not seekable.

Definition at line 78 of file fuse_common.h.

◆ padding

uint32_t fuse_file_info::padding

Padding. Reserved for future use

Definition at line 100 of file fuse_common.h.

◆ parallel_direct_writes

uint32_t fuse_file_info::parallel_direct_writes

Can be filled by open/create, to allow parallel direct writes on this file

Definition at line 97 of file fuse_common.h.

◆ poll_events

uint32_t fuse_file_info::poll_events

Requested poll events. Available in ->poll. Only set on kernels which support it. If unsupported, this field is set to zero.

Definition at line 114 of file fuse_common.h.

◆ writepage

uint32_t fuse_file_info::writepage

In case of a write operation indicates if this was caused by a delayed write from the page cache. If so, then the context's pid, uid, and gid fields will not be valid, and the fh value may not match the fh value that would have been sent with the corresponding individual write requests if write caching had been disabled.

Definition at line 60 of file fuse_common.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__loop__config.html0000644000175000017500000002257015156613443021215 0ustar berndbernd libfuse: fuse_loop_config Struct Reference
libfuse
fuse_loop_config Struct Reference

#include <fuse_i.h>

Data Fields

int clone_fd
 
unsigned int max_idle_threads
 
int max_idle_threads
 
unsigned int max_threads
 

Detailed Description

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.

Definition at line 139 of file fuse_common.h.

Field Documentation

◆ clone_fd

int fuse_loop_config::clone_fd

whether to use separate device fds for each thread (may increase performance)

Definition at line 147 of file fuse_common.h.

◆ max_idle_threads [1/2]

int fuse_loop_config::max_idle_threads

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 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.

Definition at line 159 of file fuse_common.h.

◆ max_idle_threads [2/2]

int fuse_loop_config::max_idle_threads

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.

Definition at line 159 of file fuse_i.h.

◆ max_threads

unsigned int fuse_loop_config::max_threads

max number of threads taking and processing kernel requests

As of now threads are created dynamically

Definition at line 166 of file fuse_i.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__lowlevel__ops.html0000644000175000017500000040050715156613443021431 0ustar berndbernd libfuse: fuse_lowlevel_ops Struct Reference
libfuse
fuse_lowlevel_ops Struct Reference

#include <fuse_lowlevel.h>

Data Fields

void(* init )(void *userdata, struct fuse_conn_info *conn)
 
void(* destroy )(void *userdata)
 
void(* lookup )(fuse_req_t req, fuse_ino_t parent, const char *name)
 
void(* forget )(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
 
void(* getattr )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
 
void(* setattr )(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)
 
void(* readlink )(fuse_req_t req, fuse_ino_t ino)
 
void(* mknod )(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
 
void(* mkdir )(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
 
void(* unlink )(fuse_req_t req, fuse_ino_t parent, const char *name)
 
void(* rmdir )(fuse_req_t req, fuse_ino_t parent, const char *name)
 
void(* symlink )(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)
 
void(* rename )(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags)
 
void(* link )(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)
 
void(* open )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
 
void(* read )(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
 
void(* write )(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi)
 
void(* flush )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
 
void(* release )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
 
void(* fsync )(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
 
void(* opendir )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
 
void(* readdir )(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
 
void(* releasedir )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
 
void(* fsyncdir )(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
 
void(* statfs )(fuse_req_t req, fuse_ino_t ino)
 
void(* setxattr )(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags)
 
void(* getxattr )(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
 
void(* listxattr )(fuse_req_t req, fuse_ino_t ino, size_t size)
 
void(* removexattr )(fuse_req_t req, fuse_ino_t ino, const char *name)
 
void(* access )(fuse_req_t req, fuse_ino_t ino, int mask)
 
void(* create )(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi)
 
void(* getlk )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
 
void(* setlk )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)
 
void(* bmap )(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)
 
void(* ioctl )(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz)
 
void(* poll )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph)
 
void(* write_buf )(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, off_t off, struct fuse_file_info *fi)
 
void(* retrieve_reply )(fuse_req_t req, void *cookie, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv)
 
void(* forget_multi )(fuse_req_t req, size_t count, struct fuse_forget_data *forgets)
 
void(* flock )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op)
 
void(* fallocate )(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi)
 
void(* readdirplus )(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
 
void(* copy_file_range )(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fuse_file_info *fi_in, fuse_ino_t ino_out, off_t off_out, struct fuse_file_info *fi_out, size_t len, int flags)
 
void(* lseek )(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi)
 
void(* tmpfile )(fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info *fi)
 
void(* statx )(fuse_req_t req, fuse_ino_t ino, int flags, int mask, struct fuse_file_info *fi)
 

Detailed Description

Low level filesystem operations

Most of the methods (with the exception of init and destroy) receive a request handle (fuse_req_t) as their first argument. This handle must be passed to one of the specified reply functions.

This may be done inside the method invocation, or after the call has returned. The request handle is valid until one of the reply functions is called.

Other pointer arguments (name, fuse_file_info, etc) are not valid after the call has returned, so if they are needed later, their contents have to be copied.

In general, all methods are expected to perform any necessary permission checking. However, a filesystem may delegate this task to the kernel by passing the default_permissions mount option to fuse_session_new(). In this case, methods will only be called if the kernel's permission check has succeeded.

The filesystem sometimes needs to handle a return value of -ENOENT from the reply function, which means, that the request was interrupted, and the reply discarded. For example if fuse_reply_open() return -ENOENT means, that the release method for this file will not be called.

This data structure is ABI sensitive, on adding new functions these need to be appended at the end of the struct

Low level filesystem operations

Most of the methods (with the exception of init and destroy) receive a request handle (fuse_req_t) as their first argument. This handle must be passed to one of the specified reply functions.

This may be done inside the method invocation, or after the call has returned. The request handle is valid until one of the reply functions is called.

Other pointer arguments (name, fuse_file_info, etc) are not valid after the call has returned, so if they are needed later, their contents have to be copied.

In general, all methods are expected to perform any necessary permission checking. However, a filesystem may delegate this task to the kernel by passing the default_permissions mount option to fuse_session_new(). In this case, methods will only be called if the kernel's permission check has succeeded.

It is generally not really necessary to check the fuse_reply_* return values for errors, as any error in sending a reply indicates an unrecoverable problem with the kernel fuse connection, which will also terminate the session loop anyway.

This data structure is ABI sensitive, on adding new functions these need to be appended at the end of the struct

Definition at line 206 of file fuse_lowlevel.h.

Field Documentation

◆ access

void(* fuse_lowlevel_ops::access)(fuse_req_t req, fuse_ino_t ino, int mask)

Check file access permissions

This will be called for the access() and chdir() system calls. If the 'default_permissions' mount option is given, this method is not called.

This method is not called under Linux kernel versions 2.4.x

If this request is answered with an error code of ENOSYS, this is treated as a permanent success, i.e. this and all future access() requests will succeed without being send to the filesystem process.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
maskrequested access mode

Definition at line 951 of file fuse_lowlevel.h.

◆ bmap

void(* fuse_lowlevel_ops::bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)

Map block index within file to block index within device

Note: This makes sense only for block device backed filesystems mounted with the 'blkdev' option

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure, i.e. all future bmap() requests will fail with the same error code without being send to the filesystem process.

Valid replies: fuse_reply_bmap fuse_reply_err

Parameters
reqrequest handle
inothe inode number
blocksizeunit of block index
idxblock index within file

Definition at line 1044 of file fuse_lowlevel.h.

◆ copy_file_range

void(* fuse_lowlevel_ops::copy_file_range)(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fuse_file_info *fi_in, fuse_ino_t ino_out, off_t off_out, struct fuse_file_info *fi_out, size_t len, int flags)

Copy a range of data from one file to another

Performs an optimized copy between two file descriptors without the additional cost of transferring data through the FUSE kernel module to user space (glibc) and then back into the FUSE filesystem again.

In case this method is not implemented, glibc falls back to reading data from the source and writing to the destination. Effectively doing an inefficient copy of the data.

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure with error code EOPNOTSUPP, i.e. all future copy_file_range() requests will fail with EOPNOTSUPP without being send to the filesystem process.

Valid replies: fuse_reply_write fuse_reply_err

Parameters
reqrequest handle
ino_inthe inode number or the source file
off_instarting point from were the data should be read
fi_infile information of the source file
ino_outthe inode number or the destination file
off_outstarting point where the data should be written
fi_outfile information of the destination file
lenmaximum size of the data to copy
flagspassed along with the copy_file_range() syscall

Definition at line 1279 of file fuse_lowlevel.h.

◆ create

void(* fuse_lowlevel_ops::create)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi)

Create and open a file

If the file does not exist, first create it with the specified mode, and then open it.

See the description of the open handler for more information.

If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the mknod() and open() methods will be called instead.

If this request is answered with an error code of ENOSYS, the handler is treated as not implemented (i.e., for this and future requests the mknod() and open() handlers will be called instead).

Valid replies: fuse_reply_create fuse_reply_err

Parameters
reqrequest handle
parentinode number of the parent directory
nameto create
modefile type and mode with which to create the new file
fifile information

Definition at line 980 of file fuse_lowlevel.h.

◆ destroy

void(* fuse_lowlevel_ops::destroy)(void *userdata)

Clean up filesystem.

Called on filesystem exit. When this method is called, the connection to the kernel may be gone already, so that eg. calls to fuse_lowlevel_notify_* will fail.

There's no reply to this function

Parameters
userdatathe user data passed to fuse_session_new()

Definition at line 236 of file fuse_lowlevel.h.

◆ fallocate

void(* fuse_lowlevel_ops::fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi)

Allocate requested space. If this function returns success then subsequent writes to the specified range shall not fail due to the lack of free space on the file system storage media.

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure with error code EOPNOTSUPP, i.e. all future fallocate() requests will fail with EOPNOTSUPP without being send to the filesystem process.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
offsetstarting point for allocated region
lengthsize of allocated region
modedetermines the operation to be performed on the given range, see fallocate(2)

Definition at line 1218 of file fuse_lowlevel.h.

◆ flock

void(* fuse_lowlevel_ops::flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op)

Acquire, modify or release a BSD file lock

Note: if the locking methods are not implemented, the kernel will still allow file locking to work locally. Hence these are only interesting for network filesystems and similar.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information
opthe locking operation, see flock(2)

Definition at line 1195 of file fuse_lowlevel.h.

◆ flush

void(* fuse_lowlevel_ops::flush)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

Flush method

This is called on each close() of the opened file.

Since file descriptors can be duplicated (dup, dup2, fork), for one open call there may be many flush calls.

Filesystems shouldn't assume that flush will always be called after some writes, or that if will be called at all.

fi->fh will contain the value set by the open method, or will be undefined if the open method didn't set any value.

NOTE: the name of the method is misleading, since (unlike fsync) the filesystem is not forced to flush pending writes. One reason to flush data is if the filesystem wants to return write errors during close. However, such use is non-portable because POSIX does not require close to wait for delayed I/O to complete.

If the filesystem supports file locking operations (setlk, getlk) it should remove all locks belonging to 'fi->owner'.

If this request is answered with an error code of ENOSYS, this is treated as success and future calls to flush() will succeed automatically without being send to the filesystem process.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information

Definition at line 652 of file fuse_lowlevel.h.

◆ forget

void(* fuse_lowlevel_ops::forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)

Forget about an inode

This function is called when the kernel removes an inode from its internal caches.

The inode's lookup count increases by one for every call to fuse_reply_entry and fuse_reply_create. The nlookup parameter indicates by how much the lookup count should be decreased.

Inodes with a non-zero lookup count may receive request from the kernel even after calls to unlink, rmdir or (when overwriting an existing file) rename. Filesystems must handle such requests properly and it is recommended to defer removal of the inode until the lookup count reaches zero. Calls to unlink, rmdir or rename will be followed closely by forget unless the file or directory is open, in which case the kernel issues forget only after the release or releasedir calls.

Note that if a file system will be exported over NFS the inodes lifetime must extend even beyond forget. See the generation field in struct fuse_entry_param above.

On unmount the lookup count for all inodes implicitly drops to zero. It is not guaranteed that the file system will receive corresponding forget messages for the affected inodes.

Valid replies: fuse_reply_none

Parameters
reqrequest handle
inothe inode number
nlookupthe number of lookups to forget

Definition at line 287 of file fuse_lowlevel.h.

◆ forget_multi

void(* fuse_lowlevel_ops::forget_multi)(fuse_req_t req, size_t count, struct fuse_forget_data *forgets)

Forget about multiple inodes

See description of the forget function for more information.

Valid replies: fuse_reply_none

Parameters
reqrequest handle

Definition at line 1177 of file fuse_lowlevel.h.

◆ fsync

void(* fuse_lowlevel_ops::fsync)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)

Synchronize file contents

If the datasync parameter is non-zero, then only the user data should be flushed, not the meta data.

If this request is answered with an error code of ENOSYS, this is treated as success and future calls to fsync() will succeed automatically without being send to the filesystem process.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
datasyncflag indicating if only data should be flushed
fifile information

Definition at line 702 of file fuse_lowlevel.h.

◆ fsyncdir

void(* fuse_lowlevel_ops::fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)

Synchronize directory contents

If the datasync parameter is non-zero, then only the directory contents should be flushed, not the meta data.

fi->fh will contain the value set by the opendir method, or will be undefined if the opendir method didn't set any value.

If this request is answered with an error code of ENOSYS, this is treated as success and future calls to fsyncdir() will succeed automatically without being send to the filesystem process.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
datasyncflag indicating if only data should be flushed
fifile information

Definition at line 824 of file fuse_lowlevel.h.

◆ getattr

void(* fuse_lowlevel_ops::getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

Get file attributes.

If writeback caching is enabled, the kernel may have a better idea of a file's length than the FUSE file system (eg if there has been a write that extended the file size, but that has not yet been passed to the filesystem.

In this case, the st_size value provided by the file system will be ignored.

Valid replies: fuse_reply_attr fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information, or NULL

Definition at line 308 of file fuse_lowlevel.h.

◆ getlk

void(* fuse_lowlevel_ops::getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)

Test for a POSIX file lock

Valid replies: fuse_reply_lock fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information
lockthe region/type to test

Definition at line 995 of file fuse_lowlevel.h.

◆ getxattr

void(* fuse_lowlevel_ops::getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)

Get an extended attribute

If size is zero, the size of the value should be sent with fuse_reply_xattr.

If the size is non-zero, and the value fits in the buffer, the value should be sent with fuse_reply_buf.

If the size is too small for the value, the ERANGE error should be sent.

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure with error code EOPNOTSUPP, i.e. all future getxattr() requests will fail with EOPNOTSUPP without being send to the filesystem process.

Valid replies: fuse_reply_buf fuse_reply_data fuse_reply_xattr fuse_reply_err

Parameters
reqrequest handle
inothe inode number
nameof the extended attribute
sizemaximum size of the value to send

Definition at line 881 of file fuse_lowlevel.h.

◆ init

void(* fuse_lowlevel_ops::init)(void *userdata, struct fuse_conn_info *conn)

Initialize filesystem

This function is called when libfuse establishes communication with the FUSE kernel module. The file system should use this module to inspect and/or modify the connection parameters provided in the conn structure.

Note that some parameters may be overwritten by options passed to fuse_session_new() which take precedence over the values set in this handler.

There's no reply to this function

Parameters
userdatathe user data passed to fuse_session_new()

Definition at line 223 of file fuse_lowlevel.h.

◆ ioctl

void(* fuse_lowlevel_ops::ioctl)(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz)

Ioctl

Note: For unrestricted ioctls (not allowed for FUSE servers), data in and out areas can be discovered by giving iovs and setting FUSE_IOCTL_RETRY in flags. For restricted ioctls, kernel prepares in/out data area according to the information encoded in cmd.

Valid replies: fuse_reply_ioctl_retry fuse_reply_ioctl fuse_reply_ioctl_iov fuse_reply_err

Parameters
reqrequest handle
inothe inode number
cmdioctl command
argioctl argument
fifile information
flagsfor FUSE_IOCTL_* flags
in_bufdata fetched from the caller
in_bufsznumber of fetched bytes
out_bufszmaximum size of output data

Note : the unsigned long request submitted by the application is truncated to 32 bits.

Definition at line 1080 of file fuse_lowlevel.h.

◆ link

void(* fuse_lowlevel_ops::link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)

Create a hard link

Valid replies: fuse_reply_entry fuse_reply_err

Parameters
reqrequest handle
inothe old inode number
newparentinode number of the new parent directory
newnamenew name to create

Definition at line 488 of file fuse_lowlevel.h.

◆ listxattr

void(* fuse_lowlevel_ops::listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size)

List extended attribute names

If size is zero, the total size of the attribute list should be sent with fuse_reply_xattr.

If the size is non-zero, and the null character separated attribute list fits in the buffer, the list should be sent with fuse_reply_buf.

If the size is too small for the list, the ERANGE error should be sent.

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure with error code EOPNOTSUPP, i.e. all future listxattr() requests will fail with EOPNOTSUPP without being send to the filesystem process.

Valid replies: fuse_reply_buf fuse_reply_data fuse_reply_xattr fuse_reply_err

Parameters
reqrequest handle
inothe inode number
sizemaximum size of the list to send

Definition at line 912 of file fuse_lowlevel.h.

◆ lookup

void(* fuse_lowlevel_ops::lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)

Look up a directory entry by name and get its attributes.

Valid replies: fuse_reply_entry fuse_reply_err

Parameters
reqrequest handle
parentinode number of the parent directory
namethe name to look up

Definition at line 249 of file fuse_lowlevel.h.

◆ lseek

void(* fuse_lowlevel_ops::lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi)

Find next data or hole after the specified offset

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure, i.e. all future lseek() requests will fail with the same error code without being send to the filesystem process.

Valid replies: fuse_reply_lseek fuse_reply_err

Parameters
reqrequest handle
inothe inode number
offoffset to start search from
whenceeither SEEK_DATA or SEEK_HOLE
fifile information

Definition at line 1303 of file fuse_lowlevel.h.

◆ mkdir

void(* fuse_lowlevel_ops::mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)

Create a directory

Valid replies: fuse_reply_entry fuse_reply_err

Parameters
reqrequest handle
parentinode number of the parent directory
nameto create
modewith which to create the new file

Definition at line 391 of file fuse_lowlevel.h.

◆ mknod

void(* fuse_lowlevel_ops::mknod)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)

Create file node

Create a regular file, character device, block device, fifo or socket node.

Valid replies: fuse_reply_entry fuse_reply_err

Parameters
reqrequest handle
parentinode number of the parent directory
nameto create
modefile type and mode with which to create the new file
rdevthe device number (only valid if created file is a device)

Definition at line 376 of file fuse_lowlevel.h.

◆ open

void(* fuse_lowlevel_ops::open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

Open a file

Open flags are available in fi->flags. The following rules apply.

  • Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be filtered out / handled by the kernel.
  • Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used by the filesystem to check if the operation is permitted. If the -o default_permissions mount option is given, this check is already done by the kernel before calling open() and may thus be omitted by the filesystem.
  • When writeback caching is enabled, the kernel may send read requests even for files opened with O_WRONLY. The filesystem should be prepared to handle this.
  • When writeback caching is disabled, the filesystem is expected to properly handle the O_APPEND flag and ensure that each write is appending to the end of the file.
  • When writeback caching is enabled, the kernel will handle O_APPEND. However, unless all changes to the file come through the kernel this will not work reliably. The filesystem should thus either ignore the O_APPEND flag (and let the kernel handle it), or return an error (indicating that reliably O_APPEND is not available).

Filesystem may store an arbitrary file handle (pointer, index, etc) in fi->fh, and use this in other all other file operations (read, write, flush, release, fsync).

Filesystem may also implement stateless file I/O and not store anything in fi->fh.

There are also some flags (direct_io, keep_cache) which the filesystem may set in fi, to change the way the file is opened. See fuse_file_info structure in <fuse_common.h> for more details.

If this request is answered with an error code of ENOSYS and FUSE_CAP_NO_OPEN_SUPPORT is set in fuse_conn_info.capable, this is treated as success and future calls to open and release will also succeed without being sent to the filesystem process.

To get this behavior without providing an opendir handler, you may set FUSE_CAP_NO_OPEN_SUPPORT in fuse_conn_info.want on supported kernels to automatically get the zero message open().

If this callback is not provided and FUSE_CAP_NO_OPEN_SUPPORT is not set in fuse_conn_info.want then an empty reply will be sent.

Valid replies: fuse_reply_open fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information

Definition at line 554 of file fuse_lowlevel.h.

◆ opendir

void(* fuse_lowlevel_ops::opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

Open a directory

Filesystem may store an arbitrary file handle (pointer, index, etc) in fi->fh, and use this in other all other directory stream operations (readdir, releasedir, fsyncdir).

If this request is answered with an error code of ENOSYS and FUSE_CAP_NO_OPENDIR_SUPPORT is set in fuse_conn_info.capable, this is treated as success and future calls to opendir and releasedir will also succeed without being sent to the filesystem process. In addition, the kernel will cache readdir results as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR.

To get this behavior without providing an opendir handler, you may set FUSE_CAP_NO_OPENDIR_SUPPORT in fuse_conn_info.want on supported kernels to automatically get the zero message opendir().

If this callback is not provided and FUSE_CAP_NO_OPENDIR_SUPPORT is not set in fuse_conn_info.want then an empty reply will be sent.

Valid replies: fuse_reply_open fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information

Definition at line 734 of file fuse_lowlevel.h.

◆ poll

void(* fuse_lowlevel_ops::poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph)

Poll for IO readiness

The client should immediately respond with fuse_reply_poll(), setting revents appropriately according to which events are ready.

Additionally, if ph is non-NULL, the client must retain it and notify when all future IO readiness events occur by calling fuse_lowlevel_notify_poll() with the specified ph.

Regardless of the number of times poll with a non-NULL ph is received, a single notify_poll is enough to service all. (Notifying more times incurs overhead but doesn't harm correctness.) Any additional received handles can be immediately destroyed.

The callee is responsible for destroying ph with fuse_pollhandle_destroy() when no longer in use.

If this request is answered with an error code of ENOSYS, this is treated as success (with a kernel-defined default poll-mask) and future calls to poll() will succeed the same way without being send to the filesystem process.

Valid replies: fuse_reply_poll fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information
phpoll handle to be used for notification

Definition at line 1117 of file fuse_lowlevel.h.

◆ read

void(* fuse_lowlevel_ops::read)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)

Read data

Read should send exactly the number of bytes requested except on EOF or error, otherwise the rest of the data will be substituted with zeroes. An exception to this is when the file has been opened in 'direct_io' mode, in which case the return value of the read system call will reflect the return value of this operation.

fi->fh will contain the value set by the open method, or will be undefined if the open method didn't set any value.

Valid replies: fuse_reply_buf fuse_reply_iov fuse_reply_data fuse_reply_err

Parameters
reqrequest handle
inothe inode number
sizenumber of bytes to read
offoffset to read from
fifile information

Definition at line 582 of file fuse_lowlevel.h.

◆ readdir

void(* fuse_lowlevel_ops::readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)

Read directory

Send a buffer filled using fuse_add_direntry(), with size not exceeding the requested size. Send an empty buffer on end of stream.

fi->fh will contain the value set by the opendir method, or will be undefined if the opendir method didn't set any value.

Returning a directory entry from readdir() does not affect its lookup count.

If off_t is non-zero, then it will correspond to one of the off_t values that was previously returned by readdir() for the same directory handle. In this case, readdir() should skip over entries coming before the position defined by the off_t value. If entries are added or removed while the directory handle is open, the filesystem may still include the entries that have been removed, and may not report the entries that have been created. However, addition or removal of entries must never cause readdir() to skip over unrelated entries or to report them more than once. This means that off_t can not be a simple index that enumerates the entries that have been returned but must contain sufficient information to uniquely determine the next directory entry to return even when the set of entries is changing.

The function does not have to report the '.' and '..' entries, but is allowed to do so. Note that, if readdir does not return '.' or '..', they will not be implicitly returned, and this behavior is observable by the caller.

Valid replies: fuse_reply_buf fuse_reply_data fuse_reply_err

Parameters
reqrequest handle
inothe inode number
sizemaximum number of bytes to send
offoffset to continue reading the directory stream
fifile information

Definition at line 780 of file fuse_lowlevel.h.

◆ readdirplus

void(* fuse_lowlevel_ops::readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)

Read directory with attributes

Send a buffer filled using fuse_add_direntry_plus(), with size not exceeding the requested size. Send an empty buffer on end of stream.

fi->fh will contain the value set by the opendir method, or will be undefined if the opendir method didn't set any value.

In contrast to readdir() (which does not affect the lookup counts), the lookup count of every entry returned by readdirplus(), except "." and "..", is incremented by one.

Valid replies: fuse_reply_buf fuse_reply_data fuse_reply_err

Parameters
reqrequest handle
inothe inode number
sizemaximum number of bytes to send
offoffset to continue reading the directory stream
fifile information

Definition at line 1246 of file fuse_lowlevel.h.

◆ readlink

void(* fuse_lowlevel_ops::readlink)(fuse_req_t req, fuse_ino_t ino)

Read symbolic link

Valid replies: fuse_reply_readlink fuse_reply_err

Parameters
reqrequest handle
inothe inode number

Definition at line 358 of file fuse_lowlevel.h.

◆ release

void(* fuse_lowlevel_ops::release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

Release an open file

Release is called when there are no more references to an open file: all file descriptors are closed and all memory mappings are unmapped.

For every open call there will be exactly one release call (unless the filesystem is force-unmounted).

The filesystem may reply with an error, but error values are not returned to close() or munmap() which triggered the release.

fi->fh will contain the value set by the open method, or will be undefined if the open method didn't set any value. fi->flags will contain the same flags as for open.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information

Definition at line 680 of file fuse_lowlevel.h.

◆ releasedir

void(* fuse_lowlevel_ops::releasedir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

Release an open directory

For every opendir call there will be exactly one releasedir call (unless the filesystem is force-unmounted).

fi->fh will contain the value set by the opendir method, or will be undefined if the opendir method didn't set any value.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information

Definition at line 799 of file fuse_lowlevel.h.

◆ removexattr

void(* fuse_lowlevel_ops::removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name)

Remove an extended attribute

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure with error code EOPNOTSUPP, i.e. all future removexattr() requests will fail with EOPNOTSUPP without being send to the filesystem process.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
nameof the extended attribute

Definition at line 929 of file fuse_lowlevel.h.

◆ rename

void(* fuse_lowlevel_ops::rename)(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags)

Rename a file

If the target exists it should be atomically replaced. If the target's inode's lookup count is non-zero, the file system is expected to postpone any removal of the inode until the lookup count reaches zero (see description of the forget function).

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure with error code EINVAL, i.e. all future bmap requests will fail with EINVAL without being send to the filesystem process.

flags may be RENAME_EXCHANGE or RENAME_NOREPLACE. If RENAME_NOREPLACE is specified, the filesystem must not overwrite newname if it exists and return an error instead. If RENAME_EXCHANGE is specified, the filesystem must atomically exchange the two files, i.e. both must exist and neither may be deleted.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
parentinode number of the old parent directory
nameold name
newparentinode number of the new parent directory
newnamenew name

Rename a file

If the target exists it should be atomically replaced. If the target's inode's lookup count is non-zero, the file system is expected to postpone any removal of the inode until the lookup count reaches zero (see description of the forget function).

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure with error code EINVAL, i.e. all future rename requests will fail with EINVAL without being send to the filesystem process.

flags may be RENAME_EXCHANGE or RENAME_NOREPLACE. If RENAME_NOREPLACE is specified, the filesystem must not overwrite newname if it exists and return an error instead. If RENAME_EXCHANGE is specified, the filesystem must atomically exchange the two files, i.e. both must exist and neither may be deleted.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
parentinode number of the old parent directory
nameold name
newparentinode number of the new parent directory
newnamenew name

Definition at line 472 of file fuse_lowlevel.h.

◆ retrieve_reply

void(* fuse_lowlevel_ops::retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv)

Callback function for the retrieve request

Valid replies: fuse_reply_none

Parameters
reqrequest handle
cookieuser data supplied to fuse_lowlevel_notify_retrieve()
inothe inode number supplied to fuse_lowlevel_notify_retrieve()
offsetthe offset supplied to fuse_lowlevel_notify_retrieve()
bufvthe buffer containing the returned data

Definition at line 1163 of file fuse_lowlevel.h.

◆ rmdir

void(* fuse_lowlevel_ops::rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name)

Remove a directory

If the directory's inode's lookup count is non-zero, the file system is expected to postpone any removal of the inode until the lookup count reaches zero (see description of the forget function).

Valid replies: fuse_reply_err

Parameters
reqrequest handle
parentinode number of the parent directory
nameto remove

Definition at line 426 of file fuse_lowlevel.h.

◆ setattr

void(* fuse_lowlevel_ops::setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)

Set file attributes

In the 'attr' argument only members indicated by the 'to_set' bitmask contain valid values. Other members contain undefined values.

Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is 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 if the open method didn't set any value. Otherwise (not ftruncate call, or kernel version earlier than 2.6.15) the fi parameter will be NULL.

Valid replies: fuse_reply_attr fuse_reply_err

Parameters
reqrequest handle
inothe inode number
attrthe attributes
to_setbit mask of attributes which should be set
fifile information, or NULL

Definition at line 345 of file fuse_lowlevel.h.

◆ setlk

void(* fuse_lowlevel_ops::setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)

Acquire, modify or release a POSIX file lock

For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but otherwise this is not always the case. For checking lock ownership, 'fi->owner' must be used. The l_pid field in 'struct flock' should only be used to fill in this field in getlk().

Note: if the locking methods are not implemented, the kernel will still allow file locking to work locally. Hence these are only interesting for network filesystems and similar.

Valid replies: fuse_reply_err

Parameters
reqrequest handle
inothe inode number
fifile information
lockthe region/type to set
sleeplocking operation may sleep

Definition at line 1020 of file fuse_lowlevel.h.

◆ setxattr

void(* fuse_lowlevel_ops::setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags)

Set an extended attribute

If this request is answered with an error code of ENOSYS, this is treated as a permanent failure with error code EOPNOTSUPP, i.e. all future setxattr() requests will fail with EOPNOTSUPP without being send to the filesystem process.

Valid replies: fuse_reply_err

Definition at line 850 of file fuse_lowlevel.h.

◆ statfs

void(* fuse_lowlevel_ops::statfs)(fuse_req_t req, fuse_ino_t ino)

Get file system statistics

Valid replies: fuse_reply_statfs fuse_reply_err

Parameters
reqrequest handle
inothe inode number, zero means "undefined"

Definition at line 837 of file fuse_lowlevel.h.

◆ statx

void(* fuse_lowlevel_ops::statx)(fuse_req_t req, fuse_ino_t ino, int flags, int mask, struct fuse_file_info *fi)

Get extended file attributes.

Valid replies: fuse_reply_statx fuse_reply_err

Parameters
reqrequest handle
inothe inode number
flagsbitmask of requested flags
maskbitmask of requested fields
fifile information (may be NULL)

Definition at line 1342 of file fuse_lowlevel.h.

◆ symlink

void(* fuse_lowlevel_ops::symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)

Create a symbolic link

Valid replies: fuse_reply_entry fuse_reply_err

Parameters
reqrequest handle
linkthe contents of the symbolic link
parentinode number of the parent directory
nameto create

Definition at line 440 of file fuse_lowlevel.h.

◆ tmpfile

void(* fuse_lowlevel_ops::tmpfile)(fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info *fi)

Create a tempfile

Tempfile means an anonymous file. It can be made into a normal file later by using linkat or such.

If this is answered with an error ENOSYS this is treated by the kernel as a permanent failure and it will disable the feature and not ask again.

Valid replies: fuse_reply_create fuse_reply_err

Parameters
reqrequest handle
parentinode number of the parent directory
modefile type and mode with which to create the new file
fifile information

Definition at line 1325 of file fuse_lowlevel.h.

◆ unlink

void(* fuse_lowlevel_ops::unlink)(fuse_req_t req, fuse_ino_t parent, const char *name)

Remove a file

If the file's inode's lookup count is non-zero, the file system is expected to postpone any removal of the inode until the lookup count reaches zero (see description of the forget function).

Valid replies: fuse_reply_err

Parameters
reqrequest handle
parentinode number of the parent directory
nameto remove

Definition at line 409 of file fuse_lowlevel.h.

◆ write

void(* fuse_lowlevel_ops::write)(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi)

Write data

Write should return exactly the number of bytes requested except on error. An exception to this is when the file has been opened in 'direct_io' mode, in which case the return value of the write system call will reflect the return value of this operation.

Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is expected to reset the setuid and setgid bits.

fi->fh will contain the value set by the open method, or will be undefined if the open method didn't set any value.

Valid replies: fuse_reply_write fuse_reply_err

Parameters
reqrequest handle
inothe inode number
bufdata to write
sizenumber of bytes to write
offoffset to write to
fifile information

Definition at line 611 of file fuse_lowlevel.h.

◆ write_buf

void(* fuse_lowlevel_ops::write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, off_t off, struct fuse_file_info *fi)

Write data made available in a buffer

This is a more generic version of the ->write() method. If FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the kernel supports splicing from the fuse device, then the data will be made available in pipe for supporting zero copy data transfer.

buf->count is guaranteed to be one (and thus buf->idx is always zero). The write_buf handler must ensure that bufv->off is correctly updated (reflecting the number of bytes read from bufv->buf[0]).

Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is expected to reset the setuid and setgid bits.

Valid replies: fuse_reply_write fuse_reply_err

Parameters
reqrequest handle
inothe inode number
bufvbuffer containing the data
offoffset to write to
fifile information

Definition at line 1147 of file fuse_lowlevel.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__module.html0000644000175000017500000000552715156613443020050 0ustar berndbernd libfuse: fuse_module Struct Reference
libfuse
fuse_module Struct Reference

#include <fuse_i.h>

Detailed Description

Filesystem module

Filesystem modules are registered with the FUSE_REGISTER_MODULE() macro.

Definition at line 111 of file fuse_i.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__operations.html0000644000175000017500000022237215156613443020745 0ustar berndbernd libfuse: fuse_operations Struct Reference
libfuse
fuse_operations Struct Reference

#include <fuse.h>

Data Fields

int(* getattr )(const char *, struct stat *, struct fuse_file_info *fi)
 
int(* readlink )(const char *, char *, size_t)
 
int(* mknod )(const char *, mode_t, dev_t)
 
int(* mkdir )(const char *, mode_t)
 
int(* unlink )(const char *)
 
int(* rmdir )(const char *)
 
int(* symlink )(const char *, const char *)
 
int(* rename )(const char *, const char *, unsigned int flags)
 
int(* link )(const char *, const char *)
 
int(* chmod )(const char *, mode_t, struct fuse_file_info *fi)
 
int(* chown )(const char *, uid_t, gid_t, struct fuse_file_info *fi)
 
int(* truncate )(const char *, off_t, struct fuse_file_info *fi)
 
int(* open )(const char *, struct fuse_file_info *)
 
int(* read )(const char *, char *, size_t, off_t, struct fuse_file_info *)
 
int(* write )(const char *, const char *, size_t, off_t, struct fuse_file_info *)
 
int(* statfs )(const char *, struct statvfs *)
 
int(* flush )(const char *, struct fuse_file_info *)
 
int(* release )(const char *, struct fuse_file_info *)
 
int(* fsync )(const char *, int, struct fuse_file_info *)
 
int(* setxattr )(const char *, const char *, const char *, size_t, int)
 
int(* getxattr )(const char *, const char *, char *, size_t)
 
int(* listxattr )(const char *, char *, size_t)
 
int(* removexattr )(const char *, const char *)
 
int(* opendir )(const char *, struct fuse_file_info *)
 
int(* readdir )(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags)
 
int(* releasedir )(const char *, struct fuse_file_info *)
 
int(* fsyncdir )(const char *, int, struct fuse_file_info *)
 
void *(* init )(struct fuse_conn_info *conn, struct fuse_config *cfg)
 
void(* destroy )(void *private_data)
 
int(* access )(const char *, int)
 
int(* create )(const char *, mode_t, struct fuse_file_info *)
 
int(* lock )(const char *, struct fuse_file_info *, int cmd, struct flock *)
 
int(* utimens )(const char *, const struct timespec tv[2], struct fuse_file_info *fi)
 
int(* bmap )(const char *, size_t blocksize, uint64_t *idx)
 
int(* ioctl )(const char *, unsigned int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data)
 
int(* poll )(const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp)
 
int(* write_buf )(const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *)
 
int(* read_buf )(const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *)
 
int(* flock )(const char *, struct fuse_file_info *, int op)
 
int(* fallocate )(const char *, int, off_t, off_t, struct fuse_file_info *)
 
ssize_t(* copy_file_range )(const char *path_in, struct fuse_file_info *fi_in, off_t offset_in, const char *path_out, struct fuse_file_info *fi_out, off_t offset_out, size_t size, int flags)
 
off_t(* lseek )(const char *, off_t off, int whence, struct fuse_file_info *)
 
int(* statx )(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi)
 

Detailed Description

The file system operations:

Most of these should work very similarly to the well known UNIX file system operations. A major exception is that instead of returning an error in 'errno', the operation should return the negated error value (-errno) directly.

All methods are optional, but some are essential for a useful filesystem (e.g. getattr). Open, flush, release, fsync, opendir, releasedir, fsyncdir, access, create, truncate, lock, init and destroy are special purpose methods, without which a full featured filesystem can still be implemented.

In general, all methods are expected to perform any necessary permission checking. However, a filesystem may delegate this task to the kernel by passing the default_permissions mount option to fuse_new(). In this case, methods will only be called if the kernel's permission check has succeeded.

Almost all operations take a path which can be of any length.

Definition at line 349 of file fuse.h.

Field Documentation

◆ access

int(* fuse_operations::access)(const char *, int)

Check file access permissions

This will be called for the access() system call. If the 'default_permissions' mount option is given, this method is not called.

This method is not called under Linux kernel versions 2.4.x

Definition at line 660 of file fuse.h.

◆ bmap

int(* fuse_operations::bmap)(const char *, size_t blocksize, uint64_t *idx)

Map block index within file to block index within device

Note: This makes sense only for block device backed filesystems mounted with the 'blkdev' option

Definition at line 728 of file fuse.h.

◆ chmod

int(* fuse_operations::chmod)(const char *, mode_t, struct fuse_file_info *fi)

Change the permission bits of a file

fi will always be NULL if the file is not currently open, but may also be NULL if the file is open.

Definition at line 417 of file fuse.h.

◆ chown

int(* fuse_operations::chown)(const char *, uid_t, gid_t, struct fuse_file_info *fi)

Change the owner and group of a file

fi will always be NULL if the file is not currently open, but may also be NULL if the file is open.

Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is expected to reset the setuid and setgid bits.

Definition at line 427 of file fuse.h.

◆ copy_file_range

ssize_t(* fuse_operations::copy_file_range)(const char *path_in, struct fuse_file_info *fi_in, off_t offset_in, const char *path_out, struct fuse_file_info *fi_out, off_t offset_out, size_t size, int flags)

Copy a range of data from one file to another

Performs an optimized copy between two file descriptors without the additional cost of transferring data through the FUSE kernel module to user space (glibc) and then back into the FUSE filesystem again.

In case this method is not implemented, applications are expected to fall back to a regular file copy. (Some glibc versions did this emulation automatically, but the emulation has been removed from all glibc release branches.)

Definition at line 843 of file fuse.h.

◆ create

int(* fuse_operations::create)(const char *, mode_t, struct fuse_file_info *)

Create and open a file

If the file does not exist, first create it with the specified mode, and then open it.

If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the mknod() and open() methods will be called instead.

Definition at line 672 of file fuse.h.

◆ destroy

void(* fuse_operations::destroy)(void *private_data)

Clean up filesystem

Called on filesystem exit.

Definition at line 649 of file fuse.h.

◆ fallocate

int(* fuse_operations::fallocate)(const char *, int, off_t, off_t, struct fuse_file_info *)

Allocates space for an open file

This function ensures that required space is allocated for specified file. If this function returns success then any subsequent write request to specified range is guaranteed not to fail because of lack of space on the file system media.

Definition at line 828 of file fuse.h.

◆ flock

int(* fuse_operations::flock)(const char *, struct fuse_file_info *, int op)

Perform BSD file locking operation

The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN

Nonblocking requests will be indicated by ORing LOCK_NB to the above operations

For more information see the flock(2) manual page.

Additionally fi->owner will be set to a value unique to this open file. This same value will be supplied to ->release() when the file is released.

Note: if this method is not implemented, the kernel will still allow file locking to work locally. Hence it is only interesting for network filesystems and similar.

Definition at line 818 of file fuse.h.

◆ flush

int(* fuse_operations::flush)(const char *, struct fuse_file_info *)

Possibly flush cached data

BIG NOTE: This is not equivalent to fsync(). It's not a request to sync dirty data.

Flush is called on each close() of a file descriptor, as opposed to release which is called on the close of the last file descriptor for a file. Under Linux, errors returned by flush() will be passed to userspace as errors from close(), so flush() is a good place to write back any cached dirty data. However, many applications ignore errors on close(), and on non-Linux systems, close() may succeed even if flush() returns an error. For these reasons, filesystems should not assume that errors returned by flush will ever be noticed or even delivered.

NOTE: The flush() method may be called more than once for each open(). This happens if more than one file descriptor refers to an open file handle, e.g. due to dup(), dup2() or fork() calls. It is not possible to determine if a flush is final, so each flush should be treated equally. Multiple write-flush sequences are relatively rare, so this shouldn't be a problem.

Filesystems shouldn't assume that flush will be called at any particular point. It may be called more times than expected, or not at all.

Definition at line 546 of file fuse.h.

◆ fsync

int(* fuse_operations::fsync)(const char *, int, struct fuse_file_info *)

Synchronize file contents

If the datasync parameter is non-zero, then only the user data should be flushed, not the meta data.

Definition at line 567 of file fuse.h.

◆ fsyncdir

int(* fuse_operations::fsyncdir)(const char *, int, 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

Definition at line 631 of file fuse.h.

◆ getattr

int(* fuse_operations::getattr)(const char *, struct stat *, struct fuse_file_info *fi)

Get file attributes.

Similar to stat(). The 'st_dev' and 'st_blksize' fields are ignored. The 'st_ino' field is ignored except if the 'use_ino' mount option is given. In that case it is passed to userspace, but libfuse and the kernel will still assign a different inode for internal use (called the "nodeid").

fi will always be NULL if the file is not currently open, but may also be NULL if the file is open.

Definition at line 361 of file fuse.h.

◆ getxattr

int(* fuse_operations::getxattr)(const char *, const char *, char *, size_t)

Get extended attributes

Definition at line 573 of file fuse.h.

◆ init

void *(* fuse_operations::init)(struct fuse_conn_info *conn, struct fuse_config *cfg)

Initialize filesystem

The return value will passed in the private_data field of struct fuse_context to all file operations, and as a parameter to the destroy() method. It overrides the initial value provided to fuse_main() / fuse_new().

Definition at line 641 of file fuse.h.

◆ ioctl

int(* fuse_operations::ioctl)(const char *, unsigned int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data)

Ioctl

flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in 64bit environment. The size and direction of data is determined by IOC*() decoding of cmd. For _IOC_NONE, data will be NULL, for _IOC_WRITE data is out area, for _IOC_READ in area and if both are set in/out area. In all non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.

If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a directory file handle.

Note : the unsigned long request submitted by the application is truncated to 32 bits.

Definition at line 750 of file fuse.h.

◆ link

int(* fuse_operations::link)(const char *, const char *)

Create a hard link to a file

Definition at line 410 of file fuse.h.

◆ listxattr

int(* fuse_operations::listxattr)(const char *, char *, size_t)

List extended attributes

Definition at line 576 of file fuse.h.

◆ lock

int(* fuse_operations::lock)(const char *, struct fuse_file_info *, int cmd, struct flock *)

Perform POSIX file locking operation

The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.

For the meaning of fields in 'struct flock' see the man page for fcntl(2). The l_whence field will always be set to SEEK_SET.

For checking lock ownership, the 'fuse_file_info->owner' argument must be used.

For F_GETLK operation, the library will first check currently held locks, and if a conflicting lock is found it will return information without calling this method. This ensures, that for local locks the l_pid field is correctly filled in. The results may not be accurate in case of race conditions and in the presence of hard links, but it's unlikely that an application would rely on accurate GETLK results in these cases. If a conflicting lock is not found, this method will be called, and the filesystem may fill out l_pid by a meaningful value, or it may leave this field zero.

For F_SETLK and F_SETLKW the l_pid field will be set to the pid of the process performing the locking operation.

Note: if this method is not implemented, the kernel will still allow file locking to work locally. Hence it is only interesting for network filesystems and similar.

Definition at line 704 of file fuse.h.

◆ lseek

off_t(* fuse_operations::lseek)(const char *, off_t off, int whence, struct fuse_file_info *)

Find next data or hole after the specified offset

Definition at line 852 of file fuse.h.

◆ mkdir

int(* fuse_operations::mkdir)(const char *, mode_t)

Create a directory

Note that the mode argument may not have the type specification bits set, i.e. S_ISDIR(mode) can be false. To obtain the correct directory type bits use mode|S_IFDIR

Definition at line 387 of file fuse.h.

◆ mknod

int(* fuse_operations::mknod)(const char *, mode_t, dev_t)

Create a file node

This is called for creation of all non-directory, non-symlink nodes. If the filesystem defines a create() method, then for regular files that will be called instead.

Definition at line 379 of file fuse.h.

◆ open

int(* fuse_operations::open)(const char *, struct fuse_file_info *)

Open a file

Open flags are available in fi->flags. The following rules apply.

  • Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be filtered out / handled by the kernel.
  • Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH) should be used by the filesystem to check if the operation is permitted. If the -o default_permissions mount option is given, this check is already done by the kernel before calling open() and may thus be omitted by the filesystem.
  • When writeback caching is enabled, the kernel may send read requests even for files opened with O_WRONLY. The filesystem should be prepared to handle this.
  • When writeback caching is disabled, the filesystem is expected to properly handle the O_APPEND flag and ensure that each write is appending to the end of the file.
  • When writeback caching is enabled, the kernel will handle O_APPEND. However, unless all changes to the file come through the kernel this will not work reliably. The filesystem should thus either ignore the O_APPEND flag (and let the kernel handle it), or return an error (indicating that reliably O_APPEND is not available).

Filesystem may store an arbitrary file handle (pointer, index, etc) in fi->fh, and use this in other all other file operations (read, write, flush, release, fsync).

Filesystem may also implement stateless file I/O and not store anything in fi->fh.

There are also some flags (direct_io, keep_cache) which the filesystem may set in fi, to change the way the file is opened. See fuse_file_info structure in <fuse_common.h> for more details.

If this request is answered with an error code of ENOSYS and FUSE_CAP_NO_OPEN_SUPPORT is set in fuse_conn_info.capable, this is treated as success and future calls to open will also succeed without being sent to the filesystem process.

Definition at line 486 of file fuse.h.

◆ opendir

int(* fuse_operations::opendir)(const char *, struct fuse_file_info *)

Open directory

Unless the 'default_permissions' mount option is given, this method should check if opendir is permitted for this directory. Optionally opendir may also return an arbitrary filehandle in the fuse_file_info structure, which will be passed to readdir, releasedir and fsyncdir.

Definition at line 589 of file fuse.h.

◆ poll

int(* fuse_operations::poll)(const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp)

Poll for IO readiness events

Note: If ph is non-NULL, the client should notify when IO readiness events occur by calling fuse_notify_poll() with the specified ph.

Regardless of the number of times poll with a non-NULL ph is received, single notification is enough to clear all. Notifying more times incurs overhead but doesn't harm correctness.

The callee is responsible for destroying ph with fuse_pollhandle_destroy() when no longer in use.

Definition at line 769 of file fuse.h.

◆ read

int(* fuse_operations::read)(const char *, char *, size_t, off_t, struct fuse_file_info *)

Read data from an open file

Read should return exactly the number of bytes requested except on EOF or error, otherwise the rest of the data will be substituted with zeroes. An exception to this is when the 'direct_io' mount option is specified, in which case the return value of the read system call will reflect the return value of this operation.

Definition at line 497 of file fuse.h.

◆ read_buf

int(* fuse_operations::read_buf)(const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *)

Store data from an open file in a buffer

Similar to the read() method, but data is stored and returned in a generic buffer.

No actual copying of data has to take place, the source file descriptor may simply be stored in the buffer for later data transfer.

The buffer must be allocated dynamically and stored at the location pointed to by bufp. If the buffer contains memory regions, they too must be allocated using malloc(). The allocated memory will be freed by the caller.

Definition at line 798 of file fuse.h.

◆ readdir

int(* fuse_operations::readdir)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags)

Read directory

The filesystem may choose between two modes of operation:

1) The readdir implementation ignores the offset parameter, and passes zero to the filler function's offset. The filler function will not return '1' (unless an error happens), so the whole directory is read in a single readdir operation.

2) The readdir implementation keeps track of the offsets of the directory entries. It uses the offset parameter and always 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.

Definition at line 613 of file fuse.h.

◆ readlink

int(* fuse_operations::readlink)(const char *, char *, size_t)

Read the target of a symbolic link

The buffer should be filled with a null terminated string. The buffer size argument includes the space for the terminating null character. If the linkname is too long to fit in the buffer, it should be truncated. The return value should be 0 for success.

Definition at line 371 of file fuse.h.

◆ release

int(* fuse_operations::release)(const char *, struct fuse_file_info *)

Release an open file

Release is called when there are no more references to an open file: all file descriptors are closed and all memory mappings are unmapped.

For every open() call there will be exactly one release() call with the same flags and file handle. It is possible to have a file opened more than once, in which case only the last release will mean, that no more reads/writes will happen on the file. The return value of release is ignored.

Definition at line 560 of file fuse.h.

◆ releasedir

int(* fuse_operations::releasedir)(const char *, struct fuse_file_info *)

Release directory

If the directory has been removed after the call to opendir, the path parameter will be NULL.

Definition at line 621 of file fuse.h.

◆ removexattr

int(* fuse_operations::removexattr)(const char *, const char *)

Remove extended attributes

Definition at line 579 of file fuse.h.

◆ rename

int(* fuse_operations::rename)(const char *, const char *, unsigned int flags)

Rename a file

flags may be RENAME_EXCHANGE or RENAME_NOREPLACE. If RENAME_NOREPLACE is specified, the filesystem must not overwrite newname if it exists and return an error instead. If RENAME_EXCHANGE is specified, the filesystem must atomically exchange the two files, i.e. both must exist and neither may be deleted.

Definition at line 407 of file fuse.h.

◆ rmdir

int(* fuse_operations::rmdir)(const char *)

Remove a directory

Definition at line 393 of file fuse.h.

◆ setxattr

int(* fuse_operations::setxattr)(const char *, const char *, const char *, size_t, int)

Set extended attributes

Definition at line 570 of file fuse.h.

◆ statfs

int(* fuse_operations::statfs)(const char *, struct statvfs *)

Get file system statistics

The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored

Definition at line 516 of file fuse.h.

◆ statx

int(* fuse_operations::statx)(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi)

Get extended file attributes.

fi may be NULL.

If path is NULL, then the AT_EMPTY_PATH bit in flags will be already set.

Definition at line 868 of file fuse.h.

◆ symlink

int(* fuse_operations::symlink)(const char *, const char *)

Create a symbolic link

Definition at line 396 of file fuse.h.

◆ truncate

int(* fuse_operations::truncate)(const char *, off_t, struct fuse_file_info *fi)

Change the size of a file

fi will always be NULL if the file is not currently open, but may also be NULL if the file is open.

Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is expected to reset the setuid and setgid bits.

Definition at line 437 of file fuse.h.

◆ unlink

int(* fuse_operations::unlink)(const char *)

Remove a file

Definition at line 390 of file fuse.h.

◆ utimens

int(* fuse_operations::utimens)(const char *, const struct timespec tv[2], struct fuse_file_info *fi)

Change the access and modification times of a file with nanosecond resolution

This supersedes the old utime() interface. New applications should use this.

fi will always be NULL if the file is not currently open, but may also be NULL if the file is open.

See the utimensat(2) man page for details.

Definition at line 719 of file fuse.h.

◆ write

int(* fuse_operations::write)(const char *, const char *, size_t, off_t, struct fuse_file_info *)

Write data to an open file

Write should return exactly the number of bytes requested except on error. An exception to this is when the 'direct_io' mount option is specified (see read operation).

Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is expected to reset the setuid and setgid bits.

Definition at line 509 of file fuse.h.

◆ write_buf

int(* fuse_operations::write_buf)(const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *)

Write contents of buffer to an open file

Similar to the write() method, but data is supplied in a generic buffer. Use fuse_buf_copy() to transfer data to the destination.

Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is expected to reset the setuid and setgid bits.

Definition at line 781 of file fuse.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__opt.html0000644000175000017500000002026315156613443017357 0ustar berndbernd libfuse: fuse_opt Struct Reference
libfuse
fuse_opt Struct Reference

#include <fuse_opt.h>

Data Fields

const char * templ
 
unsigned long offset
 
int value
 

Detailed Description

Option description

This structure describes a single option, and action associated with it, in case it matches.

More than one such match may occur, in which case the action for each match is executed.

There are three possible actions in case of a match:

i) An integer (int or unsigned) variable determined by 'offset' is set to 'value'

ii) The processing function is called, with 'value' as the key

iii) An integer (any) or string (char *) variable determined by 'offset' is set to the value of an option parameter

'offset' should normally be either set to

  • 'offsetof(struct foo, member)' actions i) and iii)
  • -1 action ii)

The 'offsetof()' macro is defined in the <stddef.h> header.

The template determines which options match, and also have an effect on the action. Normally the action is either i) or ii), but if a format is present in the template, then action iii) is performed.

The types of templates are:

1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only themselves. Invalid values are "--" and anything beginning with "-o"

2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or the relevant option in a comma separated option list

3) "bar=", "--foo=", etc. These are variations of 1) and 2) which have a parameter

4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform action iii).

5) "-x ", etc. Matches either "-xparam" or "-x param" as two separate arguments

6) "-x %s", etc. Combination of 4) and 5)

If the format is "%s", memory is allocated for the string unlike with scanf(). The previous value (if non-NULL) stored at the this location is freed.

Definition at line 77 of file fuse_opt.h.

Field Documentation

◆ offset

unsigned long fuse_opt::offset

Offset of variable within 'data' parameter of fuse_opt_parse() or -1

Definition at line 85 of file fuse_opt.h.

◆ templ

const char * fuse_opt::templ

Matching template and optional parameter formatting

Definition at line 79 of file fuse_opt.h.

◆ value

int fuse_opt::value

Value to set the variable to, or to be passed as 'key' to the processing function. Ignored if template has a format

Definition at line 91 of file fuse_opt.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__ring__pool.html0000644000175000017500000000502015156613443020676 0ustar berndbernd libfuse: fuse_ring_pool Struct Reference
libfuse
fuse_ring_pool Struct Reference

Detailed Description

Main fuse_ring structure, holds all fuse-ring data

Definition at line 72 of file fuse_uring.c.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__supp__groups.html0000644000175000017500000000567215156613443021311 0ustar berndbernd libfuse: fuse_supp_groups Struct Reference
libfuse
fuse_supp_groups Struct Reference

#include <fuse_kernel.h>

Detailed Description

struct fuse_supp_groups - Supplementary group extension @nr_groups: number of supplementary groups @groups: flexible array of group IDs

Definition at line 1184 of file fuse_kernel.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__uring__cmd__req.html0000644000175000017500000000525315156613443021673 0ustar berndbernd libfuse: fuse_uring_cmd_req Struct Reference
libfuse
fuse_uring_cmd_req Struct Reference

#include <fuse_kernel.h>

Detailed Description

In the 80B command area of the SQE.

Definition at line 1292 of file fuse_kernel.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structfuse__uring__req__header.html0000644000175000017500000000526315156613443022361 0ustar berndbernd libfuse: fuse_uring_req_header Struct Reference
libfuse
fuse_uring_req_header Struct Reference

#include <fuse_kernel.h>

Detailed Description

Header for all fuse-io-uring requests

Definition at line 1266 of file fuse_kernel.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/structlibfuse__version.html0000644000175000017500000000554015156613443020732 0ustar berndbernd libfuse: libfuse_version Struct Reference
libfuse
libfuse_version Struct Reference

#include <fuse_common.h>

Detailed Description

libfuse version a file system was compiled with. Should be filled in from defines in 'libfuse_config.h'

Definition at line 949 of file fuse_common.h.


The documentation for this struct was generated from the following files:
fuse-3.18.2/doc/html/dir_e68e8157741866f444e17edd764ebbae.html0000644000175000017500000000376415156613443021404 0ustar berndbernd libfuse: doc Directory Reference
libfuse
doc Directory Reference
fuse-3.18.2/doc/html/dir_ff8734a6ed1553e4cdaef764f37a71c4.html0000644000175000017500000000414715156613443021524 0ustar berndbernd libfuse: fuse-3.18.1/doc Directory Reference
libfuse
doc Directory Reference
fuse-3.18.2/doc/html/dir_cfafba98a580ce4b62f8a6fa96d7cbb0.html0000644000175000017500000002216315156613443022012 0ustar berndbernd libfuse: example Directory Reference
libfuse
example Directory Reference
fuse-3.18.2/doc/html/dir_e7cc7929bd1ffaa820e10e01d9c95abd.html0000644000175000017500000001320715156613443021641 0ustar berndbernd libfuse: fuse-3.17.4/example Directory Reference
libfuse
example Directory Reference

Files

 notify_store_retrieve.c
 
 null.c
 
 passthrough.c
 
 passthrough_fh.c
 
 passthrough_helpers.h
 
 passthrough_ll.c
 
 poll.c
 
 poll_client.c
 
 printcap.c
 
fuse-3.18.2/doc/html/dir_de8f9914a0c6d3fefb7eee74461f97ba.html0000644000175000017500000002345715156613443021703 0ustar berndbernd libfuse: fuse-3.18.1/example Directory Reference
libfuse
example Directory Reference
fuse-3.18.2/doc/html/dir_a1e8bb6aacb6fb222eb29390226a8aec.html0000644000175000017500000000733515156613443021627 0ustar berndbernd libfuse: fuse-3.17.4 Directory Reference
libfuse
fuse-3.17.4 Directory Reference

Directories

 example
 
 include
 
 lib
 
 test
 
 util
 
fuse-3.18.2/doc/html/dir_4d35850f380bf2b0e1e23612de44a403.html0000644000175000017500000000733515156613443021165 0ustar berndbernd libfuse: fuse-3.18.1 Directory Reference
libfuse
fuse-3.18.1 Directory Reference

Directories

 example
 
 include
 
 lib
 
 test
 
 util
 
fuse-3.18.2/doc/html/dir_ee79e556e893a265551a6396eb768ff4.html0000644000175000017500000001215215156613443021250 0ustar berndbernd libfuse: fuse-3.17.4/include Directory Reference
libfuse
include Directory Reference

Files

 cuse_lowlevel.h
 
 fuse.h
 
 fuse_common.h
 
 fuse_kernel.h
 
 fuse_log.h
 
 fuse_lowlevel.h
 
 fuse_mount_compat.h
 
 fuse_opt.h
 
fuse-3.18.2/doc/html/dir_567612448af13c8fdbe428427cb913c5.html0000644000175000017500000001215215156613443021211 0ustar berndbernd libfuse: fuse-3.18.1/include Directory Reference
libfuse
include Directory Reference

Files

 cuse_lowlevel.h
 
 fuse.h
 
 fuse_common.h
 
 fuse_kernel.h
 
 fuse_log.h
 
 fuse_lowlevel.h
 
 fuse_mount_compat.h
 
 fuse_opt.h
 
fuse-3.18.2/doc/html/dir_d44c64559bbebec7f509842c48db8b23.html0000644000175000017500000001146415156613443021443 0ustar berndbernd libfuse: include Directory Reference
libfuse
include Directory Reference

Files

 cuse_lowlevel.h
 
 fuse.h
 
 fuse_common.h
 
 fuse_kernel.h
 
 fuse_log.h
 
 fuse_lowlevel.h
 
 fuse_mount_compat.h
 
 fuse_opt.h
 
fuse-3.18.2/doc/html/dir_151531ba243f27a508f84a018a5044f9.html0000644000175000017500000002114015156613443021021 0ustar berndbernd libfuse: fuse-3.17.4/lib Directory Reference
libfuse
lib Directory Reference

Directories

 modules
 

Files

 buffer.c
 
 compat.c
 
 cuse_lowlevel.c
 
 fuse.c
 
 fuse_i.h
 
 fuse_log.c
 
 fuse_loop.c
 
 fuse_loop_mt.c
 
 fuse_lowlevel.c
 
 fuse_misc.h
 
 fuse_opt.c
 
 fuse_signals.c
 
 helper.c
 
 mount.c
 
 mount_bsd.c
 
 mount_util.c
 
 mount_util.h
 
 util.c
 
 util.h
 
fuse-3.18.2/doc/html/dir_bea620c7bc056d32632a5444abf88a37.html0000644000175000017500000002303315156613443021326 0ustar berndbernd libfuse: fuse-3.18.1/lib Directory Reference
libfuse
lib Directory Reference

Directories

 modules
 

Files

 buffer.c
 
 compat.c
 
 cuse_lowlevel.c
 
 fuse.c
 
 fuse_i.h
 
 fuse_log.c
 
 fuse_loop.c
 
 fuse_loop_mt.c
 
 fuse_lowlevel.c
 
 fuse_misc.h
 
 fuse_opt.c
 
 fuse_signals.c
 
 fuse_uring.c
 
 fuse_uring_i.h
 
 helper.c
 
 mount.c
 
 mount_bsd.c
 
 mount_util.c
 
 mount_util.h
 
 usdt.h
 
 util.c
 
 util.h
 
fuse-3.18.2/doc/html/dir_97aefd0d527b934f1d99a682da8fe6a9.html0000644000175000017500000002213615156613443021537 0ustar berndbernd libfuse: lib Directory Reference
libfuse
lib Directory Reference

Directories

 modules
 

Files

 buffer.c
 
 compat.c
 
 cuse_lowlevel.c
 
 fuse.c
 
 fuse_i.h
 
 fuse_log.c
 
 fuse_loop.c
 
 fuse_loop_mt.c
 
 fuse_lowlevel.c
 
 fuse_misc.h
 
 fuse_opt.c
 
 fuse_signals.c
 
 fuse_uring.c
 
 fuse_uring_i.h
 
 helper.c
 
 mount.c
 
 mount_bsd.c
 
 mount_util.c
 
 mount_util.h
 
 usdt.h
 
 util.c
 
 util.h
 
fuse-3.18.2/doc/html/dir_ef89b861fb26f7108388f05c496b561a.html0000644000175000017500000000575415156613443021242 0ustar berndbernd libfuse: fuse-3.17.4/lib/modules Directory Reference
libfuse
modules Directory Reference

Files

 iconv.c
 
 subdir.c
 
fuse-3.18.2/doc/html/dir_98164a96c6a79e43c7a77c344d27e55d.html0000644000175000017500000000575415156613443021245 0ustar berndbernd libfuse: fuse-3.18.1/lib/modules Directory Reference
libfuse
modules Directory Reference

Files

 iconv.c
 
 subdir.c
 
fuse-3.18.2/doc/html/dir_e1dbc8ba94a86723d4c32227b7c46099.html0000644000175000017500000000553315156613443021274 0ustar berndbernd libfuse: lib/modules Directory Reference
libfuse
modules Directory Reference

Files

 iconv.c
 
 subdir.c
 
fuse-3.18.2/doc/html/dir_f77c7566d5b54b3a8f6688c1c1718be3.html0000644000175000017500000001301015156613443021301 0ustar berndbernd libfuse: fuse-3.17.4/test Directory Reference
libfuse
test Directory Reference

Files

 hello.c
 
 readdir_inode.c
 
 release_unlink_race.c
 
 stracedecode.c
 
 test_abi.c
 
 test_setattr.c
 
 test_syscalls.c
 
 test_want_conversion.c
 
 test_write_cache.c
 
 wrong_command.c
 
fuse-3.18.2/doc/html/dir_3141c10b43d1ded6ee3c902c2fe1c48b.html0000644000175000017500000001351615156613443021462 0ustar berndbernd libfuse: fuse-3.18.1/test Directory Reference
libfuse
test Directory Reference

Files

 hello.c
 
 readdir_inode.c
 
 release_unlink_race.c
 
 stracedecode.c
 
 test_abi.c
 
 test_setattr.c
 
 test_signals.c
 
 test_syscalls.c
 
 test_want_conversion.c
 
 test_write_cache.c
 
 wrong_command.c
 
fuse-3.18.2/doc/html/dir_13e138d54eb8818da29c3992edef070a.html0000644000175000017500000001304715156613443021356 0ustar berndbernd libfuse: test Directory Reference
libfuse
test Directory Reference

Files

 hello.c
 
 readdir_inode.c
 
 release_unlink_race.c
 
 stracedecode.c
 
 test_abi.c
 
 test_setattr.c
 
 test_signals.c
 
 test_syscalls.c
 
 test_want_conversion.c
 
 test_write_cache.c
 
 wrong_command.c
 
fuse-3.18.2/doc/html/dir_73f848c5a0a9108732ea2e7217c7aea5.html0000644000175000017500000000560315156613443021265 0ustar berndbernd libfuse: fuse-3.17.4/util Directory Reference
libfuse
util Directory Reference

Files

 fusermount.c
 
 mount.fuse.c
 
fuse-3.18.2/doc/html/dir_aa785a52b370a0eb18aa670afa0403e9.html0000644000175000017500000000560315156613443021374 0ustar berndbernd libfuse: fuse-3.18.1/util Directory Reference
libfuse
util Directory Reference

Files

 fusermount.c
 
 mount.fuse.c
 
fuse-3.18.2/doc/html/dir_23ec12649285f9fabf3a6b7380226c28.html0000644000175000017500000000536215156613443021214 0ustar berndbernd libfuse: util Directory Reference
libfuse
util Directory Reference

Files

 fusermount.c
 
 mount.fuse.c
 
fuse-3.18.2/doc/html/menudata.js0000644000175000017500000000762415156613443015403 0ustar berndbernd/* @licstart The following is the entire license notice for the JavaScript code in this file. The MIT License (MIT) Copyright (C) 1997-2020 by Dimitri van Heesch Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @licend The above is the entire license notice for the JavaScript code in this file */ var menudata={children:[ {text:"Main Page",url:"index.html"}, {text:"Data Structures",url:"annotated.html",children:[ {text:"Data Structures",url:"annotated.html"}, {text:"Data Fields",url:"functions.html",children:[ {text:"All",url:"functions.html",children:[ {text:"a",url:"functions.html#index_a"}, {text:"b",url:"functions.html#index_b"}, {text:"c",url:"functions.html#index_c"}, {text:"d",url:"functions.html#index_d"}, {text:"e",url:"functions.html#index_e"}, {text:"f",url:"functions.html#index_f"}, {text:"g",url:"functions.html#index_g"}, {text:"h",url:"functions.html#index_h"}, {text:"i",url:"functions.html#index_i"}, {text:"k",url:"functions.html#index_k"}, {text:"l",url:"functions.html#index_l"}, {text:"m",url:"functions.html#index_m"}, {text:"n",url:"functions.html#index_n"}, {text:"o",url:"functions.html#index_o"}, {text:"p",url:"functions.html#index_p"}, {text:"r",url:"functions.html#index_r"}, {text:"s",url:"functions.html#index_s"}, {text:"t",url:"functions.html#index_t"}, {text:"u",url:"functions.html#index_u"}, {text:"v",url:"functions.html#index_v"}, {text:"w",url:"functions.html#index_w"}]}, {text:"Variables",url:"functions_vars.html",children:[ {text:"a",url:"functions_vars.html#index_a"}, {text:"b",url:"functions_vars.html#index_b"}, {text:"c",url:"functions_vars.html#index_c"}, {text:"d",url:"functions_vars.html#index_d"}, {text:"e",url:"functions_vars.html#index_e"}, {text:"f",url:"functions_vars.html#index_f"}, {text:"g",url:"functions_vars.html#index_g"}, {text:"h",url:"functions_vars.html#index_h"}, {text:"i",url:"functions_vars.html#index_i"}, {text:"k",url:"functions_vars.html#index_k"}, {text:"l",url:"functions_vars.html#index_l"}, {text:"m",url:"functions_vars.html#index_m"}, {text:"n",url:"functions_vars.html#index_n"}, {text:"o",url:"functions_vars.html#index_o"}, {text:"p",url:"functions_vars.html#index_p"}, {text:"r",url:"functions_vars.html#index_r"}, {text:"s",url:"functions_vars.html#index_s"}, {text:"t",url:"functions_vars.html#index_t"}, {text:"u",url:"functions_vars.html#index_u"}, {text:"v",url:"functions_vars.html#index_v"}, {text:"w",url:"functions_vars.html#index_w"}]}]}]}, {text:"Files",url:"files.html",children:[ {text:"File List",url:"files.html"}, {text:"Globals",url:"globals.html",children:[ {text:"All",url:"globals.html",children:[ {text:"f",url:"globals.html#index_f"}]}, {text:"Functions",url:"globals_func.html",children:[ {text:"f",url:"globals_func.html#index_f"}]}, {text:"Typedefs",url:"globals_type.html"}, {text:"Enumerations",url:"globals_enum.html"}, {text:"Enumerator",url:"globals_eval.html"}, {text:"Macros",url:"globals_defs.html",children:[ {text:"f",url:"globals_defs.html#index_f"}]}]}]}]} fuse-3.18.2/doc/html/index.html0000644000175000017500000001023515156613443015234 0ustar berndbernd libfuse: libfuse API documentation
libfuse
libfuse API documentation

FUSE (Filesystem in Userspace) is an interface for userspace programs to export a filesystem to the Linux kernel. The FUSE project consists of two components: the fuse kernel module (maintained in the regular kernel repositories) and the libfuse userspace library. libfuse provides the reference implementation for communicating with the FUSE kernel module.

A FUSE file system is typically implemented as a standalone application that links with libfuse. libfuse provides functions to mount the file system, unmount it, read requests from the kernel, and send responses back.

Getting started

libfuse offers two APIs: a "high-level", synchronous API, and a "low-level" asynchronous API. In both cases, incoming requests from the kernel are passed to the main program using callbacks. When using the high-level API, the callbacks may work with file names and paths instead of inodes, and processing of a request finishes when the callback function returns. When using the low-level API, the callbacks must work with inodes and responses must be sent explicitly using a separate set of API functions.

The high-level API that is primarily specified in fuse.h. The low-level API that is primarily documented in fuse_lowlevel.h.

Examples

FUSE comes with several examples in the examples directory. A good starting point are hello.c (for the high-level API) and hello_ll.c (for the low-level API).

FUSE internals

The authoritative source of information about libfuse internals (including the protocol used for communication with the FUSE kernel module) is the source code.

However, some people have kindly documented different aspects of FUSE in a more beginner friendly way. While this information is increasingly out of date, it still provides a good overview:

fuse-3.18.2/doc/html/annotated.html0000644000175000017500000001717615156613443016115 0ustar berndbernd libfuse: Data Structures
libfuse
Data Structures
fuse-3.18.2/doc/html/classes.html0000644000175000017500000000733215156613443015566 0ustar berndbernd libfuse: Data Structure Index
libfuse
Data Structure Index
fuse-3.18.2/doc/html/functions.html0000644000175000017500000006031115156613443016135 0ustar berndbernd libfuse: Data Fields
libfuse
Here is a list of all documented struct and union fields with links to the struct/union documentation for each field:

- a -

- b -

- c -

- d -

- e -

- f -

- g -

- h -

- i -

- k -

- l -

- m -

- n -

- o -

- p -

- r -

- s -

- t -

- u -

- v -

- w -

fuse-3.18.2/doc/html/functions_vars.html0000644000175000017500000006030715156613444017176 0ustar berndbernd libfuse: Data Fields - Variables
libfuse
Here is a list of all documented variables with links to the struct/union documentation for each field:

- a -

- b -

- c -

- d -

- e -

- f -

- g -

- h -

- i -

- k -

- l -

- m -

- n -

- o -

- p -

- r -

- s -

- t -

- u -

- v -

- w -

fuse-3.18.2/doc/html/files.html0000644000175000017500000017103615156613444015237 0ustar berndbernd libfuse: File List
libfuse
File List
Here is a list of all documented files with brief descriptions:
[detail level 1234]
  example
 cuse.c
 cuse_client.c
 hello.c
 hello_ll.c
 hello_ll_uds.c
 invalidate_path.c
 ioctl.c
 ioctl.h
 ioctl_client.c
 notify_inval_entry.c
 notify_inval_inode.c
 notify_store_retrieve.c
 null.c
 passthrough.c
 passthrough_fh.c
 passthrough_helpers.h
 passthrough_ll.c
 poll.c
 poll_client.c
 printcap.c
  fuse-3.17.4
  example
  include
  lib
  test
  util
  fuse-3.18.1
  example
  include
  lib
  test
  util
  include
 cuse_lowlevel.h
 fuse.h
 fuse_common.h
 fuse_kernel.h
 fuse_log.h
 fuse_lowlevel.h
 fuse_mount_compat.h
 fuse_opt.h
  lib
  modules
 buffer.c
 compat.c
 cuse_lowlevel.c
 fuse.c
 fuse_i.h
 fuse_log.c
 fuse_loop.c
 fuse_loop_mt.c
 fuse_lowlevel.c
 fuse_misc.h
 fuse_opt.c
 fuse_signals.c
 fuse_uring.c
 fuse_uring_i.h
 helper.c
 mount.c
 mount_bsd.c
 mount_util.c
 mount_util.h
 usdt.h
 util.c
 util.h
  test
 hello.c
 readdir_inode.c
 release_unlink_race.c
 stracedecode.c
 test_abi.c
 test_setattr.c
 test_signals.c
 test_syscalls.c
 test_want_conversion.c
 test_write_cache.c
 wrong_command.c
  util
 fusermount.c
 mount.fuse.c
fuse-3.18.2/doc/html/globals.html0000644000175000017500000006471515156613444015565 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:

- f -

fuse-3.18.2/doc/html/globals_func.html0000644000175000017500000004121715156613444016570 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented functions with links to the documentation:

- f -

fuse-3.18.2/doc/html/globals_type.html0000644000175000017500000000554315156613444016620 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented typedefs with links to the documentation:
fuse-3.18.2/doc/html/globals_enum.html0000644000175000017500000000532215156613444016576 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented enums with links to the documentation:
fuse-3.18.2/doc/html/globals_eval.html0000644000175000017500000000675115156613444016570 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented enum values with links to the documentation:
fuse-3.18.2/doc/html/globals_defs.html0000644000175000017500000002007715156613444016557 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented macros with links to the documentation:

- f -

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2cuse_8c_source.html0000644000175000017500000016620015156613427023016 0ustar berndbernd libfuse: fuse-3.18.2/example/cuse.c Source File
libfuse
cuse.c
Go to the documentation of this file.
1/*
2 CUSE example: Character device in Userspace
3 Copyright (C) 2008-2009 SUSE Linux Products GmbH
4 Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8
9*/
10
34#define FUSE_USE_VERSION 31
35
36#include <cuse_lowlevel.h>
37#include <fuse_opt.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43#include <errno.h>
44
45#include "ioctl.h"
46
47static void *cusexmp_buf;
48static size_t cusexmp_size;
49
50static const char *usage =
51"usage: cusexmp [options]\n"
52"\n"
53"options:\n"
54" --help|-h print this help message\n"
55" --maj=MAJ|-M MAJ device major number\n"
56" --min=MIN|-m MIN device minor number\n"
57" --name=NAME|-n NAME device name (mandatory)\n"
58" -d -o debug enable debug output (implies -f)\n"
59" -f foreground operation\n"
60" -s disable multi-threaded operation\n"
61"\n";
62
63static int cusexmp_resize(size_t new_size)
64{
65 void *new_buf;
66
67 if (new_size == cusexmp_size)
68 return 0;
69
70 new_buf = realloc(cusexmp_buf, new_size);
71 if (!new_buf && new_size)
72 return -ENOMEM;
73
74 if (new_size > cusexmp_size)
75 memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
76
77 cusexmp_buf = new_buf;
78 cusexmp_size = new_size;
79
80 return 0;
81}
82
83static int cusexmp_expand(size_t new_size)
84{
85 if (new_size > cusexmp_size)
86 return cusexmp_resize(new_size);
87 return 0;
88}
89
90static void cusexmp_init(void *userdata, struct fuse_conn_info *conn)
91{
92 (void)userdata;
93
94 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
95 conn->no_interrupt = 1;
96}
97
98static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
99{
100 fuse_reply_open(req, fi);
101}
102
103static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
104 struct fuse_file_info *fi)
105{
106 (void)fi;
107
108 if (off >= cusexmp_size)
109 off = cusexmp_size;
110 if (size > cusexmp_size - off)
111 size = cusexmp_size - off;
112
113 fuse_reply_buf(req, cusexmp_buf + off, size);
114}
115
116static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
117 off_t off, struct fuse_file_info *fi)
118{
119 (void)fi;
120
121 if (cusexmp_expand(off + size)) {
122 fuse_reply_err(req, ENOMEM);
123 return;
124 }
125
126 memcpy(cusexmp_buf + off, buf, size);
127 fuse_reply_write(req, size);
128}
129
130static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
131 size_t in_bufsz, size_t out_bufsz, int is_read)
132{
133 const struct fioc_rw_arg *arg;
134 struct iovec in_iov[2], out_iov[3], iov[3];
135 size_t cur_size;
136
137 /* read in arg */
138 in_iov[0].iov_base = addr;
139 in_iov[0].iov_len = sizeof(*arg);
140 if (!in_bufsz) {
141 fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
142 return;
143 }
144 arg = in_buf;
145 in_buf += sizeof(*arg);
146 in_bufsz -= sizeof(*arg);
147
148 /* prepare size outputs */
149 out_iov[0].iov_base =
150 addr + offsetof(struct fioc_rw_arg, prev_size);
151 out_iov[0].iov_len = sizeof(arg->prev_size);
152
153 out_iov[1].iov_base =
154 addr + offsetof(struct fioc_rw_arg, new_size);
155 out_iov[1].iov_len = sizeof(arg->new_size);
156
157 /* prepare client buf */
158 if (is_read) {
159 out_iov[2].iov_base = arg->buf;
160 out_iov[2].iov_len = arg->size;
161 if (!out_bufsz) {
162 fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
163 return;
164 }
165 } else {
166 in_iov[1].iov_base = arg->buf;
167 in_iov[1].iov_len = arg->size;
168 if (arg->size && !in_bufsz) {
169 fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
170 return;
171 }
172 }
173
174 /* we're all set */
175 cur_size = cusexmp_size;
176 iov[0].iov_base = &cur_size;
177 iov[0].iov_len = sizeof(cur_size);
178
179 iov[1].iov_base = &cusexmp_size;
180 iov[1].iov_len = sizeof(cusexmp_size);
181
182 if (is_read) {
183 size_t off = arg->offset;
184 size_t size = arg->size;
185
186 if (off >= cusexmp_size)
187 off = cusexmp_size;
188 if (size > cusexmp_size - off)
189 size = cusexmp_size - off;
190
191 iov[2].iov_base = cusexmp_buf + off;
192 iov[2].iov_len = size;
193 fuse_reply_ioctl_iov(req, size, iov, 3);
194 } else {
195 if (cusexmp_expand(arg->offset + in_bufsz)) {
196 fuse_reply_err(req, ENOMEM);
197 return;
198 }
199
200 memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
201 fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
202 }
203}
204
205static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
206 struct fuse_file_info *fi, unsigned flags,
207 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
208{
209 int is_read = 0;
210
211 (void)fi;
212
213 if (flags & FUSE_IOCTL_COMPAT) {
214 fuse_reply_err(req, ENOSYS);
215 return;
216 }
217
218 switch (cmd) {
219 case FIOC_GET_SIZE:
220 if (!out_bufsz) {
221 struct iovec iov = { arg, sizeof(size_t) };
222
223 fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
224 } else
225 fuse_reply_ioctl(req, 0, &cusexmp_size,
226 sizeof(cusexmp_size));
227 break;
228
229 case FIOC_SET_SIZE:
230 if (!in_bufsz) {
231 struct iovec iov = { arg, sizeof(size_t) };
232
233 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
234 } else {
235 cusexmp_resize(*(size_t *)in_buf);
236 fuse_reply_ioctl(req, 0, NULL, 0);
237 }
238 break;
239
240 case FIOC_READ:
241 is_read = 1;
242 /* fall through */
243 case FIOC_WRITE:
244 fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
245 break;
246
247 default:
248 fuse_reply_err(req, EINVAL);
249 }
250}
251
252struct cusexmp_param {
253 unsigned major;
254 unsigned minor;
255 char *dev_name;
256 int is_help;
257};
258
259#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
260
261static const struct fuse_opt cusexmp_opts[] = {
262 CUSEXMP_OPT("-M %u", major),
263 CUSEXMP_OPT("--maj=%u", major),
264 CUSEXMP_OPT("-m %u", minor),
265 CUSEXMP_OPT("--min=%u", minor),
266 CUSEXMP_OPT("-n %s", dev_name),
267 CUSEXMP_OPT("--name=%s", dev_name),
268 FUSE_OPT_KEY("-h", 0),
269 FUSE_OPT_KEY("--help", 0),
271};
272
273static int cusexmp_process_arg(void *data, const char *arg, int key,
274 struct fuse_args *outargs)
275{
276 struct cusexmp_param *param = data;
277
278 (void)outargs;
279 (void)arg;
280
281 switch (key) {
282 case 0:
283 param->is_help = 1;
284 fprintf(stderr, "%s", usage);
285 return fuse_opt_add_arg(outargs, "-ho");
286 default:
287 return 1;
288 }
289}
290
291static const struct cuse_lowlevel_ops cusexmp_clop = {
292 .init = cusexmp_init,
293 .open = cusexmp_open,
294 .read = cusexmp_read,
295 .write = cusexmp_write,
296 .ioctl = cusexmp_ioctl,
297};
298
299int main(int argc, char **argv)
300{
301 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
302 struct cusexmp_param param = { 0, 0, NULL, 0 };
303 char dev_name[128] = "DEVNAME=";
304 const char *dev_info_argv[] = { dev_name };
305 struct cuse_info ci;
306 int ret = 1;
307
308 if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
309 printf("failed to parse option\n");
310 free(param.dev_name);
311 goto out;
312 }
313
314 if (!param.is_help) {
315 if (!param.dev_name) {
316 fprintf(stderr, "Error: device name missing\n");
317 goto out;
318 }
319 strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME="));
320 free(param.dev_name);
321 }
322
323 memset(&ci, 0, sizeof(ci));
324 ci.dev_major = param.major;
325 ci.dev_minor = param.minor;
326 ci.dev_info_argc = 1;
327 ci.dev_info_argv = dev_info_argv;
328 ci.flags = CUSE_UNRESTRICTED_IOCTL;
329
330 ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL);
331
332out:
333 fuse_opt_free_args(&args);
334 return ret;
335}
#define FUSE_IOCTL_COMPAT
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2cuse__client_8c_source.html0000644000175000017500000005060215156613427024511 0ustar berndbernd libfuse: fuse-3.18.2/example/cuse_client.c Source File
libfuse
cuse_client.c
Go to the documentation of this file.
1/*
2 FUSE fioclient: FUSE ioctl example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
40#include <sys/types.h>
41#include <fcntl.h>
42#include <sys/stat.h>
43#include <sys/ioctl.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <ctype.h>
47#include <errno.h>
48#include <unistd.h>
49#include "ioctl.h"
50
51const char *usage =
52"Usage: cuse_client FIOC_FILE COMMAND\n"
53"\n"
54"COMMANDS\n"
55" s [SIZE] : get size if SIZE is omitted, set size otherwise\n"
56" r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n"
57" w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n"
58"\n";
59
60static int do_rw(int fd, int is_read, size_t size, off_t offset,
61 size_t *prev_size, size_t *new_size)
62{
63 struct fioc_rw_arg arg = { .offset = offset };
64 ssize_t ret;
65
66 arg.buf = calloc(1, size);
67 if (!arg.buf) {
68 fprintf(stderr, "failed to allocated %zu bytes\n", size);
69 return -1;
70 }
71
72 if (is_read) {
73 arg.size = size;
74 ret = ioctl(fd, FIOC_READ, &arg);
75 if (ret >= 0)
76 fwrite(arg.buf, 1, ret, stdout);
77 } else {
78 arg.size = fread(arg.buf, 1, size, stdin);
79 fprintf(stderr, "Writing %zu bytes\n", arg.size);
80 ret = ioctl(fd, FIOC_WRITE, &arg);
81 }
82
83 if (ret >= 0) {
84 *prev_size = arg.prev_size;
85 *new_size = arg.new_size;
86 } else
87 perror("ioctl");
88
89 free(arg.buf);
90 return ret;
91}
92
93int main(int argc, char **argv)
94{
95 size_t param[2] = { };
96 size_t size, prev_size = 0, new_size = 0;
97 char cmd;
98 int fd, i, rc;
99
100 if (argc < 3)
101 goto usage;
102
103 fd = open(argv[1], O_RDWR);
104 if (fd < 0) {
105 perror("open");
106 return 1;
107 }
108
109 cmd = tolower(argv[2][0]);
110 argc -= 3;
111 argv += 3;
112
113 for (i = 0; i < argc; i++) {
114 char *endp;
115 param[i] = strtoul(argv[i], &endp, 0);
116 if (endp == argv[i] || *endp != '\0')
117 goto usage;
118 }
119
120 switch (cmd) {
121 case 's':
122 if (!argc) {
123 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
124 perror("ioctl");
125 goto error;
126 }
127 printf("%zu\n", size);
128 } else {
129 size = param[0];
130 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
131 perror("ioctl");
132 goto error;
133 }
134 }
135 close(fd);
136 return 0;
137
138 case 'r':
139 case 'w':
140 rc = do_rw(fd, cmd == 'r', param[0], param[1],
141 &prev_size, &new_size);
142 if (rc < 0)
143 goto error;
144 fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n",
145 rc, prev_size, new_size);
146 close(fd);
147 return 0;
148 }
149
150usage:
151 fprintf(stderr, "%s", usage);
152 return 1;
153
154error:
155 close(fd);
156 return 1;
157}
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2hello_8c_source.html0000644000175000017500000011620715156613427023164 0ustar berndbernd libfuse: fuse-3.18.2/example/hello.c Source File
libfuse
hello.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
22#define FUSE_USE_VERSION 31
23
24#include <fuse.h>
25#include <stdio.h>
26#include <string.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <stddef.h>
30#include <assert.h>
31
32/*
33 * Command line options
34 *
35 * We can't set default values for the char* fields here because
36 * fuse_opt_parse would attempt to free() them when the user specifies
37 * different values on the command line.
38 */
39static struct options {
40 const char *filename;
41 const char *contents;
42 int show_help;
43} options;
44
45#define OPTION(t, p) \
46 { t, offsetof(struct options, p), 1 }
47static const struct fuse_opt option_spec[] = {
48 OPTION("--name=%s", filename),
49 OPTION("--contents=%s", contents),
50 OPTION("-h", show_help),
51 OPTION("--help", show_help),
53};
54
55static void *hello_init(struct fuse_conn_info *conn,
56 struct fuse_config *cfg)
57{
58 (void) conn;
59 cfg->kernel_cache = 1;
60
61 /* Test setting flags the old way */
64
65 return NULL;
66}
67
68static int hello_getattr(const char *path, struct stat *stbuf,
69 struct fuse_file_info *fi)
70{
71 (void) fi;
72 int res = 0;
73
74 memset(stbuf, 0, sizeof(struct stat));
75 if (strcmp(path, "/") == 0) {
76 stbuf->st_mode = S_IFDIR | 0755;
77 stbuf->st_nlink = 2;
78 } else if (strcmp(path+1, options.filename) == 0) {
79 stbuf->st_mode = S_IFREG | 0444;
80 stbuf->st_nlink = 1;
81 stbuf->st_size = strlen(options.contents);
82 } else
83 res = -ENOENT;
84
85 return res;
86}
87
88static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
89 off_t offset, struct fuse_file_info *fi,
90 enum fuse_readdir_flags flags)
91{
92 (void) offset;
93 (void) fi;
94 (void) flags;
95
96 if (strcmp(path, "/") != 0)
97 return -ENOENT;
98
99 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
100 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
101 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
102
103 return 0;
104}
105
106static int hello_open(const char *path, struct fuse_file_info *fi)
107{
108 if (strcmp(path+1, options.filename) != 0)
109 return -ENOENT;
110
111 if ((fi->flags & O_ACCMODE) != O_RDONLY)
112 return -EACCES;
113
114 return 0;
115}
116
117static int hello_read(const char *path, char *buf, size_t size, off_t offset,
118 struct fuse_file_info *fi)
119{
120 size_t len;
121 (void) fi;
122 if(strcmp(path+1, options.filename) != 0)
123 return -ENOENT;
124
125 len = strlen(options.contents);
126 if (offset < len) {
127 if (offset + size > len)
128 size = len - offset;
129 memcpy(buf, options.contents + offset, size);
130 } else
131 size = 0;
132
133 return size;
134}
135
136static const struct fuse_operations hello_oper = {
137 .init = hello_init,
138 .getattr = hello_getattr,
139 .readdir = hello_readdir,
140 .open = hello_open,
141 .read = hello_read,
142};
143
144static void show_help(const char *progname)
145{
146 printf("usage: %s [options] <mountpoint>\n\n", progname);
147 printf("File-system specific options:\n"
148 " --name=<s> Name of the \"hello\" file\n"
149 " (default: \"hello\")\n"
150 " --contents=<s> Contents \"hello\" file\n"
151 " (default \"Hello, World!\\n\")\n"
152 "\n");
153}
154
155int main(int argc, char *argv[])
156{
157 int ret;
158 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
159
160 /* Set defaults -- we have to use strdup so that
161 fuse_opt_parse can free the defaults if other
162 values are specified */
163 options.filename = strdup("hello");
164 options.contents = strdup("Hello World!\n");
165
166 /* Parse options */
167 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
168 return 1;
169
170 /* When --help is specified, first print our own file-system
171 specific help text, then signal fuse_main to show
172 additional help (by adding `--help` to the options again)
173 without usage: line (by setting argv[0] to the empty
174 string) */
175 if (options.show_help) {
176 show_help(argv[0]);
177 assert(fuse_opt_add_arg(&args, "--help") == 0);
178 args.argv[0][0] = '\0';
179 }
180
181 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
182 fuse_opt_free_args(&args);
183 return ret;
184}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2hello_8c_source.html0000644000175000017500000011407315156613427022507 0ustar berndbernd libfuse: fuse-3.18.2/test/hello.c Source File
libfuse
hello.c
Go to the documentation of this file.
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 *
5 * This program can be distributed under the terms of the GNU GPLv2.
6 * See the file GPL2.txt.
7 */
8
21#define FUSE_USE_VERSION 31
22
23#include <fuse.h>
24#include <stdio.h>
25#include <string.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <stddef.h>
29#include <assert.h>
30
31/*
32 * Command line options
33 *
34 * We can't set default values for the char* fields here because
35 * fuse_opt_parse would attempt to free() them when the user specifies
36 * different values on the command line.
37 */
38static struct options {
39 const char *filename;
40 const char *contents;
41 int show_help;
42} options;
43
44#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
45static const struct fuse_opt option_spec[] = {
46 OPTION("--name=%s", filename), OPTION("--contents=%s", contents),
47 OPTION("-h", show_help), OPTION("--help", show_help), FUSE_OPT_END
48};
49
50static void *hello_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
51{
52 (void)conn;
53 cfg->kernel_cache = 1;
54
55 /* Test setting flags the old way */
57 conn->want &= ~FUSE_CAP_ASYNC_READ;
58
59 return NULL;
60}
61
62static int hello_getattr(const char *path, struct stat *stbuf,
63 struct fuse_file_info *fi)
64{
65 (void)fi;
66 int res = 0;
67
68 memset(stbuf, 0, sizeof(struct stat));
69 if (strcmp(path, "/") == 0) {
70 stbuf->st_mode = S_IFDIR | 0755;
71 stbuf->st_nlink = 2;
72 } else if (strcmp(path + 1, options.filename) == 0) {
73 stbuf->st_mode = S_IFREG | 0444;
74 stbuf->st_nlink = 1;
75 stbuf->st_size = strlen(options.contents);
76 } else
77 res = -ENOENT;
78
79 return res;
80}
81
82static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
83 off_t offset, struct fuse_file_info *fi,
84 enum fuse_readdir_flags flags)
85{
86 (void)offset;
87 (void)fi;
88 (void)flags;
89
90 if (strcmp(path, "/") != 0)
91 return -ENOENT;
92
93 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
94 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
95 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
96
97 return 0;
98}
99
100static int hello_open(const char *path, struct fuse_file_info *fi)
101{
102 if (strcmp(path + 1, options.filename) != 0)
103 return -ENOENT;
104
105 if ((fi->flags & O_ACCMODE) != O_RDONLY)
106 return -EACCES;
107
108 return 0;
109}
110
111static int hello_read(const char *path, char *buf, size_t size, off_t offset,
112 struct fuse_file_info *fi)
113{
114 size_t len;
115 (void)fi;
116 if (strcmp(path + 1, options.filename) != 0)
117 return -ENOENT;
118
119 len = strlen(options.contents);
120 if (offset < len) {
121 if (offset + size > len)
122 size = len - offset;
123 memcpy(buf, options.contents + offset, size);
124 } else
125 size = 0;
126
127 return size;
128}
129
130static const struct fuse_operations hello_oper = {
131 .init = hello_init,
132 .getattr = hello_getattr,
133 .readdir = hello_readdir,
134 .open = hello_open,
135 .read = hello_read,
136};
137
138static void show_help(const char *progname)
139{
140 printf("usage: %s [options] <mountpoint>\n\n", progname);
141 printf("File-system specific options:\n"
142 " --name=<s> Name of the \"hello\" file\n"
143 " (default: \"hello\")\n"
144 " --contents=<s> Contents \"hello\" file\n"
145 " (default \"Hello, World!\\n\")\n"
146 "\n");
147}
148
149int main(int argc, char *argv[])
150{
151 int ret;
152 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
153
154 /* Set defaults -- we have to use strdup so that
155 * fuse_opt_parse can free the defaults if other
156 * values are specified
157 */
158 options.filename = strdup("hello");
159 options.contents = strdup("Hello World!\n");
160
161 /* Parse options */
162 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
163 return 1;
164
165 /* When --help is specified, first print our own file-system
166 * specific help text, then signal fuse_main to show
167 * additional help (by adding `--help` to the options again)
168 * without usage: line (by setting argv[0] to the empty
169 * string)
170 */
171 if (options.show_help) {
172 show_help(argv[0]);
173 assert(fuse_opt_add_arg(&args, "--help") == 0);
174 args.argv[0][0] = '\0';
175 }
176
177 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
178 fuse_opt_free_args(&args);
179 return ret;
180}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2hello__ll_8c_source.html0000644000175000017500000017367015156613427024021 0ustar berndbernd libfuse: fuse-3.18.2/example/hello_ll.c Source File
libfuse
hello_ll.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
25#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
26
27#include <fuse_lowlevel.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <unistd.h>
34#include <assert.h>
35
36static const char *hello_str = "Hello World!\n";
37static const char *hello_name = "hello";
38
39static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
40{
41 stbuf->st_ino = ino;
42 switch (ino) {
43 case 1:
44 stbuf->st_mode = S_IFDIR | 0755;
45 stbuf->st_nlink = 2;
46 break;
47
48 case 2:
49 stbuf->st_mode = S_IFREG | 0444;
50 stbuf->st_nlink = 1;
51 stbuf->st_size = strlen(hello_str);
52 break;
53
54 default:
55 return -1;
56 }
57 return 0;
58}
59
60static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
61{
62 (void)userdata;
63
64 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
65 conn->no_interrupt = 1;
66
67 /* Test setting flags the old way */
69 conn->want &= ~FUSE_CAP_ASYNC_READ;
70}
71
72static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
73 struct fuse_file_info *fi)
74{
75 struct stat stbuf;
76
77 (void) fi;
78
79 memset(&stbuf, 0, sizeof(stbuf));
80 if (hello_stat(ino, &stbuf) == -1)
81 fuse_reply_err(req, ENOENT);
82 else
83 fuse_reply_attr(req, &stbuf, 1.0);
84}
85
86static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
87{
88 struct fuse_entry_param e;
89
90 if (parent != 1 || strcmp(name, hello_name) != 0)
91 fuse_reply_err(req, ENOENT);
92 else {
93 memset(&e, 0, sizeof(e));
94 e.ino = 2;
95 e.attr_timeout = 1.0;
96 e.entry_timeout = 1.0;
97 hello_stat(e.ino, &e.attr);
98
99 fuse_reply_entry(req, &e);
100 }
101}
102
103struct dirbuf {
104 char *p;
105 size_t size;
106};
107
108static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
109 fuse_ino_t ino)
110{
111 struct stat stbuf;
112 size_t oldsize = b->size;
113 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
114 b->p = (char *) realloc(b->p, b->size);
115 memset(&stbuf, 0, sizeof(stbuf));
116 stbuf.st_ino = ino;
117 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
118 b->size);
119}
120
121#define min(x, y) ((x) < (y) ? (x) : (y))
122
123static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
124 off_t off, size_t maxsize)
125{
126 if (off < bufsize)
127 return fuse_reply_buf(req, buf + off,
128 min(bufsize - off, maxsize));
129 else
130 return fuse_reply_buf(req, NULL, 0);
131}
132
133static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
134 off_t off, struct fuse_file_info *fi)
135{
136 (void) fi;
137
138 if (ino != 1)
139 fuse_reply_err(req, ENOTDIR);
140 else {
141 struct dirbuf b;
142
143 memset(&b, 0, sizeof(b));
144 dirbuf_add(req, &b, ".", 1);
145 dirbuf_add(req, &b, "..", 1);
146 dirbuf_add(req, &b, hello_name, 2);
147 reply_buf_limited(req, b.p, b.size, off, size);
148 free(b.p);
149 }
150}
151
152static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
153 struct fuse_file_info *fi)
154{
155 if (ino != 2)
156 fuse_reply_err(req, EISDIR);
157 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
158 fuse_reply_err(req, EACCES);
159 else
160 fuse_reply_open(req, fi);
161}
162
163static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
164 off_t off, struct fuse_file_info *fi)
165{
166 (void) fi;
167
168 assert(ino == 2);
169 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
170}
171
172static void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
173 size_t size)
174{
175 (void)size;
176 assert(ino == 1 || ino == 2);
177 if (strcmp(name, "hello_ll_getxattr_name") == 0)
178 {
179 const char *buf = "hello_ll_getxattr_value";
180 fuse_reply_buf(req, buf, strlen(buf));
181 }
182 else
183 {
184 fuse_reply_err(req, ENOTSUP);
185 }
186}
187
188static void hello_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
189 const char *value, size_t size, int flags)
190{
191 (void)flags;
192 (void)size;
193 assert(ino == 1 || ino == 2);
194 const char* exp_val = "hello_ll_setxattr_value";
195 if (strcmp(name, "hello_ll_setxattr_name") == 0 &&
196 strlen(exp_val) == size &&
197 strncmp(value, exp_val, size) == 0)
198 {
199 fuse_reply_err(req, 0);
200 }
201 else
202 {
203 fuse_reply_err(req, ENOTSUP);
204 }
205}
206
207static void hello_ll_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
208{
209 assert(ino == 1 || ino == 2);
210 if (strcmp(name, "hello_ll_removexattr_name") == 0)
211 {
212 fuse_reply_err(req, 0);
213 }
214 else
215 {
216 fuse_reply_err(req, ENOTSUP);
217 }
218}
219
220static const struct fuse_lowlevel_ops hello_ll_oper = {
221 .init = hello_ll_init,
222 .lookup = hello_ll_lookup,
223 .getattr = hello_ll_getattr,
224 .readdir = hello_ll_readdir,
225 .open = hello_ll_open,
226 .read = hello_ll_read,
227 .setxattr = hello_ll_setxattr,
228 .getxattr = hello_ll_getxattr,
229 .removexattr = hello_ll_removexattr,
230};
231
232int main(int argc, char *argv[])
233{
234 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
235 struct fuse_session *se;
236 struct fuse_cmdline_opts opts;
237 struct fuse_loop_config *config;
238 int ret = -1;
239
240 if (fuse_parse_cmdline(&args, &opts) != 0)
241 return 1;
242 if (opts.show_help) {
243 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
246 ret = 0;
247 goto err_out1;
248 } else if (opts.show_version) {
249 printf("FUSE library version %s\n", fuse_pkgversion());
251 ret = 0;
252 goto err_out1;
253 }
254
255 if(opts.mountpoint == NULL) {
256 printf("usage: %s [options] <mountpoint>\n", argv[0]);
257 printf(" %s --help\n", argv[0]);
258 ret = 1;
259 goto err_out1;
260 }
261
262 se = fuse_session_new(&args, &hello_ll_oper,
263 sizeof(hello_ll_oper), NULL);
264 if (se == NULL)
265 goto err_out1;
266
267 if (fuse_set_signal_handlers(se) != 0)
268 goto err_out2;
269
270 if (fuse_session_mount(se, opts.mountpoint) != 0)
271 goto err_out3;
272
273 fuse_daemonize(opts.foreground);
274
275 /* Block until ctrl+c or fusermount -u */
276 if (opts.singlethread)
277 ret = fuse_session_loop(se);
278 else {
279 config = fuse_loop_cfg_create();
280 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
281 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
282 ret = fuse_session_loop_mt(se, config);
283 fuse_loop_cfg_destroy(config);
284 config = NULL;
285 }
286
288err_out3:
290err_out2:
292err_out1:
293 free(opts.mountpoint);
294 fuse_opt_free_args(&args);
295
296 return ret ? 1 : 0;
297}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2hello__ll__uds_8c_source.html0000644000175000017500000020750315156613427025024 0ustar berndbernd libfuse: fuse-3.18.2/example/hello_ll_uds.c Source File
libfuse
hello_ll_uds.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2022 Tofik Sonono <tofik.sonono@intel.com>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
23#define FUSE_USE_VERSION 34
24
25
26#ifndef _GNU_SOURCE
27#define _GNU_SOURCE
28#endif
29
30#include <fuse_lowlevel.h>
31#include <fuse_kernel.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <unistd.h>
38#include <assert.h>
39#include <sys/socket.h>
40#include <sys/un.h>
41
42static const char *hello_str = "Hello World!\n";
43static const char *hello_name = "hello";
44
45static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
46{
47 stbuf->st_ino = ino;
48 switch (ino) {
49 case 1:
50 stbuf->st_mode = S_IFDIR | 0755;
51 stbuf->st_nlink = 2;
52 break;
53
54 case 2:
55 stbuf->st_mode = S_IFREG | 0444;
56 stbuf->st_nlink = 1;
57 stbuf->st_size = strlen(hello_str);
58 break;
59
60 default:
61 return -1;
62 }
63 return 0;
64}
65
66static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
67 struct fuse_file_info *fi)
68{
69 struct stat stbuf;
70
71 (void) fi;
72
73 memset(&stbuf, 0, sizeof(stbuf));
74 if (hello_stat(ino, &stbuf) == -1)
75 fuse_reply_err(req, ENOENT);
76 else
77 fuse_reply_attr(req, &stbuf, 1.0);
78}
79
80static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
81{
82 (void)userdata;
83
84 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
85 conn->no_interrupt = 1;
86}
87
88static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
89{
90 struct fuse_entry_param e;
91
92 if (parent != 1 || strcmp(name, hello_name) != 0)
93 fuse_reply_err(req, ENOENT);
94 else {
95 memset(&e, 0, sizeof(e));
96 e.ino = 2;
97 e.attr_timeout = 1.0;
98 e.entry_timeout = 1.0;
99 hello_stat(e.ino, &e.attr);
100
101 fuse_reply_entry(req, &e);
102 }
103}
104
105struct dirbuf {
106 char *p;
107 size_t size;
108};
109
110static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
111 fuse_ino_t ino)
112{
113 struct stat stbuf;
114 size_t oldsize = b->size;
115 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
116 b->p = (char *) realloc(b->p, b->size);
117 memset(&stbuf, 0, sizeof(stbuf));
118 stbuf.st_ino = ino;
119 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
120 b->size);
121}
122
123#define min(x, y) ((x) < (y) ? (x) : (y))
124
125static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
126 off_t off, size_t maxsize)
127{
128 if (off < bufsize)
129 return fuse_reply_buf(req, buf + off,
130 min(bufsize - off, maxsize));
131 else
132 return fuse_reply_buf(req, NULL, 0);
133}
134
135static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
136 off_t off, struct fuse_file_info *fi)
137{
138 (void) fi;
139
140 if (ino != 1)
141 fuse_reply_err(req, ENOTDIR);
142 else {
143 struct dirbuf b;
144
145 memset(&b, 0, sizeof(b));
146 dirbuf_add(req, &b, ".", 1);
147 dirbuf_add(req, &b, "..", 1);
148 dirbuf_add(req, &b, hello_name, 2);
149 reply_buf_limited(req, b.p, b.size, off, size);
150 free(b.p);
151 }
152}
153
154static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
155 struct fuse_file_info *fi)
156{
157 if (ino != 2)
158 fuse_reply_err(req, EISDIR);
159 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
160 fuse_reply_err(req, EACCES);
161 else
162 fuse_reply_open(req, fi);
163}
164
165static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
166 off_t off, struct fuse_file_info *fi)
167{
168 (void) fi;
169
170 assert(ino == 2);
171 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
172}
173
174static const struct fuse_lowlevel_ops hello_ll_oper = {
175 .init = hello_ll_init,
176 .lookup = hello_ll_lookup,
177 .getattr = hello_ll_getattr,
178 .readdir = hello_ll_readdir,
179 .open = hello_ll_open,
180 .read = hello_ll_read,
181};
182
183static int create_socket(const char *socket_path) {
184 struct sockaddr_un addr;
185
186 if (strnlen(socket_path, sizeof(addr.sun_path)) >=
187 sizeof(addr.sun_path)) {
188 printf("Socket path may not be longer than %zu characters\n",
189 sizeof(addr.sun_path) - 1);
190 return -1;
191 }
192
193 if (remove(socket_path) == -1 && errno != ENOENT) {
194 printf("Could not delete previous socket file entry at %s. Error: "
195 "%s\n", socket_path, strerror(errno));
196 return -1;
197 }
198
199 memset(&addr, 0, sizeof(struct sockaddr_un));
200 strcpy(addr.sun_path, socket_path);
201
202 int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
203 if (sfd == -1) {
204 printf("Could not create socket. Error: %s\n", strerror(errno));
205 return -1;
206 }
207
208 addr.sun_family = AF_UNIX;
209 if (bind(sfd, (struct sockaddr *) &addr,
210 sizeof(struct sockaddr_un)) == -1) {
211 printf("Could not bind socket. Error: %s\n", strerror(errno));
212 return -1;
213 }
214
215 if (listen(sfd, 1) == -1)
216 return -1;
217
218 printf("Awaiting connection on socket at %s...\n", socket_path);
219 int cfd = accept(sfd, NULL, NULL);
220 if (cfd == -1) {
221 printf("Could not accept connection. Error: %s\n",
222 strerror(errno));
223 return -1;
224 } else {
225 printf("Accepted connection!\n");
226 }
227 return cfd;
228}
229
230static ssize_t stream_writev(int fd, struct iovec *iov, int count,
231 void *userdata) {
232 (void)userdata;
233
234 ssize_t written = 0;
235 int cur = 0;
236 for (;;) {
237 written = writev(fd, iov+cur, count-cur);
238 if (written < 0)
239 return written;
240
241 while (cur < count && written >= iov[cur].iov_len)
242 written -= iov[cur++].iov_len;
243 if (cur == count)
244 break;
245
246 iov[cur].iov_base = (char *)iov[cur].iov_base + written;
247 iov[cur].iov_len -= written;
248 }
249 return written;
250}
251
252
253static ssize_t readall(int fd, void *buf, size_t len) {
254 size_t count = 0;
255
256 while (count < len) {
257 int i = read(fd, (char *)buf + count, len - count);
258 if (!i)
259 break;
260
261 if (i < 0)
262 return i;
263
264 count += i;
265 }
266 return count;
267}
268
269static ssize_t stream_read(int fd, void *buf, size_t buf_len, void *userdata) {
270 (void)userdata;
271
272 int res = readall(fd, buf, sizeof(struct fuse_in_header));
273 if (res == -1)
274 return res;
275
276
277 uint32_t packet_len = ((struct fuse_in_header *)buf)->len;
278 if (packet_len > buf_len)
279 return -1;
280
281 int prev_res = res;
282
283 res = readall(fd, (char *)buf + sizeof(struct fuse_in_header),
284 packet_len - sizeof(struct fuse_in_header));
285
286 return (res == -1) ? res : (res + prev_res);
287}
288
289static ssize_t stream_splice_send(int fdin, off_t *offin, int fdout,
290 off_t *offout, size_t len,
291 unsigned int flags, void *userdata) {
292 (void)userdata;
293
294 size_t count = 0;
295 while (count < len) {
296 int i = splice(fdin, offin, fdout, offout, len - count, flags);
297 if (i < 1)
298 return i;
299
300 count += i;
301 }
302 return count;
303}
304
305static void fuse_cmdline_help_uds(void)
306{
307 printf(" -h --help print help\n"
308 " -V --version print version\n"
309 " -d -o debug enable debug output (implies -f)\n");
310}
311
312int main(int argc, char *argv[])
313{
314 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
315 struct fuse_session *se;
316 struct fuse_cmdline_opts opts;
317 const struct fuse_custom_io io = {
318 .writev = stream_writev,
319 .read = stream_read,
320 .splice_receive = NULL,
321 .splice_send = stream_splice_send,
322 };
323 int cfd = -1;
324 int ret = -1;
325
326 if (fuse_parse_cmdline(&args, &opts) != 0)
327 return 1;
328 if (opts.show_help) {
329 printf("usage: %s [options]\n\n", argv[0]);
330 fuse_cmdline_help_uds();
332 ret = 0;
333 goto err_out1;
334 } else if (opts.show_version) {
335 printf("FUSE library version %s\n", fuse_pkgversion());
337 ret = 0;
338 goto err_out1;
339 }
340
341 se = fuse_session_new(&args, &hello_ll_oper,
342 sizeof(hello_ll_oper), NULL);
343 if (se == NULL)
344 goto err_out1;
345
346 if (fuse_set_signal_handlers(se) != 0)
347 goto err_out2;
348
349 cfd = create_socket("/tmp/libfuse-hello-ll.sock");
350 if (cfd == -1)
351 goto err_out3;
352
353 if (fuse_session_custom_io(se, &io, cfd) != 0)
354 goto err_out3;
355
356 /* Block until ctrl+c */
357 ret = fuse_session_loop(se);
358err_out3:
360err_out2:
362err_out1:
363 free(opts.mountpoint);
364 fuse_opt_free_args(&args);
365
366 return ret ? 1 : 0;
367}
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_lowlevel_help(void)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2invalidate__path_8c_source.html0000644000175000017500000016504415156613427025357 0ustar berndbernd libfuse: fuse-3.18.2/example/invalidate_path.c Source File
libfuse
invalidate_path.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4 (C) 2017 EditShare LLC <slawek.rudnicki@editshare.com>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8 */
9
28#define FUSE_USE_VERSION 34
29
30#include <fuse.h>
31#include <fuse_lowlevel.h> /* for fuse_cmdline_opts */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <assert.h>
39#include <stddef.h>
40#include <unistd.h>
41#include <pthread.h>
42
43/* We can't actually tell the kernel that there is no
44 timeout, so we just send a big value */
45#define NO_TIMEOUT 500000
46
47#define MAX_STR_LEN 128
48#define TIME_FILE_NAME "current_time"
49#define TIME_FILE_INO 2
50#define GROW_FILE_NAME "growing"
51#define GROW_FILE_INO 3
52
53static char time_file_contents[MAX_STR_LEN];
54static size_t grow_file_size;
55
56/* Command line parsing */
57struct options {
58 int no_notify;
59 int update_interval;
60};
61static struct options options = {
62 .no_notify = 0,
63 .update_interval = 1,
64};
65
66#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
67static const struct fuse_opt option_spec[] = {
68 OPTION("--no-notify", no_notify),
69 OPTION("--update-interval=%d", update_interval),
71};
72
73static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
74{
75 (void) conn;
76 cfg->entry_timeout = NO_TIMEOUT;
77 cfg->attr_timeout = NO_TIMEOUT;
78 cfg->negative_timeout = 0;
79
80 return NULL;
81}
82
83static int xmp_getattr(const char *path,
84 struct stat *stbuf, struct fuse_file_info* fi) {
85 (void) fi;
86 if (strcmp(path, "/") == 0) {
87 stbuf->st_ino = 1;
88 stbuf->st_mode = S_IFDIR | 0755;
89 stbuf->st_nlink = 1;
90 } else if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
91 stbuf->st_ino = TIME_FILE_INO;
92 stbuf->st_mode = S_IFREG | 0444;
93 stbuf->st_nlink = 1;
94 stbuf->st_size = strlen(time_file_contents);
95 } else if (strcmp(path, "/" GROW_FILE_NAME) == 0) {
96 stbuf->st_ino = GROW_FILE_INO;
97 stbuf->st_mode = S_IFREG | 0444;
98 stbuf->st_nlink = 1;
99 stbuf->st_size = grow_file_size;
100 } else {
101 return -ENOENT;
102 }
103
104 return 0;
105}
106
107static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
108 off_t offset, struct fuse_file_info *fi,
109 enum fuse_readdir_flags flags) {
110 (void) fi;
111 (void) offset;
112 (void) flags;
113 if (strcmp(path, "/") != 0) {
114 return -ENOTDIR;
115 } else {
116 (void) filler;
117 (void) buf;
118 struct stat file_stat;
119 xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL);
120 filler(buf, TIME_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
121 xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL);
122 filler(buf, GROW_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
123 return 0;
124 }
125}
126
127static int xmp_open(const char *path, struct fuse_file_info *fi) {
128 (void) path;
129 /* Make cache persistent even if file is closed,
130 this makes it easier to see the effects */
131 fi->keep_cache = 1;
132 return 0;
133}
134
135static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
136 struct fuse_file_info *fi) {
137 (void) fi;
138 (void) offset;
139 if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
140 int file_length = strlen(time_file_contents);
141 int to_copy = offset + size <= file_length
142 ? size
143 : file_length - offset;
144 memcpy(buf, time_file_contents, to_copy);
145 return to_copy;
146 } else {
147 assert(strcmp(path, "/" GROW_FILE_NAME) == 0);
148 int to_copy = offset + size <= grow_file_size
149 ? size
150 : grow_file_size - offset;
151 memset(buf, 'x', to_copy);
152 return to_copy;
153 }
154}
155
156static const struct fuse_operations xmp_oper = {
157 .init = xmp_init,
158 .getattr = xmp_getattr,
159 .readdir = xmp_readdir,
160 .open = xmp_open,
161 .read = xmp_read,
162};
163
164static void update_fs(void) {
165 static int count = 0;
166 struct tm *now;
167 time_t t;
168 t = time(NULL);
169 now = localtime(&t);
170 assert(now != NULL);
171
172 int time_file_size = strftime(time_file_contents, MAX_STR_LEN,
173 "The current time is %H:%M:%S\n", now);
174 assert(time_file_size != 0);
175
176 grow_file_size = count++;
177}
178
179static int invalidate(struct fuse *fuse, const char *path) {
180 int status = fuse_invalidate_path(fuse, path);
181 if (status == -ENOENT) {
182 return 0;
183 } else {
184 return status;
185 }
186}
187
188static void* update_fs_loop(void *data) {
189 struct fuse *fuse = (struct fuse*) data;
190
191 while (1) {
192 update_fs();
193 if (!options.no_notify) {
194 assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0);
195 assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0);
196 }
197 sleep(options.update_interval);
198 }
199 return NULL;
200}
201
202static void show_help(const char *progname)
203{
204 printf("usage: %s [options] <mountpoint>\n\n", progname);
205 printf("File-system specific options:\n"
206 " --update-interval=<secs> Update-rate of file system contents\n"
207 " --no-notify Disable kernel notifications\n"
208 "\n");
209}
210
211int main(int argc, char *argv[]) {
212 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
213 struct fuse *fuse;
214 struct fuse_cmdline_opts opts;
215 struct fuse_loop_config config;
216 int res;
217
218 /* Initialize the files */
219 update_fs();
220
221 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
222 return 1;
223
224 if (fuse_parse_cmdline(&args, &opts) != 0)
225 return 1;
226
227 if (opts.show_version) {
228 printf("FUSE library version %s\n", fuse_pkgversion());
230 res = 0;
231 goto out1;
232 } else if (opts.show_help) {
233 show_help(argv[0]);
235 fuse_lib_help(&args);
236 res = 0;
237 goto out1;
238 } else if (!opts.mountpoint) {
239 fprintf(stderr, "error: no mountpoint specified\n");
240 res = 1;
241 goto out1;
242 }
243
244 fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL);
245 if (fuse == NULL) {
246 res = 1;
247 goto out1;
248 }
249
250 if (fuse_mount(fuse,opts.mountpoint) != 0) {
251 res = 1;
252 goto out2;
253 }
254
255 if (fuse_daemonize(opts.foreground) != 0) {
256 res = 1;
257 goto out3;
258 }
259
260 pthread_t updater; /* Start thread to update file contents */
261 int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse);
262 if (ret != 0) {
263 fprintf(stderr, "pthread_create failed with %s\n", strerror(ret));
264 return 1;
265 };
266
267 struct fuse_session *se = fuse_get_session(fuse);
268 if (fuse_set_signal_handlers(se) != 0) {
269 res = 1;
270 goto out3;
271 }
272
273 if (opts.singlethread)
274 res = fuse_loop(fuse);
275 else {
276 config.clone_fd = opts.clone_fd;
277 config.max_idle_threads = opts.max_idle_threads;
278 res = fuse_loop_mt(fuse, &config);
279 }
280 if (res)
281 res = 1;
282
284out3:
285 fuse_unmount(fuse);
286out2:
287 fuse_destroy(fuse);
288out1:
289 free(opts.mountpoint);
290 fuse_opt_free_args(&args);
291 return res;
292}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t keep_cache
Definition fuse_common.h:69
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2ioctl_8c_source.html0000644000175000017500000010742715156613427023177 0ustar berndbernd libfuse: fuse-3.18.2/example/ioctl.c Source File
libfuse
ioctl.c
Go to the documentation of this file.
1/*
2 FUSE fioc: FUSE ioctl example
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
25#define FUSE_USE_VERSION 35
26
27#include <fuse.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31#include <unistd.h>
32#include <time.h>
33#include <errno.h>
34
35#include "ioctl.h"
36
37#define FIOC_NAME "fioc"
38
39enum {
40 FIOC_NONE,
41 FIOC_ROOT,
42 FIOC_FILE,
43};
44
45static void *fioc_buf;
46static size_t fioc_size;
47
48static int fioc_resize(size_t new_size)
49{
50 void *new_buf;
51
52 if (new_size == fioc_size)
53 return 0;
54
55 new_buf = realloc(fioc_buf, new_size);
56 if (!new_buf && new_size)
57 return -ENOMEM;
58
59 if (new_size > fioc_size)
60 memset(new_buf + fioc_size, 0, new_size - fioc_size);
61
62 fioc_buf = new_buf;
63 fioc_size = new_size;
64
65 return 0;
66}
67
68static int fioc_expand(size_t new_size)
69{
70 if (new_size > fioc_size)
71 return fioc_resize(new_size);
72 return 0;
73}
74
75static int fioc_file_type(const char *path)
76{
77 if (strcmp(path, "/") == 0)
78 return FIOC_ROOT;
79 if (strcmp(path, "/" FIOC_NAME) == 0)
80 return FIOC_FILE;
81 return FIOC_NONE;
82}
83
84static int fioc_getattr(const char *path, struct stat *stbuf,
85 struct fuse_file_info *fi)
86{
87 (void) fi;
88 stbuf->st_uid = getuid();
89 stbuf->st_gid = getgid();
90 stbuf->st_atime = stbuf->st_mtime = time(NULL);
91
92 switch (fioc_file_type(path)) {
93 case FIOC_ROOT:
94 stbuf->st_mode = S_IFDIR | 0755;
95 stbuf->st_nlink = 2;
96 break;
97 case FIOC_FILE:
98 stbuf->st_mode = S_IFREG | 0644;
99 stbuf->st_nlink = 1;
100 stbuf->st_size = fioc_size;
101 break;
102 case FIOC_NONE:
103 return -ENOENT;
104 }
105
106 return 0;
107}
108
109static int fioc_open(const char *path, struct fuse_file_info *fi)
110{
111 (void) fi;
112
113 if (fioc_file_type(path) != FIOC_NONE)
114 return 0;
115 return -ENOENT;
116}
117
118static int fioc_do_read(char *buf, size_t size, off_t offset)
119{
120 if (offset >= fioc_size)
121 return 0;
122
123 if (size > fioc_size - offset)
124 size = fioc_size - offset;
125
126 memcpy(buf, fioc_buf + offset, size);
127
128 return size;
129}
130
131static int fioc_read(const char *path, char *buf, size_t size,
132 off_t offset, struct fuse_file_info *fi)
133{
134 (void) fi;
135
136 if (fioc_file_type(path) != FIOC_FILE)
137 return -EINVAL;
138
139 return fioc_do_read(buf, size, offset);
140}
141
142static int fioc_do_write(const char *buf, size_t size, off_t offset)
143{
144 if (fioc_expand(offset + size))
145 return -ENOMEM;
146
147 memcpy(fioc_buf + offset, buf, size);
148
149 return size;
150}
151
152static int fioc_write(const char *path, const char *buf, size_t size,
153 off_t offset, struct fuse_file_info *fi)
154{
155 (void) fi;
156
157 if (fioc_file_type(path) != FIOC_FILE)
158 return -EINVAL;
159
160 return fioc_do_write(buf, size, offset);
161}
162
163static int fioc_truncate(const char *path, off_t size,
164 struct fuse_file_info *fi)
165{
166 (void) fi;
167 if (fioc_file_type(path) != FIOC_FILE)
168 return -EINVAL;
169
170 return fioc_resize(size);
171}
172
173static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
174 off_t offset, struct fuse_file_info *fi,
175 enum fuse_readdir_flags flags)
176{
177 (void) fi;
178 (void) offset;
179 (void) flags;
180
181 if (fioc_file_type(path) != FIOC_ROOT)
182 return -ENOENT;
183
184 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
185 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
186 filler(buf, FIOC_NAME, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
187
188 return 0;
189}
190
191static int fioc_ioctl(const char *path, unsigned int cmd, void *arg,
192 struct fuse_file_info *fi, unsigned int flags, void *data)
193{
194 (void) arg;
195 (void) fi;
196 (void) flags;
197
198 if (fioc_file_type(path) != FIOC_FILE)
199 return -EINVAL;
200
201 if (flags & FUSE_IOCTL_COMPAT)
202 return -ENOSYS;
203
204 switch (cmd) {
205 case FIOC_GET_SIZE:
206 *(size_t *)data = fioc_size;
207 return 0;
208
209 case FIOC_SET_SIZE:
210 fioc_resize(*(size_t *)data);
211 return 0;
212 }
213
214 return -EINVAL;
215}
216
217static const struct fuse_operations fioc_oper = {
218 .getattr = fioc_getattr,
219 .readdir = fioc_readdir,
220 .truncate = fioc_truncate,
221 .open = fioc_open,
222 .read = fioc_read,
223 .write = fioc_write,
224 .ioctl = fioc_ioctl,
225};
226
227int main(int argc, char *argv[])
228{
229 return fuse_main(argc, argv, &fioc_oper, NULL);
230}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_IOCTL_COMPAT
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2ioctl_8h_source.html0000644000175000017500000001610715156613427023176 0ustar berndbernd libfuse: fuse-3.18.2/example/ioctl.h Source File
libfuse
ioctl.h
Go to the documentation of this file.
1/*
2 FUSE-ioctl: ioctl support for FUSE
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
20#include <sys/types.h>
21#include <sys/uio.h>
22#include <sys/ioctl.h>
23
24enum {
25 FIOC_GET_SIZE = _IOR('E', 0, size_t),
26 FIOC_SET_SIZE = _IOW('E', 1, size_t),
27
28 /*
29 * The following two ioctls don't follow usual encoding rules
30 * and transfer variable amount of data.
31 */
32 FIOC_READ = _IO('E', 2),
33 FIOC_WRITE = _IO('E', 3),
34};
35
36struct fioc_rw_arg {
37 off_t offset;
38 void *buf;
39 size_t size;
40 size_t prev_size; /* out param for previous total size */
41 size_t new_size; /* out param for new total size */
42};
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2ioctl__client_8c_source.html0000644000175000017500000002630315156613427024665 0ustar berndbernd libfuse: fuse-3.18.2/example/ioctl_client.c Source File
libfuse
ioctl_client.c
Go to the documentation of this file.
1/*
2 FUSE fioclient: FUSE ioctl example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
22#include <sys/types.h>
23#include <fcntl.h>
24#include <sys/stat.h>
25#include <sys/ioctl.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <ctype.h>
29#include <errno.h>
30#include <unistd.h>
31#include "ioctl.h"
32
33const char *usage =
34"Usage: fioclient FIOC_FILE [size]\n"
35"\n"
36"Get size if <size> is omitted, set size otherwise\n"
37"\n";
38
39int main(int argc, char **argv)
40{
41 size_t size;
42 int fd;
43 int ret = 0;
44
45 if (argc < 2) {
46 fprintf(stderr, "%s", usage);
47 return 1;
48 }
49
50 fd = open(argv[1], O_RDWR);
51 if (fd < 0) {
52 perror("open");
53 return 1;
54 }
55
56 if (argc == 2) {
57 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
58 perror("ioctl");
59 ret = 1;
60 goto out;
61 }
62 printf("%zu\n", size);
63 } else {
64 size = strtoul(argv[2], NULL, 0);
65 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
66 perror("ioctl");
67 ret = 1;
68 goto out;
69 }
70 }
71out:
72 close(fd);
73 return ret;
74}
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2notify__inval__entry_8c_source.html0000644000175000017500000022053115156613427026275 0ustar berndbernd libfuse: fuse-3.18.2/example/notify_inval_entry.c Source File
libfuse
notify_inval_entry.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
85#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
86
87#include <fuse_lowlevel.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <string.h>
91#include <errno.h>
92#include <fcntl.h>
93#include <assert.h>
94#include <signal.h>
95#include <stddef.h>
96#include <sys/stat.h>
97#include <unistd.h>
98#include <pthread.h>
99
100#define MAX_STR_LEN 128
101static char file_name[MAX_STR_LEN];
102static fuse_ino_t file_ino = 2;
103static int lookup_cnt = 0;
104static pthread_t main_thread;
105
106/* Command line parsing */
107struct options {
108 int no_notify;
109 float timeout;
110 int update_interval;
111 int only_expire;
112 int inc_epoch;
113};
114static struct options options = {
115 .timeout = 5,
116 .no_notify = 0,
117 .update_interval = 1,
118 .only_expire = 0,
119 .inc_epoch = 0,
120};
121
122#define OPTION(t, p) \
123 { t, offsetof(struct options, p), 1 }
124static const struct fuse_opt option_spec[] = {
125 OPTION("--no-notify", no_notify),
126 OPTION("--update-interval=%d", update_interval),
127 OPTION("--timeout=%f", timeout),
128 OPTION("--only-expire", only_expire),
129 OPTION("--inc-epoch", inc_epoch),
131};
132
133static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
134 stbuf->st_ino = ino;
135 if (ino == FUSE_ROOT_ID) {
136 stbuf->st_mode = S_IFDIR | 0755;
137 stbuf->st_nlink = 1;
138 }
139
140 else if (ino == file_ino) {
141 stbuf->st_mode = S_IFREG | 0000;
142 stbuf->st_nlink = 1;
143 stbuf->st_size = 0;
144 }
145
146 else
147 return -1;
148
149 return 0;
150}
151
152static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
153 (void)userdata;
154
155 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
156 conn->no_interrupt = 1;
157}
158
159static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
160 const char *name) {
161 struct fuse_entry_param e;
162 memset(&e, 0, sizeof(e));
163
164 if (parent != FUSE_ROOT_ID)
165 goto err_out;
166 else if (strcmp(name, file_name) == 0) {
167 e.ino = file_ino;
168 lookup_cnt++;
169 } else
170 goto err_out;
171
172 e.attr_timeout = options.timeout;
173 e.entry_timeout = options.timeout;
174 if (tfs_stat(e.ino, &e.attr) != 0)
175 goto err_out;
176 fuse_reply_entry(req, &e);
177 return;
178
179err_out:
180 fuse_reply_err(req, ENOENT);
181}
182
183static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
184 uint64_t nlookup) {
185 (void) req;
186 if(ino == file_ino)
187 lookup_cnt -= nlookup;
188 else
189 assert(ino == FUSE_ROOT_ID);
190 fuse_reply_none(req);
191}
192
193static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
194 struct fuse_file_info *fi) {
195 struct stat stbuf;
196
197 (void) fi;
198
199 memset(&stbuf, 0, sizeof(stbuf));
200 if (tfs_stat(ino, &stbuf) != 0)
201 fuse_reply_err(req, ENOENT);
202 else
203 fuse_reply_attr(req, &stbuf, options.timeout);
204}
205
206struct dirbuf {
207 char *p;
208 size_t size;
209};
210
211static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
212 fuse_ino_t ino) {
213 struct stat stbuf;
214 size_t oldsize = b->size;
215 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
216 b->p = (char *) realloc(b->p, b->size);
217 memset(&stbuf, 0, sizeof(stbuf));
218 stbuf.st_ino = ino;
219 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
220 b->size);
221}
222
223#define min(x, y) ((x) < (y) ? (x) : (y))
224
225static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
226 off_t off, size_t maxsize) {
227 if (off < bufsize)
228 return fuse_reply_buf(req, buf + off,
229 min(bufsize - off, maxsize));
230 else
231 return fuse_reply_buf(req, NULL, 0);
232}
233
234static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
235 off_t off, struct fuse_file_info *fi) {
236 (void) fi;
237
238 if (ino != FUSE_ROOT_ID)
239 fuse_reply_err(req, ENOTDIR);
240 else {
241 struct dirbuf b;
242
243 memset(&b, 0, sizeof(b));
244 dirbuf_add(req, &b, file_name, file_ino);
245 reply_buf_limited(req, b.p, b.size, off, size);
246 free(b.p);
247 }
248}
249
250static const struct fuse_lowlevel_ops tfs_oper = {
251 .init = tfs_init,
252 .lookup = tfs_lookup,
253 .getattr = tfs_getattr,
254 .readdir = tfs_readdir,
255 .forget = tfs_forget,
256};
257
258static void update_fs(void) {
259 time_t t;
260 struct tm *now;
261 ssize_t ret;
262
263 t = time(NULL);
264 now = localtime(&t);
265 assert(now != NULL);
266
267 ret = strftime(file_name, MAX_STR_LEN,
268 "Time_is_%Hh_%Mm_%Ss", now);
269 assert(ret != 0);
270}
271
272static void* update_fs_loop(void *data) {
273 struct fuse_session *se = (struct fuse_session*) data;
274 char *old_name;
275 int ret = 0;
276
277 while(!fuse_session_exited(se)) {
278 old_name = strdup(file_name);
279 update_fs();
280
281 if (!options.no_notify && lookup_cnt) {
282 if(options.only_expire) { // expire entry
284 (se, FUSE_ROOT_ID, old_name, strlen(old_name));
285
286 // no kernel support
287 if (ret == -ENOSYS) {
288 printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
289 break;
290 }
291
292 // 1) ret == 0: successful expire of an existing entry
293 // 2) ret == -ENOENT: kernel has already expired the entry /
294 // entry does not exist anymore in the kernel
295 assert(ret == 0 || ret == -ENOENT);
296 } else if (options.inc_epoch) { // increment epoch
298
299 if (ret == -ENOSYS) {
300 printf("fuse_lowlevel_notify_increment_epoch not supported by kernel\n");
301 break;
302 }
303 assert(ret == 0);
304 } else { // invalidate entry
306 (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
307 }
308 }
309 free(old_name);
310 sleep(options.update_interval);
311 }
312
313 if (ret == -ENOSYS) {
314 printf("Exiting...\n");
315
317 // Make sure to exit now, rather than on next request from userspace
318 pthread_kill(main_thread, SIGPIPE);
319 }
320
321 return NULL;
322}
323
324static void show_help(const char *progname)
325{
326 printf("usage: %s [options] <mountpoint>\n\n", progname);
327 printf("File-system specific options:\n"
328 " --timeout=<secs> Timeout for kernel caches\n"
329 " --update-interval=<secs> Update-rate of file system contents\n"
330 " --no-notify Disable kernel notifications\n"
331 " --only-expire Expire entries instead of invalidating them\n"
332 " --inc-epoch Increment epoch, invalidating all dentries\n"
333 "\n");
334}
335
336int main(int argc, char *argv[]) {
337 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
338 struct fuse_session *se;
339 struct fuse_cmdline_opts opts;
340 struct fuse_loop_config *config;
341 pthread_t updater;
342 int ret = -1;
343
344 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
345 return 1;
346
347 if (fuse_parse_cmdline(&args, &opts) != 0)
348 return 1;
349 if (opts.show_help) {
350 show_help(argv[0]);
353 ret = 0;
354 goto err_out1;
355 } else if (opts.show_version) {
356 printf("FUSE library version %s\n", fuse_pkgversion());
358 ret = 0;
359 goto err_out1;
360 }
361 if (options.only_expire && options.inc_epoch) {
362 printf("'only-expire' and 'inc-epoch' options are exclusive\n");
363 ret = 0;
364 goto err_out1;
365 }
366
367 /* Initial contents */
368 update_fs();
369
370 se = fuse_session_new(&args, &tfs_oper,
371 sizeof(tfs_oper), &se);
372 if (se == NULL)
373 goto err_out1;
374
375 if (fuse_set_signal_handlers(se) != 0)
376 goto err_out2;
377
378 if (fuse_session_mount(se, opts.mountpoint) != 0)
379 goto err_out3;
380
381 fuse_daemonize(opts.foreground);
382
383 // Needed to ensure that the main thread continues/restarts processing as soon
384 // as the fuse session ends (immediately after calling fuse_session_exit() )
385 // and not only on the next request from userspace
386 main_thread = pthread_self();
387
388 /* Start thread to update file contents */
389 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
390 if (ret != 0) {
391 fprintf(stderr, "pthread_create failed with %s\n",
392 strerror(ret));
393 goto err_out3;
394 }
395
396 /* Block until ctrl+c or fusermount -u */
397 if (opts.singlethread) {
398 ret = fuse_session_loop(se);
399 } else {
400 config = fuse_loop_cfg_create();
401 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
402 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
403 ret = fuse_session_loop_mt(se, config);
404 fuse_loop_cfg_destroy(config);
405 config = NULL;
406 }
407
409err_out3:
411err_out2:
413err_out1:
414 free(opts.mountpoint);
415 fuse_opt_free_args(&args);
416
417 return ret ? 1 : 0;
418}
419
420
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2notify__inval__inode_8c_source.html0000644000175000017500000021744515156613427026244 0ustar berndbernd libfuse: fuse-3.18.2/example/notify_inval_inode.c Source File
libfuse
notify_inval_inode.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
62#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
63
64#include <fuse_lowlevel.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <errno.h>
69#include <fcntl.h>
70#include <assert.h>
71#include <stddef.h>
72#include <unistd.h>
73#include <pthread.h>
74#include <stdbool.h>
75#include <stdatomic.h>
76
77/* We can't actually tell the kernel that there is no
78 timeout, so we just send a big value */
79#define NO_TIMEOUT 500000
80
81#define MAX_STR_LEN 128
82#define FILE_INO 2
83#define FILE_NAME "current_time"
84static char file_contents[MAX_STR_LEN];
85static int lookup_cnt = 0;
86static size_t file_size;
87static _Atomic bool is_stop = false;
88
89/* Command line parsing */
90struct options {
91 int no_notify;
92 int update_interval;
93};
94static struct options options = {
95 .no_notify = 0,
96 .update_interval = 1,
97};
98
99#define OPTION(t, p) \
100 { t, offsetof(struct options, p), 1 }
101static const struct fuse_opt option_spec[] = {
102 OPTION("--no-notify", no_notify),
103 OPTION("--update-interval=%d", update_interval),
105};
106
107static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
108 stbuf->st_ino = ino;
109 if (ino == FUSE_ROOT_ID) {
110 stbuf->st_mode = S_IFDIR | 0755;
111 stbuf->st_nlink = 1;
112 }
113
114 else if (ino == FILE_INO) {
115 stbuf->st_mode = S_IFREG | 0444;
116 stbuf->st_nlink = 1;
117 stbuf->st_size = file_size;
118 }
119
120 else
121 return -1;
122
123 return 0;
124}
125
126static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
127 (void)userdata;
128
129 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
130 conn->no_interrupt = 1;
131}
132
133static void tfs_destroy(void *userarg)
134{
135 (void)userarg;
136
137 is_stop = true;
138}
139
140static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
141 const char *name) {
142 struct fuse_entry_param e;
143 memset(&e, 0, sizeof(e));
144
145 if (parent != FUSE_ROOT_ID)
146 goto err_out;
147 else if (strcmp(name, FILE_NAME) == 0) {
148 e.ino = FILE_INO;
149 lookup_cnt++;
150 } else
151 goto err_out;
152
153 e.attr_timeout = NO_TIMEOUT;
154 e.entry_timeout = NO_TIMEOUT;
155 if (tfs_stat(e.ino, &e.attr) != 0)
156 goto err_out;
157 fuse_reply_entry(req, &e);
158 return;
159
160err_out:
161 fuse_reply_err(req, ENOENT);
162}
163
164static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
165 uint64_t nlookup) {
166 (void) req;
167 if(ino == FILE_INO)
168 lookup_cnt -= nlookup;
169 else
170 assert(ino == FUSE_ROOT_ID);
171 fuse_reply_none(req);
172}
173
174static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
175 struct fuse_file_info *fi) {
176 struct stat stbuf;
177
178 (void) fi;
179
180 memset(&stbuf, 0, sizeof(stbuf));
181 if (tfs_stat(ino, &stbuf) != 0)
182 fuse_reply_err(req, ENOENT);
183 else
184 fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
185}
186
187struct dirbuf {
188 char *p;
189 size_t size;
190};
191
192static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
193 fuse_ino_t ino) {
194 struct stat stbuf;
195 size_t oldsize = b->size;
196 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
197 b->p = (char *) realloc(b->p, b->size);
198 memset(&stbuf, 0, sizeof(stbuf));
199 stbuf.st_ino = ino;
200 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
201 b->size);
202}
203
204#define min(x, y) ((x) < (y) ? (x) : (y))
205
206static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
207 off_t off, size_t maxsize) {
208 if (off < bufsize)
209 return fuse_reply_buf(req, buf + off,
210 min(bufsize - off, maxsize));
211 else
212 return fuse_reply_buf(req, NULL, 0);
213}
214
215static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
216 off_t off, struct fuse_file_info *fi) {
217 (void) fi;
218
219 if (ino != FUSE_ROOT_ID)
220 fuse_reply_err(req, ENOTDIR);
221 else {
222 struct dirbuf b;
223
224 memset(&b, 0, sizeof(b));
225 dirbuf_add(req, &b, FILE_NAME, FILE_INO);
226 reply_buf_limited(req, b.p, b.size, off, size);
227 free(b.p);
228 }
229}
230
231static void tfs_open(fuse_req_t req, fuse_ino_t ino,
232 struct fuse_file_info *fi) {
233
234 /* Make cache persistent even if file is closed,
235 this makes it easier to see the effects */
236 fi->keep_cache = 1;
237
238 if (ino == FUSE_ROOT_ID)
239 fuse_reply_err(req, EISDIR);
240 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
241 fuse_reply_err(req, EACCES);
242 else if (ino == FILE_INO)
243 fuse_reply_open(req, fi);
244 else {
245 // This should not happen
246 fprintf(stderr, "Got open for non-existing inode!\n");
247 fuse_reply_err(req, ENOENT);
248 }
249}
250
251static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
252 off_t off, struct fuse_file_info *fi) {
253 (void) fi;
254
255 assert(ino == FILE_INO);
256 reply_buf_limited(req, file_contents, file_size, off, size);
257}
258
259static const struct fuse_lowlevel_ops tfs_oper = {
260 .init = tfs_init,
261 .destroy = tfs_destroy,
262 .lookup = tfs_lookup,
263 .getattr = tfs_getattr,
264 .readdir = tfs_readdir,
265 .open = tfs_open,
266 .read = tfs_read,
267 .forget = tfs_forget,
268};
269
270static void update_fs(void) {
271 struct tm *now;
272 time_t t;
273 t = time(NULL);
274 now = localtime(&t);
275 assert(now != NULL);
276
277 file_size = strftime(file_contents, MAX_STR_LEN,
278 "The current time is %H:%M:%S\n", now);
279 assert(file_size != 0);
280}
281
282static void* update_fs_loop(void *data) {
283 struct fuse_session *se = (struct fuse_session*) data;
284
285 while(!is_stop) {
286 update_fs();
287 if (!options.no_notify && lookup_cnt) {
288 /* Only send notification if the kernel is aware of the inode */
289
290 /* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
291 * might come up during umount, when kernel side already releases
292 * all inodes, but does not send FUSE_DESTROY yet.
293 */
294 int ret =
295 fuse_lowlevel_notify_inval_inode(se, FILE_INO, 0, 0);
296 if ((ret != 0 && !is_stop) &&
297 ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
298 fprintf(stderr,
299 "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
300 strerror(-ret), -ret);
301 abort();
302 }
303 }
304 sleep(options.update_interval);
305 }
306 return NULL;
307}
308
309static void show_help(const char *progname)
310{
311 printf("usage: %s [options] <mountpoint>\n\n", progname);
312 printf("File-system specific options:\n"
313 " --update-interval=<secs> Update-rate of file system contents\n"
314 " --no-notify Disable kernel notifications\n"
315 "\n");
316}
317
318int main(int argc, char *argv[]) {
319 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
320 struct fuse_session *se;
321 struct fuse_cmdline_opts opts;
322 struct fuse_loop_config *config;
323 pthread_t updater;
324 int ret = -1;
325
326 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
327 return 1;
328
329 if (fuse_parse_cmdline(&args, &opts) != 0) {
330 ret = 1;
331 goto err_out1;
332 }
333
334 if (opts.show_help) {
335 show_help(argv[0]);
338 ret = 0;
339 goto err_out1;
340 } else if (opts.show_version) {
341 printf("FUSE library version %s\n", fuse_pkgversion());
343 ret = 0;
344 goto err_out1;
345 }
346
347 /* Initial contents */
348 update_fs();
349
350 se = fuse_session_new(&args, &tfs_oper,
351 sizeof(tfs_oper), NULL);
352 if (se == NULL)
353 goto err_out1;
354
355 if (fuse_set_signal_handlers(se) != 0)
356 goto err_out2;
357
358 if (fuse_session_mount(se, opts.mountpoint) != 0)
359 goto err_out3;
360
361 fuse_daemonize(opts.foreground);
362
363 /* Start thread to update file contents */
364 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
365 if (ret != 0) {
366 fprintf(stderr, "pthread_create failed with %s\n",
367 strerror(ret));
368 goto err_out3;
369 }
370
371 /* Block until ctrl+c or fusermount -u */
372 if (opts.singlethread)
373 ret = fuse_session_loop(se);
374 else {
375 config = fuse_loop_cfg_create();
376 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
377 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
378 ret = fuse_session_loop_mt(se, config);
379 fuse_loop_cfg_destroy(config);
380 config = NULL;
381 }
382
384err_out3:
386err_out2:
388err_out1:
389 fuse_opt_free_args(&args);
390 free(opts.mountpoint);
391
392 return ret ? 1 : 0;
393}
394
395
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2notify__store__retrieve_8c_source.html0000644000175000017500000025112115156613427027003 0ustar berndbernd libfuse: fuse-3.18.2/example/notify_store_retrieve.c Source File
libfuse
notify_store_retrieve.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
61#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
62
63#include <fuse_lowlevel.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <errno.h>
68#include <fcntl.h>
69#include <assert.h>
70#include <stddef.h>
71#include <unistd.h>
72#include <pthread.h>
73#include <stdbool.h>
74
75/* We can't actually tell the kernel that there is no
76 timeout, so we just send a big value */
77#define NO_TIMEOUT 500000
78
79#define MAX_STR_LEN 128
80#define FILE_INO 2
81#define FILE_NAME "current_time"
82static char file_contents[MAX_STR_LEN];
83static int lookup_cnt = 0;
84static int open_cnt = 0;
85static size_t file_size;
86static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
87
88/* Keep track if we ever stored data (==1), and
89 received it back correctly (==2) */
90static int retrieve_status = 0;
91
92static bool is_umount = false;
93
94/* updater thread tid */
95static pthread_t updater;
96
97
98/* Command line parsing */
99struct options {
100 int no_notify;
101 int update_interval;
102};
103static struct options options = {
104 .no_notify = 0,
105 .update_interval = 1,
106};
107
108#define OPTION(t, p) \
109 { t, offsetof(struct options, p), 1 }
110static const struct fuse_opt option_spec[] = {
111 OPTION("--no-notify", no_notify),
112 OPTION("--update-interval=%d", update_interval),
114};
115
116static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
117 stbuf->st_ino = ino;
118 if (ino == FUSE_ROOT_ID) {
119 stbuf->st_mode = S_IFDIR | 0755;
120 stbuf->st_nlink = 1;
121 }
122
123 else if (ino == FILE_INO) {
124 stbuf->st_mode = S_IFREG | 0444;
125 stbuf->st_nlink = 1;
126 stbuf->st_size = file_size;
127 }
128
129 else
130 return -1;
131
132 return 0;
133}
134
135static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
136 (void)userdata;
137
138 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
139 conn->no_interrupt = 1;
140}
141
142static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
143 const char *name) {
144 struct fuse_entry_param e;
145 memset(&e, 0, sizeof(e));
146
147 if (parent != FUSE_ROOT_ID)
148 goto err_out;
149 else if (strcmp(name, FILE_NAME) == 0) {
150 e.ino = FILE_INO;
151 } else
152 goto err_out;
153
154 e.attr_timeout = NO_TIMEOUT;
155 e.entry_timeout = NO_TIMEOUT;
156 if (tfs_stat(e.ino, &e.attr) != 0)
157 goto err_out;
158 fuse_reply_entry(req, &e);
159
160 /*
161 * must only be set when the kernel knows about the entry,
162 * otherwise update_fs_loop() might see a positive count, but kernel
163 * would not have the entry yet
164 */
165 if (e.ino == FILE_INO) {
166 pthread_mutex_lock(&lock);
167 lookup_cnt++;
168 pthread_mutex_unlock(&lock);
169 }
170
171 return;
172
173err_out:
174 fuse_reply_err(req, ENOENT);
175}
176
177static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
178 uint64_t nlookup) {
179 (void) req;
180 if(ino == FILE_INO) {
181 pthread_mutex_lock(&lock);
182 lookup_cnt -= nlookup;
183 pthread_mutex_unlock(&lock);
184 } else
185 assert(ino == FUSE_ROOT_ID);
186 fuse_reply_none(req);
187}
188
189static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
190 struct fuse_file_info *fi) {
191 struct stat stbuf;
192
193 (void) fi;
194
195 memset(&stbuf, 0, sizeof(stbuf));
196 if (tfs_stat(ino, &stbuf) != 0)
197 fuse_reply_err(req, ENOENT);
198 else
199 fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
200}
201
202struct dirbuf {
203 char *p;
204 size_t size;
205};
206
207static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
208 fuse_ino_t ino) {
209 struct stat stbuf;
210 size_t oldsize = b->size;
211 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
212 b->p = (char *) realloc(b->p, b->size);
213 memset(&stbuf, 0, sizeof(stbuf));
214 stbuf.st_ino = ino;
215 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
216 b->size);
217}
218
219#define min(x, y) ((x) < (y) ? (x) : (y))
220
221static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
222 off_t off, size_t maxsize) {
223 if (off < bufsize)
224 return fuse_reply_buf(req, buf + off,
225 min(bufsize - off, maxsize));
226 else
227 return fuse_reply_buf(req, NULL, 0);
228}
229
230static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
231 off_t off, struct fuse_file_info *fi) {
232 (void) fi;
233
234 if (ino != FUSE_ROOT_ID)
235 fuse_reply_err(req, ENOTDIR);
236 else {
237 struct dirbuf b;
238
239 memset(&b, 0, sizeof(b));
240 dirbuf_add(req, &b, FILE_NAME, FILE_INO);
241 reply_buf_limited(req, b.p, b.size, off, size);
242 free(b.p);
243 }
244}
245
246static void tfs_open(fuse_req_t req, fuse_ino_t ino,
247 struct fuse_file_info *fi) {
248
249 /* Make cache persistent even if file is closed,
250 this makes it easier to see the effects */
251 fi->keep_cache = 1;
252
253 if (ino == FUSE_ROOT_ID)
254 fuse_reply_err(req, EISDIR);
255 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
256 fuse_reply_err(req, EACCES);
257 else if (ino == FILE_INO) {
258 fuse_reply_open(req, fi);
259 pthread_mutex_lock(&lock);
260 open_cnt++;
261 pthread_mutex_unlock(&lock);
262 } else {
263 // This should not happen
264 fprintf(stderr, "Got open for non-existing inode!\n");
265 fuse_reply_err(req, ENOENT);
266 }
267}
268
269static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
270 off_t off, struct fuse_file_info *fi) {
271 (void) fi;
272
273 assert(ino == FILE_INO);
274 reply_buf_limited(req, file_contents, file_size, off, size);
275}
276
277static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
278 off_t offset, struct fuse_bufvec *data) {
279 struct fuse_bufvec bufv;
280 char buf[MAX_STR_LEN];
281 char *expected;
282 ssize_t ret;
283
284 assert(ino == FILE_INO);
285 assert(offset == 0);
286 expected = (char*) cookie;
287
288 bufv.count = 1;
289 bufv.idx = 0;
290 bufv.off = 0;
291 bufv.buf[0].size = MAX_STR_LEN;
292 bufv.buf[0].mem = buf;
293 bufv.buf[0].flags = 0;
294
295 ret = fuse_buf_copy(&bufv, data, 0);
296 assert(ret > 0);
297 assert(strncmp(buf, expected, ret) == 0);
298 free(expected);
299 retrieve_status = 2;
300 fuse_reply_none(req);
301}
302
303static void tfs_destroy(void *userdata)
304{
305 (void)userdata;
306
307 is_umount = true;
308
309 pthread_join(updater, NULL);
310}
311
312
313static const struct fuse_lowlevel_ops tfs_oper = {
314 .init = tfs_init,
315 .lookup = tfs_lookup,
316 .getattr = tfs_getattr,
317 .readdir = tfs_readdir,
318 .open = tfs_open,
319 .read = tfs_read,
320 .forget = tfs_forget,
321 .retrieve_reply = tfs_retrieve_reply,
322 .destroy = tfs_destroy,
323};
324
325static void update_fs(void) {
326 struct tm *now;
327 time_t t;
328 t = time(NULL);
329 now = localtime(&t);
330 assert(now != NULL);
331
332 file_size = strftime(file_contents, MAX_STR_LEN,
333 "The current time is %H:%M:%S\n", now);
334 assert(file_size != 0);
335}
336
337static void* update_fs_loop(void *data) {
338 struct fuse_session *se = (struct fuse_session*) data;
339 struct fuse_bufvec bufv;
340 int ret;
341
342 while(!is_umount) {
343 update_fs();
344 pthread_mutex_lock(&lock);
345 if (!options.no_notify && open_cnt && lookup_cnt) {
346 /* Only send notification if the kernel
347 is aware of the inode */
348 bufv.count = 1;
349 bufv.idx = 0;
350 bufv.off = 0;
351 bufv.buf[0].size = file_size;
352 bufv.buf[0].mem = file_contents;
353 bufv.buf[0].flags = 0;
354
355 /*
356 * Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
357 * might come up during umount, when kernel side already releases
358 * all inodes, but does not send FUSE_DESTROY yet.
359 */
360
361 ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
362 if ((ret != 0 && !is_umount) &&
363 ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
364 fprintf(stderr,
365 "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
366 strerror(-ret), -ret);
367 abort();
368 }
369
370 /* To make sure that everything worked correctly, ask the
371 kernel to send us back the stored data */
372 ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
373 0, (void*) strdup(file_contents));
374 assert((ret == 0 || is_umount) || ret == -ENOENT || ret == -EBADF ||
375 ret != -ENODEV);
376 if(retrieve_status == 0)
377 retrieve_status = 1;
378 }
379 pthread_mutex_unlock(&lock);
380 sleep(options.update_interval);
381 }
382 return NULL;
383}
384
385static void show_help(const char *progname)
386{
387 printf("usage: %s [options] <mountpoint>\n\n", progname);
388 printf("File-system specific options:\n"
389 " --update-interval=<secs> Update-rate of file system contents\n"
390 " --no-notify Disable kernel notifications\n"
391 "\n");
392}
393
394int main(int argc, char *argv[]) {
395 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
396 struct fuse_session *se;
397 struct fuse_cmdline_opts opts;
398 struct fuse_loop_config *config;
399 int ret = -1;
400
401 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
402 return 1;
403
404 if (fuse_parse_cmdline(&args, &opts) != 0)
405 return 1;
406 if (opts.show_help) {
407 show_help(argv[0]);
410 ret = 0;
411 goto err_out1;
412 } else if (opts.show_version) {
413 printf("FUSE library version %s\n", fuse_pkgversion());
415 ret = 0;
416 goto err_out1;
417 }
418
419 /* Initial contents */
420 update_fs();
421
422 se = fuse_session_new(&args, &tfs_oper,
423 sizeof(tfs_oper), NULL);
424 if (se == NULL)
425 goto err_out1;
426
427 if (fuse_set_signal_handlers(se) != 0)
428 goto err_out2;
429
430 if (fuse_session_mount(se, opts.mountpoint) != 0)
431 goto err_out3;
432
433 fuse_daemonize(opts.foreground);
434
435 /* Start thread to update file contents */
436 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
437 if (ret != 0) {
438 fprintf(stderr, "pthread_create failed with %s\n",
439 strerror(ret));
440 goto err_out3;
441 }
442
443 /* Block until ctrl+c or fusermount -u */
444 if (opts.singlethread)
445 ret = fuse_session_loop(se);
446 else {
447 config = fuse_loop_cfg_create();
448 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
449 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
450 ret = fuse_session_loop_mt(se, config);
451 fuse_loop_cfg_destroy(config);
452 config = NULL;
453 }
454
455 assert(retrieve_status != 1);
457err_out3:
459err_out2:
461err_out1:
462 free(opts.mountpoint);
463 fuse_opt_free_args(&args);
464
465 return ret ? 1 : 0;
466}
467
468
int fuse_set_signal_handlers(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
struct fuse_buf buf[1]
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2null_8c_source.html0000644000175000017500000005734015156613427023035 0ustar berndbernd libfuse: fuse-3.18.2/example/null.c Source File
libfuse
null.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
25#define FUSE_USE_VERSION 31
26
27#include <fuse.h>
28#include <fuse_lowlevel.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <time.h>
34#include <errno.h>
35
36static int null_getattr(const char *path, struct stat *stbuf,
37 struct fuse_file_info *fi)
38{
39 (void) fi;
40
41 if(strcmp(path, "/") != 0)
42 return -ENOENT;
43
44 stbuf->st_mode = S_IFREG | 0644;
45 stbuf->st_nlink = 1;
46 stbuf->st_uid = getuid();
47 stbuf->st_gid = getgid();
48 stbuf->st_size = (1ULL << 32); /* 4G */
49 stbuf->st_blocks = 0;
50 stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
51
52 return 0;
53}
54
55static int null_truncate(const char *path, off_t size,
56 struct fuse_file_info *fi)
57{
58 (void) size;
59 (void) fi;
60
61 if(strcmp(path, "/") != 0)
62 return -ENOENT;
63
64 return 0;
65}
66
67static int null_open(const char *path, struct fuse_file_info *fi)
68{
69 (void) fi;
70
71 if(strcmp(path, "/") != 0)
72 return -ENOENT;
73
74 return 0;
75}
76
77static int null_read(const char *path, char *buf, size_t size,
78 off_t offset, struct fuse_file_info *fi)
79{
80 (void) buf;
81 (void) offset;
82 (void) fi;
83
84 if(strcmp(path, "/") != 0)
85 return -ENOENT;
86
87 if (offset >= (1ULL << 32))
88 return 0;
89
90 memset(buf, 0, size);
91 return size;
92}
93
94static int null_write(const char *path, const char *buf, size_t size,
95 off_t offset, struct fuse_file_info *fi)
96{
97 (void) buf;
98 (void) offset;
99 (void) fi;
100
101 if(strcmp(path, "/") != 0)
102 return -ENOENT;
103
104 return size;
105}
106
107static const struct fuse_operations null_oper = {
108 .getattr = null_getattr,
109 .truncate = null_truncate,
110 .open = null_open,
111 .read = null_read,
112 .write = null_write,
113};
114
115int main(int argc, char *argv[])
116{
117 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
118 struct fuse_cmdline_opts opts;
119 struct stat stbuf;
120
121 if (fuse_parse_cmdline(&args, &opts) != 0)
122 return 1;
123 fuse_opt_free_args(&args);
124
125 if (!opts.mountpoint) {
126 fprintf(stderr, "missing mountpoint parameter\n");
127 return 1;
128 }
129
130 if (stat(opts.mountpoint, &stbuf) == -1) {
131 fprintf(stderr ,"failed to access mountpoint %s: %s\n",
132 opts.mountpoint, strerror(errno));
133 free(opts.mountpoint);
134 return 1;
135 }
136 free(opts.mountpoint);
137 if (!S_ISREG(stbuf.st_mode)) {
138 fprintf(stderr, "mountpoint is not a regular file\n");
139 return 1;
140 }
141
142 return fuse_main(argc, argv, &null_oper, NULL);
143}
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2passthrough_8c_source.html0000644000175000017500000026714615156613427024441 0ustar berndbernd libfuse: fuse-3.18.2/example/passthrough.c Source File
libfuse
passthrough.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
26#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
27
28#define _GNU_SOURCE
29
30#ifdef linux
31/* For pread()/pwrite()/utimensat() */
32#define _XOPEN_SOURCE 700
33#endif
34
35#include <fuse.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39#include <fcntl.h>
40#include <sys/stat.h>
41#include <dirent.h>
42#include <errno.h>
43#include <sys/time.h>
44#ifdef HAVE_SETXATTR
45#include <sys/xattr.h>
46#endif
47
48#include "passthrough_helpers.h"
49
50static int fill_dir_plus = 0;
51static int readdir_zero_ino;
52
53static void *xmp_init(struct fuse_conn_info *conn,
54 struct fuse_config *cfg)
55{
56 (void) conn;
57 cfg->use_ino = !readdir_zero_ino;
58
59 /* parallel_direct_writes feature depends on direct_io features.
60 To make parallel_direct_writes valid, need either set cfg->direct_io
61 in current function (recommended in high level API) or set fi->direct_io
62 in xmp_create() or xmp_open(). */
63 // cfg->direct_io = 1;
65
66 /* Pick up changes from lower filesystem right away. This is
67 also necessary for better hardlink support. When the kernel
68 calls the unlink() handler, it does not know the inode of
69 the to-be-removed entry and can therefore not invalidate
70 the cache of the associated inode - resulting in an
71 incorrect st_nlink value being reported for any remaining
72 hardlinks to this inode. */
73 if (!cfg->auto_cache) {
74 cfg->entry_timeout = 0;
75 cfg->attr_timeout = 0;
76 cfg->negative_timeout = 0;
77 }
78
79 return NULL;
80}
81
82static int xmp_getattr(const char *path, struct stat *stbuf,
83 struct fuse_file_info *fi)
84{
85 (void) fi;
86 int res;
87
88 res = lstat(path, stbuf);
89 if (res == -1)
90 return -errno;
91
92 return 0;
93}
94
95static int xmp_access(const char *path, int mask)
96{
97 int res;
98
99 res = access(path, mask);
100 if (res == -1)
101 return -errno;
102
103 return 0;
104}
105
106static int xmp_readlink(const char *path, char *buf, size_t size)
107{
108 int res;
109
110 res = readlink(path, buf, size - 1);
111 if (res == -1)
112 return -errno;
113
114 buf[res] = '\0';
115 return 0;
116}
117
118
119static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
120 off_t offset, struct fuse_file_info *fi,
121 enum fuse_readdir_flags flags)
122{
123 DIR *dp;
124 struct dirent *de;
125
126 (void) offset;
127 (void) fi;
128 (void) flags;
129
130 dp = opendir(path);
131 if (dp == NULL)
132 return -errno;
133
134 while ((de = readdir(dp)) != NULL) {
135 struct stat st;
136 if (fill_dir_plus) {
137 fstatat(dirfd(dp), de->d_name, &st,
138 AT_SYMLINK_NOFOLLOW);
139 } else {
140 memset(&st, 0, sizeof(st));
141 st.st_ino = de->d_ino;
142 st.st_mode = de->d_type << 12;
143 }
144 if (readdir_zero_ino)
145 st.st_ino = 0;
146 if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
147 break;
148 }
149
150 closedir(dp);
151 return 0;
152}
153
154static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
155{
156 int res;
157
158 res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
159 if (res == -1)
160 return -errno;
161
162 return 0;
163}
164
165static int xmp_mkdir(const char *path, mode_t mode)
166{
167 int res;
168
169 res = mkdir(path, mode);
170 if (res == -1)
171 return -errno;
172
173 return 0;
174}
175
176static int xmp_unlink(const char *path)
177{
178 int res;
179
180 res = unlink(path);
181 if (res == -1)
182 return -errno;
183
184 return 0;
185}
186
187static int xmp_rmdir(const char *path)
188{
189 int res;
190
191 res = rmdir(path);
192 if (res == -1)
193 return -errno;
194
195 return 0;
196}
197
198static int xmp_symlink(const char *from, const char *to)
199{
200 int res;
201
202 res = symlink(from, to);
203 if (res == -1)
204 return -errno;
205
206 return 0;
207}
208
209static int xmp_rename(const char *from, const char *to, unsigned int flags)
210{
211 int res;
212
213 if (flags)
214 return -EINVAL;
215
216 res = rename(from, to);
217 if (res == -1)
218 return -errno;
219
220 return 0;
221}
222
223static int xmp_link(const char *from, const char *to)
224{
225 int res;
226
227 res = link(from, to);
228 if (res == -1)
229 return -errno;
230
231 return 0;
232}
233
234static int xmp_chmod(const char *path, mode_t mode,
235 struct fuse_file_info *fi)
236{
237 (void) fi;
238 int res;
239
240 res = chmod(path, mode);
241 if (res == -1)
242 return -errno;
243
244 return 0;
245}
246
247static int xmp_chown(const char *path, uid_t uid, gid_t gid,
248 struct fuse_file_info *fi)
249{
250 (void) fi;
251 int res;
252
253 res = lchown(path, uid, gid);
254 if (res == -1)
255 return -errno;
256
257 return 0;
258}
259
260static int xmp_truncate(const char *path, off_t size,
261 struct fuse_file_info *fi)
262{
263 int res;
264
265 if (fi != NULL)
266 res = ftruncate(fi->fh, size);
267 else
268 res = truncate(path, size);
269 if (res == -1)
270 return -errno;
271
272 return 0;
273}
274
275#ifdef HAVE_UTIMENSAT
276static int xmp_utimens(const char *path, const struct timespec ts[2],
277 struct fuse_file_info *fi)
278{
279 (void) fi;
280 int res;
281
282 /* don't use utime/utimes since they follow symlinks */
283 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
284 if (res == -1)
285 return -errno;
286
287 return 0;
288}
289#endif
290
291static int xmp_create(const char *path, mode_t mode,
292 struct fuse_file_info *fi)
293{
294 int res;
295
296 res = open(path, fi->flags, mode);
297 if (res == -1)
298 return -errno;
299
300 fi->fh = res;
301 return 0;
302}
303
304static int xmp_open(const char *path, struct fuse_file_info *fi)
305{
306 int res;
307
308 res = open(path, fi->flags);
309 if (res == -1)
310 return -errno;
311
312 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
313 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
314 for writes to the same file). */
315 if (fi->flags & O_DIRECT) {
316 fi->direct_io = 1;
318 }
319
320 fi->fh = res;
321 return 0;
322}
323
324static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
325 struct fuse_file_info *fi)
326{
327 int fd;
328 int res;
329
330 if(fi == NULL)
331 fd = open(path, O_RDONLY);
332 else
333 fd = fi->fh;
334
335 if (fd == -1)
336 return -errno;
337
338 res = pread(fd, buf, size, offset);
339 if (res == -1)
340 res = -errno;
341
342 if(fi == NULL)
343 close(fd);
344 return res;
345}
346
347static int xmp_write(const char *path, const char *buf, size_t size,
348 off_t offset, struct fuse_file_info *fi)
349{
350 int fd;
351 int res;
352
353 (void) fi;
354 if(fi == NULL)
355 fd = open(path, O_WRONLY);
356 else
357 fd = fi->fh;
358
359 if (fd == -1)
360 return -errno;
361
362 res = pwrite(fd, buf, size, offset);
363 if (res == -1)
364 res = -errno;
365
366 if(fi == NULL)
367 close(fd);
368 return res;
369}
370
371static int xmp_statfs(const char *path, struct statvfs *stbuf)
372{
373 int res;
374
375 res = statvfs(path, stbuf);
376 if (res == -1)
377 return -errno;
378
379 return 0;
380}
381
382static int xmp_release(const char *path, struct fuse_file_info *fi)
383{
384 (void) path;
385 close(fi->fh);
386 return 0;
387}
388
389static int xmp_fsync(const char *path, int isdatasync,
390 struct fuse_file_info *fi)
391{
392 /* Just a stub. This method is optional and can safely be left
393 unimplemented */
394
395 (void) path;
396 (void) isdatasync;
397 (void) fi;
398 return 0;
399}
400
401static int xmp_fallocate(const char *path, int mode,
402 off_t offset, off_t length, struct fuse_file_info *fi)
403{
404 int fd;
405 int res;
406
407 (void) fi;
408
409 if(fi == NULL)
410 fd = open(path, O_WRONLY);
411 else
412 fd = fi->fh;
413
414 if (fd == -1)
415 return -errno;
416
417 res = do_fallocate(fd, mode, offset, length);
418
419 if(fi == NULL)
420 close(fd);
421 return res;
422}
423
424#ifdef HAVE_SETXATTR
425/* xattr operations are optional and can safely be left unimplemented */
426static int xmp_setxattr(const char *path, const char *name, const char *value,
427 size_t size, int flags)
428{
429 int res = lsetxattr(path, name, value, size, flags);
430 if (res == -1)
431 return -errno;
432 return 0;
433}
434
435static int xmp_getxattr(const char *path, const char *name, char *value,
436 size_t size)
437{
438 int res = lgetxattr(path, name, value, size);
439 if (res == -1)
440 return -errno;
441 return res;
442}
443
444static int xmp_listxattr(const char *path, char *list, size_t size)
445{
446 int res = llistxattr(path, list, size);
447 if (res == -1)
448 return -errno;
449 return res;
450}
451
452static int xmp_removexattr(const char *path, const char *name)
453{
454 int res = lremovexattr(path, name);
455 if (res == -1)
456 return -errno;
457 return 0;
458}
459#endif /* HAVE_SETXATTR */
460
461#ifdef HAVE_COPY_FILE_RANGE
462static ssize_t xmp_copy_file_range(const char *path_in,
463 struct fuse_file_info *fi_in,
464 off_t offset_in, const char *path_out,
465 struct fuse_file_info *fi_out,
466 off_t offset_out, size_t len, int flags)
467{
468 int fd_in, fd_out;
469 ssize_t res;
470
471 if(fi_in == NULL)
472 fd_in = open(path_in, O_RDONLY);
473 else
474 fd_in = fi_in->fh;
475
476 if (fd_in == -1)
477 return -errno;
478
479 if(fi_out == NULL)
480 fd_out = open(path_out, O_WRONLY);
481 else
482 fd_out = fi_out->fh;
483
484 if (fd_out == -1) {
485 close(fd_in);
486 return -errno;
487 }
488
489 res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
490 flags);
491 if (res == -1)
492 res = -errno;
493
494 if (fi_out == NULL)
495 close(fd_out);
496 if (fi_in == NULL)
497 close(fd_in);
498
499 return res;
500}
501#endif
502
503static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
504{
505 int fd;
506 off_t res;
507
508 if (fi == NULL)
509 fd = open(path, O_RDONLY);
510 else
511 fd = fi->fh;
512
513 if (fd == -1)
514 return -errno;
515
516 res = lseek(fd, off, whence);
517 if (res == -1)
518 res = -errno;
519
520 if (fi == NULL)
521 close(fd);
522 return res;
523}
524
525#ifdef HAVE_STATX
526static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
527 struct fuse_file_info *fi)
528{
529 int fd = -1;
530 int res;
531
532 if (fi)
533 fd = fi->fh;
534
535 res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
536 if (res == -1)
537 return -errno;
538
539 return 0;
540}
541#endif
542
543static const struct fuse_operations xmp_oper = {
544 .init = xmp_init,
545 .getattr = xmp_getattr,
546 .access = xmp_access,
547 .readlink = xmp_readlink,
548 .readdir = xmp_readdir,
549 .mknod = xmp_mknod,
550 .mkdir = xmp_mkdir,
551 .symlink = xmp_symlink,
552 .unlink = xmp_unlink,
553 .rmdir = xmp_rmdir,
554 .rename = xmp_rename,
555 .link = xmp_link,
556 .chmod = xmp_chmod,
557 .chown = xmp_chown,
558 .truncate = xmp_truncate,
559#ifdef HAVE_UTIMENSAT
560 .utimens = xmp_utimens,
561#endif
562 .open = xmp_open,
563 .create = xmp_create,
564 .read = xmp_read,
565 .write = xmp_write,
566 .statfs = xmp_statfs,
567 .release = xmp_release,
568 .fsync = xmp_fsync,
569 .fallocate = xmp_fallocate,
570#ifdef HAVE_SETXATTR
571 .setxattr = xmp_setxattr,
572 .getxattr = xmp_getxattr,
573 .listxattr = xmp_listxattr,
574 .removexattr = xmp_removexattr,
575#endif
576#ifdef HAVE_COPY_FILE_RANGE
577 .copy_file_range = xmp_copy_file_range,
578#endif
579 .lseek = xmp_lseek,
580#ifdef HAVE_STATX
581 .statx = xmp_statx,
582#endif
583};
584
585int main(int argc, char *argv[])
586{
587 enum { MAX_ARGS = 10 };
588 int i,new_argc;
589 char *new_argv[MAX_ARGS];
590
591 umask(0);
592 /* Process the "--plus" option apart */
593 for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
594 if (!strcmp(argv[i], "--plus")) {
595 fill_dir_plus = FUSE_FILL_DIR_PLUS;
596 } else if (!strcmp(argv[i], "--readdir-zero-inodes")) {
597 // Return zero inodes from readdir
598 readdir_zero_ino = 1;
599 } else {
600 new_argv[new_argc++] = argv[i];
601 }
602 }
603 return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
604}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_readdir_flags
Definition fuse.h:42
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t auto_cache
Definition fuse.h:253
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2passthrough__fh_8c_source.html0000644000175000017500000034623015156613427025245 0ustar berndbernd libfuse: fuse-3.18.2/example/passthrough_fh.c Source File
libfuse
passthrough_fh.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
26#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
27
28#define _GNU_SOURCE
29
30#include <fuse.h>
31
32#ifdef HAVE_LIBULOCKMGR
33#include <ulockmgr.h>
34#endif
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <sys/stat.h>
42#include <dirent.h>
43#include <errno.h>
44#include <sys/time.h>
45#ifdef HAVE_SETXATTR
46#include <sys/xattr.h>
47#endif
48#include <sys/file.h> /* flock(2) */
49
50#include "passthrough_helpers.h"
51
52static void *xmp_init(struct fuse_conn_info *conn,
53 struct fuse_config *cfg)
54{
55 (void) conn;
56 cfg->use_ino = 1;
57 cfg->nullpath_ok = 1;
58
59 /* parallel_direct_writes feature depends on direct_io features.
60 To make parallel_direct_writes valid, need either set cfg->direct_io
61 in current function (recommended in high level API) or set fi->direct_io
62 in xmp_create() or xmp_open(). */
63 // cfg->direct_io = 1;
65
66 /* Pick up changes from lower filesystem right away. This is
67 also necessary for better hardlink support. When the kernel
68 calls the unlink() handler, it does not know the inode of
69 the to-be-removed entry and can therefore not invalidate
70 the cache of the associated inode - resulting in an
71 incorrect st_nlink value being reported for any remaining
72 hardlinks to this inode. */
73 cfg->entry_timeout = 0;
74 cfg->attr_timeout = 0;
75 cfg->negative_timeout = 0;
76
77 return NULL;
78}
79
80static int xmp_getattr(const char *path, struct stat *stbuf,
81 struct fuse_file_info *fi)
82{
83 int res;
84
85 (void) path;
86
87 if(fi)
88 res = fstat(fi->fh, stbuf);
89 else
90 res = lstat(path, stbuf);
91 if (res == -1)
92 return -errno;
93
94 return 0;
95}
96
97static int xmp_access(const char *path, int mask)
98{
99 int res;
100
101 res = access(path, mask);
102 if (res == -1)
103 return -errno;
104
105 return 0;
106}
107
108static int xmp_readlink(const char *path, char *buf, size_t size)
109{
110 int res;
111
112 res = readlink(path, buf, size - 1);
113 if (res == -1)
114 return -errno;
115
116 buf[res] = '\0';
117 return 0;
118}
119
120struct xmp_dirp {
121 DIR *dp;
122 struct dirent *entry;
123 off_t offset;
124};
125
126static int xmp_opendir(const char *path, struct fuse_file_info *fi)
127{
128 int res;
129 struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
130 if (d == NULL)
131 return -ENOMEM;
132
133 d->dp = opendir(path);
134 if (d->dp == NULL) {
135 res = -errno;
136 free(d);
137 return res;
138 }
139 d->offset = 0;
140 d->entry = NULL;
141
142 fi->fh = (unsigned long) d;
143 return 0;
144}
145
146static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
147{
148 return (struct xmp_dirp *) (uintptr_t) fi->fh;
149}
150
151static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
152 off_t offset, struct fuse_file_info *fi,
153 enum fuse_readdir_flags flags)
154{
155 struct xmp_dirp *d = get_dirp(fi);
156
157 (void) path;
158 if (offset != d->offset) {
159#ifndef __FreeBSD__
160 seekdir(d->dp, offset);
161#else
162 /* Subtract the one that we add when calling
163 telldir() below */
164 seekdir(d->dp, offset-1);
165#endif
166 d->entry = NULL;
167 d->offset = offset;
168 }
169 while (1) {
170 struct stat st;
171 off_t nextoff;
173
174 if (!d->entry) {
175 d->entry = readdir(d->dp);
176 if (!d->entry)
177 break;
178 }
179#ifdef HAVE_FSTATAT
180 if (flags & FUSE_READDIR_PLUS) {
181 int res;
182
183 res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
184 AT_SYMLINK_NOFOLLOW);
185 if (res != -1)
186 fill_flags |= FUSE_FILL_DIR_PLUS;
187 }
188#endif
189 if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
190 memset(&st, 0, sizeof(st));
191 st.st_ino = d->entry->d_ino;
192 st.st_mode = d->entry->d_type << 12;
193 }
194 nextoff = telldir(d->dp);
195#ifdef __FreeBSD__
196 /* Under FreeBSD, telldir() may return 0 the first time
197 it is called. But for libfuse, an offset of zero
198 means that offsets are not supported, so we shift
199 everything by one. */
200 nextoff++;
201#endif
202 if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
203 break;
204
205 d->entry = NULL;
206 d->offset = nextoff;
207 }
208
209 return 0;
210}
211
212static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
213{
214 struct xmp_dirp *d = get_dirp(fi);
215 (void) path;
216 closedir(d->dp);
217 free(d);
218 return 0;
219}
220
221static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
222{
223 int res;
224
225 if (S_ISFIFO(mode))
226 res = mkfifo(path, mode);
227 else
228 res = mknod(path, mode, rdev);
229 if (res == -1)
230 return -errno;
231
232 return 0;
233}
234
235static int xmp_mkdir(const char *path, mode_t mode)
236{
237 int res;
238
239 res = mkdir(path, mode);
240 if (res == -1)
241 return -errno;
242
243 return 0;
244}
245
246static int xmp_unlink(const char *path)
247{
248 int res;
249
250 res = unlink(path);
251 if (res == -1)
252 return -errno;
253
254 return 0;
255}
256
257static int xmp_rmdir(const char *path)
258{
259 int res;
260
261 res = rmdir(path);
262 if (res == -1)
263 return -errno;
264
265 return 0;
266}
267
268static int xmp_symlink(const char *from, const char *to)
269{
270 int res;
271
272 res = symlink(from, to);
273 if (res == -1)
274 return -errno;
275
276 return 0;
277}
278
279static int xmp_rename(const char *from, const char *to, unsigned int flags)
280{
281 int res;
282
283 /* When we have renameat2() in libc, then we can implement flags */
284 if (flags)
285 return -EINVAL;
286
287 res = rename(from, to);
288 if (res == -1)
289 return -errno;
290
291 return 0;
292}
293
294static int xmp_link(const char *from, const char *to)
295{
296 int res;
297
298 res = link(from, to);
299 if (res == -1)
300 return -errno;
301
302 return 0;
303}
304
305static int xmp_chmod(const char *path, mode_t mode,
306 struct fuse_file_info *fi)
307{
308 int res;
309
310 if(fi)
311 res = fchmod(fi->fh, mode);
312 else
313 res = chmod(path, mode);
314 if (res == -1)
315 return -errno;
316
317 return 0;
318}
319
320static int xmp_chown(const char *path, uid_t uid, gid_t gid,
321 struct fuse_file_info *fi)
322{
323 int res;
324
325 if (fi)
326 res = fchown(fi->fh, uid, gid);
327 else
328 res = lchown(path, uid, gid);
329 if (res == -1)
330 return -errno;
331
332 return 0;
333}
334
335static int xmp_truncate(const char *path, off_t size,
336 struct fuse_file_info *fi)
337{
338 int res;
339
340 if(fi)
341 res = ftruncate(fi->fh, size);
342 else
343 res = truncate(path, size);
344
345 if (res == -1)
346 return -errno;
347
348 return 0;
349}
350
351#ifdef HAVE_UTIMENSAT
352static int xmp_utimens(const char *path, const struct timespec ts[2],
353 struct fuse_file_info *fi)
354{
355 int res;
356
357 /* don't use utime/utimes since they follow symlinks */
358 if (fi)
359 res = futimens(fi->fh, ts);
360 else
361 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
362 if (res == -1)
363 return -errno;
364
365 return 0;
366}
367#endif
368
369static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
370{
371 int fd;
372
373 fd = open(path, fi->flags, mode);
374 if (fd == -1)
375 return -errno;
376
377 fi->fh = fd;
378 return 0;
379}
380
381static int xmp_open(const char *path, struct fuse_file_info *fi)
382{
383 int fd;
384
385 fd = open(path, fi->flags);
386 if (fd == -1)
387 return -errno;
388
389 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
390 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
391 for writes to the same file). */
392 if (fi->flags & O_DIRECT) {
393 fi->direct_io = 1;
395 }
396
397 fi->fh = fd;
398 return 0;
399}
400
401static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
402 struct fuse_file_info *fi)
403{
404 int res;
405
406 (void) path;
407 res = pread(fi->fh, buf, size, offset);
408 if (res == -1)
409 res = -errno;
410
411 return res;
412}
413
414static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
415 size_t size, off_t offset, struct fuse_file_info *fi)
416{
417 struct fuse_bufvec *src;
418
419 (void) path;
420
421 src = malloc(sizeof(struct fuse_bufvec));
422 if (src == NULL)
423 return -ENOMEM;
424
425 *src = FUSE_BUFVEC_INIT(size);
426
428 src->buf[0].fd = fi->fh;
429 src->buf[0].pos = offset;
430
431 *bufp = src;
432
433 return 0;
434}
435
436static int xmp_write(const char *path, const char *buf, size_t size,
437 off_t offset, struct fuse_file_info *fi)
438{
439 int res;
440
441 (void) path;
442 res = pwrite(fi->fh, buf, size, offset);
443 if (res == -1)
444 res = -errno;
445
446 return res;
447}
448
449static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
450 off_t offset, struct fuse_file_info *fi)
451{
452 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
453
454 (void) path;
455
457 dst.buf[0].fd = fi->fh;
458 dst.buf[0].pos = offset;
459
461}
462
463static int xmp_statfs(const char *path, struct statvfs *stbuf)
464{
465 int res;
466
467 res = statvfs(path, stbuf);
468 if (res == -1)
469 return -errno;
470
471 return 0;
472}
473
474static int xmp_flush(const char *path, struct fuse_file_info *fi)
475{
476 int res;
477
478 (void) path;
479 /* This is called from every close on an open file, so call the
480 close on the underlying filesystem. But since flush may be
481 called multiple times for an open file, this must not really
482 close the file. This is important if used on a network
483 filesystem like NFS which flush the data/metadata on close() */
484 res = close(dup(fi->fh));
485 if (res == -1)
486 return -errno;
487
488 return 0;
489}
490
491static int xmp_release(const char *path, struct fuse_file_info *fi)
492{
493 (void) path;
494 close(fi->fh);
495
496 return 0;
497}
498
499static int xmp_fsync(const char *path, int isdatasync,
500 struct fuse_file_info *fi)
501{
502 int res;
503 (void) path;
504
505#ifndef HAVE_FDATASYNC
506 (void) isdatasync;
507#else
508 if (isdatasync)
509 res = fdatasync(fi->fh);
510 else
511#endif
512 res = fsync(fi->fh);
513 if (res == -1)
514 return -errno;
515
516 return 0;
517}
518
519static int xmp_fallocate(const char *path, int mode,
520 off_t offset, off_t length, struct fuse_file_info *fi)
521{
522 (void) path;
523
524 return do_fallocate(fi->fh, mode, offset, length);
525}
526
527#ifdef HAVE_SETXATTR
528/* xattr operations are optional and can safely be left unimplemented */
529static int xmp_setxattr(const char *path, const char *name, const char *value,
530 size_t size, int flags)
531{
532 int res = lsetxattr(path, name, value, size, flags);
533 if (res == -1)
534 return -errno;
535 return 0;
536}
537
538static int xmp_getxattr(const char *path, const char *name, char *value,
539 size_t size)
540{
541 int res = lgetxattr(path, name, value, size);
542 if (res == -1)
543 return -errno;
544 return res;
545}
546
547static int xmp_listxattr(const char *path, char *list, size_t size)
548{
549 int res = llistxattr(path, list, size);
550 if (res == -1)
551 return -errno;
552 return res;
553}
554
555static int xmp_removexattr(const char *path, const char *name)
556{
557 int res = lremovexattr(path, name);
558 if (res == -1)
559 return -errno;
560 return 0;
561}
562#endif /* HAVE_SETXATTR */
563
564#ifdef HAVE_LIBULOCKMGR
565static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
566 struct flock *lock)
567{
568 (void) path;
569
570 return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
571 sizeof(fi->lock_owner));
572}
573#endif
574
575static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
576{
577 int res;
578 (void) path;
579
580 res = flock(fi->fh, op);
581 if (res == -1)
582 return -errno;
583
584 return 0;
585}
586
587#ifdef HAVE_COPY_FILE_RANGE
588static ssize_t xmp_copy_file_range(const char *path_in,
589 struct fuse_file_info *fi_in,
590 off_t off_in, const char *path_out,
591 struct fuse_file_info *fi_out,
592 off_t off_out, size_t len, int flags)
593{
594 ssize_t res;
595 (void) path_in;
596 (void) path_out;
597
598 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
599 flags);
600 if (res == -1)
601 return -errno;
602
603 return res;
604}
605#endif
606
607static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
608{
609 off_t res;
610 (void) path;
611
612 res = lseek(fi->fh, off, whence);
613 if (res == -1)
614 return -errno;
615
616 return res;
617}
618
619#ifdef HAVE_STATX
620static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
621 struct fuse_file_info *fi)
622{
623 int fd = -1;
624 int res;
625
626 if (fi)
627 fd = fi->fh;
628
629 res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
630 if (res == -1)
631 return -errno;
632
633 return 0;
634}
635#endif
636
637static const struct fuse_operations xmp_oper = {
638 .init = xmp_init,
639 .getattr = xmp_getattr,
640 .access = xmp_access,
641 .readlink = xmp_readlink,
642 .opendir = xmp_opendir,
643 .readdir = xmp_readdir,
644 .releasedir = xmp_releasedir,
645 .mknod = xmp_mknod,
646 .mkdir = xmp_mkdir,
647 .symlink = xmp_symlink,
648 .unlink = xmp_unlink,
649 .rmdir = xmp_rmdir,
650 .rename = xmp_rename,
651 .link = xmp_link,
652 .chmod = xmp_chmod,
653 .chown = xmp_chown,
654 .truncate = xmp_truncate,
655#ifdef HAVE_UTIMENSAT
656 .utimens = xmp_utimens,
657#endif
658 .create = xmp_create,
659 .open = xmp_open,
660 .read = xmp_read,
661 .read_buf = xmp_read_buf,
662 .write = xmp_write,
663 .write_buf = xmp_write_buf,
664 .statfs = xmp_statfs,
665 .flush = xmp_flush,
666 .release = xmp_release,
667 .fsync = xmp_fsync,
668 .fallocate = xmp_fallocate,
669#ifdef HAVE_SETXATTR
670 .setxattr = xmp_setxattr,
671 .getxattr = xmp_getxattr,
672 .listxattr = xmp_listxattr,
673 .removexattr = xmp_removexattr,
674#endif
675#ifdef HAVE_LIBULOCKMGR
676 .lock = xmp_lock,
677#endif
678 .flock = xmp_flock,
679#ifdef HAVE_COPY_FILE_RANGE
680 .copy_file_range = xmp_copy_file_range,
681#endif
682 .lseek = xmp_lseek,
683#ifdef HAVE_STATX
684 .statx = xmp_statx,
685#endif
686};
687
688int main(int argc, char *argv[])
689{
690 umask(0);
691 return fuse_main(argc, argv, &xmp_oper, NULL);
692}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2passthrough__helpers_8h_source.html0000644000175000017500000005114115156613427026311 0ustar berndbernd libfuse: fuse-3.18.2/example/passthrough_helpers.h Source File
libfuse
passthrough_helpers.h
1/*
2 * FUSE: Filesystem in Userspace
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE
24 */
25
26#ifndef FUSE_EXAMPLE_PASSTHROUGH_HELPERS_H_
27#define FUSE_EXAMPLE_PASSTHROUGH_HELPERS_H_
28
29#include <errno.h>
30#include <fcntl.h>
31#include <string.h>
32#include <sys/stat.h>
33#include <unistd.h>
34
35#ifdef __FreeBSD__
36#include <sys/socket.h>
37#include <sys/un.h>
38#endif
39
40static inline int do_fallocate(int fd, int mode, off_t offset, off_t length)
41{
42#ifdef HAVE_FALLOCATE
43 if (fallocate(fd, mode, offset, length) == -1)
44 return -errno;
45 return 0;
46#else // HAVE_FALLOCATE
47
48#ifdef HAVE_POSIX_FALLOCATE
49 if (mode == 0)
50 return -posix_fallocate(fd, offset, length);
51#endif
52
53#ifdef HAVE_FSPACECTL
54 // 0x3 == FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE
55 if (mode == 0x3) {
56 struct spacectl_range sr;
57
58 sr.r_offset = offset;
59 sr.r_len = length;
60 if (fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, NULL) == -1)
61 return -errno;
62 return 0;
63 }
64#endif
65
66 return -EOPNOTSUPP;
67#endif // HAVE_FALLOCATE
68}
69
70/*
71 * Creates files on the underlying file system in response to a FUSE_MKNOD
72 * operation
73 */
74static inline int mknod_wrapper(int dirfd, const char *path, const char *link,
75 int mode, dev_t rdev)
76{
77 int res;
78
79 if (S_ISREG(mode)) {
80 res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode);
81 if (res >= 0)
82 res = close(res);
83 } else if (S_ISDIR(mode)) {
84 res = mkdirat(dirfd, path, mode);
85 } else if (S_ISLNK(mode) && link != NULL) {
86 res = symlinkat(link, dirfd, path);
87 } else if (S_ISFIFO(mode)) {
88 res = mkfifoat(dirfd, path, mode);
89#ifdef __FreeBSD__
90 } else if (S_ISSOCK(mode)) {
91 struct sockaddr_un su;
92 int fd;
93
94 if (strlen(path) >= sizeof(su.sun_path)) {
95 errno = ENAMETOOLONG;
96 return -1;
97 }
98 fd = socket(AF_UNIX, SOCK_STREAM, 0);
99 if (fd >= 0) {
100 /*
101 * We must bind the socket to the underlying file
102 * system to create the socket file, even though
103 * we'll never listen on this socket.
104 */
105 su.sun_family = AF_UNIX;
106 strncpy(su.sun_path, path, sizeof(su.sun_path));
107 res = bindat(dirfd, fd, (struct sockaddr*)&su,
108 sizeof(su));
109 if (res == 0)
110 close(fd);
111 } else {
112 res = -1;
113 }
114#endif
115 } else {
116 res = mknodat(dirfd, path, mode, rdev);
117 }
118
119 return res;
120}
121
122#endif // FUSE_PASSTHROUGH_HELPERS_H_
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2passthrough__ll_8c_source.html0000644000175000017500000100626715156613427025263 0ustar berndbernd libfuse: fuse-3.18.2/example/passthrough_ll.c Source File
libfuse
passthrough_ll.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
33#define _GNU_SOURCE
34#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
35
36#include <fuse_lowlevel.h>
37#include <unistd.h>
38#include <stdlib.h>
39#include <stdio.h>
40#include <stddef.h>
41#include <stdbool.h>
42#include <string.h>
43#include <limits.h>
44#include <dirent.h>
45#include <assert.h>
46#include <errno.h>
47#include <inttypes.h>
48#include <pthread.h>
49#include <sys/file.h>
50#include <sys/xattr.h>
51
52#include "passthrough_helpers.h"
53
54/* We are re-using pointers to our `struct lo_inode` and `struct
55 lo_dirp` elements as inodes. This means that we must be able to
56 store uintptr_t values in a fuse_ino_t variable. The following
57 incantation checks this condition at compile time. */
58#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
59_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
60 "fuse_ino_t too small to hold uintptr_t values!");
61#else
62struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
63 { unsigned _uintptr_to_must_hold_fuse_ino_t:
64 ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
65#endif
66
67struct lo_inode {
68 struct lo_inode *next; /* protected by lo->mutex */
69 struct lo_inode *prev; /* protected by lo->mutex */
70 int fd;
71 ino_t ino;
72 dev_t dev;
73 uint64_t refcount; /* protected by lo->mutex */
74};
75
76enum {
77 CACHE_NEVER,
78 CACHE_NORMAL,
79 CACHE_ALWAYS,
80};
81
82struct lo_data {
83 pthread_mutex_t mutex;
84 int debug;
85 int writeback;
86 int flock;
87 int xattr;
88 char *source;
89 double timeout;
90 int cache;
91 int timeout_set;
92 struct lo_inode root; /* protected by lo->mutex */
93};
94
95static const struct fuse_opt lo_opts[] = {
96 { "writeback",
97 offsetof(struct lo_data, writeback), 1 },
98 { "no_writeback",
99 offsetof(struct lo_data, writeback), 0 },
100 { "source=%s",
101 offsetof(struct lo_data, source), 0 },
102 { "flock",
103 offsetof(struct lo_data, flock), 1 },
104 { "no_flock",
105 offsetof(struct lo_data, flock), 0 },
106 { "xattr",
107 offsetof(struct lo_data, xattr), 1 },
108 { "no_xattr",
109 offsetof(struct lo_data, xattr), 0 },
110 { "timeout=%lf",
111 offsetof(struct lo_data, timeout), 0 },
112 { "timeout=",
113 offsetof(struct lo_data, timeout_set), 1 },
114 { "cache=never",
115 offsetof(struct lo_data, cache), CACHE_NEVER },
116 { "cache=auto",
117 offsetof(struct lo_data, cache), CACHE_NORMAL },
118 { "cache=always",
119 offsetof(struct lo_data, cache), CACHE_ALWAYS },
120
122};
123
124static void passthrough_ll_help(void)
125{
126 printf(
127" -o writeback Enable writeback\n"
128" -o no_writeback Disable write back\n"
129" -o source=/home/dir Source directory to be mounted\n"
130" -o flock Enable flock\n"
131" -o no_flock Disable flock\n"
132" -o xattr Enable xattr\n"
133" -o no_xattr Disable xattr\n"
134" -o timeout=1.0 Caching timeout\n"
135" -o timeout=0/1 Timeout is set\n"
136" -o cache=never Disable cache\n"
137" -o cache=auto Auto enable cache\n"
138" -o cache=always Cache always\n");
139}
140
141static struct lo_data *lo_data(fuse_req_t req)
142{
143 return (struct lo_data *) fuse_req_userdata(req);
144}
145
146static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
147{
148 if (ino == FUSE_ROOT_ID)
149 return &lo_data(req)->root;
150 else
151 return (struct lo_inode *) (uintptr_t) ino;
152}
153
154static int lo_fd(fuse_req_t req, fuse_ino_t ino)
155{
156 return lo_inode(req, ino)->fd;
157}
158
159static bool lo_debug(fuse_req_t req)
160{
161 return lo_data(req)->debug != 0;
162}
163
164static void lo_init(void *userdata,
165 struct fuse_conn_info *conn)
166{
167 struct lo_data *lo = (struct lo_data *)userdata;
168 bool has_flag;
169
170 if (lo->writeback) {
172 if (lo->debug && has_flag)
173 fuse_log(FUSE_LOG_DEBUG,
174 "lo_init: activating writeback\n");
175 }
176 if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
178 if (lo->debug && has_flag)
179 fuse_log(FUSE_LOG_DEBUG,
180 "lo_init: activating flock locks\n");
181 }
182
183 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
184 conn->no_interrupt = 1;
185}
186
187static void lo_destroy(void *userdata)
188{
189 struct lo_data *lo = (struct lo_data*) userdata;
190
191 while (lo->root.next != &lo->root) {
192 struct lo_inode* next = lo->root.next;
193 lo->root.next = next->next;
194 close(next->fd);
195 free(next);
196 }
197}
198
199static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
200 struct fuse_file_info *fi)
201{
202 int res;
203 struct stat buf;
204 struct lo_data *lo = lo_data(req);
205 int fd = fi ? fi->fh : lo_fd(req, ino);
206
207 (void) fi;
208
209 res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
210 if (res == -1)
211 return (void) fuse_reply_err(req, errno);
212
213 fuse_reply_attr(req, &buf, lo->timeout);
214}
215
216static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
217 int valid, struct fuse_file_info *fi)
218{
219 int saverr;
220 char procname[64];
221 struct lo_inode *inode = lo_inode(req, ino);
222 int ifd = inode->fd;
223 int res;
224
225 if (valid & FUSE_SET_ATTR_MODE) {
226 if (fi) {
227 res = fchmod(fi->fh, attr->st_mode);
228 } else {
229 sprintf(procname, "/proc/self/fd/%i", ifd);
230 res = chmod(procname, attr->st_mode);
231 }
232 if (res == -1)
233 goto out_err;
234 }
235 if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
236 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
237 attr->st_uid : (uid_t) -1;
238 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
239 attr->st_gid : (gid_t) -1;
240
241 res = fchownat(ifd, "", uid, gid,
242 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
243 if (res == -1)
244 goto out_err;
245 }
246 if (valid & FUSE_SET_ATTR_SIZE) {
247 if (fi) {
248 res = ftruncate(fi->fh, attr->st_size);
249 } else {
250 sprintf(procname, "/proc/self/fd/%i", ifd);
251 res = truncate(procname, attr->st_size);
252 }
253 if (res == -1)
254 goto out_err;
255 }
256 if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
257 struct timespec tv[2];
258
259 tv[0].tv_sec = 0;
260 tv[1].tv_sec = 0;
261 tv[0].tv_nsec = UTIME_OMIT;
262 tv[1].tv_nsec = UTIME_OMIT;
263
264 if (valid & FUSE_SET_ATTR_ATIME_NOW)
265 tv[0].tv_nsec = UTIME_NOW;
266 else if (valid & FUSE_SET_ATTR_ATIME)
267 tv[0] = attr->st_atim;
268
269 if (valid & FUSE_SET_ATTR_MTIME_NOW)
270 tv[1].tv_nsec = UTIME_NOW;
271 else if (valid & FUSE_SET_ATTR_MTIME)
272 tv[1] = attr->st_mtim;
273
274 if (fi)
275 res = futimens(fi->fh, tv);
276 else {
277 sprintf(procname, "/proc/self/fd/%i", ifd);
278 res = utimensat(AT_FDCWD, procname, tv, 0);
279 }
280 if (res == -1)
281 goto out_err;
282 }
283
284 return lo_getattr(req, ino, fi);
285
286out_err:
287 saverr = errno;
288 fuse_reply_err(req, saverr);
289}
290
291static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
292{
293 struct lo_inode *p;
294 struct lo_inode *ret = NULL;
295
296 pthread_mutex_lock(&lo->mutex);
297 for (p = lo->root.next; p != &lo->root; p = p->next) {
298 if (p->ino == st->st_ino && p->dev == st->st_dev) {
299 assert(p->refcount > 0);
300 ret = p;
301 ret->refcount++;
302 break;
303 }
304 }
305 pthread_mutex_unlock(&lo->mutex);
306 return ret;
307}
308
309
310static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo)
311{
312 struct lo_inode *inode = NULL;
313 struct lo_inode *prev, *next;
314
315 inode = calloc(1, sizeof(struct lo_inode));
316 if (!inode)
317 return NULL;
318
319 inode->refcount = 1;
320 inode->fd = fd;
321 inode->ino = e->attr.st_ino;
322 inode->dev = e->attr.st_dev;
323
324 pthread_mutex_lock(&lo->mutex);
325 prev = &lo->root;
326 next = prev->next;
327 next->prev = inode;
328 inode->next = next;
329 inode->prev = prev;
330 prev->next = inode;
331 pthread_mutex_unlock(&lo->mutex);
332 return inode;
333}
334
335static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e)
336{
337 int res;
338 struct lo_data *lo = lo_data(req);
339
340 memset(e, 0, sizeof(*e));
341 e->attr_timeout = lo->timeout;
342 e->entry_timeout = lo->timeout;
343
344 res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
345 if (res == -1)
346 return errno;
347
348 e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo);
349
350 if (lo_debug(req))
351 fuse_log(FUSE_LOG_DEBUG, " %lli/%d -> %lli\n",
352 (unsigned long long) parent, fd, (unsigned long long) e->ino);
353
354 return 0;
355
356}
357
358static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
359 struct fuse_entry_param *e)
360{
361 int newfd;
362 int res;
363 int saverr;
364 struct lo_data *lo = lo_data(req);
365 struct lo_inode *inode;
366
367 memset(e, 0, sizeof(*e));
368 e->attr_timeout = lo->timeout;
369 e->entry_timeout = lo->timeout;
370
371 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
372 if (newfd == -1)
373 goto out_err;
374
375 res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
376 if (res == -1)
377 goto out_err;
378
379 inode = lo_find(lo_data(req), &e->attr);
380 if (inode) {
381 close(newfd);
382 newfd = -1;
383 } else {
384 inode = create_new_inode(newfd, e, lo);
385 if (!inode)
386 goto out_err;
387 }
388 e->ino = (uintptr_t) inode;
389
390 if (lo_debug(req))
391 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
392 (unsigned long long) parent, name, (unsigned long long) e->ino);
393
394 return 0;
395
396out_err:
397 saverr = errno;
398 if (newfd != -1)
399 close(newfd);
400 return saverr;
401}
402
403static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
404{
405 struct fuse_entry_param e;
406 int err;
407
408 if (lo_debug(req))
409 fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
410 parent, name);
411
412 err = lo_do_lookup(req, parent, name, &e);
413 if (err)
414 fuse_reply_err(req, err);
415 else
416 fuse_reply_entry(req, &e);
417}
418
419static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
420 const char *name, mode_t mode, dev_t rdev,
421 const char *link)
422{
423 int res;
424 int saverr;
425 struct lo_inode *dir = lo_inode(req, parent);
426 struct fuse_entry_param e;
427
428 res = mknod_wrapper(dir->fd, name, link, mode, rdev);
429
430 saverr = errno;
431 if (res == -1)
432 goto out;
433
434 saverr = lo_do_lookup(req, parent, name, &e);
435 if (saverr)
436 goto out;
437
438 if (lo_debug(req))
439 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
440 (unsigned long long) parent, name, (unsigned long long) e.ino);
441
442 fuse_reply_entry(req, &e);
443 return;
444
445out:
446 fuse_reply_err(req, saverr);
447}
448
449static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
450 const char *name, mode_t mode, dev_t rdev)
451{
452 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
453}
454
455static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
456 mode_t mode)
457{
458 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
459}
460
461static void lo_symlink(fuse_req_t req, const char *link,
462 fuse_ino_t parent, const char *name)
463{
464 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
465}
466
467static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
468 const char *name)
469{
470 int res;
471 struct lo_data *lo = lo_data(req);
472 struct lo_inode *inode = lo_inode(req, ino);
473 struct fuse_entry_param e;
474 char procname[64];
475 int saverr;
476
477 memset(&e, 0, sizeof(struct fuse_entry_param));
478 e.attr_timeout = lo->timeout;
479 e.entry_timeout = lo->timeout;
480
481 sprintf(procname, "/proc/self/fd/%i", inode->fd);
482 res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
483 AT_SYMLINK_FOLLOW);
484 if (res == -1)
485 goto out_err;
486
487 res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
488 if (res == -1)
489 goto out_err;
490
491 pthread_mutex_lock(&lo->mutex);
492 inode->refcount++;
493 pthread_mutex_unlock(&lo->mutex);
494 e.ino = (uintptr_t) inode;
495
496 if (lo_debug(req))
497 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
498 (unsigned long long) parent, name,
499 (unsigned long long) e.ino);
500
501 fuse_reply_entry(req, &e);
502 return;
503
504out_err:
505 saverr = errno;
506 fuse_reply_err(req, saverr);
507}
508
509static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
510{
511 int res;
512
513 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
514
515 fuse_reply_err(req, res == -1 ? errno : 0);
516}
517
518static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
519 fuse_ino_t newparent, const char *newname,
520 unsigned int flags)
521{
522 int res;
523
524 if (flags) {
525 fuse_reply_err(req, EINVAL);
526 return;
527 }
528
529 res = renameat(lo_fd(req, parent), name,
530 lo_fd(req, newparent), newname);
531
532 fuse_reply_err(req, res == -1 ? errno : 0);
533}
534
535static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
536{
537 int res;
538
539 res = unlinkat(lo_fd(req, parent), name, 0);
540
541 fuse_reply_err(req, res == -1 ? errno : 0);
542}
543
544static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
545{
546 if (!inode)
547 return;
548
549 pthread_mutex_lock(&lo->mutex);
550 assert(inode->refcount >= n);
551 inode->refcount -= n;
552 if (!inode->refcount) {
553 struct lo_inode *prev, *next;
554
555 prev = inode->prev;
556 next = inode->next;
557 next->prev = prev;
558 prev->next = next;
559
560 pthread_mutex_unlock(&lo->mutex);
561 close(inode->fd);
562 free(inode);
563
564 } else {
565 pthread_mutex_unlock(&lo->mutex);
566 }
567}
568
569static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
570{
571 struct lo_data *lo = lo_data(req);
572 struct lo_inode *inode = lo_inode(req, ino);
573
574 if (lo_debug(req)) {
575 fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
576 (unsigned long long) ino,
577 (unsigned long long) inode->refcount,
578 (unsigned long long) nlookup);
579 }
580
581 unref_inode(lo, inode, nlookup);
582}
583
584static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
585{
586 lo_forget_one(req, ino, nlookup);
587 fuse_reply_none(req);
588}
589
590static void lo_forget_multi(fuse_req_t req, size_t count,
591 struct fuse_forget_data *forgets)
592{
593 int i;
594
595 for (i = 0; i < count; i++)
596 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
597 fuse_reply_none(req);
598}
599
600static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
601{
602 char buf[PATH_MAX + 1];
603 int res;
604
605 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
606 if (res == -1)
607 return (void) fuse_reply_err(req, errno);
608
609 if (res == sizeof(buf))
610 return (void) fuse_reply_err(req, ENAMETOOLONG);
611
612 buf[res] = '\0';
613
614 fuse_reply_readlink(req, buf);
615}
616
617struct lo_dirp {
618 DIR *dp;
619 struct dirent *entry;
620 off_t offset;
621};
622
623static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
624{
625 return (struct lo_dirp *) (uintptr_t) fi->fh;
626}
627
628static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
629{
630 int error = ENOMEM;
631 struct lo_data *lo = lo_data(req);
632 struct lo_dirp *d;
633 int fd = -1;
634
635 d = calloc(1, sizeof(struct lo_dirp));
636 if (d == NULL)
637 goto out_err;
638
639 fd = openat(lo_fd(req, ino), ".", O_RDONLY);
640 if (fd == -1)
641 goto out_errno;
642
643 d->dp = fdopendir(fd);
644 if (d->dp == NULL)
645 goto out_errno;
646
647 d->offset = 0;
648 d->entry = NULL;
649
650 fi->fh = (uintptr_t) d;
651 if (lo->cache != CACHE_NEVER)
652 fi->cache_readdir = 1;
653 if (lo->cache == CACHE_ALWAYS)
654 fi->keep_cache = 1;
655 fuse_reply_open(req, fi);
656 return;
657
658out_errno:
659 error = errno;
660out_err:
661 if (d) {
662 if (fd != -1)
663 close(fd);
664 free(d);
665 }
666 fuse_reply_err(req, error);
667}
668
669static int is_dot_or_dotdot(const char *name)
670{
671 return name[0] == '.' && (name[1] == '\0' ||
672 (name[1] == '.' && name[2] == '\0'));
673}
674
675static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
676 off_t offset, struct fuse_file_info *fi, int plus)
677{
678 struct lo_dirp *d = lo_dirp(fi);
679 char *buf;
680 char *p;
681 size_t rem = size;
682 int err;
683
684 (void) ino;
685
686 buf = calloc(1, size);
687 if (!buf) {
688 err = ENOMEM;
689 goto error;
690 }
691 p = buf;
692
693 if (offset != d->offset) {
694 seekdir(d->dp, offset);
695 d->entry = NULL;
696 d->offset = offset;
697 }
698 while (1) {
699 size_t entsize;
700 off_t nextoff;
701 const char *name;
702
703 if (!d->entry) {
704 errno = 0;
705 d->entry = readdir(d->dp);
706 if (!d->entry) {
707 if (errno) { // Error
708 err = errno;
709 goto error;
710 } else { // End of stream
711 break;
712 }
713 }
714 }
715 nextoff = d->entry->d_off;
716 name = d->entry->d_name;
717 fuse_ino_t entry_ino = 0;
718 if (plus) {
719 struct fuse_entry_param e;
720 if (is_dot_or_dotdot(name)) {
721 e = (struct fuse_entry_param) {
722 .attr.st_ino = d->entry->d_ino,
723 .attr.st_mode = d->entry->d_type << 12,
724 };
725 } else {
726 err = lo_do_lookup(req, ino, name, &e);
727 if (err)
728 goto error;
729 entry_ino = e.ino;
730 }
731
732 entsize = fuse_add_direntry_plus(req, p, rem, name,
733 &e, nextoff);
734 } else {
735 struct stat st = {
736 .st_ino = d->entry->d_ino,
737 .st_mode = d->entry->d_type << 12,
738 };
739 entsize = fuse_add_direntry(req, p, rem, name,
740 &st, nextoff);
741 }
742 if (entsize > rem) {
743 if (entry_ino != 0)
744 lo_forget_one(req, entry_ino, 1);
745 break;
746 }
747
748 p += entsize;
749 rem -= entsize;
750
751 d->entry = NULL;
752 d->offset = nextoff;
753 }
754
755 err = 0;
756error:
757 // If there's an error, we can only signal it if we haven't stored
758 // any entries yet - otherwise we'd end up with wrong lookup
759 // counts for the entries that are already in the buffer. So we
760 // return what we've collected until that point.
761 if (err && rem == size)
762 fuse_reply_err(req, err);
763 else
764 fuse_reply_buf(req, buf, size - rem);
765 free(buf);
766}
767
768static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
769 off_t offset, struct fuse_file_info *fi)
770{
771 lo_do_readdir(req, ino, size, offset, fi, 0);
772}
773
774static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
775 off_t offset, struct fuse_file_info *fi)
776{
777 lo_do_readdir(req, ino, size, offset, fi, 1);
778}
779
780static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
781{
782 struct lo_dirp *d = lo_dirp(fi);
783 (void) ino;
784 closedir(d->dp);
785 free(d);
786 fuse_reply_err(req, 0);
787}
788
789static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent,
790 mode_t mode, struct fuse_file_info *fi)
791{
792 int fd;
793 struct lo_data *lo = lo_data(req);
794 struct fuse_entry_param e;
795 int err;
796
797 if (lo_debug(req))
798 fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n",
799 parent);
800
801 fd = openat(lo_fd(req, parent), ".",
802 (fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode);
803 if (fd == -1)
804 return (void) fuse_reply_err(req, errno);
805
806 fi->fh = fd;
807 if (lo->cache == CACHE_NEVER)
808 fi->direct_io = 1;
809 else if (lo->cache == CACHE_ALWAYS)
810 fi->keep_cache = 1;
811
812 /* parallel_direct_writes feature depends on direct_io features.
813 To make parallel_direct_writes valid, need set fi->direct_io
814 in current function. */
816
817 err = fill_entry_param_new_inode(req, parent, fd, &e);
818 if (err)
819 fuse_reply_err(req, err);
820 else
821 fuse_reply_create(req, &e, fi);
822}
823
824static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
825 mode_t mode, struct fuse_file_info *fi)
826{
827 int fd;
828 struct lo_data *lo = lo_data(req);
829 struct fuse_entry_param e;
830 int err;
831
832 if (lo_debug(req))
833 fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
834 parent, name);
835
836 fd = openat(lo_fd(req, parent), name,
837 (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
838 if (fd == -1)
839 return (void) fuse_reply_err(req, errno);
840
841 fi->fh = fd;
842 if (lo->cache == CACHE_NEVER)
843 fi->direct_io = 1;
844 else if (lo->cache == CACHE_ALWAYS)
845 fi->keep_cache = 1;
846
847 /* parallel_direct_writes feature depends on direct_io features.
848 To make parallel_direct_writes valid, need set fi->direct_io
849 in current function. */
851
852 err = lo_do_lookup(req, parent, name, &e);
853 if (err)
854 fuse_reply_err(req, err);
855 else
856 fuse_reply_create(req, &e, fi);
857}
858
859static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
860 struct fuse_file_info *fi)
861{
862 int res;
863 int fd = dirfd(lo_dirp(fi)->dp);
864 (void) ino;
865 if (datasync)
866 res = fdatasync(fd);
867 else
868 res = fsync(fd);
869 fuse_reply_err(req, res == -1 ? errno : 0);
870}
871
872static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
873{
874 int fd;
875 char buf[64];
876 struct lo_data *lo = lo_data(req);
877
878 if (lo_debug(req))
879 fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
880 ino, fi->flags);
881
882 /* With writeback cache, kernel may send read requests even
883 when userspace opened write-only */
884 if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
885 fi->flags &= ~O_ACCMODE;
886 fi->flags |= O_RDWR;
887 }
888
889 /* With writeback cache, O_APPEND is handled by the kernel.
890 This breaks atomicity (since the file may change in the
891 underlying filesystem, so that the kernel's idea of the
892 end of the file isn't accurate anymore). In this example,
893 we just accept that. A more rigorous filesystem may want
894 to return an error here */
895 if (lo->writeback && (fi->flags & O_APPEND))
896 fi->flags &= ~O_APPEND;
897
898 sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
899 fd = open(buf, fi->flags & ~O_NOFOLLOW);
900 if (fd == -1)
901 return (void) fuse_reply_err(req, errno);
902
903 fi->fh = fd;
904 if (lo->cache == CACHE_NEVER)
905 fi->direct_io = 1;
906 else if (lo->cache == CACHE_ALWAYS)
907 fi->keep_cache = 1;
908
909 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
910 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
911 for writes to the same file in the kernel). */
912 if (fi->flags & O_DIRECT)
913 fi->direct_io = 1;
914
915 /* parallel_direct_writes feature depends on direct_io features.
916 To make parallel_direct_writes valid, need set fi->direct_io
917 in current function. */
919
920 fuse_reply_open(req, fi);
921}
922
923static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
924{
925 (void) ino;
926
927 close(fi->fh);
928 fuse_reply_err(req, 0);
929}
930
931static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
932{
933 int res;
934 (void) ino;
935 res = close(dup(fi->fh));
936 fuse_reply_err(req, res == -1 ? errno : 0);
937}
938
939static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
940 struct fuse_file_info *fi)
941{
942 int res;
943 (void) ino;
944 if (datasync)
945 res = fdatasync(fi->fh);
946 else
947 res = fsync(fi->fh);
948 fuse_reply_err(req, res == -1 ? errno : 0);
949}
950
951static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
952 off_t offset, struct fuse_file_info *fi)
953{
954 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
955
956 if (lo_debug(req))
957 fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
958 "off=%lu)\n", ino, size, (unsigned long) offset);
959
961 buf.buf[0].fd = fi->fh;
962 buf.buf[0].pos = offset;
963
965}
966
967static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
968 struct fuse_bufvec *in_buf, off_t off,
969 struct fuse_file_info *fi)
970{
971 (void) ino;
972 ssize_t res;
973 struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
974
976 out_buf.buf[0].fd = fi->fh;
977 out_buf.buf[0].pos = off;
978
979 if (lo_debug(req))
980 fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%jd)\n",
981 ino, out_buf.buf[0].size, (intmax_t) off);
982
983 res = fuse_buf_copy(&out_buf, in_buf, 0);
984 if(res < 0)
985 fuse_reply_err(req, -res);
986 else
987 fuse_reply_write(req, (size_t) res);
988}
989
990static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
991{
992 int res;
993 struct statvfs stbuf;
994
995 res = fstatvfs(lo_fd(req, ino), &stbuf);
996 if (res == -1)
997 fuse_reply_err(req, errno);
998 else
999 fuse_reply_statfs(req, &stbuf);
1000}
1001
1002static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
1003 off_t offset, off_t length, struct fuse_file_info *fi)
1004{
1005 int err;
1006 (void) ino;
1007
1008 err = -do_fallocate(fi->fh, mode, offset, length);
1009
1010 fuse_reply_err(req, err);
1011}
1012
1013static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
1014 int op)
1015{
1016 int res;
1017 (void) ino;
1018
1019 res = flock(fi->fh, op);
1020
1021 fuse_reply_err(req, res == -1 ? errno : 0);
1022}
1023
1024static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1025 size_t size)
1026{
1027 char *value = NULL;
1028 char procname[64];
1029 struct lo_inode *inode = lo_inode(req, ino);
1030 ssize_t ret;
1031 int saverr;
1032
1033 saverr = ENOSYS;
1034 if (!lo_data(req)->xattr)
1035 goto out;
1036
1037 if (lo_debug(req)) {
1038 fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
1039 ino, name, size);
1040 }
1041
1042 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1043
1044 if (size) {
1045 value = malloc(size);
1046 if (!value)
1047 goto out_err;
1048
1049 ret = getxattr(procname, name, value, size);
1050 if (ret == -1)
1051 goto out_err;
1052 saverr = 0;
1053 if (ret == 0)
1054 goto out;
1055
1056 fuse_reply_buf(req, value, ret);
1057 } else {
1058 ret = getxattr(procname, name, NULL, 0);
1059 if (ret == -1)
1060 goto out_err;
1061
1062 fuse_reply_xattr(req, ret);
1063 }
1064out_free:
1065 free(value);
1066 return;
1067
1068out_err:
1069 saverr = errno;
1070out:
1071 fuse_reply_err(req, saverr);
1072 goto out_free;
1073}
1074
1075static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
1076{
1077 char *value = NULL;
1078 char procname[64];
1079 struct lo_inode *inode = lo_inode(req, ino);
1080 ssize_t ret;
1081 int saverr;
1082
1083 saverr = ENOSYS;
1084 if (!lo_data(req)->xattr)
1085 goto out;
1086
1087 if (lo_debug(req)) {
1088 fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
1089 ino, size);
1090 }
1091
1092 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1093
1094 if (size) {
1095 value = malloc(size);
1096 if (!value)
1097 goto out_err;
1098
1099 ret = listxattr(procname, value, size);
1100 if (ret == -1)
1101 goto out_err;
1102 saverr = 0;
1103 if (ret == 0)
1104 goto out;
1105
1106 fuse_reply_buf(req, value, ret);
1107 } else {
1108 ret = listxattr(procname, NULL, 0);
1109 if (ret == -1)
1110 goto out_err;
1111
1112 fuse_reply_xattr(req, ret);
1113 }
1114out_free:
1115 free(value);
1116 return;
1117
1118out_err:
1119 saverr = errno;
1120out:
1121 fuse_reply_err(req, saverr);
1122 goto out_free;
1123}
1124
1125static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1126 const char *value, size_t size, int flags)
1127{
1128 char procname[64];
1129 struct lo_inode *inode = lo_inode(req, ino);
1130 ssize_t ret;
1131 int saverr;
1132
1133 saverr = ENOSYS;
1134 if (!lo_data(req)->xattr)
1135 goto out;
1136
1137 if (lo_debug(req)) {
1138 fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
1139 ino, name, value, size);
1140 }
1141
1142 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1143
1144 ret = setxattr(procname, name, value, size, flags);
1145 saverr = ret == -1 ? errno : 0;
1146
1147out:
1148 fuse_reply_err(req, saverr);
1149}
1150
1151static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
1152{
1153 char procname[64];
1154 struct lo_inode *inode = lo_inode(req, ino);
1155 ssize_t ret;
1156 int saverr;
1157
1158 saverr = ENOSYS;
1159 if (!lo_data(req)->xattr)
1160 goto out;
1161
1162 if (lo_debug(req)) {
1163 fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
1164 ino, name);
1165 }
1166
1167 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1168
1169 ret = removexattr(procname, name);
1170 saverr = ret == -1 ? errno : 0;
1171
1172out:
1173 fuse_reply_err(req, saverr);
1174}
1175
1176#ifdef HAVE_COPY_FILE_RANGE
1177static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
1178 struct fuse_file_info *fi_in,
1179 fuse_ino_t ino_out, off_t off_out,
1180 struct fuse_file_info *fi_out, size_t len,
1181 int flags)
1182{
1183 ssize_t res;
1184
1185 if (lo_debug(req))
1186 fuse_log(FUSE_LOG_DEBUG,
1187 "%s(ino=%lld fd=%lld off=%jd ino=%lld fd=%lld off=%jd, size=%zd, flags=0x%x)\n",
1188 __func__, (unsigned long long)ino_in,
1189 (unsigned long long)fi_in->fh,
1190 (intmax_t) off_in, (unsigned long long)ino_out,
1191 (unsigned long long)fi_out->fh, (intmax_t) off_out,
1192 len, flags);
1193
1194 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
1195 flags);
1196 if (res < 0)
1197 fuse_reply_err(req, errno);
1198 else
1199 fuse_reply_write(req, res);
1200}
1201#endif
1202
1203static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1204 struct fuse_file_info *fi)
1205{
1206 off_t res;
1207
1208 (void)ino;
1209 res = lseek(fi->fh, off, whence);
1210 if (res != -1)
1211 fuse_reply_lseek(req, res);
1212 else
1213 fuse_reply_err(req, errno);
1214}
1215
1216#ifdef HAVE_STATX
1217static void lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
1218 struct fuse_file_info *fi)
1219{
1220 struct lo_data *lo = lo_data(req);
1221 struct statx buf;
1222 int res;
1223 int fd;
1224
1225 if (fi)
1226 fd = fi->fh;
1227 else
1228 fd = lo_fd(req, ino);
1229
1230 res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, &buf);
1231 if (res == -1)
1232 fuse_reply_err(req, errno);
1233 else
1234 fuse_reply_statx(req, 0, &buf, lo->timeout);
1235}
1236#endif
1237
1238static const struct fuse_lowlevel_ops lo_oper = {
1239 .init = lo_init,
1240 .destroy = lo_destroy,
1241 .lookup = lo_lookup,
1242 .mkdir = lo_mkdir,
1243 .mknod = lo_mknod,
1244 .symlink = lo_symlink,
1245 .link = lo_link,
1246 .unlink = lo_unlink,
1247 .rmdir = lo_rmdir,
1248 .rename = lo_rename,
1249 .forget = lo_forget,
1250 .forget_multi = lo_forget_multi,
1251 .getattr = lo_getattr,
1252 .setattr = lo_setattr,
1253 .readlink = lo_readlink,
1254 .opendir = lo_opendir,
1255 .readdir = lo_readdir,
1256 .readdirplus = lo_readdirplus,
1257 .releasedir = lo_releasedir,
1258 .fsyncdir = lo_fsyncdir,
1259 .create = lo_create,
1260 .tmpfile = lo_tmpfile,
1261 .open = lo_open,
1262 .release = lo_release,
1263 .flush = lo_flush,
1264 .fsync = lo_fsync,
1265 .read = lo_read,
1266 .write_buf = lo_write_buf,
1267 .statfs = lo_statfs,
1268 .fallocate = lo_fallocate,
1269 .flock = lo_flock,
1270 .getxattr = lo_getxattr,
1271 .listxattr = lo_listxattr,
1272 .setxattr = lo_setxattr,
1273 .removexattr = lo_removexattr,
1274#ifdef HAVE_COPY_FILE_RANGE
1275 .copy_file_range = lo_copy_file_range,
1276#endif
1277 .lseek = lo_lseek,
1278#ifdef HAVE_STATX
1279 .statx = lo_statx,
1280#endif
1281};
1282
1283int main(int argc, char *argv[])
1284{
1285 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1286 struct fuse_session *se;
1287 struct fuse_cmdline_opts opts;
1288 struct fuse_loop_config *config;
1289 struct lo_data lo = { .debug = 0,
1290 .writeback = 0 };
1291 int ret = -1;
1292
1293 /* Don't mask creation mode, kernel already did that */
1294 umask(0);
1295
1296 pthread_mutex_init(&lo.mutex, NULL);
1297 lo.root.next = lo.root.prev = &lo.root;
1298 lo.root.fd = -1;
1299 lo.cache = CACHE_NORMAL;
1300
1301 if (fuse_parse_cmdline(&args, &opts) != 0)
1302 return 1;
1303 if (opts.show_help) {
1304 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
1307 passthrough_ll_help();
1308 ret = 0;
1309 goto err_out1;
1310 } else if (opts.show_version) {
1311 printf("FUSE library version %s\n", fuse_pkgversion());
1313 ret = 0;
1314 goto err_out1;
1315 }
1316
1317 if(opts.mountpoint == NULL) {
1318 printf("usage: %s [options] <mountpoint>\n", argv[0]);
1319 printf(" %s --help\n", argv[0]);
1320 ret = 1;
1321 goto err_out1;
1322 }
1323
1324 if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
1325 return 1;
1326
1327 lo.debug = opts.debug;
1328 lo.root.refcount = 2;
1329 if (lo.source) {
1330 struct stat stat;
1331 int res;
1332
1333 res = lstat(lo.source, &stat);
1334 if (res == -1) {
1335 fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
1336 lo.source);
1337 exit(1);
1338 }
1339 if (!S_ISDIR(stat.st_mode)) {
1340 fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
1341 exit(1);
1342 }
1343
1344 } else {
1345 lo.source = strdup("/");
1346 if(!lo.source) {
1347 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
1348 exit(1);
1349 }
1350 }
1351 if (!lo.timeout_set) {
1352 switch (lo.cache) {
1353 case CACHE_NEVER:
1354 lo.timeout = 0.0;
1355 break;
1356
1357 case CACHE_NORMAL:
1358 lo.timeout = 1.0;
1359 break;
1360
1361 case CACHE_ALWAYS:
1362 lo.timeout = 86400.0;
1363 break;
1364 }
1365 } else if (lo.timeout < 0) {
1366 fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
1367 lo.timeout);
1368 exit(1);
1369 }
1370
1371 lo.root.fd = open(lo.source, O_PATH);
1372 if (lo.root.fd == -1) {
1373 fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
1374 lo.source);
1375 exit(1);
1376 }
1377
1378 se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
1379 if (se == NULL)
1380 goto err_out1;
1381
1382 if (fuse_set_signal_handlers(se) != 0)
1383 goto err_out2;
1384
1385 if (fuse_session_mount(se, opts.mountpoint) != 0)
1386 goto err_out3;
1387
1388 fuse_daemonize(opts.foreground);
1389
1390 /* Block until ctrl+c or fusermount -u */
1391 if (opts.singlethread)
1392 ret = fuse_session_loop(se);
1393 else {
1394 config = fuse_loop_cfg_create();
1395 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
1396 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
1397 ret = fuse_session_loop_mt(se, config);
1398 fuse_loop_cfg_destroy(config);
1399 config = NULL;
1400 }
1401
1403err_out3:
1405err_out2:
1407err_out1:
1408 free(opts.mountpoint);
1409 fuse_opt_free_args(&args);
1410
1411 if (lo.root.fd >= 0)
1412 close(lo.root.fd);
1413
1414 free(lo.source);
1415 return ret ? 1 : 0;
1416}
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
off_t pos
size_t size
struct fuse_buf buf[1]
uint32_t no_interrupt
uint32_t capable
double entry_timeout
fuse_ino_t ino
double attr_timeout
struct stat attr
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2poll_8c_source.html0000644000175000017500000014343615156613427023033 0ustar berndbernd libfuse: fuse-3.18.2/example/poll.c Source File
libfuse
poll.c
Go to the documentation of this file.
1/*
2 FUSE fsel: FUSE select example
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
24#define FUSE_USE_VERSION 31
25
26#include <fuse.h>
27#include <unistd.h>
28#include <ctype.h>
29#include <string.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <time.h>
34#include <pthread.h>
35#include <poll.h>
36#include <stdbool.h>
37
38/*
39 * fsel_open_mask is used to limit the number of opens to 1 per file.
40 * This is to use file index (0-F) as fh as poll support requires
41 * unique fh per open file. Lifting this would require proper open
42 * file management.
43 */
44static unsigned fsel_open_mask;
45static const char fsel_hex_map[] = "0123456789ABCDEF";
46static struct fuse *fsel_fuse; /* needed for poll notification */
47
48#define FSEL_CNT_MAX 10 /* each file can store up to 10 chars */
49#define FSEL_FILES 16
50
51static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
52static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
53static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
54static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
55static _Atomic bool fsel_stop = false;
56static pthread_t fsel_producer_thread;
57
58
59static int fsel_path_index(const char *path)
60{
61 char ch = path[1];
62
63 if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
64 return -1;
65 return ch <= '9' ? ch - '0' : ch - 'A' + 10;
66}
67
68static void fsel_destroy(void *private_data)
69{
70 (void)private_data;
71
72 fsel_stop = true;
73
74 pthread_join(fsel_producer_thread, NULL);
75}
76
77static int fsel_getattr(const char *path, struct stat *stbuf,
78 struct fuse_file_info *fi)
79{
80 (void) fi;
81 int idx;
82
83 memset(stbuf, 0, sizeof(struct stat));
84
85 if (strcmp(path, "/") == 0) {
86 stbuf->st_mode = S_IFDIR | 0555;
87 stbuf->st_nlink = 2;
88 return 0;
89 }
90
91 idx = fsel_path_index(path);
92 if (idx < 0)
93 return -ENOENT;
94
95 stbuf->st_mode = S_IFREG | 0444;
96 stbuf->st_nlink = 1;
97 stbuf->st_size = fsel_cnt[idx];
98 return 0;
99}
100
101static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
102 off_t offset, struct fuse_file_info *fi,
103 enum fuse_readdir_flags flags)
104{
105 char name[2] = { };
106 int i;
107
108 (void) offset;
109 (void) fi;
110 (void) flags;
111
112 if (strcmp(path, "/") != 0)
113 return -ENOENT;
114
115 for (i = 0; i < FSEL_FILES; i++) {
116 name[0] = fsel_hex_map[i];
117 filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
118 }
119
120 return 0;
121}
122
123static int fsel_open(const char *path, struct fuse_file_info *fi)
124{
125 int idx = fsel_path_index(path);
126
127 if (idx < 0)
128 return -ENOENT;
129 if ((fi->flags & O_ACCMODE) != O_RDONLY)
130 return -EACCES;
131 if (fsel_open_mask & (1 << idx))
132 return -EBUSY;
133 fsel_open_mask |= (1 << idx);
134
135 /*
136 * fsel files are nonseekable somewhat pipe-like files which
137 * gets filled up periodically by producer thread and consumed
138 * on read. Tell FUSE as such.
139 */
140 fi->fh = idx;
141 fi->direct_io = 1;
142 fi->nonseekable = 1;
143
144 return 0;
145}
146
147static int fsel_release(const char *path, struct fuse_file_info *fi)
148{
149 int idx = fi->fh;
150
151 (void) path;
152
153 fsel_open_mask &= ~(1 << idx);
154 return 0;
155}
156
157static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
158 struct fuse_file_info *fi)
159{
160 int idx = fi->fh;
161
162 (void) path;
163 (void) offset;
164
165 pthread_mutex_lock(&fsel_mutex);
166 if (fsel_cnt[idx] < size)
167 size = fsel_cnt[idx];
168 printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
169 fsel_cnt[idx] -= size;
170 pthread_mutex_unlock(&fsel_mutex);
171
172 memset(buf, fsel_hex_map[idx], size);
173 return size;
174}
175
176static int fsel_poll(const char *path, struct fuse_file_info *fi,
177 struct fuse_pollhandle *ph, unsigned *reventsp)
178{
179 static unsigned polled_zero;
180 int idx = fi->fh;
181
182 (void) path;
183
184 /*
185 * Poll notification requires pointer to struct fuse which
186 * can't be obtained when using fuse_main(). As notification
187 * happens only after poll is called, fill it here from
188 * fuse_context.
189 */
190 if (!fsel_fuse) {
191 struct fuse_context *cxt = fuse_get_context();
192 if (cxt)
193 fsel_fuse = cxt->fuse;
194 }
195
196 pthread_mutex_lock(&fsel_mutex);
197
198 if (ph != NULL) {
199 struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
200
201 if (oldph)
203
204 fsel_poll_notify_mask |= (1 << idx);
205 fsel_poll_handle[idx] = ph;
206 }
207
208 if (fsel_cnt[idx]) {
209 *reventsp |= POLLIN;
210 printf("POLL %X cnt=%u polled_zero=%u\n",
211 idx, fsel_cnt[idx], polled_zero);
212 polled_zero = 0;
213 } else
214 polled_zero++;
215
216 pthread_mutex_unlock(&fsel_mutex);
217 return 0;
218}
219
220static const struct fuse_operations fsel_oper = {
221 .destroy = fsel_destroy,
222 .getattr = fsel_getattr,
223 .readdir = fsel_readdir,
224 .open = fsel_open,
225 .release = fsel_release,
226 .read = fsel_read,
227 .poll = fsel_poll,
228};
229
230static void *fsel_producer(void *data)
231{
232 const struct timespec interval = { 0, 250000000 };
233 unsigned idx = 0, nr = 1;
234
235 (void) data;
236
237 while (!fsel_stop) {
238 int i, t;
239
240 pthread_mutex_lock(&fsel_mutex);
241
242 /*
243 * This is the main producer loop which is executed
244 * ever 500ms. On each iteration, it fills one byte
245 * to 1, 2 or 4 files and sends poll notification if
246 * requested.
247 */
248 for (i = 0, t = idx; i < nr;
249 i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
250 if (fsel_cnt[t] == FSEL_CNT_MAX)
251 continue;
252
253 fsel_cnt[t]++;
254 if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
255 struct fuse_pollhandle *ph;
256
257 printf("NOTIFY %X\n", t);
258 ph = fsel_poll_handle[t];
259 fuse_notify_poll(ph);
261 fsel_poll_notify_mask &= ~(1 << t);
262 fsel_poll_handle[t] = NULL;
263 }
264 }
265
266 idx = (idx + 1) % FSEL_FILES;
267 if (idx == 0)
268 nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
269
270 pthread_mutex_unlock(&fsel_mutex);
271
272 nanosleep(&interval, NULL);
273 }
274
275 return NULL;
276}
277
278int main(int argc, char *argv[])
279{
280 pthread_attr_t attr;
281 int ret;
282
283 errno = pthread_mutex_init(&fsel_mutex, NULL);
284 if (errno) {
285 perror("pthread_mutex_init");
286 return 1;
287 }
288
289 errno = pthread_attr_init(&attr);
290 if (errno) {
291 perror("pthread_attr_init");
292 return 1;
293 }
294
295 errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL);
296 if (errno) {
297 perror("pthread_create");
298 return 1;
299 }
300
301 ret = fuse_main(argc, argv, &fsel_oper, NULL);
302
303 return ret;
304}
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
struct fuse * fuse
Definition fuse.h:862
uint32_t nonseekable
Definition fuse_common.h:78
uint32_t direct_io
Definition fuse_common.h:63
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2poll__client_8c_source.html0000644000175000017500000003104015156613427024513 0ustar berndbernd libfuse: fuse-3.18.2/example/poll_client.c Source File
libfuse
poll_client.c
Go to the documentation of this file.
1/*
2 FUSE fselclient: FUSE select example client
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU GPLv2.
7 See the file GPL2.txt.
8*/
9
23#include <sys/select.h>
24#include <sys/time.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include <ctype.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33
34#define FSEL_FILES 16
35
36int main(void)
37{
38 static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
39 int fds[FSEL_FILES];
40 int i, nfds, tries;
41
42 for (i = 0; i < FSEL_FILES; i++) {
43 char name[] = { hex_map[i], '\0' };
44 fds[i] = open(name, O_RDONLY);
45 if (fds[i] < 0) {
46 perror("open");
47 return 1;
48 }
49 }
50 nfds = fds[FSEL_FILES - 1] + 1;
51
52 for(tries=0; tries < 16; tries++) {
53 static char buf[4096];
54 fd_set rfds;
55 int rc;
56
57 FD_ZERO(&rfds);
58 for (i = 0; i < FSEL_FILES; i++)
59 FD_SET(fds[i], &rfds);
60
61 rc = select(nfds, &rfds, NULL, NULL, NULL);
62
63 if (rc < 0) {
64 perror("select");
65 return 1;
66 }
67
68 for (i = 0; i < FSEL_FILES; i++) {
69 if (!FD_ISSET(fds[i], &rfds)) {
70 printf("_: ");
71 continue;
72 }
73 printf("%X:", i);
74 rc = read(fds[i], buf, sizeof(buf));
75 if (rc < 0) {
76 perror("read");
77 return 1;
78 }
79 printf("%02d ", rc);
80 }
81 printf("\n");
82 }
83 return 0;
84}
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2printcap_8c_source.html0000644000175000017500000013345715156613427023707 0ustar berndbernd libfuse: fuse-3.18.2/example/printcap.c Source File
libfuse
printcap.c
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2017 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
22#define FUSE_USE_VERSION 31
23
24#include <fuse_lowlevel.h>
25#include <stdio.h>
26#include <unistd.h>
27#include <string.h>
28#include <stdlib.h>
29
30struct fuse_session *se;
31
32// Define a structure to hold capability information
33struct cap_info {
34 uint64_t flag;
35 const char *name;
36};
37
38// Define an array of all capabilities
39static const struct cap_info capabilities[] = {
40 {FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"},
41 {FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"},
42 {FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"},
43 {FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"},
44 {FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"},
45 {FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"},
46 {FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"},
47 {FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"},
48 {FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"},
49 {FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"},
50 {FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"},
51 {FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"},
52 {FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"},
53 {FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"},
54 {FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"},
55 {FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"},
56 {FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"},
57 {FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"},
58 {FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"},
59 {FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"},
60 {FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"},
61 {FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"},
62 {FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"},
63 {FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"},
64 {FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"},
65 {FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"},
66 {FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"},
67 {FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"},
68 // Add any new capabilities here
69 {0, NULL} // Sentinel to mark the end of the array
70};
71
72static void print_capabilities(struct fuse_conn_info *conn)
73{
74 printf("Capabilities:\n");
75 for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) {
76 if (fuse_get_feature_flag(conn, cap->flag)) {
77 printf("\t%s\n", cap->name);
78 }
79 }
80}
81
82static void pc_init(void *userdata, struct fuse_conn_info *conn)
83{
84 (void) userdata;
85
86 printf("Protocol version: %d.%d\n", conn->proto_major,
87 conn->proto_minor);
88 print_capabilities(conn);
90}
91
92
93static const struct fuse_lowlevel_ops pc_oper = {
94 .init = pc_init,
95};
96
97int main(int argc, char **argv)
98{
99 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
100 char *mountpoint;
101 int ret = -1;
102
103 mountpoint = strdup("/tmp/fuse_printcap_XXXXXX");
104 if(mkdtemp(mountpoint) == NULL) {
105 perror("mkdtemp");
106 return 1;
107 }
108
109 printf("FUSE library version %s\n", fuse_pkgversion());
111
112 se = fuse_session_new(&args, &pc_oper,
113 sizeof(pc_oper), NULL);
114 if (se == NULL)
115 goto err_out1;
116
117 if (fuse_set_signal_handlers(se) != 0)
118 goto err_out2;
119
120 if (fuse_session_mount(se, mountpoint) != 0)
121 goto err_out3;
122
123 ret = fuse_session_loop(se);
124
126err_out3:
128err_out2:
130err_out1:
131 rmdir(mountpoint);
132 free(mountpoint);
133 fuse_opt_free_args(&args);
134
135 return ret ? 1 : 0;
136}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t proto_major
uint32_t proto_minor
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_82_2include_2cuse__lowlevel_8h_source.html0000644000175000017500000004601115156613427025060 0ustar berndbernd libfuse: fuse-3.18.2/include/cuse_lowlevel.h Source File
libfuse
cuse_lowlevel.h
1/*
2 CUSE: Character device in Userspace
3 Copyright (C) 2008-2009 SUSE Linux Products GmbH
4 Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
5
6 This program can be distributed under the terms of the GNU LGPLv2.
7 See the file LGPL2.txt.
8
9 Read example/cusexmp.c for usages.
10*/
11
12#ifndef CUSE_LOWLEVEL_H_
13#define CUSE_LOWLEVEL_H_
14
15#ifndef FUSE_USE_VERSION
16#define FUSE_USE_VERSION 29
17#endif
18
19#include "fuse_lowlevel.h"
20
21#include <fcntl.h>
22#include <sys/types.h>
23#include <sys/uio.h>
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */
30
31struct fuse_session;
32
33struct cuse_info {
34 unsigned dev_major;
35 unsigned dev_minor;
36 unsigned dev_info_argc;
37 const char **dev_info_argv;
38 unsigned flags;
39};
40
41/*
42 * Most ops behave almost identically to the matching fuse_lowlevel
43 * ops except that they don't take @ino.
44 *
45 * init_done : called after initialization is complete
46 * read/write : always direct IO, simultaneous operations allowed
47 * ioctl : might be in unrestricted mode depending on ci->flags
48 */
49struct cuse_lowlevel_ops {
50 void (*init) (void *userdata, struct fuse_conn_info *conn);
51 void (*init_done) (void *userdata);
52 void (*destroy) (void *userdata);
53 void (*open) (fuse_req_t req, struct fuse_file_info *fi);
54 void (*read) (fuse_req_t req, size_t size, off_t off,
55 struct fuse_file_info *fi);
56 void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off,
57 struct fuse_file_info *fi);
58 void (*flush) (fuse_req_t req, struct fuse_file_info *fi);
59 void (*release) (fuse_req_t req, struct fuse_file_info *fi);
60 void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi);
61 void (*ioctl) (fuse_req_t req, int cmd, void *arg,
62 struct fuse_file_info *fi, unsigned int flags,
63 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
64 void (*poll) (fuse_req_t req, struct fuse_file_info *fi,
65 struct fuse_pollhandle *ph);
66};
67
68struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
69 const struct cuse_info *ci,
70 const struct cuse_lowlevel_ops *clop,
71 void *userdata);
72
73struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
74 const struct cuse_info *ci,
75 const struct cuse_lowlevel_ops *clop,
76 int *multithreaded, void *userdata);
77
78void cuse_lowlevel_teardown(struct fuse_session *se);
79
80int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
81 const struct cuse_lowlevel_ops *clop, void *userdata);
82
83#ifdef __cplusplus
84}
85#endif
86
87#endif /* CUSE_LOWLEVEL_H_ */
struct fuse_req * fuse_req_t
fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse_8h_source.html0000644000175000017500000042735115156613427023025 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse.h Source File
libfuse
fuse.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_H_
10#define FUSE_H_
11
19#include "fuse_common.h"
20
21#include <fcntl.h>
22#include <time.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <sys/statvfs.h>
26#include <sys/uio.h>
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32/* ----------------------------------------------------------- *
33 * Basic FUSE API *
34 * ----------------------------------------------------------- */
35
36/* Forward declaration */
37struct statx;
38
40struct fuse;
41
55 FUSE_READDIR_PLUS = (1 << 0)
56};
57
75 FUSE_FILL_DIR_PLUS = (1 << 1)
76};
77
93typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
94 const struct stat *stbuf, off_t off,
95 enum fuse_fill_dir_flags flags);
107struct fuse_config {
112 int32_t set_gid;
113 uint32_t gid;
114
119 int32_t set_uid;
120 uint32_t uid;
121
126 int32_t set_mode;
127 uint32_t umask;
128
133 double entry_timeout;
134
143 double negative_timeout;
144
149 double attr_timeout;
150
154 int32_t intr;
155
161 int32_t intr_signal;
162
173 int32_t remember;
174
191 int32_t hard_remove;
192
204 int32_t use_ino;
205
213 int32_t readdir_ino;
214
232 int32_t direct_io;
233
251 int32_t kernel_cache;
252
259 int32_t auto_cache;
260
261 /*
262 * The timeout in seconds for which file attributes are cached
263 * for the purpose of checking if auto_cache should flush the
264 * file data on open.
265 */
266 int32_t ac_attr_timeout_set;
267 double ac_attr_timeout;
268
279 int32_t nullpath_ok;
280
285 int32_t show_help;
286 char *modules;
287 int32_t debug;
288
294 uint32_t fmask;
295 uint32_t dmask;
296
303 int32_t no_rofd_flush;
304
319
320
324 uint32_t flags;
325
329 uint64_t reserved[48];
330};
331
332
355struct fuse_operations {
367 int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi);
368
377 int (*readlink) (const char *, char *, size_t);
378
385 int (*mknod) (const char *, mode_t, dev_t);
386
393 int (*mkdir) (const char *, mode_t);
394
396 int (*unlink) (const char *);
397
399 int (*rmdir) (const char *);
400
402 int (*symlink) (const char *, const char *);
403
413 int (*rename) (const char *, const char *, unsigned int flags);
414
416 int (*link) (const char *, const char *);
417
423 int (*chmod) (const char *, mode_t, struct fuse_file_info *fi);
424
433 int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi);
434
443 int (*truncate) (const char *, off_t, struct fuse_file_info *fi);
444
492 int (*open) (const char *, struct fuse_file_info *);
493
503 int (*read) (const char *, char *, size_t, off_t,
504 struct fuse_file_info *);
505
515 int (*write) (const char *, const char *, size_t, off_t,
516 struct fuse_file_info *);
517
522 int (*statfs) (const char *, struct statvfs *);
523
552 int (*flush) (const char *, struct fuse_file_info *);
553
566 int (*release) (const char *, struct fuse_file_info *);
567
573 int (*fsync) (const char *, int, struct fuse_file_info *);
574
576 int (*setxattr) (const char *, const char *, const char *, size_t, int);
577
579 int (*getxattr) (const char *, const char *, char *, size_t);
580
582 int (*listxattr) (const char *, char *, size_t);
583
585 int (*removexattr) (const char *, const char *);
586
595 int (*opendir) (const char *, struct fuse_file_info *);
596
619 int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
620 struct fuse_file_info *, enum fuse_readdir_flags);
621
627 int (*releasedir) (const char *, struct fuse_file_info *);
628
637 int (*fsyncdir) (const char *, int, struct fuse_file_info *);
638
647 void *(*init) (struct fuse_conn_info *conn,
648 struct fuse_config *cfg);
649
655 void (*destroy) (void *private_data);
656
666 int (*access) (const char *, int);
667
678 int (*create) (const char *, mode_t, struct fuse_file_info *);
679
710 int (*lock) (const char *, struct fuse_file_info *, int cmd,
711 struct flock *);
712
725 int (*utimens) (const char *, const struct timespec tv[2],
726 struct fuse_file_info *fi);
727
734 int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
735
736#if FUSE_USE_VERSION < 35
737 int (*ioctl) (const char *, int cmd, void *arg,
738 struct fuse_file_info *, unsigned int flags, void *data);
739#else
756 int (*ioctl) (const char *, unsigned int cmd, void *arg,
757 struct fuse_file_info *, unsigned int flags, void *data);
758#endif
759
775 int (*poll) (const char *, struct fuse_file_info *,
776 struct fuse_pollhandle *ph, unsigned *reventsp);
777
787 int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off,
788 struct fuse_file_info *);
789
804 int (*read_buf) (const char *, struct fuse_bufvec **bufp,
805 size_t size, off_t off, struct fuse_file_info *);
824 int (*flock) (const char *, struct fuse_file_info *, int op);
825
834 int (*fallocate) (const char *, int, off_t, off_t,
835 struct fuse_file_info *);
836
849 ssize_t (*copy_file_range) (const char *path_in,
850 struct fuse_file_info *fi_in,
851 off_t offset_in, const char *path_out,
852 struct fuse_file_info *fi_out,
853 off_t offset_out, size_t size, int flags);
854
858 off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
859
868 int (*statx)(const char *path, int flags, int mask, struct statx *stxbuf,
869 struct fuse_file_info *fi);
870};
871
877struct fuse_context {
879 struct fuse *fuse;
880
882 uid_t uid;
883
885 gid_t gid;
886
888 pid_t pid;
889
891 void *private_data;
892
894 mode_t umask;
895};
896
902int fuse_main_real_versioned(int argc, char *argv[],
903 const struct fuse_operations *op, size_t op_size,
904 struct libfuse_version *version, void *user_data);
905static inline int fuse_main_real(int argc, char *argv[],
906 const struct fuse_operations *op,
907 size_t op_size, void *user_data)
908{
909 struct libfuse_version version = { .major = FUSE_MAJOR_VERSION,
910 .minor = FUSE_MINOR_VERSION,
911 .hotfix = FUSE_HOTFIX_VERSION,
912 .padding = 0 };
913
914 fuse_log(FUSE_LOG_ERR,
915 "%s is a libfuse internal function, please use fuse_main()\n",
916 __func__);
917
918 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
919 user_data);
920}
921
976static inline int fuse_main_fn(int argc, char *argv[],
977 const struct fuse_operations *op,
978 void *user_data)
979{
980 struct libfuse_version version = {
981 .major = FUSE_MAJOR_VERSION,
982 .minor = FUSE_MINOR_VERSION,
983 .hotfix = FUSE_HOTFIX_VERSION,
984 .padding = 0
985 };
986
987 return fuse_main_real_versioned(argc, argv, op, sizeof(*(op)), &version,
988 user_data);
989}
990#define fuse_main(argc, argv, op, user_data) \
991 fuse_main_fn(argc, argv, op, user_data)
992
993/* ----------------------------------------------------------- *
994 * More detailed API *
995 * ----------------------------------------------------------- */
996
1008void fuse_lib_help(struct fuse_args *args);
1009
1010/* Do not call this directly, use fuse_new() instead */
1011struct fuse *_fuse_new_30(struct fuse_args *args,
1012 const struct fuse_operations *op, size_t op_size,
1013 struct libfuse_version *version, void *user_data);
1014struct fuse *_fuse_new_31(struct fuse_args *args,
1015 const struct fuse_operations *op, size_t op_size,
1016 struct libfuse_version *version, void *user_data);
1017
1045#if FUSE_USE_VERSION == 30
1046static inline struct fuse *fuse_new_fn(struct fuse_args *args,
1047 const struct fuse_operations *op,
1048 size_t op_size, void *user_data)
1049{
1050 struct libfuse_version version = {
1051 .major = FUSE_MAJOR_VERSION,
1052 .minor = FUSE_MINOR_VERSION,
1053 .hotfix = FUSE_HOTFIX_VERSION,
1054 .padding = 0
1055 };
1056
1057 return _fuse_new_30(args, op, op_size, &version, user_data);
1058}
1059#else /* FUSE_USE_VERSION */
1060static inline struct fuse *fuse_new_fn(struct fuse_args *args,
1061 const struct fuse_operations *op,
1062 size_t op_size, void *user_data)
1063{
1064 struct libfuse_version version = {
1065 .major = FUSE_MAJOR_VERSION,
1066 .minor = FUSE_MINOR_VERSION,
1067 .hotfix = FUSE_HOTFIX_VERSION,
1068 .padding = 0
1069 };
1070
1071 return _fuse_new_31(args, op, op_size, &version, user_data);
1072}
1073#endif
1074#define fuse_new(args, op, size, data) fuse_new_fn(args, op, size, data)
1075
1084int fuse_mount(struct fuse *f, const char *mountpoint);
1085
1093void fuse_unmount(struct fuse *f);
1094
1103void fuse_destroy(struct fuse *f);
1104
1120int fuse_loop(struct fuse *f);
1121
1130void fuse_exit(struct fuse *f);
1131
1132#if FUSE_USE_VERSION < 32
1133int fuse_loop_mt_31(struct fuse *f, int clone_fd);
1134#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd)
1135#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
1136int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
1137#define fuse_loop_mt(f, config) fuse_loop_mt_32(f, config)
1138#else
1170#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
1171int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config);
1172#else
1173#define fuse_loop_mt(f, config) fuse_loop_mt_312(f, config)
1174#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
1175#endif
1176
1177
1186struct fuse_context *fuse_get_context(void);
1187
1206int fuse_getgroups(int size, gid_t list[]);
1207
1213int fuse_interrupted(void);
1214
1226int fuse_invalidate_path(struct fuse *f, const char *path);
1227
1236
1243void fuse_stop_cleanup_thread(struct fuse *fuse);
1244
1254int fuse_clean_cache(struct fuse *fuse);
1255
1256/*
1257 * Stacking API
1258 */
1259
1265struct fuse_fs;
1266
1267/*
1268 * These functions call the relevant filesystem operation, and return
1269 * the result.
1270 *
1271 * If the operation is not defined, they return -ENOSYS, with the
1272 * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
1273 * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
1274 */
1275
1276int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
1277 struct fuse_file_info *fi);
1278int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
1279 const char *newpath, unsigned int flags);
1280int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
1281int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
1282int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
1283 const char *path);
1284int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
1285int fuse_fs_release(struct fuse_fs *fs, const char *path,
1286 struct fuse_file_info *fi);
1287int fuse_fs_open(struct fuse_fs *fs, const char *path,
1288 struct fuse_file_info *fi);
1289int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
1290 off_t off, struct fuse_file_info *fi);
1291int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
1292 struct fuse_bufvec **bufp, size_t size, off_t off,
1293 struct fuse_file_info *fi);
1294int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
1295 size_t size, off_t off, struct fuse_file_info *fi);
1296int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
1297 struct fuse_bufvec *buf, off_t off,
1298 struct fuse_file_info *fi);
1299int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
1300 struct fuse_file_info *fi);
1301int fuse_fs_flush(struct fuse_fs *fs, const char *path,
1302 struct fuse_file_info *fi);
1303int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
1304int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
1305 struct fuse_file_info *fi);
1306int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
1307 fuse_fill_dir_t filler, off_t off,
1308 struct fuse_file_info *fi, enum fuse_readdir_flags flags);
1309int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
1310 struct fuse_file_info *fi);
1311int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
1312 struct fuse_file_info *fi);
1313int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
1314 struct fuse_file_info *fi);
1315int fuse_fs_lock(struct fuse_fs *fs, const char *path,
1316 struct fuse_file_info *fi, int cmd, struct flock *lock);
1317int fuse_fs_flock(struct fuse_fs *fs, const char *path,
1318 struct fuse_file_info *fi, int op);
1319int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
1320 struct fuse_file_info *fi);
1321int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid,
1322 struct fuse_file_info *fi);
1323int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
1324 struct fuse_file_info *fi);
1325int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
1326 const struct timespec tv[2], struct fuse_file_info *fi);
1327int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
1328int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
1329 size_t len);
1330int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
1331 dev_t rdev);
1332int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
1333int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
1334 const char *value, size_t size, int flags);
1335int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
1336 char *value, size_t size);
1337int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
1338 size_t size);
1339int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
1340 const char *name);
1341int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
1342 uint64_t *idx);
1343#if FUSE_USE_VERSION < 35
1344int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd,
1345 void *arg, struct fuse_file_info *fi, unsigned int flags,
1346 void *data);
1347#else
1348int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
1349 void *arg, struct fuse_file_info *fi, unsigned int flags,
1350 void *data);
1351#endif
1352int fuse_fs_poll(struct fuse_fs *fs, const char *path,
1353 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
1354 unsigned *reventsp);
1355int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
1356 off_t offset, off_t length, struct fuse_file_info *fi);
1357ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
1358 struct fuse_file_info *fi_in, off_t off_in,
1359 const char *path_out,
1360 struct fuse_file_info *fi_out, off_t off_out,
1361 size_t len, int flags);
1362off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
1363 struct fuse_file_info *fi);
1364int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
1365 struct statx *stxbuf, struct fuse_file_info *fi);
1366void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
1367 struct fuse_config *cfg);
1368void fuse_fs_destroy(struct fuse_fs *fs);
1369
1370int fuse_notify_poll(struct fuse_pollhandle *ph);
1371
1385struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
1386 void *private_data);
1387
1402typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
1403 struct fuse_fs *fs[]);
1414#define FUSE_REGISTER_MODULE(name_, factory_) \
1415 fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
1416
1418struct fuse_session *fuse_get_session(struct fuse *f);
1419
1428int fuse_open_channel(const char *mountpoint, const char *options);
1429
1430#ifdef __cplusplus
1431}
1432#endif
1433
1434#endif /* FUSE_H_ */
int fuse_getgroups(int size, gid_t list[])
Definition fuse.c:4654
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
int fuse_interrupted(void)
Definition fuse.c:4663
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
Definition helper.c:307
int fuse_start_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4906
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_exit(struct fuse *f)
Definition fuse.c:4639
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4433
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4914
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
@ FUSE_READDIR_DEFAULTS
Definition fuse.h:51
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:93
fuse_readdir_flags
Definition fuse.h:45
uint32_t fmask
Definition fuse.h:288
int32_t show_help
Definition fuse.h:279
uint32_t flags
Definition fuse.h:318
int32_t direct_io
Definition fuse.h:226
int32_t no_rofd_flush
Definition fuse.h:297
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t intr_signal
Definition fuse.h:155
int32_t remember
Definition fuse.h:167
int32_t kernel_cache
Definition fuse.h:245
int32_t readdir_ino
Definition fuse.h:207
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t set_mode
Definition fuse.h:120
int32_t auto_cache
Definition fuse.h:253
uint64_t reserved[48]
Definition fuse.h:323
int32_t intr
Definition fuse.h:148
double negative_timeout
Definition fuse.h:137
int32_t set_gid
Definition fuse.h:106
int32_t set_uid
Definition fuse.h:113
int32_t hard_remove
Definition fuse.h:185
double attr_timeout
Definition fuse.h:143
void * private_data
Definition fuse.h:874
uid_t uid
Definition fuse.h:865
pid_t pid
Definition fuse.h:871
struct fuse * fuse
Definition fuse.h:862
gid_t gid
Definition fuse.h:868
mode_t umask
Definition fuse.h:877
int(* mkdir)(const char *, mode_t)
Definition fuse.h:387
int(* truncate)(const char *, off_t, struct fuse_file_info *fi)
Definition fuse.h:437
int(* mknod)(const char *, mode_t, dev_t)
Definition fuse.h:379
int(* utimens)(const char *, const struct timespec tv[2], struct fuse_file_info *fi)
Definition fuse.h:719
int(* open)(const char *, struct fuse_file_info *)
Definition fuse.h:486
int(* opendir)(const char *, struct fuse_file_info *)
Definition fuse.h:589
int(* link)(const char *, const char *)
Definition fuse.h:410
int(* lock)(const char *, struct fuse_file_info *, int cmd, struct flock *)
Definition fuse.h:704
int(* read_buf)(const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *)
Definition fuse.h:798
int(* access)(const char *, int)
Definition fuse.h:660
int(* read)(const char *, char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:497
int(* poll)(const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp)
Definition fuse.h:769
void(* destroy)(void *private_data)
Definition fuse.h:649
int(* statfs)(const char *, struct statvfs *)
Definition fuse.h:516
int(* fallocate)(const char *, int, off_t, off_t, struct fuse_file_info *)
Definition fuse.h:828
int(* removexattr)(const char *, const char *)
Definition fuse.h:579
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
int(* releasedir)(const char *, struct fuse_file_info *)
Definition fuse.h:621
int(* rename)(const char *, const char *, unsigned int flags)
Definition fuse.h:407
off_t(* lseek)(const char *, off_t off, int whence, struct fuse_file_info *)
Definition fuse.h:852
int(* write)(const char *, const char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:509
int(* write_buf)(const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *)
Definition fuse.h:781
int(* unlink)(const char *)
Definition fuse.h:390
ssize_t(* copy_file_range)(const char *path_in, struct fuse_file_info *fi_in, off_t offset_in, const char *path_out, struct fuse_file_info *fi_out, off_t offset_out, size_t size, int flags)
Definition fuse.h:843
int(* fsync)(const char *, int, struct fuse_file_info *)
Definition fuse.h:567
int(* create)(const char *, mode_t, struct fuse_file_info *)
Definition fuse.h:672
int(* setxattr)(const char *, const char *, const char *, size_t, int)
Definition fuse.h:570
int(* statx)(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi)
Definition fuse.h:868
int(* listxattr)(const char *, char *, size_t)
Definition fuse.h:576
int(* readlink)(const char *, char *, size_t)
Definition fuse.h:371
int(* symlink)(const char *, const char *)
Definition fuse.h:396
int(* fsyncdir)(const char *, int, struct fuse_file_info *)
Definition fuse.h:631
int(* release)(const char *, struct fuse_file_info *)
Definition fuse.h:560
int(* chown)(const char *, uid_t, gid_t, struct fuse_file_info *fi)
Definition fuse.h:427
int(* chmod)(const char *, mode_t, struct fuse_file_info *fi)
Definition fuse.h:417
int(* rmdir)(const char *)
Definition fuse.h:393
int(* flush)(const char *, struct fuse_file_info *)
Definition fuse.h:546
int(* flock)(const char *, struct fuse_file_info *, int op)
Definition fuse.h:818
int(* ioctl)(const char *, unsigned int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data)
Definition fuse.h:750
int(* readdir)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags)
Definition fuse.h:613
int(* getxattr)(const char *, const char *, char *, size_t)
Definition fuse.h:573
int(* bmap)(const char *, size_t blocksize, uint64_t *idx)
Definition fuse.h:728
fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__common_8h_source.html0000644000175000017500000023752415156613427024535 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_common.h Source File
libfuse
fuse_common.h
Go to the documentation of this file.
1/* FUSE: Filesystem in Userspace
2 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
3
4 This program can be distributed under the terms of the GNU LGPLv2.
5 See the file LGPL2.txt.
6*/
7
10#if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_)
11#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
12#endif
13
14#ifndef FUSE_COMMON_H_
15#define FUSE_COMMON_H_
16
17#ifdef HAVE_LIBFUSE_PRIVATE_CONFIG_H
18#include "fuse_config.h"
19#endif
20
21#include "libfuse_config.h"
22
23#include "fuse_opt.h"
24#include "fuse_log.h"
25#include <stdint.h>
26#include <stdbool.h>
27#include <sys/types.h>
28#include <assert.h>
29
30#define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min))
31#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
32
33#ifdef __cplusplus
34extern "C" {
35#endif
36
50struct fuse_file_info {
52 int32_t flags;
53
60 uint32_t writepage : 1;
61
63 uint32_t direct_io : 1;
64
69 uint32_t keep_cache : 1;
70
74 uint32_t flush : 1;
75
78 uint32_t nonseekable : 1;
79
80 /* Indicates that flock locks for this file should be
81 released. If set, lock_owner shall contain a valid value.
82 May only be set in ->release(). */
83 uint32_t flock_release : 1;
84
89 uint32_t cache_readdir : 1;
90
93 uint32_t noflush : 1;
94
97 uint32_t parallel_direct_writes : 1;
98
100 uint32_t padding : 23;
101 uint32_t padding2 : 32;
102 uint32_t padding3 : 32;
103
107 uint64_t fh;
108
110 uint64_t lock_owner;
111
114 uint32_t poll_events;
115
119 int32_t backing_id;
120
122 uint64_t compat_flags;
123
124 uint64_t reserved[2];
125};
126
137#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
138struct fuse_loop_config_v1; /* forward declaration */
139struct fuse_loop_config {
140#else
141struct fuse_loop_config_v1 {
142#endif
147 int clone_fd;
148
159 unsigned int max_idle_threads;
160};
161
162
163/**************************************************************************
164 * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
165 **************************************************************************/
166
177#define FUSE_CAP_ASYNC_READ (1UL << 0)
178
185#define FUSE_CAP_POSIX_LOCKS (1UL << 1)
186
194#define FUSE_CAP_ATOMIC_O_TRUNC (1UL << 3)
195
206#define FUSE_CAP_EXPORT_SUPPORT (1UL << 4)
207
214#define FUSE_CAP_DONT_MASK (1UL << 6)
215
222#define FUSE_CAP_SPLICE_WRITE (1UL << 7)
223
230#define FUSE_CAP_SPLICE_MOVE (1UL << 8)
231
239#define FUSE_CAP_SPLICE_READ (1UL << 9)
240
252#define FUSE_CAP_FLOCK_LOCKS (1UL << 10)
253
259#define FUSE_CAP_IOCTL_DIR (1UL << 11)
260
281#define FUSE_CAP_AUTO_INVAL_DATA (1UL << 12)
282
289#define FUSE_CAP_READDIRPLUS (1UL << 13)
290
317#define FUSE_CAP_READDIRPLUS_AUTO (1UL << 14)
318
328#define FUSE_CAP_ASYNC_DIO (1UL << 15)
329
337#define FUSE_CAP_WRITEBACK_CACHE (1UL << 16)
338
352#define FUSE_CAP_NO_OPEN_SUPPORT (1UL << 17)
353
360#define FUSE_CAP_PARALLEL_DIROPS (1UL << 18)
361
379#define FUSE_CAP_POSIX_ACL (1UL << 19)
380
388#define FUSE_CAP_HANDLE_KILLPRIV (1UL << 20)
389
405#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1UL << 21)
406
418#define FUSE_CAP_CACHE_SYMLINKS (1UL << 23)
419
433#define FUSE_CAP_NO_OPENDIR_SUPPORT (1UL << 24)
434
456#define FUSE_CAP_EXPLICIT_INVAL_DATA (1UL << 25)
457
472#define FUSE_CAP_EXPIRE_ONLY (1UL << 26)
473
479#define FUSE_CAP_SETXATTR_EXT (1UL << 27)
480
488#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1UL << 28)
489
500#define FUSE_CAP_PASSTHROUGH (1UL << 29)
501
508#define FUSE_CAP_NO_EXPORT_SUPPORT (1UL << 30)
509
513#define FUSE_CAP_OVER_IO_URING (1UL << 31)
514
525#define FUSE_IOCTL_COMPAT (1 << 0)
526#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
527#define FUSE_IOCTL_RETRY (1 << 2)
528#define FUSE_IOCTL_DIR (1 << 4)
529
530#define FUSE_IOCTL_MAX_IOV 256
531
543struct fuse_conn_info {
547 uint32_t proto_major;
548
552 uint32_t proto_minor;
553
557 uint32_t max_write;
558
571 uint32_t max_read;
572
576 uint32_t max_readahead;
577
583 uint32_t capable;
584
595 uint32_t want;
596
625 uint32_t max_background;
626
635 uint32_t congestion_threshold;
636
652 uint32_t time_gran;
653
670#define FUSE_BACKING_STACKED_UNDER (0)
671#define FUSE_BACKING_STACKED_OVER (1)
672 uint32_t max_backing_stack_depth;
673
682 uint32_t no_interrupt : 1;
683
684 /* reserved bits for future use */
685 uint32_t padding : 31;
686
691 uint64_t capable_ext;
692
701 uint64_t want_ext;
702
707 uint16_t request_timeout;
708
712 uint16_t reserved[31];
713};
714
715struct fuse_session;
716struct fuse_pollhandle;
717struct fuse_conn_info_opts;
718
761struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
762
770void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
771 struct fuse_conn_info *conn);
772
779int fuse_daemonize(int foreground);
780
786int fuse_version(void);
787
793const char *fuse_pkgversion(void);
794
800void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
801
802/* ----------------------------------------------------------- *
803 * Data buffer *
804 * ----------------------------------------------------------- */
805
816 FUSE_BUF_IS_FD = (1 << 1),
817
826
834 FUSE_BUF_FD_RETRY = (1 << 3)
836
878
885struct fuse_buf {
889 size_t size;
890
894 enum fuse_buf_flags flags;
895
901 void *mem;
902
908 int fd;
909
915 off_t pos;
916
923 size_t mem_size;
924};
925
934struct fuse_bufvec {
938 size_t count;
939
943 size_t idx;
944
948 size_t off;
949
953 struct fuse_buf buf[1];
954};
955
960struct libfuse_version
961{
962 uint32_t major;
963 uint32_t minor;
964 uint32_t hotfix;
965 uint32_t padding;
966};
967
968/* Initialize bufvec with a single buffer of given size */
969#define FUSE_BUFVEC_INIT(size__) \
970 ((struct fuse_bufvec) { \
971 /* .count= */ 1, \
972 /* .idx = */ 0, \
973 /* .off = */ 0, \
974 /* .buf = */ { /* [0] = */ { \
975 /* .size = */ (size__), \
976 /* .flags = */ (enum fuse_buf_flags) 0, \
977 /* .mem = */ NULL, \
978 /* .fd = */ -1, \
979 /* .pos = */ 0, \
980 /* .mem_size = */ 0, \
981 } } \
982 } )
983
990size_t fuse_buf_size(const struct fuse_bufvec *bufv);
991
1000ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
1001 enum fuse_buf_copy_flags flags);
1002
1003/* ----------------------------------------------------------- *
1004 * Signal handling *
1005 * ----------------------------------------------------------- */
1006
1022int fuse_set_signal_handlers(struct fuse_session *se);
1023
1039int fuse_set_fail_signal_handlers(struct fuse_session *se);
1040
1052void fuse_remove_signal_handlers(struct fuse_session *se);
1053
1059#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
1065struct fuse_loop_config *fuse_loop_cfg_create(void);
1066
1070void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
1071
1075void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
1076 unsigned int value);
1077
1081void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
1082 unsigned int value);
1083
1087void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
1088 unsigned int value);
1089
1096void fuse_loop_cfg_convert(struct fuse_loop_config *config,
1097 struct fuse_loop_config_v1 *v1_conf);
1098#endif
1099
1107bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1108
1115void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1116
1124bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
1125
1126/*
1127 * DO NOT USE: Not part of public API, for internal test use only.
1128 * The function signature or any use of it is not guaranteeed to
1129 * remain stable. And neither are results of what this function does.
1130 */
1132
1133
1134
1135/* ----------------------------------------------------------- *
1136 * Compatibility stuff *
1137 * ----------------------------------------------------------- */
1138
1139#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30
1140# error only API version 30 or greater is supported
1141#endif
1142
1143#ifdef __cplusplus
1144}
1145#endif
1146
1147
1148/*
1149 * This interface uses 64 bit off_t.
1150 *
1151 * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags!
1152 */
1153
1154#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
1155_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit");
1156#else
1157struct _fuse_off_t_must_be_64bit_dummy_struct \
1158 { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); };
1159#endif
1160
1161#endif /* FUSE_COMMON_H_ */
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
int fuse_set_fail_signal_handlers(struct fuse_session *se)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
Definition helper.c:412
fuse_buf_flags
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_FD_RETRY
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
Definition helper.c:466
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
int fuse_version(void)
Definition fuse.c:5206
void fuse_remove_signal_handlers(struct fuse_session *se)
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t nonseekable
Definition fuse_common.h:78
int32_t backing_id
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t padding
uint32_t noflush
Definition fuse_common.h:93
uint64_t compat_flags
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__kernel_8h_source.html0000644000175000017500000045246615156613427024531 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_kernel.h Source File
libfuse
fuse_kernel.h
1/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */
2/*
3 This file defines the kernel interface of FUSE
4 Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
5
6 This program can be distributed under the terms of the GNU GPL.
7 See the file COPYING.
8
9 This -- and only this -- header file may also be distributed under
10 the terms of the BSD Licence as follows:
11
12 Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
13
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions
16 are met:
17 1. Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19 2. Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in the
21 documentation and/or other materials provided with the distribution.
22
23 THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
27 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 SUCH DAMAGE.
34*/
35
36/*
37 * This file defines the kernel interface of FUSE
38 *
39 * Protocol changelog:
40 *
41 * 7.1:
42 * - add the following messages:
43 * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK,
44 * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE,
45 * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR,
46 * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR,
47 * FUSE_RELEASEDIR
48 * - add padding to messages to accommodate 32-bit servers on 64-bit kernels
49 *
50 * 7.2:
51 * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags
52 * - add FUSE_FSYNCDIR message
53 *
54 * 7.3:
55 * - add FUSE_ACCESS message
56 * - add FUSE_CREATE message
57 * - add filehandle to fuse_setattr_in
58 *
59 * 7.4:
60 * - add frsize to fuse_kstatfs
61 * - clean up request size limit checking
62 *
63 * 7.5:
64 * - add flags and max_write to fuse_init_out
65 *
66 * 7.6:
67 * - add max_readahead to fuse_init_in and fuse_init_out
68 *
69 * 7.7:
70 * - add FUSE_INTERRUPT message
71 * - add POSIX file lock support
72 *
73 * 7.8:
74 * - add lock_owner and flags fields to fuse_release_in
75 * - add FUSE_BMAP message
76 * - add FUSE_DESTROY message
77 *
78 * 7.9:
79 * - new fuse_getattr_in input argument of GETATTR
80 * - add lk_flags in fuse_lk_in
81 * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
82 * - add blksize field to fuse_attr
83 * - add file flags field to fuse_read_in and fuse_write_in
84 * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in
85 *
86 * 7.10
87 * - add nonseekable open flag
88 *
89 * 7.11
90 * - add IOCTL message
91 * - add unsolicited notification support
92 * - add POLL message and NOTIFY_POLL notification
93 *
94 * 7.12
95 * - add umask flag to input argument of create, mknod and mkdir
96 * - add notification messages for invalidation of inodes and
97 * directory entries
98 *
99 * 7.13
100 * - make max number of background requests and congestion threshold
101 * tunables
102 *
103 * 7.14
104 * - add splice support to fuse device
105 *
106 * 7.15
107 * - add store notify
108 * - add retrieve notify
109 *
110 * 7.16
111 * - add BATCH_FORGET request
112 * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct
113 * fuse_ioctl_iovec' instead of ambiguous 'struct iovec'
114 * - add FUSE_IOCTL_32BIT flag
115 *
116 * 7.17
117 * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK
118 *
119 * 7.18
120 * - add FUSE_IOCTL_DIR flag
121 * - add FUSE_NOTIFY_DELETE
122 *
123 * 7.19
124 * - add FUSE_FALLOCATE
125 *
126 * 7.20
127 * - add FUSE_AUTO_INVAL_DATA
128 *
129 * 7.21
130 * - add FUSE_READDIRPLUS
131 * - send the requested events in POLL request
132 *
133 * 7.22
134 * - add FUSE_ASYNC_DIO
135 *
136 * 7.23
137 * - add FUSE_WRITEBACK_CACHE
138 * - add time_gran to fuse_init_out
139 * - add reserved space to fuse_init_out
140 * - add FATTR_CTIME
141 * - add ctime and ctimensec to fuse_setattr_in
142 * - add FUSE_RENAME2 request
143 * - add FUSE_NO_OPEN_SUPPORT flag
144 *
145 * 7.24
146 * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support
147 *
148 * 7.25
149 * - add FUSE_PARALLEL_DIROPS
150 *
151 * 7.26
152 * - add FUSE_HANDLE_KILLPRIV
153 * - add FUSE_POSIX_ACL
154 *
155 * 7.27
156 * - add FUSE_ABORT_ERROR
157 *
158 * 7.28
159 * - add FUSE_COPY_FILE_RANGE
160 * - add FOPEN_CACHE_DIR
161 * - add FUSE_MAX_PAGES, add max_pages to init_out
162 * - add FUSE_CACHE_SYMLINKS
163 *
164 * 7.29
165 * - add FUSE_NO_OPENDIR_SUPPORT flag
166 *
167 * 7.30
168 * - add FUSE_EXPLICIT_INVAL_DATA
169 * - add FUSE_IOCTL_COMPAT_X32
170 *
171 * 7.31
172 * - add FUSE_WRITE_KILL_PRIV flag
173 * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
174 * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
175 *
176 * 7.32
177 * - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
178 *
179 * 7.33
180 * - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
181 * - add FUSE_OPEN_KILL_SUIDGID
182 * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
183 * - add FUSE_SETXATTR_ACL_KILL_SGID
184 *
185 * 7.34
186 * - add FUSE_SYNCFS
187 *
188 * 7.35
189 * - add FOPEN_NOFLUSH
190 *
191 * 7.36
192 * - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
193 * - add flags2 to fuse_init_in and fuse_init_out
194 * - add FUSE_SECURITY_CTX init flag
195 * - add security context to create, mkdir, symlink, and mknod requests
196 * - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
197 *
198 * 7.37
199 * - add FUSE_TMPFILE
200 *
201 * 7.38
202 * - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
203 * - add FOPEN_PARALLEL_DIRECT_WRITES
204 * - add total_extlen to fuse_in_header
205 * - add FUSE_MAX_NR_SECCTX
206 * - add extension header
207 * - add FUSE_EXT_GROUPS
208 * - add FUSE_CREATE_SUPP_GROUP
209 * - add FUSE_HAS_EXPIRE_ONLY
210 *
211 * 7.39
212 * - add FUSE_DIRECT_IO_ALLOW_MMAP
213 * - add FUSE_STATX and related structures
214 *
215 * 7.40
216 * - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag
217 * - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag
218 * - add FUSE_NO_EXPORT_SUPPORT init flag
219 * - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag
220 *
221 * 7.41
222 * - add FUSE_ALLOW_IDMAP
223 * 7.42
224 * - Add FUSE_OVER_IO_URING and all other io-uring related flags and data
225 * structures:
226 * - struct fuse_uring_ent_in_out
227 * - struct fuse_uring_req_header
228 * - struct fuse_uring_cmd_req
229 * - FUSE_URING_IN_OUT_HEADER_SZ
230 * - FUSE_URING_OP_IN_OUT_SZ
231 * - enum fuse_uring_cmd
232 *
233 * 7.43
234 * - add FUSE_REQUEST_TIMEOUT
235 *
236 * 7.44
237 * - add FUSE_NOTIFY_INC_EPOCH
238 *
239 * 7.45
240 * - add FUSE_COPY_FILE_RANGE_64
241 * - add struct fuse_copy_file_range_out
242 */
243
244#ifndef _LINUX_FUSE_H
245#define _LINUX_FUSE_H
246
247#ifdef __KERNEL__
248#include <linux/types.h>
249#else
250#include <stdint.h>
251#endif
252
253/*
254 * Version negotiation:
255 *
256 * Both the kernel and userspace send the version they support in the
257 * INIT request and reply respectively.
258 *
259 * If the major versions match then both shall use the smallest
260 * of the two minor versions for communication.
261 *
262 * If the kernel supports a larger major version, then userspace shall
263 * reply with the major version it supports, ignore the rest of the
264 * INIT message and expect a new INIT message from the kernel with a
265 * matching major version.
266 *
267 * If the library supports a larger major version, then it shall fall
268 * back to the major protocol version sent by the kernel for
269 * communication and reply with that major version (and an arbitrary
270 * supported minor version).
271 */
272
274#define FUSE_KERNEL_VERSION 7
275
277#define FUSE_KERNEL_MINOR_VERSION 45
278
280#define FUSE_ROOT_ID 1
281
282/* Make sure all structures are padded to 64bit boundary, so 32bit
283 userspace works under 64bit kernels */
284
285struct fuse_attr {
286 uint64_t ino;
287 uint64_t size;
288 uint64_t blocks;
289 uint64_t atime;
290 uint64_t mtime;
291 uint64_t ctime;
292 uint32_t atimensec;
293 uint32_t mtimensec;
294 uint32_t ctimensec;
295 uint32_t mode;
296 uint32_t nlink;
297 uint32_t uid;
298 uint32_t gid;
299 uint32_t rdev;
300 uint32_t blksize;
301 uint32_t flags;
302};
303
304/*
305 * The following structures are bit-for-bit compatible with the statx(2) ABI in
306 * Linux.
307 */
308struct fuse_sx_time {
309 int64_t tv_sec;
310 uint32_t tv_nsec;
311 int32_t __reserved;
312};
313
314struct fuse_statx {
315 uint32_t mask;
316 uint32_t blksize;
317 uint64_t attributes;
318 uint32_t nlink;
319 uint32_t uid;
320 uint32_t gid;
321 uint16_t mode;
322 uint16_t __spare0[1];
323 uint64_t ino;
324 uint64_t size;
325 uint64_t blocks;
326 uint64_t attributes_mask;
327 struct fuse_sx_time atime;
328 struct fuse_sx_time btime;
329 struct fuse_sx_time ctime;
330 struct fuse_sx_time mtime;
331 uint32_t rdev_major;
332 uint32_t rdev_minor;
333 uint32_t dev_major;
334 uint32_t dev_minor;
335 uint64_t __spare2[14];
336};
337
338struct fuse_kstatfs {
339 uint64_t blocks;
340 uint64_t bfree;
341 uint64_t bavail;
342 uint64_t files;
343 uint64_t ffree;
344 uint32_t bsize;
345 uint32_t namelen;
346 uint32_t frsize;
347 uint32_t padding;
348 uint32_t spare[6];
349};
350
351struct fuse_file_lock {
352 uint64_t start;
353 uint64_t end;
354 uint32_t type;
355 uint32_t pid; /* tgid */
356};
357
361#define FATTR_MODE (1 << 0)
362#define FATTR_UID (1 << 1)
363#define FATTR_GID (1 << 2)
364#define FATTR_SIZE (1 << 3)
365#define FATTR_ATIME (1 << 4)
366#define FATTR_MTIME (1 << 5)
367#define FATTR_FH (1 << 6)
368#define FATTR_ATIME_NOW (1 << 7)
369#define FATTR_MTIME_NOW (1 << 8)
370#define FATTR_LOCKOWNER (1 << 9)
371#define FATTR_CTIME (1 << 10)
372#define FATTR_KILL_SUIDGID (1 << 11)
373
386#define FOPEN_DIRECT_IO (1 << 0)
387#define FOPEN_KEEP_CACHE (1 << 1)
388#define FOPEN_NONSEEKABLE (1 << 2)
389#define FOPEN_CACHE_DIR (1 << 3)
390#define FOPEN_STREAM (1 << 4)
391#define FOPEN_NOFLUSH (1 << 5)
392#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
393#define FOPEN_PASSTHROUGH (1 << 7)
394
451#define FUSE_ASYNC_READ (1 << 0)
452#define FUSE_POSIX_LOCKS (1 << 1)
453#define FUSE_FILE_OPS (1 << 2)
454#define FUSE_ATOMIC_O_TRUNC (1 << 3)
455#define FUSE_EXPORT_SUPPORT (1 << 4)
456#define FUSE_BIG_WRITES (1 << 5)
457#define FUSE_DONT_MASK (1 << 6)
458#define FUSE_SPLICE_WRITE (1 << 7)
459#define FUSE_SPLICE_MOVE (1 << 8)
460#define FUSE_SPLICE_READ (1 << 9)
461#define FUSE_FLOCK_LOCKS (1 << 10)
462#define FUSE_HAS_IOCTL_DIR (1 << 11)
463#define FUSE_AUTO_INVAL_DATA (1 << 12)
464#define FUSE_DO_READDIRPLUS (1 << 13)
465#define FUSE_READDIRPLUS_AUTO (1 << 14)
466#define FUSE_ASYNC_DIO (1 << 15)
467#define FUSE_WRITEBACK_CACHE (1 << 16)
468#define FUSE_NO_OPEN_SUPPORT (1 << 17)
469#define FUSE_PARALLEL_DIROPS (1 << 18)
470#define FUSE_HANDLE_KILLPRIV (1 << 19)
471#define FUSE_POSIX_ACL (1 << 20)
472#define FUSE_ABORT_ERROR (1 << 21)
473#define FUSE_MAX_PAGES (1 << 22)
474#define FUSE_CACHE_SYMLINKS (1 << 23)
475#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
476#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
477#define FUSE_MAP_ALIGNMENT (1 << 26)
478#define FUSE_SUBMOUNTS (1 << 27)
479#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
480#define FUSE_SETXATTR_EXT (1 << 29)
481#define FUSE_INIT_EXT (1 << 30)
482#define FUSE_INIT_RESERVED (1 << 31)
483/* bits 32..63 get shifted down 32 bits into the flags2 field */
484#define FUSE_SECURITY_CTX (1ULL << 32)
485#define FUSE_HAS_INODE_DAX (1ULL << 33)
486#define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
487#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35)
488#define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36)
489#define FUSE_PASSTHROUGH (1ULL << 37)
490#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38)
491#define FUSE_HAS_RESEND (1ULL << 39)
492/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
493#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
494#define FUSE_ALLOW_IDMAP (1ULL << 40)
495#define FUSE_OVER_IO_URING (1ULL << 41)
496#define FUSE_REQUEST_TIMEOUT (1ULL << 42)
497
503#define CUSE_UNRESTRICTED_IOCTL (1 << 0)
504
508#define FUSE_RELEASE_FLUSH (1 << 0)
509#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1)
510
514#define FUSE_GETATTR_FH (1 << 0)
515
519#define FUSE_LK_FLOCK (1 << 0)
520
528#define FUSE_WRITE_CACHE (1 << 0)
529#define FUSE_WRITE_LOCKOWNER (1 << 1)
530#define FUSE_WRITE_KILL_SUIDGID (1 << 2)
531
532/* Obsolete alias; this flag implies killing suid/sgid only. */
533#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID
534
538#define FUSE_READ_LOCKOWNER (1 << 1)
539
552#define FUSE_IOCTL_COMPAT (1 << 0)
553#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
554#define FUSE_IOCTL_RETRY (1 << 2)
555#define FUSE_IOCTL_32BIT (1 << 3)
556#define FUSE_IOCTL_DIR (1 << 4)
557#define FUSE_IOCTL_COMPAT_X32 (1 << 5)
558
559#define FUSE_IOCTL_MAX_IOV 256
560
566#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
567
573#define FUSE_FSYNC_FDATASYNC (1 << 0)
574
581#define FUSE_ATTR_SUBMOUNT (1 << 0)
582#define FUSE_ATTR_DAX (1 << 1)
583
588#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
589
594#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
595
600#define FUSE_EXPIRE_ONLY (1 << 0)
601
607enum fuse_ext_type {
608 /* Types 0..31 are reserved for fuse_secctx_header */
609 FUSE_MAX_NR_SECCTX = 31,
610 FUSE_EXT_GROUPS = 32,
611};
612
613enum fuse_opcode {
614 FUSE_LOOKUP = 1,
615 FUSE_FORGET = 2, /* no reply */
616 FUSE_GETATTR = 3,
617 FUSE_SETATTR = 4,
618 FUSE_READLINK = 5,
619 FUSE_SYMLINK = 6,
620 FUSE_MKNOD = 8,
621 FUSE_MKDIR = 9,
622 FUSE_UNLINK = 10,
623 FUSE_RMDIR = 11,
624 FUSE_RENAME = 12,
625 FUSE_LINK = 13,
626 FUSE_OPEN = 14,
627 FUSE_READ = 15,
628 FUSE_WRITE = 16,
629 FUSE_STATFS = 17,
630 FUSE_RELEASE = 18,
631 FUSE_FSYNC = 20,
632 FUSE_SETXATTR = 21,
633 FUSE_GETXATTR = 22,
634 FUSE_LISTXATTR = 23,
635 FUSE_REMOVEXATTR = 24,
636 FUSE_FLUSH = 25,
637 FUSE_INIT = 26,
638 FUSE_OPENDIR = 27,
639 FUSE_READDIR = 28,
640 FUSE_RELEASEDIR = 29,
641 FUSE_FSYNCDIR = 30,
642 FUSE_GETLK = 31,
643 FUSE_SETLK = 32,
644 FUSE_SETLKW = 33,
645 FUSE_ACCESS = 34,
646 FUSE_CREATE = 35,
647 FUSE_INTERRUPT = 36,
648 FUSE_BMAP = 37,
649 FUSE_DESTROY = 38,
650 FUSE_IOCTL = 39,
651 FUSE_POLL = 40,
652 FUSE_NOTIFY_REPLY = 41,
653 FUSE_BATCH_FORGET = 42,
654 FUSE_FALLOCATE = 43,
655 FUSE_READDIRPLUS = 44,
656 FUSE_RENAME2 = 45,
657 FUSE_LSEEK = 46,
658 FUSE_COPY_FILE_RANGE = 47,
659 FUSE_SETUPMAPPING = 48,
660 FUSE_REMOVEMAPPING = 49,
661 FUSE_SYNCFS = 50,
662 FUSE_TMPFILE = 51,
663 FUSE_STATX = 52,
664 FUSE_COPY_FILE_RANGE_64 = 53,
665
666 /* CUSE specific operations */
667 CUSE_INIT = 4096,
668
669 /* Reserved opcodes: helpful to detect structure endian-ness */
670 CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */
671 FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */
672};
673
674enum fuse_notify_code {
675 FUSE_NOTIFY_POLL = 1,
676 FUSE_NOTIFY_INVAL_INODE = 2,
677 FUSE_NOTIFY_INVAL_ENTRY = 3,
678 FUSE_NOTIFY_STORE = 4,
679 FUSE_NOTIFY_RETRIEVE = 5,
680 FUSE_NOTIFY_DELETE = 6,
681 FUSE_NOTIFY_RESEND = 7,
682 FUSE_NOTIFY_INC_EPOCH = 8,
683 FUSE_NOTIFY_CODE_MAX,
684};
685
686/* The read buffer is required to be at least 8k, but may be much larger */
687#define FUSE_MIN_READ_BUFFER 8192
688
689#define FUSE_COMPAT_ENTRY_OUT_SIZE 120
690
691struct fuse_entry_out {
692 uint64_t nodeid; /* Inode ID */
693 uint64_t generation; /* Inode generation: nodeid:gen must
694 be unique for the fs's lifetime */
695 uint64_t entry_valid; /* Cache timeout for the name */
696 uint64_t attr_valid; /* Cache timeout for the attributes */
697 uint32_t entry_valid_nsec;
698 uint32_t attr_valid_nsec;
699 struct fuse_attr attr;
700};
701
702struct fuse_forget_in {
703 uint64_t nlookup;
704};
705
706struct fuse_forget_one {
707 uint64_t nodeid;
708 uint64_t nlookup;
709};
710
711struct fuse_batch_forget_in {
712 uint32_t count;
713 uint32_t dummy;
714};
715
716struct fuse_getattr_in {
717 uint32_t getattr_flags;
718 uint32_t dummy;
719 uint64_t fh;
720};
721
722#define FUSE_COMPAT_ATTR_OUT_SIZE 96
723
724struct fuse_attr_out {
725 uint64_t attr_valid; /* Cache timeout for the attributes */
726 uint32_t attr_valid_nsec;
727 uint32_t dummy;
728 struct fuse_attr attr;
729};
730
731struct fuse_statx_in {
732 uint32_t getattr_flags;
733 uint32_t reserved;
734 uint64_t fh;
735 uint32_t sx_flags;
736 uint32_t sx_mask;
737};
738
739struct fuse_statx_out {
740 uint64_t attr_valid; /* Cache timeout for the attributes */
741 uint32_t attr_valid_nsec;
742 uint32_t flags;
743 uint64_t spare[2];
744 struct fuse_statx stat;
745};
746
747#define FUSE_COMPAT_MKNOD_IN_SIZE 8
748
749struct fuse_mknod_in {
750 uint32_t mode;
751 uint32_t rdev;
752 uint32_t umask;
753 uint32_t padding;
754};
755
756struct fuse_mkdir_in {
757 uint32_t mode;
758 uint32_t umask;
759};
760
761struct fuse_rename_in {
762 uint64_t newdir;
763};
764
765struct fuse_rename2_in {
766 uint64_t newdir;
767 uint32_t flags;
768 uint32_t padding;
769};
770
771struct fuse_link_in {
772 uint64_t oldnodeid;
773};
774
775struct fuse_setattr_in {
776 uint32_t valid;
777 uint32_t padding;
778 uint64_t fh;
779 uint64_t size;
780 uint64_t lock_owner;
781 uint64_t atime;
782 uint64_t mtime;
783 uint64_t ctime;
784 uint32_t atimensec;
785 uint32_t mtimensec;
786 uint32_t ctimensec;
787 uint32_t mode;
788 uint32_t unused4;
789 uint32_t uid;
790 uint32_t gid;
791 uint32_t unused5;
792};
793
794struct fuse_open_in {
795 uint32_t flags;
796 uint32_t open_flags; /* FUSE_OPEN_... */
797};
798
799struct fuse_create_in {
800 uint32_t flags;
801 uint32_t mode;
802 uint32_t umask;
803 uint32_t open_flags; /* FUSE_OPEN_... */
804};
805
806struct fuse_open_out {
807 uint64_t fh;
808 uint32_t open_flags;
809 int32_t backing_id;
810};
811
812struct fuse_release_in {
813 uint64_t fh;
814 uint32_t flags;
815 uint32_t release_flags;
816 uint64_t lock_owner;
817};
818
819struct fuse_flush_in {
820 uint64_t fh;
821 uint32_t unused;
822 uint32_t padding;
823 uint64_t lock_owner;
824};
825
826struct fuse_read_in {
827 uint64_t fh;
828 uint64_t offset;
829 uint32_t size;
830 uint32_t read_flags;
831 uint64_t lock_owner;
832 uint32_t flags;
833 uint32_t padding;
834};
835
836#define FUSE_COMPAT_WRITE_IN_SIZE 24
837
838struct fuse_write_in {
839 uint64_t fh;
840 uint64_t offset;
841 uint32_t size;
842 uint32_t write_flags;
843 uint64_t lock_owner;
844 uint32_t flags;
845 uint32_t padding;
846};
847
848struct fuse_write_out {
849 uint32_t size;
850 uint32_t padding;
851};
852
853#define FUSE_COMPAT_STATFS_SIZE 48
854
855struct fuse_statfs_out {
856 struct fuse_kstatfs st;
857};
858
859struct fuse_fsync_in {
860 uint64_t fh;
861 uint32_t fsync_flags;
862 uint32_t padding;
863};
864
865#define FUSE_COMPAT_SETXATTR_IN_SIZE 8
866
867struct fuse_setxattr_in {
868 uint32_t size;
869 uint32_t flags;
870 uint32_t setxattr_flags;
871 uint32_t padding;
872};
873
874struct fuse_getxattr_in {
875 uint32_t size;
876 uint32_t padding;
877};
878
879struct fuse_getxattr_out {
880 uint32_t size;
881 uint32_t padding;
882};
883
884struct fuse_lk_in {
885 uint64_t fh;
886 uint64_t owner;
887 struct fuse_file_lock lk;
888 uint32_t lk_flags;
889 uint32_t padding;
890};
891
892struct fuse_lk_out {
893 struct fuse_file_lock lk;
894};
895
896struct fuse_access_in {
897 uint32_t mask;
898 uint32_t padding;
899};
900
901struct fuse_init_in {
902 uint32_t major;
903 uint32_t minor;
904 uint32_t max_readahead;
905 uint32_t flags;
906 uint32_t flags2;
907 uint32_t unused[11];
908};
909
910#define FUSE_COMPAT_INIT_OUT_SIZE 8
911#define FUSE_COMPAT_22_INIT_OUT_SIZE 24
912
913struct fuse_init_out {
914 uint32_t major;
915 uint32_t minor;
916 uint32_t max_readahead;
917 uint32_t flags;
918 uint16_t max_background;
919 uint16_t congestion_threshold;
920 uint32_t max_write;
921 uint32_t time_gran;
922 uint16_t max_pages;
923 uint16_t map_alignment;
924 uint32_t flags2;
925 uint32_t max_stack_depth;
926 uint16_t request_timeout;
927 uint16_t unused[11];
928};
929
930#define CUSE_INIT_INFO_MAX 4096
931
932struct cuse_init_in {
933 uint32_t major;
934 uint32_t minor;
935 uint32_t unused;
936 uint32_t flags;
937};
938
939struct cuse_init_out {
940 uint32_t major;
941 uint32_t minor;
942 uint32_t unused;
943 uint32_t flags;
944 uint32_t max_read;
945 uint32_t max_write;
946 uint32_t dev_major; /* chardev major */
947 uint32_t dev_minor; /* chardev minor */
948 uint32_t spare[10];
949};
950
951struct fuse_interrupt_in {
952 uint64_t unique;
953};
954
955struct fuse_bmap_in {
956 uint64_t block;
957 uint32_t blocksize;
958 uint32_t padding;
959};
960
961struct fuse_bmap_out {
962 uint64_t block;
963};
964
965struct fuse_ioctl_in {
966 uint64_t fh;
967 uint32_t flags;
968 uint32_t cmd;
969 uint64_t arg;
970 uint32_t in_size;
971 uint32_t out_size;
972};
973
974struct fuse_ioctl_iovec {
975 uint64_t base;
976 uint64_t len;
977};
978
979struct fuse_ioctl_out {
980 int32_t result;
981 uint32_t flags;
982 uint32_t in_iovs;
983 uint32_t out_iovs;
984};
985
986struct fuse_poll_in {
987 uint64_t fh;
988 uint64_t kh;
989 uint32_t flags;
990 uint32_t events;
991};
992
993struct fuse_poll_out {
994 uint32_t revents;
995 uint32_t padding;
996};
997
998struct fuse_notify_poll_wakeup_out {
999 uint64_t kh;
1000};
1001
1002struct fuse_fallocate_in {
1003 uint64_t fh;
1004 uint64_t offset;
1005 uint64_t length;
1006 uint32_t mode;
1007 uint32_t padding;
1008};
1009
1016#define FUSE_UNIQUE_RESEND (1ULL << 63)
1017
1031#define FUSE_INVALID_UIDGID ((uint32_t)(-1))
1032
1033struct fuse_in_header {
1034 uint32_t len;
1035 uint32_t opcode;
1036 uint64_t unique;
1037 uint64_t nodeid;
1038 uint32_t uid;
1039 uint32_t gid;
1040 uint32_t pid;
1041 uint16_t total_extlen; /* length of extensions in 8byte units */
1042 uint16_t padding;
1043};
1044
1045struct fuse_out_header {
1046 uint32_t len;
1047 int32_t error;
1048 uint64_t unique;
1049};
1050
1051struct fuse_dirent {
1052 uint64_t ino;
1053 uint64_t off;
1054 uint32_t namelen;
1055 uint32_t type;
1056 char name[];
1057};
1058
1059/* Align variable length records to 64bit boundary */
1060#define FUSE_REC_ALIGN(x) \
1061 (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
1062
1063#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
1064#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
1065#define FUSE_DIRENT_SIZE(d) \
1066 FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
1067
1068struct fuse_direntplus {
1069 struct fuse_entry_out entry_out;
1070 struct fuse_dirent dirent;
1071};
1072
1073#define FUSE_NAME_OFFSET_DIRENTPLUS \
1074 offsetof(struct fuse_direntplus, dirent.name)
1075#define FUSE_DIRENTPLUS_SIZE(d) \
1076 FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen)
1077
1078struct fuse_notify_inval_inode_out {
1079 uint64_t ino;
1080 int64_t off;
1081 int64_t len;
1082};
1083
1084struct fuse_notify_inval_entry_out {
1085 uint64_t parent;
1086 uint32_t namelen;
1087 uint32_t flags;
1088};
1089
1090struct fuse_notify_delete_out {
1091 uint64_t parent;
1092 uint64_t child;
1093 uint32_t namelen;
1094 uint32_t padding;
1095};
1096
1097struct fuse_notify_store_out {
1098 uint64_t nodeid;
1099 uint64_t offset;
1100 uint32_t size;
1101 uint32_t padding;
1102};
1103
1104struct fuse_notify_retrieve_out {
1105 uint64_t notify_unique;
1106 uint64_t nodeid;
1107 uint64_t offset;
1108 uint32_t size;
1109 uint32_t padding;
1110};
1111
1112/* Matches the size of fuse_write_in */
1113struct fuse_notify_retrieve_in {
1114 uint64_t dummy1;
1115 uint64_t offset;
1116 uint32_t size;
1117 uint32_t dummy2;
1118 uint64_t dummy3;
1119 uint64_t dummy4;
1120};
1121
1122struct fuse_backing_map {
1123 int32_t fd;
1124 uint32_t flags;
1125 uint64_t padding;
1126};
1127
1128/* Device ioctls: */
1129#define FUSE_DEV_IOC_MAGIC 229
1130#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
1131#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \
1132 struct fuse_backing_map)
1133#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t)
1134
1135struct fuse_lseek_in {
1136 uint64_t fh;
1137 uint64_t offset;
1138 uint32_t whence;
1139 uint32_t padding;
1140};
1141
1142struct fuse_lseek_out {
1143 uint64_t offset;
1144};
1145
1146struct fuse_copy_file_range_in {
1147 uint64_t fh_in;
1148 uint64_t off_in;
1149 uint64_t nodeid_out;
1150 uint64_t fh_out;
1151 uint64_t off_out;
1152 uint64_t len;
1153 uint64_t flags;
1154};
1155
1156/* For FUSE_COPY_FILE_RANGE_64 */
1157struct fuse_copy_file_range_out {
1158 uint64_t bytes_copied;
1159};
1160
1161#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
1162#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
1163struct fuse_setupmapping_in {
1164 /* An already open handle */
1165 uint64_t fh;
1166 /* Offset into the file to start the mapping */
1167 uint64_t foffset;
1168 /* Length of mapping required */
1169 uint64_t len;
1170 /* Flags, FUSE_SETUPMAPPING_FLAG_* */
1171 uint64_t flags;
1172 /* Offset in Memory Window */
1173 uint64_t moffset;
1174};
1175
1176struct fuse_removemapping_in {
1177 /* number of fuse_removemapping_one follows */
1178 uint32_t count;
1179};
1180
1181struct fuse_removemapping_one {
1182 /* Offset into the dax window start the unmapping */
1183 uint64_t moffset;
1184 /* Length of mapping required */
1185 uint64_t len;
1186};
1187
1188#define FUSE_REMOVEMAPPING_MAX_ENTRY \
1189 (PAGE_SIZE / sizeof(struct fuse_removemapping_one))
1190
1191struct fuse_syncfs_in {
1192 uint64_t padding;
1193};
1194
1195/*
1196 * For each security context, send fuse_secctx with size of security context
1197 * fuse_secctx will be followed by security context name and this in turn
1198 * will be followed by actual context label.
1199 * fuse_secctx, name, context
1200 */
1201struct fuse_secctx {
1202 uint32_t size;
1203 uint32_t padding;
1204};
1205
1206/*
1207 * Contains the information about how many fuse_secctx structures are being
1208 * sent and what's the total size of all security contexts (including
1209 * size of fuse_secctx_header).
1210 *
1211 */
1212struct fuse_secctx_header {
1213 uint32_t size;
1214 uint32_t nr_secctx;
1215};
1216
1225struct fuse_ext_header {
1226 uint32_t size;
1227 uint32_t type;
1228};
1229
1235struct fuse_supp_groups {
1236 uint32_t nr_groups;
1237 uint32_t groups[];
1238};
1239
1243#define FUSE_URING_IN_OUT_HEADER_SZ 128
1244#define FUSE_URING_OP_IN_OUT_SZ 128
1245
1246/* Used as part of the fuse_uring_req_header */
1247struct fuse_uring_ent_in_out {
1248 uint64_t flags;
1249
1250 /*
1251 * commit ID to be used in a reply to a ring request (see also
1252 * struct fuse_uring_cmd_req)
1253 */
1254 uint64_t commit_id;
1255
1256 /* size of user payload buffer */
1257 uint32_t payload_sz;
1258 uint32_t padding;
1259
1260 uint64_t reserved;
1261};
1262
1266struct fuse_uring_req_header {
1267 /* struct fuse_in_header / struct fuse_out_header */
1268 char in_out[FUSE_URING_IN_OUT_HEADER_SZ];
1269
1270 /* per op code header */
1271 char op_in[FUSE_URING_OP_IN_OUT_SZ];
1272
1273 struct fuse_uring_ent_in_out ring_ent_in_out;
1274};
1275
1279enum fuse_uring_cmd {
1280 FUSE_IO_URING_CMD_INVALID = 0,
1281
1282 /* register the request buffer and fetch a fuse request */
1283 FUSE_IO_URING_CMD_REGISTER = 1,
1284
1285 /* commit fuse request result and fetch next request */
1286 FUSE_IO_URING_CMD_COMMIT_AND_FETCH = 2,
1287};
1288
1292struct fuse_uring_cmd_req {
1293 uint64_t flags;
1294
1295 /* entry identifier for commits */
1296 uint64_t commit_id;
1297
1298 /* queue the command is for (queue index) */
1299 uint16_t qid;
1300 uint8_t padding[6];
1301};
1302
1303#endif /* _LINUX_FUSE_H */
fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__log_8h_source.html0000644000175000017500000003043015156613427024011 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_log.h Source File
libfuse
fuse_log.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2019 Red Hat, Inc.
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_LOG_H_
10#define FUSE_LOG_H_
11
17#include <stdarg.h>
18
19#ifdef __cplusplus
20extern "C" {
21#endif
22
29 FUSE_LOG_EMERG,
30 FUSE_LOG_ALERT,
31 FUSE_LOG_CRIT,
32 FUSE_LOG_ERR,
33 FUSE_LOG_WARNING,
34 FUSE_LOG_NOTICE,
35 FUSE_LOG_INFO,
36 FUSE_LOG_DEBUG
37};
38
52typedef void (*fuse_log_func_t)(enum fuse_log_level level,
53 const char *fmt, va_list ap);
54
69
76void fuse_log(enum fuse_log_level level, const char *fmt, ...)
77 __attribute__((format(printf, 2, 3)));
78
84void fuse_log_enable_syslog(const char *ident, int option, int facility);
85
89void fuse_log_close_syslog(void);
90
91#ifdef __cplusplus
92}
93#endif
94
95#endif /* FUSE_LOG_H_ */
void fuse_log_close_syslog(void)
Definition fuse_log.c:93
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void(* fuse_log_func_t)(enum fuse_log_level level, const char *fmt, va_list ap)
Definition fuse_log.h:52
void fuse_log_enable_syslog(const char *ident, int option, int facility)
Definition fuse_log.c:86
fuse_log_level
Definition fuse_log.h:28
void fuse_set_log_func(fuse_log_func_t func)
Definition fuse_log.c:69
fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__lowlevel_8h_source.html0000644000175000017500000052103715156613430025063 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_lowlevel.h Source File
libfuse
fuse_lowlevel.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_LOWLEVEL_H_
10#define FUSE_LOWLEVEL_H_
11
21#ifndef FUSE_USE_VERSION
22#error FUSE_USE_VERSION not defined
23#endif
24
25#include "fuse_common.h"
26
27#include <stddef.h>
28#include <utime.h>
29#include <fcntl.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/statvfs.h>
33#include <sys/uio.h>
34
35#ifdef __cplusplus
36extern "C" {
37#endif
38
39/* ----------------------------------------------------------- *
40 * Miscellaneous definitions *
41 * ----------------------------------------------------------- */
42
44#define FUSE_ROOT_ID 1
45
47typedef uint64_t fuse_ino_t;
48
50typedef struct fuse_req *fuse_req_t;
51
52/* Forward declaration */
53struct statx;
54
60struct fuse_session;
61
63struct fuse_entry_param {
72
83 uint64_t generation;
84
92 struct stat attr;
93
98 double attr_timeout;
99
104 double entry_timeout;
105};
106
115struct fuse_ctx {
117 uid_t uid;
118
120 gid_t gid;
121
123 pid_t pid;
124
126 mode_t umask;
127};
128
129struct fuse_forget_data {
130 fuse_ino_t ino;
131 uint64_t nlookup;
132};
133
134struct fuse_custom_io {
135 ssize_t (*writev)(int fd, struct iovec *iov, int count, void *userdata);
136 ssize_t (*read)(int fd, void *buf, size_t buf_len, void *userdata);
137 ssize_t (*splice_receive)(int fdin, off_t *offin, int fdout,
138 off_t *offout, size_t len,
139 unsigned int flags, void *userdata);
140 ssize_t (*splice_send)(int fdin, off_t *offin, int fdout,
141 off_t *offout, size_t len,
142 unsigned int flags, void *userdata);
143 int (*clone_fd)(int master_fd);
144};
145
152 FUSE_LL_INVALIDATE = 0,
153 FUSE_LL_EXPIRE_ONLY = (1 << 0),
154};
155
156/* 'to_set' flags in setattr */
157#define FUSE_SET_ATTR_MODE (1 << 0)
158#define FUSE_SET_ATTR_UID (1 << 1)
159#define FUSE_SET_ATTR_GID (1 << 2)
160#define FUSE_SET_ATTR_SIZE (1 << 3)
161#define FUSE_SET_ATTR_ATIME (1 << 4)
162#define FUSE_SET_ATTR_MTIME (1 << 5)
163#define FUSE_SET_ATTR_ATIME_NOW (1 << 7)
164#define FUSE_SET_ATTR_MTIME_NOW (1 << 8)
165#define FUSE_SET_ATTR_FORCE (1 << 9)
166#define FUSE_SET_ATTR_CTIME (1 << 10)
167#define FUSE_SET_ATTR_KILL_SUID (1 << 11)
168#define FUSE_SET_ATTR_KILL_SGID (1 << 12)
169#define FUSE_SET_ATTR_FILE (1 << 13)
170#define FUSE_SET_ATTR_KILL_PRIV (1 << 14)
171#define FUSE_SET_ATTR_OPEN (1 << 15)
172#define FUSE_SET_ATTR_TIMES_SET (1 << 16)
173#define FUSE_SET_ATTR_TOUCH (1 << 17)
174
175/* ----------------------------------------------------------- *
176 * Request methods and replies *
177 * ----------------------------------------------------------- */
178
208struct fuse_lowlevel_ops {
225 void (*init) (void *userdata, struct fuse_conn_info *conn);
226
238 void (*destroy) (void *userdata);
239
251 void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
252
289 void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup);
290
310 void (*getattr) (fuse_req_t req, fuse_ino_t ino,
311 struct fuse_file_info *fi);
312
347 void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
348 int to_set, struct fuse_file_info *fi);
349
360 void (*readlink) (fuse_req_t req, fuse_ino_t ino);
361
378 void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
379 mode_t mode, dev_t rdev);
380
393 void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
394 mode_t mode);
395
411 void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
412
428 void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
429
442 void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
443 const char *name);
444
474 void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
475 fuse_ino_t newparent, const char *newname,
476 unsigned int flags);
477
490 void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
491 const char *newname);
492
556 void (*open) (fuse_req_t req, fuse_ino_t ino,
557 struct fuse_file_info *fi);
558
584 void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
585 struct fuse_file_info *fi);
586
613 void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
614 size_t size, off_t off, struct fuse_file_info *fi);
615
654 void (*flush) (fuse_req_t req, fuse_ino_t ino,
655 struct fuse_file_info *fi);
656
682 void (*release) (fuse_req_t req, fuse_ino_t ino,
683 struct fuse_file_info *fi);
684
704 void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
705 struct fuse_file_info *fi);
706
736 void (*opendir) (fuse_req_t req, fuse_ino_t ino,
737 struct fuse_file_info *fi);
738
782 void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
783 struct fuse_file_info *fi);
784
801 void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
802 struct fuse_file_info *fi);
803
826 void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
827 struct fuse_file_info *fi);
828
839 void (*statfs) (fuse_req_t req, fuse_ino_t ino);
840
852 void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
853 const char *value, size_t size, int flags);
854
883 void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
884 size_t size);
885
914 void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
915
931 void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
932
953 void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
954
982 void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
983 mode_t mode, struct fuse_file_info *fi);
984
997 void (*getlk) (fuse_req_t req, fuse_ino_t ino,
998 struct fuse_file_info *fi, struct flock *lock);
999
1022 void (*setlk) (fuse_req_t req, fuse_ino_t ino,
1023 struct fuse_file_info *fi,
1024 struct flock *lock, int sleep);
1025
1046 void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
1047 uint64_t idx);
1048
1049#if FUSE_USE_VERSION < 35
1050 void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd,
1051 void *arg, struct fuse_file_info *fi, unsigned flags,
1052 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
1053#else
1082 void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
1083 void *arg, struct fuse_file_info *fi, unsigned flags,
1084 const void *in_buf, size_t in_bufsz, size_t out_bufsz);
1085#endif
1086
1119 void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
1120 struct fuse_pollhandle *ph);
1121
1149 void (*write_buf) (fuse_req_t req, fuse_ino_t ino,
1150 struct fuse_bufvec *bufv, off_t off,
1151 struct fuse_file_info *fi);
1152
1165 void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino,
1166 off_t offset, struct fuse_bufvec *bufv);
1167
1179 void (*forget_multi) (fuse_req_t req, size_t count,
1180 struct fuse_forget_data *forgets);
1181
1197 void (*flock) (fuse_req_t req, fuse_ino_t ino,
1198 struct fuse_file_info *fi, int op);
1199
1220 void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
1221 off_t offset, off_t length, struct fuse_file_info *fi);
1222
1248 void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1249 struct fuse_file_info *fi);
1250
1281 void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in,
1282 off_t off_in, struct fuse_file_info *fi_in,
1283 fuse_ino_t ino_out, off_t off_out,
1284 struct fuse_file_info *fi_out, size_t len,
1285 int flags);
1286
1305 void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1306 struct fuse_file_info *fi);
1307
1326 void (*tmpfile) (fuse_req_t req, fuse_ino_t parent,
1327 mode_t mode, struct fuse_file_info *fi);
1328
1342 void (*statx)(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
1343 struct fuse_file_info *fi);
1344};
1345
1367int fuse_reply_err(fuse_req_t req, int err);
1368
1379void fuse_reply_none(fuse_req_t req);
1380
1394int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
1395
1414int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
1415 const struct fuse_file_info *fi);
1416
1428int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
1429 double attr_timeout);
1430
1441int fuse_reply_readlink(fuse_req_t req, const char *link);
1442
1455int fuse_passthrough_open(fuse_req_t req, int fd);
1456int fuse_passthrough_close(fuse_req_t req, int backing_id);
1457
1472int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi);
1473
1484int fuse_reply_write(fuse_req_t req, size_t count);
1485
1497int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size);
1498
1542int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
1544
1556int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count);
1557
1568int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf);
1569
1580int fuse_reply_xattr(fuse_req_t req, size_t count);
1581
1592int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
1593
1604int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
1605
1606/* ----------------------------------------------------------- *
1607 * Filling a buffer in readdir *
1608 * ----------------------------------------------------------- */
1609
1637size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
1638 const char *name, const struct stat *stbuf,
1639 off_t off);
1640
1654size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
1655 const char *name,
1656 const struct fuse_entry_param *e, off_t off);
1657
1674 const struct iovec *in_iov, size_t in_count,
1675 const struct iovec *out_iov, size_t out_count);
1676
1688int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
1689
1701int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
1702 int count);
1703
1710int fuse_reply_poll(fuse_req_t req, unsigned revents);
1711
1722int fuse_reply_lseek(fuse_req_t req, off_t off);
1723
1736int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout);
1737
1738/* ----------------------------------------------------------- *
1739 * Notification *
1740 * ----------------------------------------------------------- */
1741
1749int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
1750
1774int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
1775 off_t off, off_t len);
1776
1789int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se);
1790
1815int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
1816 const char *name, size_t namelen);
1817
1846int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
1847 const char *name, size_t namelen);
1848
1877int fuse_lowlevel_notify_delete(struct fuse_session *se,
1878 fuse_ino_t parent, fuse_ino_t child,
1879 const char *name, size_t namelen);
1880
1906int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
1907 off_t offset, struct fuse_bufvec *bufv,
1938int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
1939 size_t size, off_t offset, void *cookie);
1940
1941
1942/* ----------------------------------------------------------- *
1943 * Utility functions *
1944 * ----------------------------------------------------------- */
1945
1952void *fuse_req_userdata(fuse_req_t req);
1953
1963const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
1964
1984int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
1985
1992typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data);
1993
2006 void *data);
2007
2015
2016
2017/* ----------------------------------------------------------- *
2018 * Inquiry functions *
2019 * ----------------------------------------------------------- */
2020
2024void fuse_lowlevel_version(void);
2025
2031void fuse_lowlevel_help(void);
2032
2036void fuse_cmdline_help(void);
2037
2038/* ----------------------------------------------------------- *
2039 * Filesystem setup & teardown *
2040 * ----------------------------------------------------------- */
2041
2047struct fuse_cmdline_opts {
2048 int singlethread;
2049 int foreground;
2050 int debug;
2051 int nodefault_subtype;
2052 char *mountpoint;
2053 int show_version;
2054 int show_help;
2055 int clone_fd;
2056 unsigned int max_idle_threads; /* discouraged, due to thread
2057 * destruct overhead */
2058
2059 /* Added in libfuse-3.12 */
2060 unsigned int max_threads;
2061};
2062
2081#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
2082int fuse_parse_cmdline(struct fuse_args *args,
2083 struct fuse_cmdline_opts *opts);
2084#else
2085#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
2086int fuse_parse_cmdline_30(struct fuse_args *args,
2087 struct fuse_cmdline_opts *opts);
2088#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_30(args, opts)
2089#else
2090int fuse_parse_cmdline_312(struct fuse_args *args,
2091 struct fuse_cmdline_opts *opts);
2092#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_312(args, opts)
2093#endif
2094#endif
2095
2096/* Do not call this directly, use fuse_session_new() instead */
2097struct fuse_session *
2098fuse_session_new_versioned(struct fuse_args *args,
2099 const struct fuse_lowlevel_ops *op, size_t op_size,
2100 struct libfuse_version *version, void *userdata);
2101
2132static inline struct fuse_session *
2133fuse_session_new_fn(struct fuse_args *args, const struct fuse_lowlevel_ops *op,
2134 size_t op_size, void *userdata)
2135{
2136 struct libfuse_version version = {
2137 .major = FUSE_MAJOR_VERSION,
2138 .minor = FUSE_MINOR_VERSION,
2139 .hotfix = FUSE_HOTFIX_VERSION,
2140 .padding = 0
2141 };
2142
2143 return fuse_session_new_versioned(args, op, op_size, &version,
2144 userdata);
2145}
2146#define fuse_session_new(args, op, op_size, userdata) \
2147 fuse_session_new_fn(args, op, op_size, userdata)
2148
2149/*
2150 * This should mostly not be called directly, but instead the
2151 * fuse_session_custom_io() should be used.
2152 */
2153int fuse_session_custom_io_317(struct fuse_session *se,
2154 const struct fuse_custom_io *io, size_t op_size, int fd);
2155
2183#if FUSE_MAKE_VERSION(3, 17) <= FUSE_USE_VERSION
2184static inline int fuse_session_custom_io(struct fuse_session *se,
2185 const struct fuse_custom_io *io, size_t op_size, int fd)
2186{
2187 return fuse_session_custom_io_317(se, io, op_size, fd);
2188}
2189#else
2190static inline int fuse_session_custom_io(struct fuse_session *se,
2191 const struct fuse_custom_io *io, int fd)
2192{
2193 return fuse_session_custom_io_317(se, io,
2194 offsetof(struct fuse_custom_io, clone_fd), fd);
2195}
2196#endif
2197
2206int fuse_session_mount(struct fuse_session *se, const char *mountpoint);
2207
2230int fuse_session_loop(struct fuse_session *se);
2231
2232#if FUSE_USE_VERSION < 32
2233 int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
2234 #define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd)
2235#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
2236 int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
2237 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_32(se, config)
2238#else
2239 #if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
2251 int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config);
2252 #else
2253 int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
2254 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_312(se, config)
2255 #endif
2256#endif
2257
2270void fuse_session_exit(struct fuse_session *se);
2271
2277void fuse_session_reset(struct fuse_session *se);
2278
2285int fuse_session_exited(struct fuse_session *se);
2286
2311void fuse_session_unmount(struct fuse_session *se);
2312
2318void fuse_session_destroy(struct fuse_session *se);
2319
2320/* ----------------------------------------------------------- *
2321 * Custom event loop support *
2322 * ----------------------------------------------------------- */
2323
2338int fuse_session_fd(struct fuse_session *se);
2339
2348void fuse_session_process_buf(struct fuse_session *se,
2349 const struct fuse_buf *buf);
2350
2362int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf);
2363
2368
2384int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
2385 void **mr);
2386
2387#ifdef __cplusplus
2388}
2389#endif
2390
2391#endif /* FUSE_LOWLEVEL_H_ */
fuse_buf_copy_flags
void fuse_session_destroy(struct fuse_session *se)
fuse_notify_entry_flags
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_session_fd(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
void fuse_session_reset(struct fuse_session *se)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_passthrough_open(fuse_req_t req, int fd)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_xattr(fuse_req_t req, size_t count)
bool fuse_req_is_uring(fuse_req_t req)
int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
mode_t umask
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
int32_t backing_id
void(* flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op)
void(* write)(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi)
void(* lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi)
void(* listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size)
void(* removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name)
void(* forget_multi)(fuse_req_t req, size_t count, struct fuse_forget_data *forgets)
void(* retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv)
void(* create)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi)
void(* mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
void(* symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)
void(* write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, off_t off, struct fuse_file_info *fi)
void(* rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name)
void(* link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)
void(* rename)(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags)
void(* copy_file_range)(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fuse_file_info *fi_in, fuse_ino_t ino_out, off_t off_out, struct fuse_file_info *fi_out, size_t len, int flags)
void(* poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph)
void(* opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* mknod)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
void(* fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi)
void(* setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)
void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
void(* fsync)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
void(* destroy)(void *userdata)
void(* forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
void(* getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* ioctl)(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz)
void(* open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
void(* fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
void(* init)(void *userdata, struct fuse_conn_info *conn)
void(* read)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags)
void(* release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* access)(fuse_req_t req, fuse_ino_t ino, int mask)
void(* releasedir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* statx)(fuse_req_t req, fuse_ino_t ino, int flags, int mask, struct fuse_file_info *fi)
void(* tmpfile)(fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info *fi)
void(* bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)
void(* readlink)(fuse_req_t req, fuse_ino_t ino)
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
void(* statfs)(fuse_req_t req, fuse_ino_t ino)
void(* readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)
void(* readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
void(* flush)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void(* unlink)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__mount__compat_8h_source.html0000644000175000017500000002247315156613430026076 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_mount_compat.h Source File
libfuse
fuse_mount_compat.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2023 Giulio Benetti <giulio.benetti@benettiengineering.com>
4
5 Logging API.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LICENSE
9*/
10
11#ifndef FUSE_MOUNT_COMPAT_H_
12#define FUSE_MOUNT_COMPAT_H_
13
14#include <sys/mount.h>
15
16/* Some libc don't define MS_*, so define them manually
17 * (values taken from https://elixir.bootlin.com/linux/v6.10/source/include/uapi/linux/mount.h#L13 on)
18 */
19#ifndef MS_DIRSYNC
20#define MS_DIRSYNC 128
21#endif
22
23#ifndef MS_NOSYMFOLLOW
24#define MS_NOSYMFOLLOW 256
25#endif
26
27#ifndef MS_REC
28#define MS_REC 16384
29#endif
30
31#ifndef MS_PRIVATE
32#define MS_PRIVATE (1<<18)
33#endif
34
35#ifndef MS_LAZYTIME
36#define MS_LAZYTIME (1<<25)
37#endif
38
39#ifndef UMOUNT_DETACH
40#define UMOUNT_DETACH 0x00000002 /* Just detach from the tree */
41#endif
42#ifndef UMOUNT_NOFOLLOW
43#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
44#endif
45#ifndef UMOUNT_UNUSED
46#define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
47#endif
48
49#endif /* FUSE_MOUNT_COMPAT_H_ */
fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__opt_8h_source.html0000644000175000017500000005221615156613430024032 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_opt.h Source File
libfuse
fuse_opt.h
Go to the documentation of this file.
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#ifndef FUSE_OPT_H_
10#define FUSE_OPT_H_
11
17#ifdef __cplusplus
18extern "C" {
19#endif
20
77struct fuse_opt {
79 const char *templ;
80
85 unsigned long offset;
86
91 int value;
92};
93
98#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
99
104#define FUSE_OPT_END { NULL, 0, 0 }
105
109struct fuse_args {
111 int argc;
112
114 char **argv;
115
117 int allocated;
118};
119
123#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
124
129#define FUSE_OPT_KEY_OPT -1
130
137#define FUSE_OPT_KEY_NONOPT -2
138
145#define FUSE_OPT_KEY_KEEP -3
146
153#define FUSE_OPT_KEY_DISCARD -4
154
180typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
181 struct fuse_args *outargs);
182
203int fuse_opt_parse(struct fuse_args *args, void *data,
204 const struct fuse_opt opts[], fuse_opt_proc_t proc);
205
213int fuse_opt_add_opt(char **opts, const char *opt);
214
222int fuse_opt_add_opt_escaped(char **opts, const char *opt);
223
231int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
232
246int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
247
255void fuse_opt_free_args(struct fuse_args *args);
256
257
265int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
266
267#ifdef __cplusplus
268}
269#endif
270
271#endif /* FUSE_OPT_H_ */
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition fuse_opt.h:180
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition fuse_opt.c:95
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
int allocated
Definition fuse_opt.h:117
char ** argv
Definition fuse_opt.h:114
unsigned long offset
Definition fuse_opt.h:85
const char * templ
Definition fuse_opt.h:79
int value
Definition fuse_opt.h:91
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2buffer_8c_source.html0000644000175000017500000020552315156613430022437 0ustar berndbernd libfuse: fuse-3.18.2/lib/buffer.c Source File
libfuse
buffer.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
4
5 Functions for dealing with `struct fuse_buf` and `struct
6 fuse_bufvec`.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_lowlevel.h"
17#include <string.h>
18#include <unistd.h>
19#include <errno.h>
20#include <assert.h>
21
22size_t fuse_buf_size(const struct fuse_bufvec *bufv)
23{
24 size_t i;
25 size_t size = 0;
26
27 for (i = 0; i < bufv->count; i++) {
28 if (bufv->buf[i].size >= SIZE_MAX - size)
29 return SIZE_MAX;
30
31 size += bufv->buf[i].size;
32 }
33
34 return size;
35}
36
37static size_t min_size(size_t s1, size_t s2)
38{
39 return s1 < s2 ? s1 : s2;
40}
41
42static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
43 const struct fuse_buf *src, size_t src_off,
44 size_t len)
45{
46 ssize_t res = 0;
47 size_t copied = 0;
48
49 while (len) {
50 if (dst->flags & FUSE_BUF_FD_SEEK) {
51 res = pwrite(dst->fd, (char *)src->mem + src_off, len,
52 dst->pos + dst_off);
53 } else {
54 res = write(dst->fd, (char *)src->mem + src_off, len);
55 }
56 if (res == -1) {
57 if (!copied)
58 return -errno;
59 break;
60 }
61 if (res == 0)
62 break;
63
64 copied += res;
65 if (!(dst->flags & FUSE_BUF_FD_RETRY))
66 break;
67
68 src_off += res;
69 dst_off += res;
70 len -= res;
71 }
72
73 return copied;
74}
75
76static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
77 const struct fuse_buf *src, size_t src_off,
78 size_t len)
79{
80 ssize_t res = 0;
81 size_t copied = 0;
82
83 while (len) {
84 if (src->flags & FUSE_BUF_FD_SEEK) {
85 res = pread(src->fd, (char *)dst->mem + dst_off, len,
86 src->pos + src_off);
87 } else {
88 res = read(src->fd, (char *)dst->mem + dst_off, len);
89 }
90 if (res == -1) {
91 if (!copied)
92 return -errno;
93 break;
94 }
95 if (res == 0)
96 break;
97
98 copied += res;
99 if (!(src->flags & FUSE_BUF_FD_RETRY))
100 break;
101
102 dst_off += res;
103 src_off += res;
104 len -= res;
105 }
106
107 return copied;
108}
109
110static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
111 const struct fuse_buf *src, size_t src_off,
112 size_t len)
113{
114 char buf[4096];
115 struct fuse_buf tmp = {
116 .size = sizeof(buf),
117 .flags = 0,
118 };
119 ssize_t res;
120 size_t copied = 0;
121
122 tmp.mem = buf;
123
124 while (len) {
125 size_t this_len = min_size(tmp.size, len);
126 size_t read_len;
127
128 res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
129 if (res < 0) {
130 if (!copied)
131 return res;
132 break;
133 }
134 if (res == 0)
135 break;
136
137 read_len = res;
138 res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
139 if (res < 0) {
140 if (!copied)
141 return res;
142 break;
143 }
144 if (res == 0)
145 break;
146
147 copied += res;
148
149 if (res < this_len)
150 break;
151
152 dst_off += res;
153 src_off += res;
154 len -= res;
155 }
156
157 return copied;
158}
159
160#ifdef HAVE_SPLICE
161static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
162 const struct fuse_buf *src, size_t src_off,
163 size_t len, enum fuse_buf_copy_flags flags)
164{
165 int splice_flags = 0;
166 off_t *srcpos = NULL;
167 off_t *dstpos = NULL;
168 off_t srcpos_val;
169 off_t dstpos_val;
170 ssize_t res;
171 size_t copied = 0;
172
174 splice_flags |= SPLICE_F_MOVE;
176 splice_flags |= SPLICE_F_NONBLOCK;
177
178 if (src->flags & FUSE_BUF_FD_SEEK) {
179 srcpos_val = src->pos + src_off;
180 srcpos = &srcpos_val;
181 }
182 if (dst->flags & FUSE_BUF_FD_SEEK) {
183 dstpos_val = dst->pos + dst_off;
184 dstpos = &dstpos_val;
185 }
186
187 while (len) {
188 res = splice(src->fd, srcpos, dst->fd, dstpos, len,
189 splice_flags);
190 if (res == -1) {
191 if (copied)
192 break;
193
194 if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
195 return -errno;
196
197 /* Maybe splice is not supported for this combination */
198 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
199 len);
200 }
201 if (res == 0)
202 break;
203
204 copied += res;
205 if (!(src->flags & FUSE_BUF_FD_RETRY) &&
206 !(dst->flags & FUSE_BUF_FD_RETRY)) {
207 break;
208 }
209
210 len -= res;
211 }
212
213 return copied;
214}
215#else
216static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
217 const struct fuse_buf *src, size_t src_off,
218 size_t len, enum fuse_buf_copy_flags flags)
219{
220 (void) flags;
221
222 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
223}
224#endif
225
226
227static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
228 const struct fuse_buf *src, size_t src_off,
229 size_t len, enum fuse_buf_copy_flags flags)
230{
231 int src_is_fd = src->flags & FUSE_BUF_IS_FD;
232 int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
233
234 if (!src_is_fd && !dst_is_fd) {
235 char *dstmem = (char *)dst->mem + dst_off;
236 char *srcmem = (char *)src->mem + src_off;
237
238 if (dstmem != srcmem) {
239 if (dstmem + len <= srcmem || srcmem + len <= dstmem)
240 memcpy(dstmem, srcmem, len);
241 else
242 memmove(dstmem, srcmem, len);
243 }
244
245 return len;
246 } else if (!src_is_fd) {
247 return fuse_buf_write(dst, dst_off, src, src_off, len);
248 } else if (!dst_is_fd) {
249 return fuse_buf_read(dst, dst_off, src, src_off, len);
250 } else if (flags & FUSE_BUF_NO_SPLICE) {
251 return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
252 } else {
253 return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
254 }
255}
256
257static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
258{
259 if (bufv->idx < bufv->count)
260 return &bufv->buf[bufv->idx];
261 else
262 return NULL;
263}
264
265static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
266{
267 const struct fuse_buf *buf = fuse_bufvec_current(bufv);
268
269 if (!buf)
270 return 0;
271
272 bufv->off += len;
273 assert(bufv->off <= buf->size);
274 if (bufv->off == buf->size) {
275 assert(bufv->idx < bufv->count);
276 bufv->idx++;
277 if (bufv->idx == bufv->count)
278 return 0;
279 bufv->off = 0;
280 }
281 return 1;
282}
283
284ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
286{
287 size_t copied = 0;
288
289 if (dstv == srcv)
290 return fuse_buf_size(dstv);
291
292 for (;;) {
293 const struct fuse_buf *src = fuse_bufvec_current(srcv);
294 const struct fuse_buf *dst = fuse_bufvec_current(dstv);
295 size_t src_len;
296 size_t dst_len;
297 size_t len;
298 ssize_t res;
299
300 if (src == NULL || dst == NULL)
301 break;
302
303 src_len = src->size - srcv->off;
304 dst_len = dst->size - dstv->off;
305 len = min_size(src_len, dst_len);
306
307 res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
308 if (res < 0) {
309 if (!copied)
310 return res;
311 break;
312 }
313 copied += res;
314
315 if (!fuse_bufvec_advance(srcv, res) ||
316 !fuse_bufvec_advance(dstv, res))
317 break;
318
319 if (res < len)
320 break;
321 }
322
323 return copied;
324}
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_FD_RETRY
@ FUSE_BUF_IS_FD
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
enum fuse_buf_flags flags
off_t pos
void * mem
size_t size
struct fuse_buf buf[1]
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2compat_8c_source.html0000644000175000017500000004534215156613430022452 0ustar berndbernd libfuse: fuse-3.18.2/lib/compat.c Source File
libfuse
compat.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Helper functions to create (simple) standalone programs. With the
6 aid of these functions it should be possible to create full FUSE
7 file system by implementing nothing but the request handlers.
8
9 This program can be distributed under the terms of the GNU LGPLv2.
10 See the file LGPL2.txt.
11*/
12
13/* Description:
14 This file has compatibility symbols for platforms that do not
15 support version symboling
16*/
17
18#include "libfuse_config.h"
19
20#include <stddef.h>
21#include <stdint.h>
22
23struct fuse_args;
26struct fuse_session;
27struct fuse_custom_io;
28struct fuse_operations;
30
34#if (!defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
35/* With current libfuse fuse_parse_cmdline is a macro pointing to the
36 * versioned function. Here in this file we need to provide the ABI symbol
37 * and the redirecting macro is conflicting.
38 */
39#ifdef fuse_parse_cmdline
40#undef fuse_parse_cmdline
41#endif
42int fuse_parse_cmdline_30(struct fuse_args *args,
43 struct fuse_cmdline_opts *opts);
44int fuse_parse_cmdline(struct fuse_args *args,
45 struct fuse_cmdline_opts *opts);
46int fuse_parse_cmdline(struct fuse_args *args,
47 struct fuse_cmdline_opts *opts)
48{
49 return fuse_parse_cmdline_30(args, opts);
50}
51
52int fuse_session_custom_io_30(struct fuse_session *se,
53 const struct fuse_custom_io *io, int fd);
54int fuse_session_custom_io(struct fuse_session *se,
55 const struct fuse_custom_io *io, int fd);
56int fuse_session_custom_io(struct fuse_session *se,
57 const struct fuse_custom_io *io, int fd)
58
59{
60 return fuse_session_custom_io_30(se, io, fd);
61}
62
63#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
64
65int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
66 size_t op_size, void *user_data);
67int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
68 size_t op_size, void *user_data);
69int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
70 size_t op_size, void *user_data)
71{
72 return fuse_main_real_30(argc, argv, op, op_size, user_data);
73}
74
75struct fuse_session *fuse_session_new_30(struct fuse_args *args,
76 const struct fuse_lowlevel_ops *op,
77 size_t op_size, void *userdata);
78struct fuse_session *fuse_session_new(struct fuse_args *args,
79 const struct fuse_lowlevel_ops *op,
80 size_t op_size, void *userdata);
81struct fuse_session *fuse_session_new(struct fuse_args *args,
82 const struct fuse_lowlevel_ops *op,
83 size_t op_size, void *userdata)
84{
85 return fuse_session_new_30(args, op, op_size, userdata);
86}
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2cuse__lowlevel_8c_source.html0000644000175000017500000021504415156613430024174 0ustar berndbernd libfuse: fuse-3.18.2/lib/cuse_lowlevel.c Source File
libfuse
cuse_lowlevel.c
1/*
2 CUSE: Character device in Userspace
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
5
6 This program can be distributed under the terms of the GNU LGPLv2.
7 See the file LGPL2.txt.
8*/
9
10#include "fuse_config.h"
11#include "cuse_lowlevel.h"
12#include "fuse_kernel.h"
13#include "fuse_i.h"
14#include "fuse_opt.h"
15
16#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19#include <stddef.h>
20#include <errno.h>
21#include <unistd.h>
22
23struct cuse_data {
24 struct cuse_lowlevel_ops clop;
25 unsigned max_read;
26 unsigned dev_major;
27 unsigned dev_minor;
28 unsigned flags;
29 unsigned dev_info_len;
30 char dev_info[];
31};
32
33static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
34{
35 return &req->se->cuse_data->clop;
36}
37
38static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
39 struct fuse_file_info *fi)
40{
41 (void)ino;
42 req_clop(req)->open(req, fi);
43}
44
45static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
46 off_t off, struct fuse_file_info *fi)
47{
48 (void)ino;
49 req_clop(req)->read(req, size, off, fi);
50}
51
52static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
53 size_t size, off_t off, struct fuse_file_info *fi)
54{
55 (void)ino;
56 req_clop(req)->write(req, buf, size, off, fi);
57}
58
59static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
60 struct fuse_file_info *fi)
61{
62 (void)ino;
63 req_clop(req)->flush(req, fi);
64}
65
66static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
67 struct fuse_file_info *fi)
68{
69 (void)ino;
70 req_clop(req)->release(req, fi);
71}
72
73static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
74 struct fuse_file_info *fi)
75{
76 (void)ino;
77 req_clop(req)->fsync(req, datasync, fi);
78}
79
80static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg,
81 struct fuse_file_info *fi, unsigned int flags,
82 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
83{
84 (void)ino;
85 req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
86 out_bufsz);
87}
88
89static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
90 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
91{
92 (void)ino;
93 req_clop(req)->poll(req, fi, ph);
94}
95
96static size_t cuse_pack_info(int argc, const char **argv, char *buf)
97{
98 size_t size = 0;
99 int i;
100
101 for (i = 0; i < argc; i++) {
102 size_t len;
103
104 len = strlen(argv[i]) + 1;
105 size += len;
106 if (buf) {
107 memcpy(buf, argv[i], len);
108 buf += len;
109 }
110 }
111
112 return size;
113}
114
115static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
116 const struct cuse_lowlevel_ops *clop)
117{
118 struct cuse_data *cd;
119 size_t dev_info_len;
120
121 dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
122 NULL);
123
124 if (dev_info_len > CUSE_INIT_INFO_MAX) {
125 fuse_log(FUSE_LOG_ERR, "cuse: dev_info (%zu) too large, limit=%u\n",
126 dev_info_len, CUSE_INIT_INFO_MAX);
127 return NULL;
128 }
129
130 cd = calloc(1, sizeof(*cd) + dev_info_len);
131 if (!cd) {
132 fuse_log(FUSE_LOG_ERR, "cuse: failed to allocate cuse_data\n");
133 return NULL;
134 }
135
136 memcpy(&cd->clop, clop, sizeof(cd->clop));
137 cd->max_read = 131072;
138 cd->dev_major = ci->dev_major;
139 cd->dev_minor = ci->dev_minor;
140 cd->dev_info_len = dev_info_len;
141 cd->flags = ci->flags;
142 cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
143
144 return cd;
145}
146
147struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
148 const struct cuse_info *ci,
149 const struct cuse_lowlevel_ops *clop,
150 void *userdata)
151{
152 struct fuse_lowlevel_ops lop;
153 struct cuse_data *cd;
154 struct fuse_session *se;
155
156 cd = cuse_prep_data(ci, clop);
157 if (!cd)
158 return NULL;
159
160 memset(&lop, 0, sizeof(lop));
161 lop.init = clop->init;
162 lop.destroy = clop->destroy;
163 lop.open = clop->open ? cuse_fll_open : NULL;
164 lop.read = clop->read ? cuse_fll_read : NULL;
165 lop.write = clop->write ? cuse_fll_write : NULL;
166 lop.flush = clop->flush ? cuse_fll_flush : NULL;
167 lop.release = clop->release ? cuse_fll_release : NULL;
168 lop.fsync = clop->fsync ? cuse_fll_fsync : NULL;
169 lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL;
170 lop.poll = clop->poll ? cuse_fll_poll : NULL;
171
172 se = fuse_session_new(args, &lop, sizeof(lop), userdata);
173 if (!se) {
174 free(cd);
175 return NULL;
176 }
177 se->cuse_data = cd;
178
179 return se;
180}
181
182static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
183 char *dev_info, unsigned dev_info_len)
184{
185 struct iovec iov[3];
186
187 iov[1].iov_base = arg;
188 iov[1].iov_len = sizeof(struct cuse_init_out);
189 iov[2].iov_base = dev_info;
190 iov[2].iov_len = dev_info_len;
191
192 return fuse_send_reply_iov_nofree(req, 0, iov, 3);
193}
194
195void _cuse_lowlevel_init(fuse_req_t req, const fuse_ino_t nodeid,
196 const void *op_in, const void *req_payload)
197{
198 const struct fuse_init_in *arg = op_in;
199 (void)req_payload;
200 struct cuse_init_out outarg;
201 struct fuse_session *se = req->se;
202 struct cuse_data *cd = se->cuse_data;
203 size_t bufsize = se->bufsize;
204 struct cuse_lowlevel_ops *clop = req_clop(req);
205
206 (void) nodeid;
207 if (se->debug) {
208 fuse_log(FUSE_LOG_DEBUG, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
209 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
210 }
211 se->conn.proto_major = arg->major;
212 se->conn.proto_minor = arg->minor;
213
214 /* XXX This is not right.*/
215 se->conn.capable_ext = 0;
216 se->conn.want_ext = 0;
217
218 if (arg->major < 7) {
219 fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n",
220 arg->major, arg->minor);
221 fuse_reply_err(req, EPROTO);
222 return;
223 }
224
225 if (bufsize < FUSE_MIN_READ_BUFFER) {
226 fuse_log(FUSE_LOG_ERR, "cuse: warning: buffer size too small: %zu\n",
227 bufsize);
228 bufsize = FUSE_MIN_READ_BUFFER;
229 }
230
231 bufsize -= 4096;
232 if (bufsize < se->conn.max_write)
233 se->conn.max_write = bufsize;
234
235 se->got_init = 1;
236 if (se->op.init)
237 se->op.init(se->userdata, &se->conn);
238
239 memset(&outarg, 0, sizeof(outarg));
240 outarg.major = FUSE_KERNEL_VERSION;
241 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
242 outarg.flags = cd->flags;
243 outarg.max_read = cd->max_read;
244 outarg.max_write = se->conn.max_write;
245 outarg.dev_major = cd->dev_major;
246 outarg.dev_minor = cd->dev_minor;
247
248 if (se->debug) {
249 fuse_log(FUSE_LOG_DEBUG, " CUSE_INIT: %u.%u\n",
250 outarg.major, outarg.minor);
251 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
252 fuse_log(FUSE_LOG_DEBUG, " max_read=0x%08x\n", outarg.max_read);
253 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
254 fuse_log(FUSE_LOG_DEBUG, " dev_major=%u\n", outarg.dev_major);
255 fuse_log(FUSE_LOG_DEBUG, " dev_minor=%u\n", outarg.dev_minor);
256 fuse_log(FUSE_LOG_DEBUG, " dev_info: %.*s\n", cd->dev_info_len,
257 cd->dev_info);
258 }
259
260 cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
261
262 if (clop->init_done)
263 clop->init_done(se->userdata);
264
265 fuse_free_req(req);
266}
267
268void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
269{
270 _cuse_lowlevel_init(req, nodeid, inarg, NULL);
271}
272
273struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
274 const struct cuse_info *ci,
275 const struct cuse_lowlevel_ops *clop,
276 int *multithreaded, void *userdata)
277{
278 const char *devname = "/dev/cuse";
279 static const struct fuse_opt kill_subtype_opts[] = {
282 };
283 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
284 struct fuse_session *se;
285 struct fuse_cmdline_opts opts;
286 int fd;
287 int res;
288
289 if (fuse_parse_cmdline(&args, &opts) == -1)
290 return NULL;
291 *multithreaded = !opts.singlethread;
292
293 /* Remove subtype= option */
294 res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
295 if (res == -1)
296 goto out1;
297
298 /*
299 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
300 * would ensue.
301 */
302 do {
303 fd = open("/dev/null", O_RDWR);
304 if (fd > 2)
305 close(fd);
306 } while (fd >= 0 && fd <= 2);
307
308 se = cuse_lowlevel_new(&args, ci, clop, userdata);
309 if (se == NULL)
310 goto out1;
311
312 fd = open(devname, O_RDWR);
313 if (fd == -1) {
314 if (errno == ENODEV || errno == ENOENT)
315 fuse_log(FUSE_LOG_ERR, "cuse: device not found, try 'modprobe cuse' first\n");
316 else
317 fuse_log(FUSE_LOG_ERR, "cuse: failed to open %s: %s\n",
318 devname, strerror(errno));
319 goto err_se;
320 }
321 se->fd = fd;
322
323 res = fuse_set_signal_handlers(se);
324 if (res == -1)
325 goto err_se;
326
327 res = fuse_daemonize(opts.foreground);
328 if (res == -1)
329 goto err_sig;
330
331 fuse_opt_free_args(&args);
332 return se;
333
334err_sig:
336err_se:
338out1:
339 free(opts.mountpoint);
340 fuse_opt_free_args(&args);
341 return NULL;
342}
343
344void cuse_lowlevel_teardown(struct fuse_session *se)
345{
348}
349
350int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
351 const struct cuse_lowlevel_ops *clop, void *userdata)
352{
353 struct fuse_session *se;
354 int multithreaded;
355 int res;
356
357 se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
358 userdata);
359 if (se == NULL)
360 return 1;
361
362 if (multithreaded) {
363 struct fuse_loop_config *config = fuse_loop_cfg_create();
364 res = fuse_session_loop_mt(se, config);
365 fuse_loop_cfg_destroy(config);
366 }
367 else
368 res = fuse_session_loop(se);
369
370 cuse_lowlevel_teardown(se);
371 if (res == -1)
372 return 1;
373
374 return 0;
375}
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
uint64_t fuse_ino_t
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_DISCARD
Definition fuse_opt.h:153
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse_8c_source.html0000644000175000017500000330527315156613430022136 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse.c Source File
libfuse
fuse.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the high-level FUSE API on top of the low-level
6 API.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#define _GNU_SOURCE
13#include "fuse.h"
14#include <pthread.h>
15
16#include "fuse_config.h"
17#include "fuse_i.h"
18#include "fuse_lowlevel.h"
19#include "fuse_opt.h"
20#include "fuse_misc.h"
21#include "fuse_kernel.h"
22#include "util.h"
23
24#include <stdint.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <stddef.h>
29#include <stdbool.h>
30#include <unistd.h>
31#include <time.h>
32#include <fcntl.h>
33#include <limits.h>
34#include <errno.h>
35#include <signal.h>
36#include <dlfcn.h>
37#include <assert.h>
38#include <poll.h>
39#include <sys/param.h>
40#include <sys/uio.h>
41#include <sys/time.h>
42#include <sys/mman.h>
43#include <sys/file.h>
44
45#define FUSE_NODE_SLAB 1
46
47#ifndef MAP_ANONYMOUS
48#undef FUSE_NODE_SLAB
49#endif
50
51#ifndef RENAME_EXCHANGE
52#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
53#endif
54
55#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
56
57#define FUSE_UNKNOWN_INO 0xffffffff
58#define OFFSET_MAX 0x7fffffffffffffffLL
59
60#define NODE_TABLE_MIN_SIZE 8192
61
62struct fuse_fs {
63 struct fuse_operations op;
64 void *user_data;
65 int debug;
66};
67
68struct fusemod_so {
69 void *handle;
70 int ctr;
71};
72
73struct lock_queue_element {
74 struct lock_queue_element *next;
75 pthread_cond_t cond;
76 fuse_ino_t nodeid1;
77 const char *name1;
78 char **path1;
79 struct node **wnode1;
80 fuse_ino_t nodeid2;
81 const char *name2;
82 char **path2;
83 struct node **wnode2;
84 int err;
85 bool done : 1;
86};
87
88struct node_table {
89 struct node **array;
90 size_t use;
91 size_t size;
92 size_t split;
93};
94
95#define list_entry(ptr, type, member) \
96 container_of(ptr, type, member)
97
98struct list_head {
99 struct list_head *next;
100 struct list_head *prev;
101};
102
103struct node_slab {
104 struct list_head list; /* must be the first member */
105 struct list_head freelist;
106 int used;
107};
108
109struct fuse {
110 struct fuse_session *se;
111 struct node_table name_table;
112 struct node_table id_table;
113 struct list_head lru_table;
114 fuse_ino_t ctr;
115 unsigned int generation;
116 unsigned int hidectr;
117 pthread_mutex_t lock;
118 struct fuse_config conf;
119 int intr_installed;
120 struct fuse_fs *fs;
121 struct lock_queue_element *lockq;
122 int pagesize;
123 struct list_head partial_slabs;
124 struct list_head full_slabs;
125 pthread_t prune_thread;
126};
127
128struct lock {
129 int type;
130 off_t start;
131 off_t end;
132 pid_t pid;
133 uint64_t owner;
134 struct lock *next;
135};
136
137struct node {
138 struct node *name_next;
139 struct node *id_next;
140 fuse_ino_t nodeid;
141 unsigned int generation;
142 int refctr;
143 struct node *parent;
144 char *name;
145 uint64_t nlookup;
146 int open_count;
147 struct timespec stat_updated;
148 struct timespec mtime;
149 off_t size;
150 struct lock *locks;
151 unsigned int is_hidden : 1;
152 unsigned int cache_valid : 1;
153 int treelock;
154 char inline_name[32];
155};
156
157#define TREELOCK_WRITE -1
158#define TREELOCK_WAIT_OFFSET INT_MIN
159
160struct node_lru {
161 struct node node;
162 struct list_head lru;
163 struct timespec forget_time;
164};
165
166struct fuse_direntry {
167 struct stat stat;
168 enum fuse_fill_dir_flags flags;
169 char *name;
170 struct fuse_direntry *next;
171};
172
173struct fuse_dh {
174 pthread_mutex_t lock;
175 struct fuse *fuse;
176 fuse_req_t req;
177 char *contents;
178 struct fuse_direntry *first;
179 struct fuse_direntry **last;
180 unsigned len;
181 unsigned size;
182 unsigned needlen;
183 int filled;
184 uint64_t fh;
185 int error;
186 fuse_ino_t nodeid;
187};
188
189struct fuse_context_i {
190 struct fuse_context ctx;
191 fuse_req_t req;
192};
193
194/* Defined by FUSE_REGISTER_MODULE() in lib/modules/subdir.c and iconv.c. */
195extern fuse_module_factory_t fuse_module_subdir_factory;
196#ifdef HAVE_ICONV
197extern fuse_module_factory_t fuse_module_iconv_factory;
198#endif
199
200static pthread_key_t fuse_context_key;
201static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
202static int fuse_context_ref;
203static struct fuse_module *fuse_modules = NULL;
204
205static int fuse_register_module(const char *name,
206 fuse_module_factory_t factory,
207 struct fusemod_so *so)
208{
209 struct fuse_module *mod;
210
211 mod = calloc(1, sizeof(struct fuse_module));
212 if (!mod) {
213 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module\n");
214 return -1;
215 }
216 mod->name = strdup(name);
217 if (!mod->name) {
218 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module name\n");
219 free(mod);
220 return -1;
221 }
222 mod->factory = factory;
223 mod->ctr = 0;
224 mod->so = so;
225 if (mod->so)
226 mod->so->ctr++;
227 mod->next = fuse_modules;
228 fuse_modules = mod;
229
230 return 0;
231}
232
233static void fuse_unregister_module(struct fuse_module *m)
234{
235 struct fuse_module **mp;
236 for (mp = &fuse_modules; *mp; mp = &(*mp)->next) {
237 if (*mp == m) {
238 *mp = (*mp)->next;
239 break;
240 }
241 }
242 free(m->name);
243 free(m);
244}
245
246static int fuse_load_so_module(const char *module)
247{
248 int ret = -1;
249 char *tmp;
250 struct fusemod_so *so;
251 fuse_module_factory_t *factory;
252
253 tmp = malloc(strlen(module) + 64);
254 if (!tmp) {
255 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
256 return -1;
257 }
258 sprintf(tmp, "libfusemod_%s.so", module);
259 so = calloc(1, sizeof(struct fusemod_so));
260 if (!so) {
261 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module so\n");
262 goto out;
263 }
264
265 so->handle = dlopen(tmp, RTLD_NOW);
266 if (so->handle == NULL) {
267 fuse_log(FUSE_LOG_ERR, "fuse: dlopen(%s) failed: %s\n",
268 tmp, dlerror());
269 goto out_free_so;
270 }
271
272 sprintf(tmp, "fuse_module_%s_factory", module);
273 factory = (fuse_module_factory_t*)dlsym(so->handle, tmp);
274 if (factory == NULL) {
275 fuse_log(FUSE_LOG_ERR, "fuse: symbol <%s> not found in module: %s\n",
276 tmp, dlerror());
277 goto out_dlclose;
278 }
279 ret = fuse_register_module(module, *factory, so);
280 if (ret)
281 goto out_dlclose;
282
283out:
284 free(tmp);
285 return ret;
286
287out_dlclose:
288 dlclose(so->handle);
289out_free_so:
290 free(so);
291 goto out;
292}
293
294static struct fuse_module *fuse_find_module(const char *module)
295{
296 struct fuse_module *m;
297 for (m = fuse_modules; m; m = m->next) {
298 if (strcmp(module, m->name) == 0) {
299 m->ctr++;
300 break;
301 }
302 }
303 return m;
304}
305
306static struct fuse_module *fuse_get_module(const char *module)
307{
308 struct fuse_module *m;
309
310 pthread_mutex_lock(&fuse_context_lock);
311 m = fuse_find_module(module);
312 if (!m) {
313 int err = fuse_load_so_module(module);
314 if (!err)
315 m = fuse_find_module(module);
316 }
317 pthread_mutex_unlock(&fuse_context_lock);
318 return m;
319}
320
321static void fuse_put_module(struct fuse_module *m)
322{
323 pthread_mutex_lock(&fuse_context_lock);
324 if (m->so)
325 assert(m->ctr > 0);
326 /* Builtin modules may already have m->ctr == 0 */
327 if (m->ctr > 0)
328 m->ctr--;
329 if (!m->ctr && m->so) {
330 struct fusemod_so *so = m->so;
331 assert(so->ctr > 0);
332 so->ctr--;
333 if (!so->ctr) {
334 struct fuse_module **mp;
335 for (mp = &fuse_modules; *mp;) {
336 if ((*mp)->so == so)
337 fuse_unregister_module(*mp);
338 else
339 mp = &(*mp)->next;
340 }
341 dlclose(so->handle);
342 free(so);
343 }
344 } else if (!m->ctr) {
345 fuse_unregister_module(m);
346 }
347 pthread_mutex_unlock(&fuse_context_lock);
348}
349
350static void init_list_head(struct list_head *list)
351{
352 list->next = list;
353 list->prev = list;
354}
355
356static int list_empty(const struct list_head *head)
357{
358 return head->next == head;
359}
360
361static void list_add(struct list_head *new, struct list_head *prev,
362 struct list_head *next)
363{
364 next->prev = new;
365 new->next = next;
366 new->prev = prev;
367 prev->next = new;
368}
369
370static inline void list_add_head(struct list_head *new, struct list_head *head)
371{
372 list_add(new, head, head->next);
373}
374
375static inline void list_add_tail(struct list_head *new, struct list_head *head)
376{
377 list_add(new, head->prev, head);
378}
379
380static inline void list_del(struct list_head *entry)
381{
382 struct list_head *prev = entry->prev;
383 struct list_head *next = entry->next;
384
385 next->prev = prev;
386 prev->next = next;
387}
388
389static inline int lru_enabled(struct fuse *f)
390{
391 return f->conf.remember > 0;
392}
393
394static struct node_lru *node_lru(struct node *node)
395{
396 return (struct node_lru *) node;
397}
398
399static size_t get_node_size(struct fuse *f)
400{
401 if (lru_enabled(f))
402 return sizeof(struct node_lru);
403 else
404 return sizeof(struct node);
405}
406
407#ifdef FUSE_NODE_SLAB
408static struct node_slab *list_to_slab(struct list_head *head)
409{
410 return (struct node_slab *) head;
411}
412
413static struct node_slab *node_to_slab(struct fuse *f, struct node *node)
414{
415 return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1));
416}
417
418static int alloc_slab(struct fuse *f)
419{
420 void *mem;
421 struct node_slab *slab;
422 char *start;
423 size_t num;
424 size_t i;
425 size_t node_size = get_node_size(f);
426
427 mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE,
428 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
429
430 if (mem == MAP_FAILED)
431 return -1;
432
433 slab = mem;
434 init_list_head(&slab->freelist);
435 slab->used = 0;
436 num = (f->pagesize - sizeof(struct node_slab)) / node_size;
437
438 start = (char *) mem + f->pagesize - num * node_size;
439 for (i = 0; i < num; i++) {
440 struct list_head *n;
441
442 n = (struct list_head *) (start + i * node_size);
443 list_add_tail(n, &slab->freelist);
444 }
445 list_add_tail(&slab->list, &f->partial_slabs);
446
447 return 0;
448}
449
450static struct node *alloc_node(struct fuse *f)
451{
452 struct node_slab *slab;
453 struct list_head *node;
454
455 if (list_empty(&f->partial_slabs)) {
456 int res = alloc_slab(f);
457 if (res != 0)
458 return NULL;
459 }
460 slab = list_to_slab(f->partial_slabs.next);
461 slab->used++;
462 node = slab->freelist.next;
463 list_del(node);
464 if (list_empty(&slab->freelist)) {
465 list_del(&slab->list);
466 list_add_tail(&slab->list, &f->full_slabs);
467 }
468 memset(node, 0, sizeof(struct node));
469
470 return (struct node *) node;
471}
472
473static void free_slab(struct fuse *f, struct node_slab *slab)
474{
475 int res;
476
477 list_del(&slab->list);
478 res = munmap(slab, f->pagesize);
479 if (res == -1)
480 fuse_log(FUSE_LOG_WARNING, "fuse warning: munmap(%p) failed\n",
481 slab);
482}
483
484static void free_node_mem(struct fuse *f, struct node *node)
485{
486 struct node_slab *slab = node_to_slab(f, node);
487 struct list_head *n = (struct list_head *) node;
488
489 slab->used--;
490 if (slab->used) {
491 if (list_empty(&slab->freelist)) {
492 list_del(&slab->list);
493 list_add_tail(&slab->list, &f->partial_slabs);
494 }
495 list_add_head(n, &slab->freelist);
496 } else {
497 free_slab(f, slab);
498 }
499}
500#else
501static struct node *alloc_node(struct fuse *f)
502{
503 return (struct node *) calloc(1, get_node_size(f));
504}
505
506static void free_node_mem(struct fuse *f, struct node *node)
507{
508 (void) f;
509 free(node);
510}
511#endif
512
513static size_t id_hash(struct fuse *f, fuse_ino_t ino)
514{
515 uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size;
516 uint64_t oldhash = hash % (f->id_table.size / 2);
517
518 if (oldhash >= f->id_table.split)
519 return oldhash;
520 else
521 return hash;
522}
523
524static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
525{
526 size_t hash = id_hash(f, nodeid);
527 struct node *node;
528
529 for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
530 if (node->nodeid == nodeid)
531 return node;
532
533 return NULL;
534}
535
536static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
537{
538 struct node *node = get_node_nocheck(f, nodeid);
539 if (!node) {
540 fuse_log(FUSE_LOG_ERR, "fuse internal error: node %llu not found\n",
541 (unsigned long long) nodeid);
542 abort();
543 }
544 return node;
545}
546
547static void curr_time(struct timespec *now);
548static double diff_timespec(const struct timespec *t1,
549 const struct timespec *t2);
550
551static void remove_node_lru(struct node *node)
552{
553 struct node_lru *lnode = node_lru(node);
554 list_del(&lnode->lru);
555 init_list_head(&lnode->lru);
556}
557
558static void set_forget_time(struct fuse *f, struct node *node)
559{
560 struct node_lru *lnode = node_lru(node);
561
562 list_del(&lnode->lru);
563 list_add_tail(&lnode->lru, &f->lru_table);
564 curr_time(&lnode->forget_time);
565}
566
567static void free_node(struct fuse *f, struct node *node)
568{
569 if (node->name != node->inline_name)
570 free(node->name);
571 free_node_mem(f, node);
572}
573
574static void node_table_reduce(struct node_table *t)
575{
576 size_t newsize = t->size / 2;
577 void *newarray;
578
579 if (newsize < NODE_TABLE_MIN_SIZE)
580 return;
581
582 newarray = realloc(t->array, sizeof(struct node *) * newsize);
583 if (newarray != NULL)
584 t->array = newarray;
585
586 t->size = newsize;
587 t->split = t->size / 2;
588}
589
590static void remerge_id(struct fuse *f)
591{
592 struct node_table *t = &f->id_table;
593 int iter;
594
595 if (t->split == 0)
596 node_table_reduce(t);
597
598 for (iter = 8; t->split > 0 && iter; iter--) {
599 struct node **upper;
600
601 t->split--;
602 upper = &t->array[t->split + t->size / 2];
603 if (*upper) {
604 struct node **nodep;
605
606 for (nodep = &t->array[t->split]; *nodep;
607 nodep = &(*nodep)->id_next);
608
609 *nodep = *upper;
610 *upper = NULL;
611 break;
612 }
613 }
614}
615
616static void unhash_id(struct fuse *f, struct node *node)
617{
618 struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
619
620 for (; *nodep != NULL; nodep = &(*nodep)->id_next)
621 if (*nodep == node) {
622 *nodep = node->id_next;
623 f->id_table.use--;
624
625 if(f->id_table.use < f->id_table.size / 4)
626 remerge_id(f);
627 return;
628 }
629}
630
631static int node_table_resize(struct node_table *t)
632{
633 size_t newsize = t->size * 2;
634 void *newarray;
635
636 newarray = realloc(t->array, sizeof(struct node *) * newsize);
637 if (newarray == NULL)
638 return -1;
639
640 t->array = newarray;
641 memset(t->array + t->size, 0, t->size * sizeof(struct node *));
642 t->size = newsize;
643 t->split = 0;
644
645 return 0;
646}
647
648static void rehash_id(struct fuse *f)
649{
650 struct node_table *t = &f->id_table;
651 struct node **nodep;
652 struct node **next;
653 size_t hash;
654
655 if (t->split == t->size / 2)
656 return;
657
658 hash = t->split;
659 t->split++;
660 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
661 struct node *node = *nodep;
662 size_t newhash = id_hash(f, node->nodeid);
663
664 if (newhash != hash) {
665 next = nodep;
666 *nodep = node->id_next;
667 node->id_next = t->array[newhash];
668 t->array[newhash] = node;
669 } else {
670 next = &node->id_next;
671 }
672 }
673 if (t->split == t->size / 2)
674 node_table_resize(t);
675}
676
677static void hash_id(struct fuse *f, struct node *node)
678{
679 size_t hash = id_hash(f, node->nodeid);
680 node->id_next = f->id_table.array[hash];
681 f->id_table.array[hash] = node;
682 f->id_table.use++;
683
684 if (f->id_table.use >= f->id_table.size / 2)
685 rehash_id(f);
686}
687
688static size_t name_hash(struct fuse *f, fuse_ino_t parent,
689 const char *name)
690{
691 uint64_t hash = parent;
692 uint64_t oldhash;
693
694 for (; *name; name++)
695 hash = hash * 31 + (unsigned char) *name;
696
697 hash %= f->name_table.size;
698 oldhash = hash % (f->name_table.size / 2);
699 if (oldhash >= f->name_table.split)
700 return oldhash;
701 else
702 return hash;
703}
704
705static void unref_node(struct fuse *f, struct node *node);
706
707static void remerge_name(struct fuse *f)
708{
709 struct node_table *t = &f->name_table;
710 int iter;
711
712 if (t->split == 0)
713 node_table_reduce(t);
714
715 for (iter = 8; t->split > 0 && iter; iter--) {
716 struct node **upper;
717
718 t->split--;
719 upper = &t->array[t->split + t->size / 2];
720 if (*upper) {
721 struct node **nodep;
722
723 for (nodep = &t->array[t->split]; *nodep;
724 nodep = &(*nodep)->name_next);
725
726 *nodep = *upper;
727 *upper = NULL;
728 break;
729 }
730 }
731}
732
733static void unhash_name(struct fuse *f, struct node *node)
734{
735 if (node->name) {
736 size_t hash = name_hash(f, node->parent->nodeid, node->name);
737 struct node **nodep = &f->name_table.array[hash];
738
739 for (; *nodep != NULL; nodep = &(*nodep)->name_next)
740 if (*nodep == node) {
741 *nodep = node->name_next;
742 node->name_next = NULL;
743 unref_node(f, node->parent);
744 if (node->name != node->inline_name)
745 free(node->name);
746 node->name = NULL;
747 node->parent = NULL;
748 f->name_table.use--;
749
750 if (f->name_table.use < f->name_table.size / 4)
751 remerge_name(f);
752 return;
753 }
754 fuse_log(FUSE_LOG_ERR,
755 "fuse internal error: unable to unhash node: %llu\n",
756 (unsigned long long) node->nodeid);
757 abort();
758 }
759}
760
761static void rehash_name(struct fuse *f)
762{
763 struct node_table *t = &f->name_table;
764 struct node **nodep;
765 struct node **next;
766 size_t hash;
767
768 if (t->split == t->size / 2)
769 return;
770
771 hash = t->split;
772 t->split++;
773 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
774 struct node *node = *nodep;
775 size_t newhash = name_hash(f, node->parent->nodeid, node->name);
776
777 if (newhash != hash) {
778 next = nodep;
779 *nodep = node->name_next;
780 node->name_next = t->array[newhash];
781 t->array[newhash] = node;
782 } else {
783 next = &node->name_next;
784 }
785 }
786 if (t->split == t->size / 2)
787 node_table_resize(t);
788}
789
790static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
791 const char *name)
792{
793 size_t hash = name_hash(f, parentid, name);
794 struct node *parent = get_node(f, parentid);
795 if (strlen(name) < sizeof(node->inline_name)) {
796 strcpy(node->inline_name, name);
797 node->name = node->inline_name;
798 } else {
799 node->name = strdup(name);
800 if (node->name == NULL)
801 return -1;
802 }
803
804 parent->refctr ++;
805 node->parent = parent;
806 node->name_next = f->name_table.array[hash];
807 f->name_table.array[hash] = node;
808 f->name_table.use++;
809
810 if (f->name_table.use >= f->name_table.size / 2)
811 rehash_name(f);
812
813 return 0;
814}
815
816static void delete_node(struct fuse *f, struct node *node)
817{
818 if (f->conf.debug)
819 fuse_log(FUSE_LOG_DEBUG, "DELETE: %llu\n",
820 (unsigned long long) node->nodeid);
821
822 assert(node->treelock == 0);
823 unhash_name(f, node);
824 if (lru_enabled(f))
825 remove_node_lru(node);
826 unhash_id(f, node);
827 free_node(f, node);
828}
829
830static void unref_node(struct fuse *f, struct node *node)
831{
832 assert(node->refctr > 0);
833 node->refctr --;
834 if (!node->refctr)
835 delete_node(f, node);
836}
837
838static fuse_ino_t next_id(struct fuse *f)
839{
840 do {
841 f->ctr = (f->ctr + 1) & 0xffffffff;
842 if (!f->ctr)
843 f->generation ++;
844 } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
845 get_node_nocheck(f, f->ctr) != NULL);
846 return f->ctr;
847}
848
849static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
850 const char *name)
851{
852 size_t hash = name_hash(f, parent, name);
853 struct node *node;
854
855 for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
856 if (node->parent->nodeid == parent &&
857 strcmp(node->name, name) == 0)
858 return node;
859
860 return NULL;
861}
862
863static void inc_nlookup(struct node *node)
864{
865 if (!node->nlookup)
866 node->refctr++;
867 node->nlookup++;
868}
869
870static struct node *find_node(struct fuse *f, fuse_ino_t parent,
871 const char *name)
872{
873 struct node *node;
874
875 pthread_mutex_lock(&f->lock);
876 if (!name)
877 node = get_node(f, parent);
878 else
879 node = lookup_node(f, parent, name);
880 if (node == NULL) {
881 node = alloc_node(f);
882 if (node == NULL)
883 goto out_err;
884
885 node->nodeid = next_id(f);
886 node->generation = f->generation;
887 if (f->conf.remember)
888 inc_nlookup(node);
889
890 if (hash_name(f, node, parent, name) == -1) {
891 free_node(f, node);
892 node = NULL;
893 goto out_err;
894 }
895 hash_id(f, node);
896 if (lru_enabled(f)) {
897 struct node_lru *lnode = node_lru(node);
898 init_list_head(&lnode->lru);
899 }
900 } else if (lru_enabled(f) && node->nlookup == 1) {
901 remove_node_lru(node);
902 }
903 inc_nlookup(node);
904out_err:
905 pthread_mutex_unlock(&f->lock);
906 return node;
907}
908
909static int lookup_path_in_cache(struct fuse *f,
910 const char *path, fuse_ino_t *inop)
911{
912 char *tmp = strdup(path);
913 if (!tmp)
914 return -ENOMEM;
915
916 pthread_mutex_lock(&f->lock);
917 fuse_ino_t ino = FUSE_ROOT_ID;
918
919 int err = 0;
920 char *save_ptr;
921 char *path_element = strtok_r(tmp, "/", &save_ptr);
922 while (path_element != NULL) {
923 struct node *node = lookup_node(f, ino, path_element);
924 if (node == NULL) {
925 err = -ENOENT;
926 break;
927 }
928 ino = node->nodeid;
929 path_element = strtok_r(NULL, "/", &save_ptr);
930 }
931 pthread_mutex_unlock(&f->lock);
932 free(tmp);
933
934 if (!err)
935 *inop = ino;
936 return err;
937}
938
939static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name)
940{
941 size_t len = strlen(name);
942
943 if (s - len <= *buf) {
944 unsigned pathlen = *bufsize - (s - *buf);
945 unsigned newbufsize = *bufsize;
946 char *newbuf;
947
948 while (newbufsize < pathlen + len + 1) {
949 if (newbufsize >= 0x80000000)
950 newbufsize = 0xffffffff;
951 else
952 newbufsize *= 2;
953 }
954
955 newbuf = realloc(*buf, newbufsize);
956 if (newbuf == NULL)
957 return NULL;
958
959 *buf = newbuf;
960 s = newbuf + newbufsize - pathlen;
961 memmove(s, newbuf + *bufsize - pathlen, pathlen);
962 *bufsize = newbufsize;
963 }
964 s -= len;
965 memcpy(s, name, len);
966 s--;
967 *s = '/';
968
969 return s;
970}
971
972static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
973 struct node *end)
974{
975 struct node *node;
976
977 if (wnode) {
978 assert(wnode->treelock == TREELOCK_WRITE);
979 wnode->treelock = 0;
980 }
981
982 for (node = get_node(f, nodeid);
983 node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) {
984 assert(node->treelock != 0);
985 assert(node->treelock != TREELOCK_WAIT_OFFSET);
986 assert(node->treelock != TREELOCK_WRITE);
987 node->treelock--;
988 if (node->treelock == TREELOCK_WAIT_OFFSET)
989 node->treelock = 0;
990 }
991}
992
993static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
994 char **path, struct node **wnodep, bool need_lock)
995{
996 unsigned bufsize = 256;
997 char *buf;
998 char *s;
999 struct node *node;
1000 struct node *wnode = NULL;
1001 int err;
1002
1003 *path = NULL;
1004
1005 err = -ENOMEM;
1006 buf = malloc(bufsize);
1007 if (buf == NULL)
1008 goto out_err;
1009
1010 s = buf + bufsize - 1;
1011 *s = '\0';
1012
1013 if (name != NULL) {
1014 s = add_name(&buf, &bufsize, s, name);
1015 err = -ENOMEM;
1016 if (s == NULL)
1017 goto out_free;
1018 }
1019
1020 if (wnodep) {
1021 assert(need_lock);
1022 wnode = lookup_node(f, nodeid, name);
1023 if (wnode) {
1024 if (wnode->treelock != 0) {
1025 if (wnode->treelock > 0)
1026 wnode->treelock += TREELOCK_WAIT_OFFSET;
1027 err = -EAGAIN;
1028 goto out_free;
1029 }
1030 wnode->treelock = TREELOCK_WRITE;
1031 }
1032 }
1033
1034 for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
1035 node = node->parent) {
1036 err = -ESTALE;
1037 if (node->name == NULL || node->parent == NULL)
1038 goto out_unlock;
1039
1040 err = -ENOMEM;
1041 s = add_name(&buf, &bufsize, s, node->name);
1042 if (s == NULL)
1043 goto out_unlock;
1044
1045 if (need_lock) {
1046 err = -EAGAIN;
1047 if (node->treelock < 0)
1048 goto out_unlock;
1049
1050 node->treelock++;
1051 }
1052 }
1053
1054 if (s[0])
1055 memmove(buf, s, bufsize - (s - buf));
1056 else
1057 strcpy(buf, "/");
1058
1059 *path = buf;
1060 if (wnodep)
1061 *wnodep = wnode;
1062
1063 return 0;
1064
1065 out_unlock:
1066 if (need_lock)
1067 unlock_path(f, nodeid, wnode, node);
1068 out_free:
1069 free(buf);
1070
1071 out_err:
1072 return err;
1073}
1074
1075static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1076 fuse_ino_t nodeid2, const char *name2,
1077 char **path1, char **path2,
1078 struct node **wnode1, struct node **wnode2)
1079{
1080 int err;
1081
1082 /* FIXME: locking two paths needs deadlock checking */
1083 err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
1084 if (!err) {
1085 err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
1086 if (err) {
1087 struct node *wn1 = wnode1 ? *wnode1 : NULL;
1088
1089 unlock_path(f, nodeid1, wn1, NULL);
1090 free(*path1);
1091 }
1092 }
1093 return err;
1094}
1095
1096static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
1097{
1098 int err;
1099
1100 if (!qe->path1) {
1101 /* Just waiting for it to be unlocked */
1102 if (get_node(f, qe->nodeid1)->treelock == 0)
1103 pthread_cond_signal(&qe->cond);
1104
1105 return;
1106 }
1107
1108 if (qe->done)
1109 return; // Don't try to double-lock the element
1110
1111 if (!qe->path2) {
1112 err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
1113 qe->wnode1, true);
1114 } else {
1115 err = try_get_path2(f, qe->nodeid1, qe->name1, qe->nodeid2,
1116 qe->name2, qe->path1, qe->path2, qe->wnode1,
1117 qe->wnode2);
1118 }
1119
1120 if (err == -EAGAIN)
1121 return; /* keep trying */
1122
1123 qe->err = err;
1124 qe->done = true;
1125 pthread_cond_signal(&qe->cond);
1126}
1127
1128static void wake_up_queued(struct fuse *f)
1129{
1130 struct lock_queue_element *qe;
1131
1132 for (qe = f->lockq; qe != NULL; qe = qe->next)
1133 queue_element_wakeup(f, qe);
1134}
1135
1136static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid,
1137 const char *name, bool wr)
1138{
1139 if (f->conf.debug) {
1140 struct node *wnode = NULL;
1141
1142 if (wr)
1143 wnode = lookup_node(f, nodeid, name);
1144
1145 if (wnode) {
1146 fuse_log(FUSE_LOG_DEBUG, "%s %llu (w)\n",
1147 msg, (unsigned long long) wnode->nodeid);
1148 } else {
1149 fuse_log(FUSE_LOG_DEBUG, "%s %llu\n",
1150 msg, (unsigned long long) nodeid);
1151 }
1152 }
1153}
1154
1155static void queue_path(struct fuse *f, struct lock_queue_element *qe)
1156{
1157 struct lock_queue_element **qp;
1158
1159 qe->done = false;
1160 pthread_cond_init(&qe->cond, NULL);
1161 qe->next = NULL;
1162 for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
1163 *qp = qe;
1164}
1165
1166static void dequeue_path(struct fuse *f, struct lock_queue_element *qe)
1167{
1168 struct lock_queue_element **qp;
1169
1170 pthread_cond_destroy(&qe->cond);
1171 for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next);
1172 *qp = qe->next;
1173}
1174
1175static int wait_path(struct fuse *f, struct lock_queue_element *qe)
1176{
1177 queue_path(f, qe);
1178
1179 do {
1180 pthread_cond_wait(&qe->cond, &f->lock);
1181 } while (!qe->done);
1182
1183 dequeue_path(f, qe);
1184
1185 return qe->err;
1186}
1187
1188static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name,
1189 char **path, struct node **wnode)
1190{
1191 int err;
1192
1193 pthread_mutex_lock(&f->lock);
1194 err = try_get_path(f, nodeid, name, path, wnode, true);
1195 if (err == -EAGAIN) {
1196 struct lock_queue_element qe = {
1197 .nodeid1 = nodeid,
1198 .name1 = name,
1199 .path1 = path,
1200 .wnode1 = wnode,
1201 };
1202 debug_path(f, "QUEUE PATH", nodeid, name, !!wnode);
1203 err = wait_path(f, &qe);
1204 debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode);
1205 }
1206 pthread_mutex_unlock(&f->lock);
1207
1208 return err;
1209}
1210
1211static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
1212{
1213 return get_path_common(f, nodeid, NULL, path, NULL);
1214}
1215
1216static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
1217{
1218 int err = 0;
1219
1220 if (f->conf.nullpath_ok) {
1221 *path = NULL;
1222 } else {
1223 err = get_path_common(f, nodeid, NULL, path, NULL);
1224 if (err == -ESTALE)
1225 err = 0;
1226 }
1227
1228 return err;
1229}
1230
1231static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
1232 char **path)
1233{
1234 return get_path_common(f, nodeid, name, path, NULL);
1235}
1236
1237static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
1238 char **path, struct node **wnode)
1239{
1240 return get_path_common(f, nodeid, name, path, wnode);
1241}
1242
1243#if defined(__FreeBSD__)
1244#define CHECK_DIR_LOOP
1245#endif
1246
1247#if defined(CHECK_DIR_LOOP)
1248static int check_dir_loop(struct fuse *f,
1249 fuse_ino_t nodeid1, const char *name1,
1250 fuse_ino_t nodeid2, const char *name2)
1251{
1252 struct node *node, *node1, *node2;
1253 fuse_ino_t id1, id2;
1254
1255 node1 = lookup_node(f, nodeid1, name1);
1256 id1 = node1 ? node1->nodeid : nodeid1;
1257
1258 node2 = lookup_node(f, nodeid2, name2);
1259 id2 = node2 ? node2->nodeid : nodeid2;
1260
1261 for (node = get_node(f, id2); node->nodeid != FUSE_ROOT_ID;
1262 node = node->parent) {
1263 if (node->name == NULL || node->parent == NULL)
1264 break;
1265
1266 if (node->nodeid != id2 && node->nodeid == id1)
1267 return -EINVAL;
1268 }
1269
1270 if (node2)
1271 {
1272 for (node = get_node(f, id1); node->nodeid != FUSE_ROOT_ID;
1273 node = node->parent) {
1274 if (node->name == NULL || node->parent == NULL)
1275 break;
1276
1277 if (node->nodeid != id1 && node->nodeid == id2)
1278 return -ENOTEMPTY;
1279 }
1280 }
1281
1282 return 0;
1283}
1284#endif
1285
1286static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1287 fuse_ino_t nodeid2, const char *name2,
1288 char **path1, char **path2,
1289 struct node **wnode1, struct node **wnode2)
1290{
1291 int err;
1292
1293 pthread_mutex_lock(&f->lock);
1294
1295#if defined(CHECK_DIR_LOOP)
1296 if (name1)
1297 {
1298 // called during rename; perform dir loop check
1299 err = check_dir_loop(f, nodeid1, name1, nodeid2, name2);
1300 if (err)
1301 goto out_unlock;
1302 }
1303#endif
1304
1305 err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
1306 path1, path2, wnode1, wnode2);
1307 if (err == -EAGAIN) {
1308 struct lock_queue_element qe = {
1309 .nodeid1 = nodeid1,
1310 .name1 = name1,
1311 .path1 = path1,
1312 .wnode1 = wnode1,
1313 .nodeid2 = nodeid2,
1314 .name2 = name2,
1315 .path2 = path2,
1316 .wnode2 = wnode2,
1317 };
1318
1319 debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1);
1320 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1321 err = wait_path(f, &qe);
1322 debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
1323 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1324 }
1325
1326#if defined(CHECK_DIR_LOOP)
1327out_unlock:
1328#endif
1329 pthread_mutex_unlock(&f->lock);
1330
1331 return err;
1332}
1333
1334static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid,
1335 struct node *wnode, char *path)
1336{
1337 pthread_mutex_lock(&f->lock);
1338 unlock_path(f, nodeid, wnode, NULL);
1339 if (f->lockq)
1340 wake_up_queued(f);
1341 pthread_mutex_unlock(&f->lock);
1342 free(path);
1343}
1344
1345static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path)
1346{
1347 if (path)
1348 free_path_wrlock(f, nodeid, NULL, path);
1349}
1350
1351static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2,
1352 struct node *wnode1, struct node *wnode2,
1353 char *path1, char *path2)
1354{
1355 pthread_mutex_lock(&f->lock);
1356 unlock_path(f, nodeid1, wnode1, NULL);
1357 unlock_path(f, nodeid2, wnode2, NULL);
1358 wake_up_queued(f);
1359 pthread_mutex_unlock(&f->lock);
1360 free(path1);
1361 free(path2);
1362}
1363
1364static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
1365{
1366 struct node *node;
1367 if (nodeid == FUSE_ROOT_ID)
1368 return;
1369 pthread_mutex_lock(&f->lock);
1370 node = get_node(f, nodeid);
1371
1372 /*
1373 * Node may still be locked due to interrupt idiocy in open,
1374 * create and opendir
1375 */
1376 while (node->nlookup == nlookup && node->treelock) {
1377 struct lock_queue_element qe = {
1378 .nodeid1 = nodeid,
1379 };
1380
1381 debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false);
1382 queue_path(f, &qe);
1383
1384 do {
1385 pthread_cond_wait(&qe.cond, &f->lock);
1386 } while (node->nlookup == nlookup && node->treelock);
1387
1388 dequeue_path(f, &qe);
1389 debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false);
1390 }
1391
1392 assert(node->nlookup >= nlookup);
1393 node->nlookup -= nlookup;
1394 if (!node->nlookup) {
1395 unref_node(f, node);
1396 } else if (lru_enabled(f) && node->nlookup == 1) {
1397 set_forget_time(f, node);
1398 }
1399 pthread_mutex_unlock(&f->lock);
1400}
1401
1402static void unlink_node(struct fuse *f, struct node *node)
1403{
1404 if (f->conf.remember) {
1405 assert(node->nlookup > 1);
1406 node->nlookup--;
1407 }
1408 unhash_name(f, node);
1409}
1410
1411static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
1412{
1413 struct node *node;
1414
1415 pthread_mutex_lock(&f->lock);
1416 node = lookup_node(f, dir, name);
1417 if (node != NULL)
1418 unlink_node(f, node);
1419 pthread_mutex_unlock(&f->lock);
1420}
1421
1422static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1423 fuse_ino_t newdir, const char *newname, int hide)
1424{
1425 struct node *node;
1426 struct node *newnode;
1427 int err = 0;
1428
1429 pthread_mutex_lock(&f->lock);
1430 node = lookup_node(f, olddir, oldname);
1431 newnode = lookup_node(f, newdir, newname);
1432 if (node == NULL)
1433 goto out;
1434
1435 if (newnode != NULL) {
1436 if (hide) {
1437 fuse_log(FUSE_LOG_ERR, "fuse: hidden file got created during hiding\n");
1438 err = -EBUSY;
1439 goto out;
1440 }
1441 unlink_node(f, newnode);
1442 }
1443
1444 unhash_name(f, node);
1445 if (hash_name(f, node, newdir, newname) == -1) {
1446 err = -ENOMEM;
1447 goto out;
1448 }
1449
1450 if (hide)
1451 node->is_hidden = 1;
1452
1453out:
1454 pthread_mutex_unlock(&f->lock);
1455 return err;
1456}
1457
1458static int exchange_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1459 fuse_ino_t newdir, const char *newname)
1460{
1461 struct node *oldnode;
1462 struct node *newnode;
1463 int err;
1464
1465 pthread_mutex_lock(&f->lock);
1466 oldnode = lookup_node(f, olddir, oldname);
1467 newnode = lookup_node(f, newdir, newname);
1468
1469 if (oldnode)
1470 unhash_name(f, oldnode);
1471 if (newnode)
1472 unhash_name(f, newnode);
1473
1474 err = -ENOMEM;
1475 if (oldnode) {
1476 if (hash_name(f, oldnode, newdir, newname) == -1)
1477 goto out;
1478 }
1479 if (newnode) {
1480 if (hash_name(f, newnode, olddir, oldname) == -1)
1481 goto out;
1482 }
1483 err = 0;
1484out:
1485 pthread_mutex_unlock(&f->lock);
1486 return err;
1487}
1488
1489static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
1490{
1491 if (!f->conf.use_ino)
1492 stbuf->st_ino = nodeid;
1493 if (f->conf.set_mode) {
1494 if (f->conf.dmask && S_ISDIR(stbuf->st_mode))
1495 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1496 (0777 & ~f->conf.dmask);
1497 else if (f->conf.fmask)
1498 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1499 (0777 & ~f->conf.fmask);
1500 else
1501 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1502 (0777 & ~f->conf.umask);
1503 }
1504 if (f->conf.set_uid)
1505 stbuf->st_uid = f->conf.uid;
1506 if (f->conf.set_gid)
1507 stbuf->st_gid = f->conf.gid;
1508}
1509
1510#ifdef HAVE_STATX
1511static void set_statx(struct fuse *f, fuse_ino_t nodeid, struct statx *stxbuf)
1512{
1513 if (!f->conf.use_ino)
1514 stxbuf->stx_ino = nodeid;
1515 if (f->conf.set_mode) {
1516 if (f->conf.dmask && S_ISDIR(stxbuf->stx_mode))
1517 stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
1518 (0777 & ~f->conf.dmask);
1519 else if (f->conf.fmask)
1520 stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
1521 (0777 & ~f->conf.fmask);
1522 else
1523 stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) |
1524 (0777 & ~f->conf.umask);
1525 }
1526 if (f->conf.set_uid)
1527 stxbuf->stx_uid = f->conf.uid;
1528 if (f->conf.set_gid)
1529 stxbuf->stx_gid = f->conf.gid;
1530}
1531#endif
1532
1533static struct fuse *req_fuse(fuse_req_t req)
1534{
1535 return (struct fuse *) fuse_req_userdata(req);
1536}
1537
1538static void fuse_intr_sighandler(int sig)
1539{
1540 (void) sig;
1541 /* Nothing to do */
1542}
1543
1544struct fuse_intr_data {
1545 pthread_t id;
1546 pthread_cond_t cond;
1547 int finished;
1548};
1549
1550static void fuse_interrupt(fuse_req_t req, void *d_)
1551{
1552 struct fuse_intr_data *d = d_;
1553 struct fuse *f = req_fuse(req);
1554
1555 if (d->id == pthread_self())
1556 return;
1557
1558 pthread_mutex_lock(&f->lock);
1559 while (!d->finished) {
1560 struct timeval now;
1561 struct timespec timeout;
1562
1563 pthread_kill(d->id, f->conf.intr_signal);
1564 gettimeofday(&now, NULL);
1565 timeout.tv_sec = now.tv_sec + 1;
1566 timeout.tv_nsec = now.tv_usec * 1000;
1567 pthread_cond_timedwait(&d->cond, &f->lock, &timeout);
1568 }
1569 pthread_mutex_unlock(&f->lock);
1570}
1571
1572static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req,
1573 struct fuse_intr_data *d)
1574{
1575 pthread_mutex_lock(&f->lock);
1576 d->finished = 1;
1577 pthread_cond_broadcast(&d->cond);
1578 pthread_mutex_unlock(&f->lock);
1579 fuse_req_interrupt_func(req, NULL, NULL);
1580 pthread_cond_destroy(&d->cond);
1581}
1582
1583static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d)
1584{
1585 d->id = pthread_self();
1586 pthread_cond_init(&d->cond, NULL);
1587 d->finished = 0;
1588 fuse_req_interrupt_func(req, fuse_interrupt, d);
1589}
1590
1591static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req,
1592 struct fuse_intr_data *d)
1593{
1594 if (f->conf.intr)
1595 fuse_do_finish_interrupt(f, req, d);
1596}
1597
1598static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
1599 struct fuse_intr_data *d)
1600{
1601 if (f->conf.intr)
1602 fuse_do_prepare_interrupt(req, d);
1603}
1604
1605static const char* file_info_string(struct fuse_file_info *fi,
1606 char* buf, size_t len)
1607{
1608 if(fi == NULL)
1609 return "NULL";
1610 snprintf(buf, len, "%llu", (unsigned long long) fi->fh);
1611 return buf;
1612}
1613
1614int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
1615 struct fuse_file_info *fi)
1616{
1617 fuse_get_context()->private_data = fs->user_data;
1618 if (!fs->op.getattr)
1619 return -ENOSYS;
1620
1621 if (fs->debug) {
1622 char buf[10];
1623
1624 fuse_log(FUSE_LOG_DEBUG, "getattr[%s] %s\n",
1625 file_info_string(fi, buf, sizeof(buf)),
1626 path);
1627 }
1628 return fs->op.getattr(path, buf, fi);
1629}
1630
1631int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
1632 const char *newpath, unsigned int flags)
1633{
1634 fuse_get_context()->private_data = fs->user_data;
1635 if (!fs->op.rename)
1636 return -ENOSYS;
1637 if (fs->debug)
1638 fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath,
1639 flags);
1640
1641 return fs->op.rename(oldpath, newpath, flags);
1642}
1643
1644int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
1645{
1646 fuse_get_context()->private_data = fs->user_data;
1647 if (!fs->op.unlink)
1648 return -ENOSYS;
1649 if (fs->debug)
1650 fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path);
1651
1652 return fs->op.unlink(path);
1653}
1654
1655int fuse_fs_rmdir(struct fuse_fs *fs, const char *path)
1656{
1657 fuse_get_context()->private_data = fs->user_data;
1658 if (!fs->op.rmdir)
1659 return -ENOSYS;
1660 if (fs->debug)
1661 fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path);
1662
1663 return fs->op.rmdir(path);
1664}
1665
1666int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path)
1667{
1668 fuse_get_context()->private_data = fs->user_data;
1669 if (!fs->op.symlink)
1670 return -ENOSYS;
1671 if (fs->debug)
1672 fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path);
1673
1674 return fs->op.symlink(linkname, path);
1675}
1676
1677int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath)
1678{
1679 fuse_get_context()->private_data = fs->user_data;
1680 if (!fs->op.link)
1681 return -ENOSYS;
1682 if (fs->debug)
1683 fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath);
1684
1685 return fs->op.link(oldpath, newpath);
1686}
1687
1688int fuse_fs_release(struct fuse_fs *fs, const char *path,
1689 struct fuse_file_info *fi)
1690{
1691 fuse_get_context()->private_data = fs->user_data;
1692 if (!fs->op.release)
1693 return 0;
1694
1695 if (fs->debug)
1696 fuse_log(FUSE_LOG_DEBUG, "release%s[%llu] flags: 0x%x\n",
1697 fi->flush ? "+flush" : "",
1698 (unsigned long long) fi->fh, fi->flags);
1699
1700 return fs->op.release(path, fi);
1701}
1702
1703int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
1704 struct fuse_file_info *fi)
1705{
1706 int err;
1707
1708 fuse_get_context()->private_data = fs->user_data;
1709 if (!fs->op.opendir)
1710 return 0;
1711
1712 if (fs->debug)
1713 fuse_log(FUSE_LOG_DEBUG, "opendir flags: 0x%x %s\n", fi->flags,
1714 path);
1715
1716 err = fs->op.opendir(path, fi);
1717
1718 if (fs->debug && !err)
1719 fuse_log(FUSE_LOG_DEBUG, " opendir[%llu] flags: 0x%x %s\n",
1720 (unsigned long long) fi->fh, fi->flags, path);
1721
1722 return err;
1723}
1724
1725int fuse_fs_open(struct fuse_fs *fs, const char *path,
1726 struct fuse_file_info *fi)
1727{
1728 int err;
1729
1730 fuse_get_context()->private_data = fs->user_data;
1731 if (!fs->op.open)
1732 return 0;
1733
1734 if (fs->debug)
1735 fuse_log(FUSE_LOG_DEBUG, "open flags: 0x%x %s\n", fi->flags,
1736 path);
1737
1738 err = fs->op.open(path, fi);
1739
1740 if (fs->debug && !err)
1741 fuse_log(FUSE_LOG_DEBUG, " open[%llu] flags: 0x%x %s\n",
1742 (unsigned long long) fi->fh, fi->flags, path);
1743
1744 return err;
1745}
1746
1747static void fuse_free_buf(struct fuse_bufvec *buf)
1748{
1749 if (buf != NULL) {
1750 size_t i;
1751
1752 for (i = 0; i < buf->count; i++)
1753 if (!(buf->buf[i].flags & FUSE_BUF_IS_FD))
1754 free(buf->buf[i].mem);
1755 free(buf);
1756 }
1757}
1758
1759int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
1760 struct fuse_bufvec **bufp, size_t size, off_t off,
1761 struct fuse_file_info *fi)
1762{
1763 int res;
1764
1765 fuse_get_context()->private_data = fs->user_data;
1766 if (!fs->op.read && !fs->op.read_buf)
1767 return -ENOSYS;
1768
1769 if (fs->debug)
1770 fuse_log(FUSE_LOG_DEBUG,
1771 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1772 (unsigned long long) fi->fh,
1773 size, (unsigned long long) off, fi->flags);
1774
1775 if (fs->op.read_buf) {
1776 res = fs->op.read_buf(path, bufp, size, off, fi);
1777 } else {
1778 struct fuse_bufvec *buf;
1779 void *mem;
1780
1781 buf = malloc(sizeof(struct fuse_bufvec));
1782 if (buf == NULL)
1783 return -ENOMEM;
1784
1785 mem = malloc(size);
1786 if (mem == NULL) {
1787 free(buf);
1788 return -ENOMEM;
1789 }
1790 *buf = FUSE_BUFVEC_INIT(size);
1791 buf->buf[0].mem = mem;
1792 *bufp = buf;
1793
1794 res = fs->op.read(path, mem, size, off, fi);
1795 if (res >= 0)
1796 buf->buf[0].size = res;
1797 }
1798
1799 if (fs->debug && res >= 0)
1800 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %zu bytes from %llu\n",
1801 (unsigned long long) fi->fh,
1802 fuse_buf_size(*bufp),
1803 (unsigned long long) off);
1804 if (res >= 0 && fuse_buf_size(*bufp) > size)
1805 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1806
1807 if (res < 0)
1808 return res;
1809
1810 return 0;
1811}
1812
1813int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
1814 off_t off, struct fuse_file_info *fi)
1815{
1816 int res;
1817
1818 fuse_get_context()->private_data = fs->user_data;
1819 if (!fs->op.read && !fs->op.read_buf)
1820 return -ENOSYS;
1821
1822 if (fs->debug)
1823 fuse_log(FUSE_LOG_DEBUG,
1824 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1825 (unsigned long long) fi->fh,
1826 size, (unsigned long long) off, fi->flags);
1827
1828 if (fs->op.read_buf) {
1829 struct fuse_bufvec *buf = NULL;
1830
1831 res = fs->op.read_buf(path, &buf, size, off, fi);
1832 if (res == 0) {
1833 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
1834
1835 dst.buf[0].mem = mem;
1836 res = fuse_buf_copy(&dst, buf, 0);
1837 }
1838 fuse_free_buf(buf);
1839 } else {
1840 res = fs->op.read(path, mem, size, off, fi);
1841 }
1842
1843 if (fs->debug && res >= 0)
1844 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %u bytes from %llu\n",
1845 (unsigned long long) fi->fh,
1846 res,
1847 (unsigned long long) off);
1848 if (res >= 0 && res > (int) size)
1849 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1850
1851 return res;
1852}
1853
1854int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
1855 struct fuse_bufvec *buf, off_t off,
1856 struct fuse_file_info *fi)
1857{
1858 int res;
1859 size_t size;
1860
1861 fuse_get_context()->private_data = fs->user_data;
1862 if (!fs->op.write_buf && !fs->op.write)
1863 return -ENOSYS;
1864
1865 size = fuse_buf_size(buf);
1866 assert(buf->idx == 0 && buf->off == 0);
1867 if (fs->debug)
1868 fuse_log(FUSE_LOG_DEBUG,
1869 "write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
1870 fi->writepage ? "page" : "",
1871 (unsigned long long) fi->fh,
1872 size,
1873 (unsigned long long) off,
1874 fi->flags);
1875
1876 if (fs->op.write_buf) {
1877 res = fs->op.write_buf(path, buf, off, fi);
1878 } else {
1879 void *mem = NULL;
1880 struct fuse_buf *flatbuf;
1881 struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size);
1882
1883 if (buf->count == 1 &&
1884 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
1885 flatbuf = &buf->buf[0];
1886 } else {
1887 res = -ENOMEM;
1888 mem = malloc(size);
1889 if (mem == NULL)
1890 goto out;
1891
1892 tmp.buf[0].mem = mem;
1893 res = fuse_buf_copy(&tmp, buf, 0);
1894 if (res <= 0)
1895 goto out_free;
1896
1897 tmp.buf[0].size = res;
1898 flatbuf = &tmp.buf[0];
1899 }
1900
1901 res = fs->op.write(path, flatbuf->mem, flatbuf->size,
1902 off, fi);
1903out_free:
1904 free(mem);
1905 }
1906out:
1907 if (fs->debug && res >= 0)
1908 fuse_log(FUSE_LOG_DEBUG, " write%s[%llu] %u bytes to %llu\n",
1909 fi->writepage ? "page" : "",
1910 (unsigned long long) fi->fh, res,
1911 (unsigned long long) off);
1912 if (res > (int) size)
1913 fuse_log(FUSE_LOG_ERR, "fuse: wrote too many bytes\n");
1914
1915 return res;
1916}
1917
1918int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem,
1919 size_t size, off_t off, struct fuse_file_info *fi)
1920{
1921 struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size);
1922
1923 bufv.buf[0].mem = (void *) mem;
1924
1925 return fuse_fs_write_buf(fs, path, &bufv, off, fi);
1926}
1927
1928int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
1929 struct fuse_file_info *fi)
1930{
1931 fuse_get_context()->private_data = fs->user_data;
1932 if (!fs->op.fsync)
1933 return -ENOSYS;
1934
1935 if (fs->debug)
1936 fuse_log(FUSE_LOG_DEBUG, "fsync[%llu] datasync: %i\n",
1937 (unsigned long long) fi->fh, datasync);
1938
1939 return fs->op.fsync(path, datasync, fi);
1940}
1941
1942int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
1943 struct fuse_file_info *fi)
1944{
1945 fuse_get_context()->private_data = fs->user_data;
1946 if (!fs->op.fsyncdir)
1947 return -ENOSYS;
1948
1949 if (fs->debug)
1950 fuse_log(FUSE_LOG_DEBUG, "fsyncdir[%llu] datasync: %i\n",
1951 (unsigned long long) fi->fh, datasync);
1952
1953 return fs->op.fsyncdir(path, datasync, fi);
1954}
1955
1956int fuse_fs_flush(struct fuse_fs *fs, const char *path,
1957 struct fuse_file_info *fi)
1958{
1959 fuse_get_context()->private_data = fs->user_data;
1960 if (!fs->op.flush)
1961 return -ENOSYS;
1962 if (fs->debug)
1963 fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n",
1964 (unsigned long long) fi->fh);
1965
1966 return fs->op.flush(path, fi);
1967}
1968
1969int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf)
1970{
1971 fuse_get_context()->private_data = fs->user_data;
1972 if (fs->op.statfs) {
1973 if (fs->debug)
1974 fuse_log(FUSE_LOG_DEBUG, "statfs %s\n", path);
1975
1976 return fs->op.statfs(path, buf);
1977 } else {
1978 buf->f_namemax = 255;
1979 buf->f_bsize = 512;
1980 return 0;
1981 }
1982}
1983
1984int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
1985 struct fuse_file_info *fi)
1986{
1987 fuse_get_context()->private_data = fs->user_data;
1988 if (!fs->op.releasedir)
1989 return 0;
1990
1991 if (fs->debug)
1992 fuse_log(FUSE_LOG_DEBUG, "releasedir[%llu] flags: 0x%x\n",
1993 (unsigned long long) fi->fh, fi->flags);
1994
1995 return fs->op.releasedir(path, fi);
1996}
1997
1998int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
1999 fuse_fill_dir_t filler, off_t off,
2000 struct fuse_file_info *fi,
2001 enum fuse_readdir_flags flags)
2002{
2003 fuse_get_context()->private_data = fs->user_data;
2004 if (!fs->op.readdir)
2005 return -ENOSYS;
2006 if (fs->debug) {
2007 fuse_log(FUSE_LOG_DEBUG, "readdir%s[%llu] from %llu\n",
2008 (flags & FUSE_READDIR_PLUS) ? "plus" : "",
2009 (unsigned long long) fi->fh,
2010 (unsigned long long) off);
2011 }
2012
2013 return fs->op.readdir(path, buf, filler, off, fi, flags);
2014}
2015
2016int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
2017 struct fuse_file_info *fi)
2018{
2019 int err;
2020
2021 fuse_get_context()->private_data = fs->user_data;
2022 if (!fs->op.create)
2023 return -ENOSYS;
2024
2025 if (fs->debug)
2026 fuse_log(FUSE_LOG_DEBUG,
2027 "create flags: 0x%x %s 0%o umask=0%03o\n",
2028 fi->flags, path, mode,
2029 fuse_get_context()->umask);
2030
2031 err = fs->op.create(path, mode, fi);
2032
2033 if (fs->debug && !err)
2034 fuse_log(FUSE_LOG_DEBUG, " create[%llu] flags: 0x%x %s\n",
2035 (unsigned long long) fi->fh, fi->flags, path);
2036
2037 return err;
2038}
2039
2040int fuse_fs_lock(struct fuse_fs *fs, const char *path,
2041 struct fuse_file_info *fi, int cmd, struct flock *lock)
2042{
2043 fuse_get_context()->private_data = fs->user_data;
2044 if (!fs->op.lock)
2045 return -ENOSYS;
2046
2047 if (fs->debug)
2048 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
2049 (unsigned long long) fi->fh,
2050 (cmd == F_GETLK ? "F_GETLK" :
2051 (cmd == F_SETLK ? "F_SETLK" :
2052 (cmd == F_SETLKW ? "F_SETLKW" : "???"))),
2053 (lock->l_type == F_RDLCK ? "F_RDLCK" :
2054 (lock->l_type == F_WRLCK ? "F_WRLCK" :
2055 (lock->l_type == F_UNLCK ? "F_UNLCK" :
2056 "???"))),
2057 (unsigned long long) lock->l_start,
2058 (unsigned long long) lock->l_len,
2059 (unsigned long long) lock->l_pid);
2060
2061 return fs->op.lock(path, fi, cmd, lock);
2062}
2063
2064int fuse_fs_flock(struct fuse_fs *fs, const char *path,
2065 struct fuse_file_info *fi, int op)
2066{
2067 fuse_get_context()->private_data = fs->user_data;
2068 if (!fs->op.flock)
2069 return -ENOSYS;
2070
2071 if (fs->debug) {
2072 int xop = op & ~LOCK_NB;
2073
2074 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s%s\n",
2075 (unsigned long long) fi->fh,
2076 xop == LOCK_SH ? "LOCK_SH" :
2077 (xop == LOCK_EX ? "LOCK_EX" :
2078 (xop == LOCK_UN ? "LOCK_UN" : "???")),
2079 (op & LOCK_NB) ? "|LOCK_NB" : "");
2080 }
2081 return fs->op.flock(path, fi, op);
2082}
2083
2084int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid,
2085 gid_t gid, struct fuse_file_info *fi)
2086{
2087 fuse_get_context()->private_data = fs->user_data;
2088 if (!fs->op.chown)
2089 return -ENOSYS;
2090 if (fs->debug) {
2091 char buf[10];
2092
2093 fuse_log(FUSE_LOG_DEBUG, "chown[%s] %s %lu %lu\n",
2094 file_info_string(fi, buf, sizeof(buf)),
2095 path, (unsigned long) uid, (unsigned long) gid);
2096 }
2097 return fs->op.chown(path, uid, gid, fi);
2098}
2099
2100int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
2101 struct fuse_file_info *fi)
2102{
2103 fuse_get_context()->private_data = fs->user_data;
2104 if (!fs->op.truncate)
2105 return -ENOSYS;
2106 if (fs->debug) {
2107 char buf[10];
2108
2109 fuse_log(FUSE_LOG_DEBUG, "truncate[%s] %llu\n",
2110 file_info_string(fi, buf, sizeof(buf)),
2111 (unsigned long long) size);
2112 }
2113 return fs->op.truncate(path, size, fi);
2114}
2115
2116int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
2117 const struct timespec tv[2], struct fuse_file_info *fi)
2118{
2119 fuse_get_context()->private_data = fs->user_data;
2120 if (!fs->op.utimens)
2121 return -ENOSYS;
2122 if (fs->debug) {
2123 char buf[10];
2124
2125 fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %jd.%09ld %jd.%09ld\n",
2126 file_info_string(fi, buf, sizeof(buf)),
2127 path, (intmax_t)tv[0].tv_sec, tv[0].tv_nsec,
2128 (intmax_t)tv[1].tv_sec, tv[1].tv_nsec);
2129 }
2130 return fs->op.utimens(path, tv, fi);
2131}
2132
2133int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask)
2134{
2135 fuse_get_context()->private_data = fs->user_data;
2136 if (!fs->op.access)
2137 return -ENOSYS;
2138 if (fs->debug)
2139 fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask);
2140
2141 return fs->op.access(path, mask);
2142}
2143
2144int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
2145 size_t len)
2146{
2147 fuse_get_context()->private_data = fs->user_data;
2148 if (!fs->op.readlink)
2149 return -ENOSYS;
2150 if (fs->debug)
2151 fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path,
2152 (unsigned long) len);
2153
2154 return fs->op.readlink(path, buf, len);
2155}
2156
2157int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
2158 dev_t rdev)
2159{
2160 fuse_get_context()->private_data = fs->user_data;
2161 if (!fs->op.mknod)
2162 return -ENOSYS;
2163 if (fs->debug)
2164 fuse_log(FUSE_LOG_DEBUG, "mknod %s 0%o 0x%llx umask=0%03o\n",
2165 path, mode, (unsigned long long) rdev,
2166 fuse_get_context()->umask);
2167
2168 return fs->op.mknod(path, mode, rdev);
2169}
2170
2171int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode)
2172{
2173 fuse_get_context()->private_data = fs->user_data;
2174 if (!fs->op.mkdir)
2175 return -ENOSYS;
2176 if (fs->debug)
2177 fuse_log(FUSE_LOG_DEBUG, "mkdir %s 0%o umask=0%03o\n",
2178 path, mode, fuse_get_context()->umask);
2179
2180 return fs->op.mkdir(path, mode);
2181}
2182
2183int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
2184 const char *value, size_t size, int flags)
2185{
2186 fuse_get_context()->private_data = fs->user_data;
2187 if (!fs->op.setxattr)
2188 return -ENOSYS;
2189 if (fs->debug)
2190 fuse_log(FUSE_LOG_DEBUG, "setxattr %s %s %lu 0x%x\n",
2191 path, name, (unsigned long) size, flags);
2192
2193 return fs->op.setxattr(path, name, value, size, flags);
2194}
2195
2196int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
2197 char *value, size_t size)
2198{
2199 fuse_get_context()->private_data = fs->user_data;
2200 if (!fs->op.getxattr)
2201 return -ENOSYS;
2202 if (fs->debug)
2203 fuse_log(FUSE_LOG_DEBUG, "getxattr %s %s %lu\n",
2204 path, name, (unsigned long) size);
2205
2206 return fs->op.getxattr(path, name, value, size);
2207}
2208
2209int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
2210 size_t size)
2211{
2212 fuse_get_context()->private_data = fs->user_data;
2213 if (!fs->op.listxattr)
2214 return -ENOSYS;
2215 if (fs->debug)
2216 fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n",
2217 path, (unsigned long) size);
2218
2219 return fs->op.listxattr(path, list, size);
2220}
2221
2222int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
2223 uint64_t *idx)
2224{
2225 fuse_get_context()->private_data = fs->user_data;
2226 if (!fs->op.bmap)
2227 return -ENOSYS;
2228 if (fs->debug)
2229 fuse_log(FUSE_LOG_DEBUG, "bmap %s blocksize: %lu index: %llu\n",
2230 path, (unsigned long) blocksize,
2231 (unsigned long long) *idx);
2232
2233 return fs->op.bmap(path, blocksize, idx);
2234}
2235
2236int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
2237{
2238 fuse_get_context()->private_data = fs->user_data;
2239 if (!fs->op.removexattr)
2240 return -ENOSYS;
2241 if (fs->debug)
2242 fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name);
2243
2244 return fs->op.removexattr(path, name);
2245}
2246
2247int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
2248 void *arg, struct fuse_file_info *fi, unsigned int flags,
2249 void *data)
2250{
2251 fuse_get_context()->private_data = fs->user_data;
2252 if (!fs->op.ioctl)
2253 return -ENOSYS;
2254 if (fs->debug)
2255 fuse_log(FUSE_LOG_DEBUG, "ioctl[%llu] 0x%x flags: 0x%x\n",
2256 (unsigned long long) fi->fh, cmd, flags);
2257
2258 return fs->op.ioctl(path, cmd, arg, fi, flags, data);
2259}
2260
2261int fuse_fs_poll(struct fuse_fs *fs, const char *path,
2262 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
2263 unsigned *reventsp)
2264{
2265 int res;
2266
2267 fuse_get_context()->private_data = fs->user_data;
2268 if (!fs->op.poll)
2269 return -ENOSYS;
2270 if (fs->debug)
2271 fuse_log(FUSE_LOG_DEBUG, "poll[%llu] ph: %p, events 0x%x\n",
2272 (unsigned long long) fi->fh, ph,
2273 fi->poll_events);
2274
2275 res = fs->op.poll(path, fi, ph, reventsp);
2276
2277 if (fs->debug && !res)
2278 fuse_log(FUSE_LOG_DEBUG, " poll[%llu] revents: 0x%x\n",
2279 (unsigned long long) fi->fh, *reventsp);
2280
2281 return res;
2282}
2283
2284int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
2285 off_t offset, off_t length, struct fuse_file_info *fi)
2286{
2287 fuse_get_context()->private_data = fs->user_data;
2288 if (!fs->op.fallocate)
2289 return -ENOSYS;
2290 if (fs->debug)
2291 fuse_log(FUSE_LOG_DEBUG, "fallocate %s mode %x, offset: %llu, length: %llu\n",
2292 path,
2293 mode,
2294 (unsigned long long) offset,
2295 (unsigned long long) length);
2296
2297 return fs->op.fallocate(path, mode, offset, length, fi);
2298}
2299
2300ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
2301 struct fuse_file_info *fi_in, off_t off_in,
2302 const char *path_out,
2303 struct fuse_file_info *fi_out, off_t off_out,
2304 size_t len, int flags)
2305{
2306 fuse_get_context()->private_data = fs->user_data;
2307 if (!fs->op.copy_file_range)
2308 return -ENOSYS;
2309 if (fs->debug)
2310 fuse_log(FUSE_LOG_DEBUG, "copy_file_range from %s:%llu to "
2311 "%s:%llu, length: %llu\n",
2312 path_in,
2313 (unsigned long long) off_in,
2314 path_out,
2315 (unsigned long long) off_out,
2316 (unsigned long long) len);
2317
2318 return fs->op.copy_file_range(path_in, fi_in, off_in, path_out,
2319 fi_out, off_out, len, flags);
2320}
2321
2322off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
2323 struct fuse_file_info *fi)
2324{
2325 fuse_get_context()->private_data = fs->user_data;
2326 if (!fs->op.lseek)
2327 return -ENOSYS;
2328 if (fs->debug) {
2329 char buf[10];
2330
2331 fuse_log(FUSE_LOG_DEBUG, "lseek[%s] %llu %d\n",
2332 file_info_string(fi, buf, sizeof(buf)),
2333 (unsigned long long) off, whence);
2334 }
2335 return fs->op.lseek(path, off, whence, fi);
2336}
2337
2338#ifdef HAVE_STATX
2339int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
2340 struct statx *stxbuf, struct fuse_file_info *fi)
2341{
2342 fuse_get_context()->private_data = fs->user_data;
2343 if (fs->op.statx) {
2344 if (fs->debug) {
2345 char buf[10];
2346
2347 fuse_log(FUSE_LOG_DEBUG, "statx[%s] %s %d %d\n",
2348 file_info_string(fi, buf, sizeof(buf)), path,
2349 flags, mask);
2350 }
2351 return fs->op.statx(path, flags, mask, stxbuf, fi);
2352 }
2353
2354 return -ENOSYS;
2355}
2356#else
2357int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask,
2358 struct statx *stxbuf, struct fuse_file_info *fi)
2359{
2360 (void)fs;
2361 (void)path;
2362 (void)flags;
2363 (void)mask;
2364 (void)stxbuf;
2365 (void)fi;
2366
2367 return -ENOSYS;
2368}
2369#endif
2370
2371static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
2372{
2373 struct node *node;
2374 int isopen = 0;
2375 pthread_mutex_lock(&f->lock);
2376 node = lookup_node(f, dir, name);
2377 if (node && node->open_count > 0)
2378 isopen = 1;
2379 pthread_mutex_unlock(&f->lock);
2380 return isopen;
2381}
2382
2383static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname,
2384 char *newname, size_t bufsize)
2385{
2386 struct stat buf;
2387 struct node *node;
2388 struct node *newnode;
2389 char *newpath;
2390 int res;
2391 int failctr = 10;
2392
2393 do {
2394 pthread_mutex_lock(&f->lock);
2395 node = lookup_node(f, dir, oldname);
2396 if (node == NULL) {
2397 pthread_mutex_unlock(&f->lock);
2398 return NULL;
2399 }
2400 do {
2401 f->hidectr ++;
2402 snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
2403 (unsigned int) node->nodeid, f->hidectr);
2404 newnode = lookup_node(f, dir, newname);
2405 } while(newnode);
2406
2407 res = try_get_path(f, dir, newname, &newpath, NULL, false);
2408 pthread_mutex_unlock(&f->lock);
2409 if (res)
2410 break;
2411
2412 memset(&buf, 0, sizeof(buf));
2413 res = fuse_fs_getattr(f->fs, newpath, &buf, NULL);
2414 if (res == -ENOENT)
2415 break;
2416 free(newpath);
2417 newpath = NULL;
2418 } while(res == 0 && --failctr);
2419
2420 return newpath;
2421}
2422
2423static int hide_node(struct fuse *f, const char *oldpath,
2424 fuse_ino_t dir, const char *oldname)
2425{
2426 char newname[64];
2427 char *newpath;
2428 int err = -EBUSY;
2429
2430 newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
2431 if (newpath) {
2432 err = fuse_fs_rename(f->fs, oldpath, newpath, 0);
2433 if (!err)
2434 err = rename_node(f, dir, oldname, dir, newname, 1);
2435 free(newpath);
2436 }
2437 return err;
2438}
2439
2440static int mtime_eq(const struct stat *stbuf, const struct timespec *ts)
2441{
2442 return stbuf->st_mtime == ts->tv_sec &&
2443 ST_MTIM_NSEC(stbuf) == ts->tv_nsec;
2444}
2445
2446#ifndef CLOCK_MONOTONIC
2447#define CLOCK_MONOTONIC CLOCK_REALTIME
2448#endif
2449
2450static void curr_time(struct timespec *now)
2451{
2452 static clockid_t clockid = CLOCK_MONOTONIC;
2453 int res = clock_gettime(clockid, now);
2454 if (res == -1 && errno == EINVAL) {
2455 clockid = CLOCK_REALTIME;
2456 res = clock_gettime(clockid, now);
2457 }
2458 if (res == -1) {
2459 perror("fuse: clock_gettime");
2460 abort();
2461 }
2462}
2463
2464static void update_stat(struct node *node, const struct stat *stbuf)
2465{
2466 if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) ||
2467 stbuf->st_size != node->size))
2468 node->cache_valid = 0;
2469 node->mtime.tv_sec = stbuf->st_mtime;
2470 node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf);
2471 node->size = stbuf->st_size;
2472 curr_time(&node->stat_updated);
2473}
2474
2475static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name,
2476 struct fuse_entry_param *e)
2477{
2478 struct node *node;
2479
2480 node = find_node(f, nodeid, name);
2481 if (node == NULL)
2482 return -ENOMEM;
2483
2484 e->ino = node->nodeid;
2485 e->generation = node->generation;
2486 e->entry_timeout = f->conf.entry_timeout;
2487 e->attr_timeout = f->conf.attr_timeout;
2488 if (f->conf.auto_cache) {
2489 pthread_mutex_lock(&f->lock);
2490 update_stat(node, &e->attr);
2491 pthread_mutex_unlock(&f->lock);
2492 }
2493 set_stat(f, e->ino, &e->attr);
2494 return 0;
2495}
2496
2497static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
2498 const char *name, const char *path,
2499 struct fuse_entry_param *e, struct fuse_file_info *fi)
2500{
2501 int res;
2502
2503 memset(e, 0, sizeof(struct fuse_entry_param));
2504 res = fuse_fs_getattr(f->fs, path, &e->attr, fi);
2505 if (res == 0) {
2506 res = do_lookup(f, nodeid, name, e);
2507 if (res == 0 && f->conf.debug) {
2508 fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu\n",
2509 (unsigned long long) e->ino);
2510 }
2511 }
2512 return res;
2513}
2514
2515static struct fuse_context_i *fuse_get_context_internal(void)
2516{
2517 return (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
2518}
2519
2520static struct fuse_context_i *fuse_create_context(struct fuse *f)
2521{
2522 struct fuse_context_i *c = fuse_get_context_internal();
2523 if (c == NULL) {
2524 c = (struct fuse_context_i *)
2525 calloc(1, sizeof(struct fuse_context_i));
2526 if (c == NULL) {
2527 /* This is hard to deal with properly, so just
2528 abort. If memory is so low that the
2529 context cannot be allocated, there's not
2530 much hope for the filesystem anyway */
2531 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate thread specific data\n");
2532 abort();
2533 }
2534 pthread_setspecific(fuse_context_key, c);
2535 } else {
2536 memset(c, 0, sizeof(*c));
2537 }
2538 c->ctx.fuse = f;
2539
2540 return c;
2541}
2542
2543static void fuse_freecontext(void *data)
2544{
2545 free(data);
2546}
2547
2548static int fuse_create_context_key(void)
2549{
2550 int err = 0;
2551 pthread_mutex_lock(&fuse_context_lock);
2552 if (!fuse_context_ref) {
2553 err = pthread_key_create(&fuse_context_key, fuse_freecontext);
2554 if (err) {
2555 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
2556 strerror(err));
2557 pthread_mutex_unlock(&fuse_context_lock);
2558 return -1;
2559 }
2560 }
2561 fuse_context_ref++;
2562 pthread_mutex_unlock(&fuse_context_lock);
2563 return 0;
2564}
2565
2566static void fuse_delete_context_key(void)
2567{
2568 pthread_mutex_lock(&fuse_context_lock);
2569 fuse_context_ref--;
2570 if (!fuse_context_ref) {
2571 free(pthread_getspecific(fuse_context_key));
2572 pthread_key_delete(fuse_context_key);
2573 }
2574 pthread_mutex_unlock(&fuse_context_lock);
2575}
2576
2577static struct fuse *req_fuse_prepare(fuse_req_t req)
2578{
2579 struct fuse_context_i *c = fuse_create_context(req_fuse(req));
2580 const struct fuse_ctx *ctx = fuse_req_ctx(req);
2581 c->req = req;
2582 c->ctx.uid = ctx->uid;
2583 c->ctx.gid = ctx->gid;
2584 c->ctx.pid = ctx->pid;
2585 c->ctx.umask = ctx->umask;
2586 return c->ctx.fuse;
2587}
2588
2589static inline void reply_err(fuse_req_t req, int err)
2590{
2591 /* fuse_reply_err() uses non-negated errno values */
2592 fuse_reply_err(req, -err);
2593}
2594
2595static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
2596 int err)
2597{
2598 if (!err) {
2599 struct fuse *f = req_fuse(req);
2600 if (fuse_reply_entry(req, e) == -ENOENT) {
2601 /* Skip forget for negative result */
2602 if (e->ino != 0)
2603 forget_node(f, e->ino, 1);
2604 }
2605 } else
2606 reply_err(req, err);
2607}
2608
2609void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
2610 struct fuse_config *cfg)
2611{
2612 fuse_get_context()->private_data = fs->user_data;
2613 if (!fs->op.write_buf)
2615 if (!fs->op.lock)
2617 if (!fs->op.flock)
2619 if (fs->op.init)
2620 fs->user_data = fs->op.init(conn, cfg);
2621}
2622
2623static int fuse_init_intr_signal(int signum, int *installed);
2624
2625static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
2626{
2627 struct fuse *f = (struct fuse *) data;
2628
2629 fuse_create_context(f);
2631 fuse_fs_init(f->fs, conn, &f->conf);
2632
2633 if (f->conf.intr) {
2634 if (fuse_init_intr_signal(f->conf.intr_signal,
2635 &f->intr_installed) == -1)
2636 fuse_log(FUSE_LOG_ERR, "fuse: failed to init interrupt signal\n");
2637 } else {
2638 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
2639 conn->no_interrupt = 1;
2640 }
2641}
2642
2643void fuse_fs_destroy(struct fuse_fs *fs)
2644{
2645 fuse_get_context()->private_data = fs->user_data;
2646 if (fs->op.destroy)
2647 fs->op.destroy(fs->user_data);
2648}
2649
2650static void fuse_lib_destroy(void *data)
2651{
2652 struct fuse *f = (struct fuse *) data;
2653
2654 fuse_create_context(f);
2655 fuse_fs_destroy(f->fs);
2656}
2657
2658static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
2659 const char *name)
2660{
2661 struct fuse *f = req_fuse_prepare(req);
2662 struct fuse_entry_param e = { .ino = 0 }; /* invalid ino */
2663 char *path;
2664 int err;
2665 struct node *dot = NULL;
2666
2667 if (name[0] == '.') {
2668 int len = strlen(name);
2669
2670 if (len == 1 || (name[1] == '.' && len == 2)) {
2671 pthread_mutex_lock(&f->lock);
2672 if (len == 1) {
2673 if (f->conf.debug)
2674 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOT\n");
2675 dot = get_node_nocheck(f, parent);
2676 if (dot == NULL) {
2677 pthread_mutex_unlock(&f->lock);
2678 reply_entry(req, &e, -ESTALE);
2679 return;
2680 }
2681 dot->refctr++;
2682 } else {
2683 if (f->conf.debug)
2684 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOTDOT\n");
2685 parent = get_node(f, parent)->parent->nodeid;
2686 }
2687 pthread_mutex_unlock(&f->lock);
2688 name = NULL;
2689 }
2690 }
2691
2692 err = get_path_name(f, parent, name, &path);
2693 if (!err) {
2694 struct fuse_intr_data d;
2695 if (f->conf.debug)
2696 fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path);
2697 fuse_prepare_interrupt(f, req, &d);
2698 err = lookup_path(f, parent, name, path, &e, NULL);
2699 if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
2700 e.ino = 0;
2701 e.entry_timeout = f->conf.negative_timeout;
2702 err = 0;
2703 }
2704 fuse_finish_interrupt(f, req, &d);
2705 free_path(f, parent, path);
2706 }
2707 if (dot) {
2708 pthread_mutex_lock(&f->lock);
2709 unref_node(f, dot);
2710 pthread_mutex_unlock(&f->lock);
2711 }
2712 reply_entry(req, &e, err);
2713}
2714
2715static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
2716{
2717 if (f->conf.debug)
2718 fuse_log(FUSE_LOG_DEBUG, "FORGET %llu/%llu\n", (unsigned long long)ino,
2719 (unsigned long long) nlookup);
2720 forget_node(f, ino, nlookup);
2721}
2722
2723static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
2724{
2725 do_forget(req_fuse(req), ino, nlookup);
2726 fuse_reply_none(req);
2727}
2728
2729static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
2730 struct fuse_forget_data *forgets)
2731{
2732 struct fuse *f = req_fuse(req);
2733 size_t i;
2734
2735 for (i = 0; i < count; i++)
2736 do_forget(f, forgets[i].ino, forgets[i].nlookup);
2737
2738 fuse_reply_none(req);
2739}
2740
2741
2742static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
2743 struct fuse_file_info *fi)
2744{
2745 struct fuse *f = req_fuse_prepare(req);
2746 struct stat buf;
2747 char *path;
2748 int err;
2749
2750 memset(&buf, 0, sizeof(buf));
2751
2752 if (fi != NULL)
2753 err = get_path_nullok(f, ino, &path);
2754 else
2755 err = get_path(f, ino, &path);
2756 if (!err) {
2757 struct fuse_intr_data d;
2758 fuse_prepare_interrupt(f, req, &d);
2759 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2760 fuse_finish_interrupt(f, req, &d);
2761 free_path(f, ino, path);
2762 }
2763 if (!err) {
2764 struct node *node;
2765
2766 pthread_mutex_lock(&f->lock);
2767 node = get_node(f, ino);
2768 if (node->is_hidden && buf.st_nlink > 0)
2769 buf.st_nlink--;
2770 if (f->conf.auto_cache)
2771 update_stat(node, &buf);
2772 pthread_mutex_unlock(&f->lock);
2773 set_stat(f, ino, &buf);
2774 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2775 } else
2776 reply_err(req, err);
2777}
2778
2779int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
2780 struct fuse_file_info *fi)
2781{
2782 fuse_get_context()->private_data = fs->user_data;
2783 if (!fs->op.chmod)
2784 return -ENOSYS;
2785
2786 if (fs->debug) {
2787 char buf[10];
2788
2789 fuse_log(FUSE_LOG_DEBUG, "chmod[%s] %s %llo\n",
2790 file_info_string(fi, buf, sizeof(buf)),
2791 path, (unsigned long long) mode);
2792 }
2793 return fs->op.chmod(path, mode, fi);
2794}
2795
2796static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
2797 int valid, struct fuse_file_info *fi)
2798{
2799 struct fuse *f = req_fuse_prepare(req);
2800 struct stat buf;
2801 char *path;
2802 int err;
2803
2804 memset(&buf, 0, sizeof(buf));
2805 if (fi != NULL)
2806 err = get_path_nullok(f, ino, &path);
2807 else
2808 err = get_path(f, ino, &path);
2809 if (!err) {
2810 struct fuse_intr_data d;
2811 fuse_prepare_interrupt(f, req, &d);
2812 err = 0;
2813 if (!err && (valid & FUSE_SET_ATTR_MODE))
2814 err = fuse_fs_chmod(f->fs, path, attr->st_mode, fi);
2815 if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) {
2816 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
2817 attr->st_uid : (uid_t) -1;
2818 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
2819 attr->st_gid : (gid_t) -1;
2820 err = fuse_fs_chown(f->fs, path, uid, gid, fi);
2821 }
2822 if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
2823 err = fuse_fs_truncate(f->fs, path,
2824 attr->st_size, fi);
2825 }
2826#ifdef HAVE_UTIMENSAT
2827 if (!err &&
2828 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
2829 struct timespec tv[2];
2830
2831 tv[0].tv_sec = 0;
2832 tv[1].tv_sec = 0;
2833 tv[0].tv_nsec = UTIME_OMIT;
2834 tv[1].tv_nsec = UTIME_OMIT;
2835
2836 if (valid & FUSE_SET_ATTR_ATIME_NOW)
2837 tv[0].tv_nsec = UTIME_NOW;
2838 else if (valid & FUSE_SET_ATTR_ATIME)
2839 tv[0] = attr->st_atim;
2840
2841 if (valid & FUSE_SET_ATTR_MTIME_NOW)
2842 tv[1].tv_nsec = UTIME_NOW;
2843 else if (valid & FUSE_SET_ATTR_MTIME)
2844 tv[1] = attr->st_mtim;
2845
2846 err = fuse_fs_utimens(f->fs, path, tv, fi);
2847 } else
2848#endif
2849 if (!err &&
2850 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
2851 (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
2852 struct timespec tv[2];
2853 tv[0].tv_sec = attr->st_atime;
2854 tv[0].tv_nsec = ST_ATIM_NSEC(attr);
2855 tv[1].tv_sec = attr->st_mtime;
2856 tv[1].tv_nsec = ST_MTIM_NSEC(attr);
2857 err = fuse_fs_utimens(f->fs, path, tv, fi);
2858 }
2859 if (!err) {
2860 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2861 }
2862 fuse_finish_interrupt(f, req, &d);
2863 free_path(f, ino, path);
2864 }
2865 if (!err) {
2866 if (f->conf.auto_cache) {
2867 pthread_mutex_lock(&f->lock);
2868 update_stat(get_node(f, ino), &buf);
2869 pthread_mutex_unlock(&f->lock);
2870 }
2871 set_stat(f, ino, &buf);
2872 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2873 } else
2874 reply_err(req, err);
2875}
2876
2877static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask)
2878{
2879 struct fuse *f = req_fuse_prepare(req);
2880 char *path;
2881 int err;
2882
2883 err = get_path(f, ino, &path);
2884 if (!err) {
2885 struct fuse_intr_data d;
2886
2887 fuse_prepare_interrupt(f, req, &d);
2888 err = fuse_fs_access(f->fs, path, mask);
2889 fuse_finish_interrupt(f, req, &d);
2890 free_path(f, ino, path);
2891 }
2892 reply_err(req, err);
2893}
2894
2895static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino)
2896{
2897 struct fuse *f = req_fuse_prepare(req);
2898 char linkname[PATH_MAX + 1];
2899 char *path;
2900 int err;
2901
2902 err = get_path(f, ino, &path);
2903 if (!err) {
2904 struct fuse_intr_data d;
2905 fuse_prepare_interrupt(f, req, &d);
2906 err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname));
2907 fuse_finish_interrupt(f, req, &d);
2908 free_path(f, ino, path);
2909 }
2910 if (!err) {
2911 linkname[PATH_MAX] = '\0';
2912 fuse_reply_readlink(req, linkname);
2913 } else
2914 reply_err(req, err);
2915}
2916
2917static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
2918 mode_t mode, dev_t rdev)
2919{
2920 struct fuse *f = req_fuse_prepare(req);
2921 struct fuse_entry_param e;
2922 char *path;
2923 int err;
2924
2925 err = get_path_name(f, parent, name, &path);
2926 if (!err) {
2927 struct fuse_intr_data d;
2928
2929 fuse_prepare_interrupt(f, req, &d);
2930 err = -ENOSYS;
2931 if (S_ISREG(mode)) {
2932 struct fuse_file_info fi;
2933
2934 memset(&fi, 0, sizeof(fi));
2935 fi.flags = O_CREAT | O_EXCL | O_WRONLY;
2936 err = fuse_fs_create(f->fs, path, mode, &fi);
2937 if (!err) {
2938 err = lookup_path(f, parent, name, path, &e,
2939 &fi);
2940 fuse_fs_release(f->fs, path, &fi);
2941 }
2942 }
2943 if (err == -ENOSYS) {
2944 err = fuse_fs_mknod(f->fs, path, mode, rdev);
2945 if (!err)
2946 err = lookup_path(f, parent, name, path, &e,
2947 NULL);
2948 }
2949 fuse_finish_interrupt(f, req, &d);
2950 free_path(f, parent, path);
2951 }
2952 reply_entry(req, &e, err);
2953}
2954
2955static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
2956 mode_t mode)
2957{
2958 struct fuse *f = req_fuse_prepare(req);
2959 struct fuse_entry_param e;
2960 char *path;
2961 int err;
2962
2963 err = get_path_name(f, parent, name, &path);
2964 if (!err) {
2965 struct fuse_intr_data d;
2966
2967 fuse_prepare_interrupt(f, req, &d);
2968 err = fuse_fs_mkdir(f->fs, path, mode);
2969 if (!err)
2970 err = lookup_path(f, parent, name, path, &e, NULL);
2971 fuse_finish_interrupt(f, req, &d);
2972 free_path(f, parent, path);
2973 }
2974 reply_entry(req, &e, err);
2975}
2976
2977static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
2978 const char *name)
2979{
2980 struct fuse *f = req_fuse_prepare(req);
2981 struct node *wnode;
2982 char *path;
2983 int err;
2984
2985 err = get_path_wrlock(f, parent, name, &path, &wnode);
2986 if (!err) {
2987 struct fuse_intr_data d;
2988
2989 fuse_prepare_interrupt(f, req, &d);
2990 if (!f->conf.hard_remove && is_open(f, parent, name)) {
2991 err = hide_node(f, path, parent, name);
2992 if (!err) {
2993 /* we have hidden the node so now check again under a lock in case it is not used any more */
2994 if (!is_open(f, parent, wnode->name)) {
2995 char *unlinkpath;
2996
2997 /* get the hidden file path, to unlink it */
2998 if (try_get_path(f, wnode->nodeid, NULL, &unlinkpath, NULL, false) == 0) {
2999 err = fuse_fs_unlink(f->fs, unlinkpath);
3000 if (!err)
3001 remove_node(f, parent, wnode->name);
3002 free(unlinkpath);
3003 }
3004 }
3005 }
3006 } else {
3007 err = fuse_fs_unlink(f->fs, path);
3008 if (!err)
3009 remove_node(f, parent, name);
3010 }
3011 fuse_finish_interrupt(f, req, &d);
3012 free_path_wrlock(f, parent, wnode, path);
3013 }
3014 reply_err(req, err);
3015}
3016
3017static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
3018{
3019 struct fuse *f = req_fuse_prepare(req);
3020 struct node *wnode;
3021 char *path;
3022 int err;
3023
3024 err = get_path_wrlock(f, parent, name, &path, &wnode);
3025 if (!err) {
3026 struct fuse_intr_data d;
3027
3028 fuse_prepare_interrupt(f, req, &d);
3029 err = fuse_fs_rmdir(f->fs, path);
3030 fuse_finish_interrupt(f, req, &d);
3031 if (!err)
3032 remove_node(f, parent, name);
3033 free_path_wrlock(f, parent, wnode, path);
3034 }
3035 reply_err(req, err);
3036}
3037
3038static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
3039 fuse_ino_t parent, const char *name)
3040{
3041 struct fuse *f = req_fuse_prepare(req);
3042 struct fuse_entry_param e;
3043 char *path;
3044 int err;
3045
3046 err = get_path_name(f, parent, name, &path);
3047 if (!err) {
3048 struct fuse_intr_data d;
3049
3050 fuse_prepare_interrupt(f, req, &d);
3051 err = fuse_fs_symlink(f->fs, linkname, path);
3052 if (!err)
3053 err = lookup_path(f, parent, name, path, &e, NULL);
3054 fuse_finish_interrupt(f, req, &d);
3055 free_path(f, parent, path);
3056 }
3057 reply_entry(req, &e, err);
3058}
3059
3060static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
3061 const char *oldname, fuse_ino_t newdir,
3062 const char *newname, unsigned int flags)
3063{
3064 struct fuse *f = req_fuse_prepare(req);
3065 char *oldpath;
3066 char *newpath;
3067 struct node *wnode1;
3068 struct node *wnode2;
3069 int err;
3070
3071 err = get_path2(f, olddir, oldname, newdir, newname,
3072 &oldpath, &newpath, &wnode1, &wnode2);
3073 if (!err) {
3074 struct fuse_intr_data d;
3075 err = 0;
3076 fuse_prepare_interrupt(f, req, &d);
3077 if (!f->conf.hard_remove && !(flags & RENAME_EXCHANGE) &&
3078 is_open(f, newdir, newname))
3079 err = hide_node(f, newpath, newdir, newname);
3080 if (!err) {
3081 err = fuse_fs_rename(f->fs, oldpath, newpath, flags);
3082 if (!err) {
3083 if (flags & RENAME_EXCHANGE) {
3084 err = exchange_node(f, olddir, oldname,
3085 newdir, newname);
3086 } else {
3087 err = rename_node(f, olddir, oldname,
3088 newdir, newname, 0);
3089 }
3090 }
3091 }
3092 fuse_finish_interrupt(f, req, &d);
3093 free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
3094 }
3095 reply_err(req, err);
3096}
3097
3098static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
3099 const char *newname)
3100{
3101 struct fuse *f = req_fuse_prepare(req);
3102 struct fuse_entry_param e;
3103 char *oldpath;
3104 char *newpath;
3105 int err;
3106
3107 err = get_path2(f, ino, NULL, newparent, newname,
3108 &oldpath, &newpath, NULL, NULL);
3109 if (!err) {
3110 struct fuse_intr_data d;
3111
3112 fuse_prepare_interrupt(f, req, &d);
3113 err = fuse_fs_link(f->fs, oldpath, newpath);
3114 if (!err)
3115 err = lookup_path(f, newparent, newname, newpath,
3116 &e, NULL);
3117 fuse_finish_interrupt(f, req, &d);
3118 free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath);
3119 }
3120 reply_entry(req, &e, err);
3121}
3122
3123static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
3124 struct fuse_file_info *fi)
3125{
3126 struct node *node;
3127 int unlink_hidden = 0;
3128
3129 fuse_fs_release(f->fs, path, fi);
3130
3131 pthread_mutex_lock(&f->lock);
3132 node = get_node(f, ino);
3133 assert(node->open_count > 0);
3134 --node->open_count;
3135 if (node->is_hidden && !node->open_count) {
3136 unlink_hidden = 1;
3137 node->is_hidden = 0;
3138 }
3139 pthread_mutex_unlock(&f->lock);
3140
3141 if(unlink_hidden) {
3142 if (path) {
3143 fuse_fs_unlink(f->fs, path);
3144 } else if (f->conf.nullpath_ok) {
3145 char *unlinkpath;
3146
3147 if (get_path(f, ino, &unlinkpath) == 0)
3148 fuse_fs_unlink(f->fs, unlinkpath);
3149
3150 free_path(f, ino, unlinkpath);
3151 }
3152 }
3153}
3154
3155static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
3156 const char *name, mode_t mode,
3157 struct fuse_file_info *fi)
3158{
3159 struct fuse *f = req_fuse_prepare(req);
3160 struct fuse_intr_data d;
3161 struct fuse_entry_param e;
3162 char *path;
3163 int err;
3164
3165 err = get_path_name(f, parent, name, &path);
3166 if (!err) {
3167 fuse_prepare_interrupt(f, req, &d);
3168 err = fuse_fs_create(f->fs, path, mode, fi);
3169 if (!err) {
3170 err = lookup_path(f, parent, name, path, &e, fi);
3171 if (err)
3172 fuse_fs_release(f->fs, path, fi);
3173 else if (!S_ISREG(e.attr.st_mode)) {
3174 err = -EIO;
3175 fuse_fs_release(f->fs, path, fi);
3176 forget_node(f, e.ino, 1);
3177 } else {
3178 if (f->conf.direct_io)
3179 fi->direct_io = 1;
3180 if (f->conf.kernel_cache)
3181 fi->keep_cache = 1;
3182 if (fi->direct_io &&
3183 f->conf.parallel_direct_writes)
3184 fi->parallel_direct_writes = 1;
3185 }
3186 }
3187 fuse_finish_interrupt(f, req, &d);
3188 }
3189 if (!err) {
3190 pthread_mutex_lock(&f->lock);
3191 get_node(f, e.ino)->open_count++;
3192 pthread_mutex_unlock(&f->lock);
3193 if (fuse_reply_create(req, &e, fi) == -ENOENT) {
3194 /* The open syscall was interrupted, so it
3195 must be cancelled */
3196 fuse_do_release(f, e.ino, path, fi);
3197 forget_node(f, e.ino, 1);
3198 }
3199 } else {
3200 reply_err(req, err);
3201 }
3202
3203 free_path(f, parent, path);
3204}
3205
3206static double diff_timespec(const struct timespec *t1,
3207 const struct timespec *t2)
3208{
3209 return (t1->tv_sec - t2->tv_sec) +
3210 ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0;
3211}
3212
3213static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path,
3214 struct fuse_file_info *fi)
3215{
3216 struct node *node;
3217
3218 pthread_mutex_lock(&f->lock);
3219 node = get_node(f, ino);
3220 if (node->cache_valid) {
3221 struct timespec now;
3222
3223 curr_time(&now);
3224 if (diff_timespec(&now, &node->stat_updated) >
3225 f->conf.ac_attr_timeout) {
3226 struct stat stbuf;
3227 int err;
3228 pthread_mutex_unlock(&f->lock);
3229 err = fuse_fs_getattr(f->fs, path, &stbuf, fi);
3230 pthread_mutex_lock(&f->lock);
3231 if (!err)
3232 update_stat(node, &stbuf);
3233 else
3234 node->cache_valid = 0;
3235 }
3236 }
3237 if (node->cache_valid)
3238 fi->keep_cache = 1;
3239
3240 node->cache_valid = 1;
3241 pthread_mutex_unlock(&f->lock);
3242}
3243
3244static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
3245 struct fuse_file_info *fi)
3246{
3247 struct fuse *f = req_fuse_prepare(req);
3248 struct fuse_intr_data d;
3249 char *path;
3250 int err;
3251
3252 err = get_path(f, ino, &path);
3253 if (!err) {
3254 fuse_prepare_interrupt(f, req, &d);
3255 err = fuse_fs_open(f->fs, path, fi);
3256 if (!err) {
3257 if (f->conf.direct_io)
3258 fi->direct_io = 1;
3259 if (f->conf.kernel_cache)
3260 fi->keep_cache = 1;
3261
3262 if (f->conf.auto_cache)
3263 open_auto_cache(f, ino, path, fi);
3264
3265 if (f->conf.no_rofd_flush &&
3266 (fi->flags & O_ACCMODE) == O_RDONLY)
3267 fi->noflush = 1;
3268
3269 if (fi->direct_io && f->conf.parallel_direct_writes)
3270 fi->parallel_direct_writes = 1;
3271
3272 }
3273 fuse_finish_interrupt(f, req, &d);
3274 }
3275 if (!err) {
3276 pthread_mutex_lock(&f->lock);
3277 get_node(f, ino)->open_count++;
3278 pthread_mutex_unlock(&f->lock);
3279 if (fuse_reply_open(req, fi) == -ENOENT) {
3280 /* The open syscall was interrupted, so it
3281 must be cancelled */
3282 fuse_do_release(f, ino, path, fi);
3283 }
3284 } else
3285 reply_err(req, err);
3286
3287 free_path(f, ino, path);
3288}
3289
3290static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
3291 off_t off, struct fuse_file_info *fi)
3292{
3293 struct fuse *f = req_fuse_prepare(req);
3294 struct fuse_bufvec *buf = NULL;
3295 char *path;
3296 int res;
3297
3298 res = get_path_nullok(f, ino, &path);
3299 if (res == 0) {
3300 struct fuse_intr_data d;
3301
3302 fuse_prepare_interrupt(f, req, &d);
3303 res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
3304 fuse_finish_interrupt(f, req, &d);
3305 free_path(f, ino, path);
3306 }
3307
3308 if (res == 0)
3310 else
3311 reply_err(req, res);
3312
3313 fuse_free_buf(buf);
3314}
3315
3316static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
3317 struct fuse_bufvec *buf, off_t off,
3318 struct fuse_file_info *fi)
3319{
3320 struct fuse *f = req_fuse_prepare(req);
3321 char *path;
3322 int res;
3323
3324 res = get_path_nullok(f, ino, &path);
3325 if (res == 0) {
3326 struct fuse_intr_data d;
3327
3328 fuse_prepare_interrupt(f, req, &d);
3329 res = fuse_fs_write_buf(f->fs, path, buf, off, fi);
3330 fuse_finish_interrupt(f, req, &d);
3331 free_path(f, ino, path);
3332 }
3333
3334 if (res >= 0)
3335 fuse_reply_write(req, res);
3336 else
3337 reply_err(req, res);
3338}
3339
3340static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
3341 struct fuse_file_info *fi)
3342{
3343 struct fuse *f = req_fuse_prepare(req);
3344 char *path;
3345 int err;
3346
3347 err = get_path_nullok(f, ino, &path);
3348 if (!err) {
3349 struct fuse_intr_data d;
3350
3351 fuse_prepare_interrupt(f, req, &d);
3352 err = fuse_fs_fsync(f->fs, path, datasync, fi);
3353 fuse_finish_interrupt(f, req, &d);
3354 free_path(f, ino, path);
3355 }
3356 reply_err(req, err);
3357}
3358
3359static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi,
3360 struct fuse_file_info *fi)
3361{
3362 struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh;
3363 memset(fi, 0, sizeof(struct fuse_file_info));
3364 fi->fh = dh->fh;
3365 return dh;
3366}
3367
3368static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
3369 struct fuse_file_info *llfi)
3370{
3371 struct fuse *f = req_fuse_prepare(req);
3372 struct fuse_intr_data d;
3373 struct fuse_dh *dh;
3374 struct fuse_file_info fi;
3375 char *path;
3376 int err;
3377
3378 dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
3379 if (dh == NULL) {
3380 reply_err(req, -ENOMEM);
3381 return;
3382 }
3383 memset(dh, 0, sizeof(struct fuse_dh));
3384 dh->fuse = f;
3385 dh->contents = NULL;
3386 dh->first = NULL;
3387 dh->len = 0;
3388 dh->filled = 0;
3389 dh->nodeid = ino;
3390 pthread_mutex_init(&dh->lock, NULL);
3391
3392 llfi->fh = (uintptr_t) dh;
3393
3394 memset(&fi, 0, sizeof(fi));
3395 fi.flags = llfi->flags;
3396
3397 err = get_path(f, ino, &path);
3398 if (!err) {
3399 fuse_prepare_interrupt(f, req, &d);
3400 err = fuse_fs_opendir(f->fs, path, &fi);
3401 fuse_finish_interrupt(f, req, &d);
3402 dh->fh = fi.fh;
3403 llfi->cache_readdir = fi.cache_readdir;
3404 llfi->keep_cache = fi.keep_cache;
3405 }
3406 if (!err) {
3407 if (fuse_reply_open(req, llfi) == -ENOENT) {
3408 /* The opendir syscall was interrupted, so it
3409 must be cancelled */
3410 fuse_fs_releasedir(f->fs, path, &fi);
3411 pthread_mutex_destroy(&dh->lock);
3412 free(dh);
3413 }
3414 } else {
3415 reply_err(req, err);
3416 pthread_mutex_destroy(&dh->lock);
3417 free(dh);
3418 }
3419 free_path(f, ino, path);
3420}
3421
3422static int extend_contents(struct fuse_dh *dh, unsigned minsize)
3423{
3424 if (minsize > dh->size) {
3425 char *newptr;
3426 unsigned newsize = dh->size;
3427 if (!newsize)
3428 newsize = 1024;
3429 while (newsize < minsize) {
3430 if (newsize >= 0x80000000)
3431 newsize = 0xffffffff;
3432 else
3433 newsize *= 2;
3434 }
3435
3436 newptr = (char *) realloc(dh->contents, newsize);
3437 if (!newptr) {
3438 dh->error = -ENOMEM;
3439 return -1;
3440 }
3441 dh->contents = newptr;
3442 dh->size = newsize;
3443 }
3444 return 0;
3445}
3446
3447static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name,
3448 struct stat *st, enum fuse_fill_dir_flags flags)
3449{
3450 struct fuse_direntry *de;
3451
3452 de = malloc(sizeof(struct fuse_direntry));
3453 if (!de) {
3454 dh->error = -ENOMEM;
3455 return -1;
3456 }
3457 de->name = strdup(name);
3458 if (!de->name) {
3459 dh->error = -ENOMEM;
3460 free(de);
3461 return -1;
3462 }
3463 de->flags = flags;
3464 de->stat = *st;
3465 de->next = NULL;
3466
3467 *dh->last = de;
3468 dh->last = &de->next;
3469
3470 return 0;
3471}
3472
3473static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent,
3474 const char *name)
3475{
3476 struct node *node;
3477 fuse_ino_t res = FUSE_UNKNOWN_INO;
3478
3479 pthread_mutex_lock(&f->lock);
3480 node = lookup_node(f, parent, name);
3481 if (node)
3482 res = node->nodeid;
3483 pthread_mutex_unlock(&f->lock);
3484
3485 return res;
3486}
3487
3488static int fill_dir(void *dh_, const char *name, const struct stat *statp,
3489 off_t off, enum fuse_fill_dir_flags flags)
3490{
3491 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3492 struct stat stbuf;
3493
3494 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3495 dh->error = -EIO;
3496 return 1;
3497 }
3498
3499 if (statp)
3500 stbuf = *statp;
3501 else {
3502 memset(&stbuf, 0, sizeof(stbuf));
3503 stbuf.st_ino = FUSE_UNKNOWN_INO;
3504 }
3505
3506 if (!dh->fuse->conf.use_ino) {
3507 stbuf.st_ino = FUSE_UNKNOWN_INO;
3508 if (dh->fuse->conf.readdir_ino) {
3509 stbuf.st_ino = (ino_t)
3510 lookup_nodeid(dh->fuse, dh->nodeid, name);
3511 }
3512 }
3513
3514 if (off) {
3515 size_t newlen;
3516
3517 if (dh->filled) {
3518 dh->error = -EIO;
3519 return 1;
3520 }
3521
3522 if (dh->first) {
3523 dh->error = -EIO;
3524 return 1;
3525 }
3526
3527 if (extend_contents(dh, dh->needlen) == -1)
3528 return 1;
3529
3530 newlen = dh->len +
3531 fuse_add_direntry(dh->req, dh->contents + dh->len,
3532 dh->needlen - dh->len, name,
3533 &stbuf, off);
3534 if (newlen > dh->needlen)
3535 return 1;
3536
3537 dh->len = newlen;
3538 } else {
3539 dh->filled = 1;
3540
3541 if (fuse_add_direntry_to_dh(dh, name, &stbuf, flags) == -1)
3542 return 1;
3543 }
3544 return 0;
3545}
3546
3547static int is_dot_or_dotdot(const char *name)
3548{
3549 return name[0] == '.' && (name[1] == '\0' ||
3550 (name[1] == '.' && name[2] == '\0'));
3551}
3552
3553static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
3554 off_t off, enum fuse_fill_dir_flags flags)
3555{
3556 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3557 struct fuse_entry_param e = {
3558 /* ino=0 tells the kernel to ignore readdirplus stat info */
3559 .ino = 0,
3560 };
3561 struct fuse *f = dh->fuse;
3562 int res;
3563
3564 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3565 dh->error = -EIO;
3566 return 1;
3567 }
3568
3569 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3570 e.attr = *statp;
3571 }
3572
3573 e.attr.st_ino = FUSE_UNKNOWN_INO;
3574 if (statp) {
3575 e.attr.st_mode = statp->st_mode;
3576 if (f->conf.use_ino)
3577 e.attr.st_ino = statp->st_ino;
3578 }
3579 if (!f->conf.use_ino && f->conf.readdir_ino) {
3580 e.attr.st_ino = (ino_t)
3581 lookup_nodeid(f, dh->nodeid, name);
3582 }
3583
3584 if (off) {
3585 size_t newlen;
3586
3587 if (dh->filled) {
3588 dh->error = -EIO;
3589 return 1;
3590 }
3591
3592 if (dh->first) {
3593 dh->error = -EIO;
3594 return 1;
3595 }
3596 if (extend_contents(dh, dh->needlen) == -1)
3597 return 1;
3598
3599 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3600 if (!is_dot_or_dotdot(name)) {
3601 res = do_lookup(f, dh->nodeid, name, &e);
3602 if (res) {
3603 dh->error = res;
3604 return 1;
3605 }
3606 }
3607 }
3608
3609 newlen = dh->len +
3610 fuse_add_direntry_plus(dh->req, dh->contents + dh->len,
3611 dh->needlen - dh->len, name,
3612 &e, off);
3613 if (newlen > dh->needlen)
3614 return 1;
3615 dh->len = newlen;
3616 } else {
3617 dh->filled = 1;
3618
3619 if (fuse_add_direntry_to_dh(dh, name, &e.attr, flags) == -1)
3620 return 1;
3621 }
3622
3623 return 0;
3624}
3625
3626static void free_direntries(struct fuse_direntry *de)
3627{
3628 while (de) {
3629 struct fuse_direntry *next = de->next;
3630 free(de->name);
3631 free(de);
3632 de = next;
3633 }
3634}
3635
3636static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3637 size_t size, off_t off, struct fuse_dh *dh,
3638 struct fuse_file_info *fi,
3639 enum fuse_readdir_flags flags)
3640{
3641 char *path;
3642 int err;
3643
3644 if (f->fs->op.readdir)
3645 err = get_path_nullok(f, ino, &path);
3646 else
3647 err = get_path(f, ino, &path);
3648 if (!err) {
3649 struct fuse_intr_data d;
3650 fuse_fill_dir_t filler = fill_dir;
3651
3652 if (flags & FUSE_READDIR_PLUS)
3653 filler = fill_dir_plus;
3654
3655 free_direntries(dh->first);
3656 dh->first = NULL;
3657 dh->last = &dh->first;
3658 dh->len = 0;
3659 dh->error = 0;
3660 dh->needlen = size;
3661 dh->filled = 0;
3662 dh->req = req;
3663 fuse_prepare_interrupt(f, req, &d);
3664 err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags);
3665 fuse_finish_interrupt(f, req, &d);
3666 dh->req = NULL;
3667 if (!err)
3668 err = dh->error;
3669 if (err)
3670 dh->filled = 0;
3671 free_path(f, ino, path);
3672 }
3673 return err;
3674}
3675
3676static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh,
3677 off_t off, enum fuse_readdir_flags flags)
3678{
3679 off_t pos;
3680 struct fuse_direntry *de = dh->first;
3681 int res;
3682
3683 dh->len = 0;
3684
3685 if (extend_contents(dh, dh->needlen) == -1)
3686 return dh->error;
3687
3688 for (pos = 0; pos < off; pos++) {
3689 if (!de)
3690 break;
3691
3692 de = de->next;
3693 }
3694 while (de) {
3695 char *p = dh->contents + dh->len;
3696 unsigned rem = dh->needlen - dh->len;
3697 unsigned thislen;
3698 unsigned newlen;
3699 pos++;
3700
3701 if (flags & FUSE_READDIR_PLUS) {
3702 struct fuse_entry_param e = {
3703 .ino = 0,
3704 .attr = de->stat,
3705 };
3706
3707 if (de->flags & FUSE_FILL_DIR_PLUS &&
3708 !is_dot_or_dotdot(de->name)) {
3709 res = do_lookup(dh->fuse, dh->nodeid,
3710 de->name, &e);
3711 if (res) {
3712 dh->error = res;
3713 return 1;
3714 }
3715 }
3716
3717 thislen = fuse_add_direntry_plus(req, p, rem,
3718 de->name, &e, pos);
3719 } else {
3720 thislen = fuse_add_direntry(req, p, rem,
3721 de->name, &de->stat, pos);
3722 }
3723 newlen = dh->len + thislen;
3724 if (newlen > dh->needlen)
3725 break;
3726 dh->len = newlen;
3727 de = de->next;
3728 }
3729 return 0;
3730}
3731
3732static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size,
3733 off_t off, struct fuse_file_info *llfi,
3734 enum fuse_readdir_flags flags)
3735{
3736 struct fuse *f = req_fuse_prepare(req);
3737 struct fuse_file_info fi;
3738 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3739 int err;
3740
3741 pthread_mutex_lock(&dh->lock);
3742 /* According to SUS, directory contents need to be refreshed on
3743 rewinddir() */
3744 if (!off)
3745 dh->filled = 0;
3746
3747 if (!dh->filled) {
3748 err = readdir_fill(f, req, ino, size, off, dh, &fi, flags);
3749 if (err) {
3750 reply_err(req, err);
3751 goto out;
3752 }
3753 }
3754 if (dh->filled) {
3755 dh->needlen = size;
3756 err = readdir_fill_from_list(req, dh, off, flags);
3757 if (err) {
3758 reply_err(req, err);
3759 goto out;
3760 }
3761 }
3762 fuse_reply_buf(req, dh->contents, dh->len);
3763out:
3764 pthread_mutex_unlock(&dh->lock);
3765}
3766
3767static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
3768 off_t off, struct fuse_file_info *llfi)
3769{
3770 fuse_readdir_common(req, ino, size, off, llfi, 0);
3771}
3772
3773static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
3774 off_t off, struct fuse_file_info *llfi)
3775{
3776 fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS);
3777}
3778
3779static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
3780 struct fuse_file_info *llfi)
3781{
3782 struct fuse *f = req_fuse_prepare(req);
3783 struct fuse_intr_data d;
3784 struct fuse_file_info fi;
3785 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3786 char *path;
3787
3788 get_path_nullok(f, ino, &path);
3789
3790 fuse_prepare_interrupt(f, req, &d);
3791 fuse_fs_releasedir(f->fs, path, &fi);
3792 fuse_finish_interrupt(f, req, &d);
3793 free_path(f, ino, path);
3794
3795 pthread_mutex_lock(&dh->lock);
3796 pthread_mutex_unlock(&dh->lock);
3797 pthread_mutex_destroy(&dh->lock);
3798 free_direntries(dh->first);
3799 free(dh->contents);
3800 free(dh);
3801 reply_err(req, 0);
3802}
3803
3804static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
3805 struct fuse_file_info *llfi)
3806{
3807 struct fuse *f = req_fuse_prepare(req);
3808 struct fuse_file_info fi;
3809 char *path;
3810 int err;
3811
3812 get_dirhandle(llfi, &fi);
3813
3814 err = get_path_nullok(f, ino, &path);
3815 if (!err) {
3816 struct fuse_intr_data d;
3817 fuse_prepare_interrupt(f, req, &d);
3818 err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi);
3819 fuse_finish_interrupt(f, req, &d);
3820 free_path(f, ino, path);
3821 }
3822 reply_err(req, err);
3823}
3824
3825static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
3826{
3827 struct fuse *f = req_fuse_prepare(req);
3828 struct statvfs buf;
3829 char *path = NULL;
3830 int err = 0;
3831
3832 memset(&buf, 0, sizeof(buf));
3833 if (ino)
3834 err = get_path(f, ino, &path);
3835
3836 if (!err) {
3837 struct fuse_intr_data d;
3838 fuse_prepare_interrupt(f, req, &d);
3839 err = fuse_fs_statfs(f->fs, path ? path : "/", &buf);
3840 fuse_finish_interrupt(f, req, &d);
3841 free_path(f, ino, path);
3842 }
3843
3844 if (!err)
3845 fuse_reply_statfs(req, &buf);
3846 else
3847 reply_err(req, err);
3848}
3849
3850static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3851 const char *value, size_t size, int flags)
3852{
3853 struct fuse *f = req_fuse_prepare(req);
3854 char *path;
3855 int err;
3856
3857 err = get_path(f, ino, &path);
3858 if (!err) {
3859 struct fuse_intr_data d;
3860 fuse_prepare_interrupt(f, req, &d);
3861 err = fuse_fs_setxattr(f->fs, path, name, value, size, flags);
3862 fuse_finish_interrupt(f, req, &d);
3863 free_path(f, ino, path);
3864 }
3865 reply_err(req, err);
3866}
3867
3868static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3869 const char *name, char *value, size_t size)
3870{
3871 int err;
3872 char *path;
3873
3874 err = get_path(f, ino, &path);
3875 if (!err) {
3876 struct fuse_intr_data d;
3877 fuse_prepare_interrupt(f, req, &d);
3878 err = fuse_fs_getxattr(f->fs, path, name, value, size);
3879 fuse_finish_interrupt(f, req, &d);
3880 free_path(f, ino, path);
3881 }
3882 return err;
3883}
3884
3885static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3886 size_t size)
3887{
3888 struct fuse *f = req_fuse_prepare(req);
3889 int res;
3890
3891 if (size) {
3892 char *value = (char *) malloc(size);
3893 if (value == NULL) {
3894 reply_err(req, -ENOMEM);
3895 return;
3896 }
3897 res = common_getxattr(f, req, ino, name, value, size);
3898 if (res > 0)
3899 fuse_reply_buf(req, value, res);
3900 else
3901 reply_err(req, res);
3902 free(value);
3903 } else {
3904 res = common_getxattr(f, req, ino, name, NULL, 0);
3905 if (res >= 0)
3906 fuse_reply_xattr(req, res);
3907 else
3908 reply_err(req, res);
3909 }
3910}
3911
3912static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3913 char *list, size_t size)
3914{
3915 char *path;
3916 int err;
3917
3918 err = get_path(f, ino, &path);
3919 if (!err) {
3920 struct fuse_intr_data d;
3921 fuse_prepare_interrupt(f, req, &d);
3922 err = fuse_fs_listxattr(f->fs, path, list, size);
3923 fuse_finish_interrupt(f, req, &d);
3924 free_path(f, ino, path);
3925 }
3926 return err;
3927}
3928
3929static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
3930{
3931 struct fuse *f = req_fuse_prepare(req);
3932 int res;
3933
3934 if (size) {
3935 char *list = (char *) malloc(size);
3936 if (list == NULL) {
3937 reply_err(req, -ENOMEM);
3938 return;
3939 }
3940 res = common_listxattr(f, req, ino, list, size);
3941 if (res > 0)
3942 fuse_reply_buf(req, list, res);
3943 else
3944 reply_err(req, res);
3945 free(list);
3946 } else {
3947 res = common_listxattr(f, req, ino, NULL, 0);
3948 if (res >= 0)
3949 fuse_reply_xattr(req, res);
3950 else
3951 reply_err(req, res);
3952 }
3953}
3954
3955static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
3956 const char *name)
3957{
3958 struct fuse *f = req_fuse_prepare(req);
3959 char *path;
3960 int err;
3961
3962 err = get_path(f, ino, &path);
3963 if (!err) {
3964 struct fuse_intr_data d;
3965 fuse_prepare_interrupt(f, req, &d);
3966 err = fuse_fs_removexattr(f->fs, path, name);
3967 fuse_finish_interrupt(f, req, &d);
3968 free_path(f, ino, path);
3969 }
3970 reply_err(req, err);
3971}
3972
3973static struct lock *locks_conflict(struct node *node, const struct lock *lock)
3974{
3975 struct lock *l;
3976
3977 for (l = node->locks; l; l = l->next)
3978 if (l->owner != lock->owner &&
3979 lock->start <= l->end && l->start <= lock->end &&
3980 (l->type == F_WRLCK || lock->type == F_WRLCK))
3981 break;
3982
3983 return l;
3984}
3985
3986static void delete_lock(struct lock **lockp)
3987{
3988 struct lock *l = *lockp;
3989 *lockp = l->next;
3990 free(l);
3991}
3992
3993static void insert_lock(struct lock **pos, struct lock *lock)
3994{
3995 lock->next = *pos;
3996 *pos = lock;
3997}
3998
3999static int locks_insert(struct node *node, struct lock *lock)
4000{
4001 struct lock **lp;
4002 struct lock *newl1 = NULL;
4003 struct lock *newl2 = NULL;
4004
4005 if (lock->type != F_UNLCK || lock->start != 0 ||
4006 lock->end != OFFSET_MAX) {
4007 newl1 = malloc(sizeof(struct lock));
4008 newl2 = malloc(sizeof(struct lock));
4009
4010 if (!newl1 || !newl2) {
4011 free(newl1);
4012 free(newl2);
4013 return -ENOLCK;
4014 }
4015 }
4016
4017 for (lp = &node->locks; *lp;) {
4018 struct lock *l = *lp;
4019 if (l->owner != lock->owner)
4020 goto skip;
4021
4022 if (lock->type == l->type) {
4023 if (l->end < lock->start - 1)
4024 goto skip;
4025 if (lock->end < l->start - 1)
4026 break;
4027 if (l->start <= lock->start && lock->end <= l->end)
4028 goto out;
4029 if (l->start < lock->start)
4030 lock->start = l->start;
4031 if (lock->end < l->end)
4032 lock->end = l->end;
4033 goto delete;
4034 } else {
4035 if (l->end < lock->start)
4036 goto skip;
4037 if (lock->end < l->start)
4038 break;
4039 if (lock->start <= l->start && l->end <= lock->end)
4040 goto delete;
4041 if (l->end <= lock->end) {
4042 l->end = lock->start - 1;
4043 goto skip;
4044 }
4045 if (lock->start <= l->start) {
4046 l->start = lock->end + 1;
4047 break;
4048 }
4049 *newl2 = *l;
4050 newl2->start = lock->end + 1;
4051 l->end = lock->start - 1;
4052 insert_lock(&l->next, newl2);
4053 newl2 = NULL;
4054 }
4055 skip:
4056 lp = &l->next;
4057 continue;
4058
4059 delete:
4060 delete_lock(lp);
4061 }
4062 if (lock->type != F_UNLCK) {
4063 *newl1 = *lock;
4064 insert_lock(lp, newl1);
4065 newl1 = NULL;
4066 }
4067out:
4068 free(newl1);
4069 free(newl2);
4070 return 0;
4071}
4072
4073static void flock_to_lock(struct flock *flock, struct lock *lock)
4074{
4075 memset(lock, 0, sizeof(struct lock));
4076 lock->type = flock->l_type;
4077 lock->start = flock->l_start;
4078 lock->end =
4079 flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
4080 lock->pid = flock->l_pid;
4081}
4082
4083static void lock_to_flock(struct lock *lock, struct flock *flock)
4084{
4085 flock->l_type = lock->type;
4086 flock->l_start = lock->start;
4087 flock->l_len =
4088 (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
4089 flock->l_pid = lock->pid;
4090}
4091
4092static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
4093 const char *path, struct fuse_file_info *fi)
4094{
4095 struct fuse_intr_data d;
4096 struct flock lock;
4097 struct lock l;
4098 int err;
4099 int errlock;
4100
4101 fuse_prepare_interrupt(f, req, &d);
4102 memset(&lock, 0, sizeof(lock));
4103 lock.l_type = F_UNLCK;
4104 lock.l_whence = SEEK_SET;
4105 err = fuse_fs_flush(f->fs, path, fi);
4106 errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock);
4107 fuse_finish_interrupt(f, req, &d);
4108
4109 if (errlock != -ENOSYS) {
4110 flock_to_lock(&lock, &l);
4111 l.owner = fi->lock_owner;
4112 pthread_mutex_lock(&f->lock);
4113 locks_insert(get_node(f, ino), &l);
4114 pthread_mutex_unlock(&f->lock);
4115
4116 /* if op.lock() is defined FLUSH is needed regardless
4117 of op.flush() */
4118 if (err == -ENOSYS)
4119 err = 0;
4120 }
4121 return err;
4122}
4123
4124static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino,
4125 struct fuse_file_info *fi)
4126{
4127 struct fuse *f = req_fuse_prepare(req);
4128 struct fuse_intr_data d;
4129 char *path;
4130 int err = 0;
4131
4132 get_path_nullok(f, ino, &path);
4133 if (fi->flush) {
4134 err = fuse_flush_common(f, req, ino, path, fi);
4135 if (err == -ENOSYS)
4136 err = 0;
4137 }
4138
4139 fuse_prepare_interrupt(f, req, &d);
4140 fuse_do_release(f, ino, path, fi);
4141 fuse_finish_interrupt(f, req, &d);
4142 free_path(f, ino, path);
4143
4144 reply_err(req, err);
4145}
4146
4147static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino,
4148 struct fuse_file_info *fi)
4149{
4150 struct fuse *f = req_fuse_prepare(req);
4151 char *path;
4152 int err;
4153
4154 get_path_nullok(f, ino, &path);
4155 err = fuse_flush_common(f, req, ino, path, fi);
4156 free_path(f, ino, path);
4157
4158 reply_err(req, err);
4159}
4160
4161static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
4162 struct fuse_file_info *fi, struct flock *lock,
4163 int cmd)
4164{
4165 struct fuse *f = req_fuse_prepare(req);
4166 char *path;
4167 int err;
4168
4169 err = get_path_nullok(f, ino, &path);
4170 if (!err) {
4171 struct fuse_intr_data d;
4172 fuse_prepare_interrupt(f, req, &d);
4173 err = fuse_fs_lock(f->fs, path, fi, cmd, lock);
4174 fuse_finish_interrupt(f, req, &d);
4175 free_path(f, ino, path);
4176 }
4177 return err;
4178}
4179
4180static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino,
4181 struct fuse_file_info *fi, struct flock *lock)
4182{
4183 int err;
4184 struct lock l;
4185 struct lock *conflict;
4186 struct fuse *f = req_fuse(req);
4187
4188 flock_to_lock(lock, &l);
4189 l.owner = fi->lock_owner;
4190 pthread_mutex_lock(&f->lock);
4191 conflict = locks_conflict(get_node(f, ino), &l);
4192 if (conflict)
4193 lock_to_flock(conflict, lock);
4194 pthread_mutex_unlock(&f->lock);
4195 if (!conflict)
4196 err = fuse_lock_common(req, ino, fi, lock, F_GETLK);
4197 else
4198 err = 0;
4199
4200 if (!err)
4201 fuse_reply_lock(req, lock);
4202 else
4203 reply_err(req, err);
4204}
4205
4206static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino,
4207 struct fuse_file_info *fi, struct flock *lock,
4208 int sleep)
4209{
4210 int err = fuse_lock_common(req, ino, fi, lock,
4211 sleep ? F_SETLKW : F_SETLK);
4212 if (!err) {
4213 struct fuse *f = req_fuse(req);
4214 struct lock l;
4215 flock_to_lock(lock, &l);
4216 l.owner = fi->lock_owner;
4217 pthread_mutex_lock(&f->lock);
4218 locks_insert(get_node(f, ino), &l);
4219 pthread_mutex_unlock(&f->lock);
4220 }
4221 reply_err(req, err);
4222}
4223
4224static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino,
4225 struct fuse_file_info *fi, int op)
4226{
4227 struct fuse *f = req_fuse_prepare(req);
4228 char *path;
4229 int err;
4230
4231 err = get_path_nullok(f, ino, &path);
4232 if (err == 0) {
4233 struct fuse_intr_data d;
4234 fuse_prepare_interrupt(f, req, &d);
4235 err = fuse_fs_flock(f->fs, path, fi, op);
4236 fuse_finish_interrupt(f, req, &d);
4237 free_path(f, ino, path);
4238 }
4239 reply_err(req, err);
4240}
4241
4242static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
4243 uint64_t idx)
4244{
4245 struct fuse *f = req_fuse_prepare(req);
4246 struct fuse_intr_data d;
4247 char *path;
4248 int err;
4249
4250 err = get_path(f, ino, &path);
4251 if (!err) {
4252 fuse_prepare_interrupt(f, req, &d);
4253 err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
4254 fuse_finish_interrupt(f, req, &d);
4255 free_path(f, ino, path);
4256 }
4257 if (!err)
4258 fuse_reply_bmap(req, idx);
4259 else
4260 reply_err(req, err);
4261}
4262
4263static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
4264 void *arg, struct fuse_file_info *llfi,
4265 unsigned int flags, const void *in_buf,
4266 size_t in_bufsz, size_t out_bufsz)
4267{
4268 struct fuse *f = req_fuse_prepare(req);
4269 struct fuse_intr_data d;
4270 struct fuse_file_info fi;
4271 char *path, *out_buf = NULL;
4272 int err;
4273
4274 err = -EPERM;
4275 if (flags & FUSE_IOCTL_UNRESTRICTED)
4276 goto err;
4277
4278 if (flags & FUSE_IOCTL_DIR)
4279 get_dirhandle(llfi, &fi);
4280 else
4281 fi = *llfi;
4282
4283 if (out_bufsz) {
4284 err = -ENOMEM;
4285 out_buf = malloc(out_bufsz);
4286 if (!out_buf)
4287 goto err;
4288 }
4289
4290 assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
4291 if (out_buf && in_bufsz)
4292 memcpy(out_buf, in_buf, in_bufsz);
4293
4294 err = get_path_nullok(f, ino, &path);
4295 if (err)
4296 goto err;
4297
4298 fuse_prepare_interrupt(f, req, &d);
4299
4300 err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags,
4301 out_buf ? out_buf : (void *)in_buf);
4302
4303 fuse_finish_interrupt(f, req, &d);
4304 free_path(f, ino, path);
4305
4306 if (err < 0)
4307 goto err;
4308 fuse_reply_ioctl(req, err, out_buf, out_bufsz);
4309 goto out;
4310err:
4311 reply_err(req, err);
4312out:
4313 free(out_buf);
4314}
4315
4316static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
4317 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
4318{
4319 struct fuse *f = req_fuse_prepare(req);
4320 struct fuse_intr_data d;
4321 char *path;
4322 int err;
4323 unsigned revents = 0;
4324
4325 err = get_path_nullok(f, ino, &path);
4326 if (!err) {
4327 fuse_prepare_interrupt(f, req, &d);
4328 err = fuse_fs_poll(f->fs, path, fi, ph, &revents);
4329 fuse_finish_interrupt(f, req, &d);
4330 free_path(f, ino, path);
4331 }
4332 if (!err)
4333 fuse_reply_poll(req, revents);
4334 else
4335 reply_err(req, err);
4336}
4337
4338static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
4339 off_t offset, off_t length, struct fuse_file_info *fi)
4340{
4341 struct fuse *f = req_fuse_prepare(req);
4342 struct fuse_intr_data d;
4343 char *path;
4344 int err;
4345
4346 err = get_path_nullok(f, ino, &path);
4347 if (!err) {
4348 fuse_prepare_interrupt(f, req, &d);
4349 err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi);
4350 fuse_finish_interrupt(f, req, &d);
4351 free_path(f, ino, path);
4352 }
4353 reply_err(req, err);
4354}
4355
4356static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
4357 off_t off_in, struct fuse_file_info *fi_in,
4358 fuse_ino_t nodeid_out, off_t off_out,
4359 struct fuse_file_info *fi_out, size_t len,
4360 int flags)
4361{
4362 struct fuse *f = req_fuse_prepare(req);
4363 struct fuse_intr_data d;
4364 char *path_in, *path_out;
4365 int err;
4366 ssize_t res;
4367
4368 err = get_path_nullok(f, nodeid_in, &path_in);
4369 if (err) {
4370 reply_err(req, err);
4371 return;
4372 }
4373
4374 err = get_path_nullok(f, nodeid_out, &path_out);
4375 if (err) {
4376 free_path(f, nodeid_in, path_in);
4377 reply_err(req, err);
4378 return;
4379 }
4380
4381 fuse_prepare_interrupt(f, req, &d);
4382 res = fuse_fs_copy_file_range(f->fs, path_in, fi_in, off_in, path_out,
4383 fi_out, off_out, len, flags);
4384 fuse_finish_interrupt(f, req, &d);
4385
4386 if (res >= 0)
4387 fuse_reply_write(req, res);
4388 else
4389 reply_err(req, res);
4390
4391 free_path(f, nodeid_in, path_in);
4392 free_path(f, nodeid_out, path_out);
4393}
4394
4395static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
4396 struct fuse_file_info *fi)
4397{
4398 struct fuse *f = req_fuse_prepare(req);
4399 struct fuse_intr_data d;
4400 char *path;
4401 int err;
4402 off_t res;
4403
4404 err = get_path(f, ino, &path);
4405 if (err) {
4406 reply_err(req, err);
4407 return;
4408 }
4409
4410 fuse_prepare_interrupt(f, req, &d);
4411 res = fuse_fs_lseek(f->fs, path, off, whence, fi);
4412 fuse_finish_interrupt(f, req, &d);
4413 free_path(f, ino, path);
4414 if (res >= 0)
4415 fuse_reply_lseek(req, res);
4416 else
4417 reply_err(req, res);
4418}
4419
4420#ifdef HAVE_STATX
4421static void fuse_lib_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
4422 struct fuse_file_info *fi)
4423{
4424 struct fuse *f = req_fuse_prepare(req);
4425 struct statx stxbuf;
4426 char *path;
4427 int err;
4428
4429 memset(&stxbuf, 0, sizeof(stxbuf));
4430
4431 if (fi != NULL)
4432 err = get_path_nullok(f, ino, &path);
4433 else
4434 err = get_path(f, ino, &path);
4435
4436 if (!err) {
4437 struct fuse_intr_data d;
4438
4439 if (!path)
4440 flags |= AT_EMPTY_PATH;
4441 fuse_prepare_interrupt(f, req, &d);
4442 err = fuse_fs_statx(f->fs, path, flags, mask, &stxbuf, fi);
4443 fuse_finish_interrupt(f, req, &d);
4444 free_path(f, ino, path);
4445 }
4446 if (!err) {
4447 struct node *node;
4448
4449 pthread_mutex_lock(&f->lock);
4450 node = get_node(f, ino);
4451 if (node->is_hidden && stxbuf.stx_nlink > 0)
4452 stxbuf.stx_nlink--;
4453 if (f->conf.auto_cache) {
4454 struct stat stbuf;
4455
4456 stbuf.st_mtime = stxbuf.stx_mtime.tv_nsec;
4457 ST_MTIM_NSEC(&stbuf) = stxbuf.stx_mtime.tv_nsec;
4458 stbuf.st_size = stxbuf.stx_size;
4459 update_stat(node, &stbuf);
4460 }
4461 pthread_mutex_unlock(&f->lock);
4462 set_statx(f, ino, &stxbuf);
4463 fuse_reply_statx(req, 0, &stxbuf, f->conf.attr_timeout);
4464 } else
4465 reply_err(req, err);
4466}
4467#endif
4468
4469static int clean_delay(struct fuse *f)
4470{
4471 /*
4472 * This is calculating the delay between clean runs. To
4473 * reduce the number of cleans we are doing them 10 times
4474 * within the remember window.
4475 */
4476 int min_sleep = 60;
4477 int max_sleep = 3600;
4478 int sleep_time = f->conf.remember / 10;
4479
4480 if (sleep_time > max_sleep)
4481 return max_sleep;
4482 if (sleep_time < min_sleep)
4483 return min_sleep;
4484 return sleep_time;
4485}
4486
4487int fuse_clean_cache(struct fuse *f)
4488{
4489 struct node_lru *lnode;
4490 struct list_head *curr, *next;
4491 struct node *node;
4492 struct timespec now;
4493
4494 pthread_mutex_lock(&f->lock);
4495
4496 curr_time(&now);
4497
4498 for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) {
4499 double age;
4500
4501 next = curr->next;
4502 lnode = list_entry(curr, struct node_lru, lru);
4503 node = &lnode->node;
4504
4505 age = diff_timespec(&now, &lnode->forget_time);
4506 if (age <= f->conf.remember)
4507 break;
4508
4509 assert(node->nlookup == 1);
4510
4511 /* Don't forget active directories */
4512 if (node->refctr > 1)
4513 continue;
4514
4515 node->nlookup = 0;
4516 unhash_name(f, node);
4517 unref_node(f, node);
4518 }
4519 pthread_mutex_unlock(&f->lock);
4520
4521 return clean_delay(f);
4522}
4523
4524static struct fuse_lowlevel_ops fuse_path_ops = {
4525 .init = fuse_lib_init,
4526 .destroy = fuse_lib_destroy,
4527 .lookup = fuse_lib_lookup,
4528 .forget = fuse_lib_forget,
4529 .forget_multi = fuse_lib_forget_multi,
4530 .getattr = fuse_lib_getattr,
4531 .setattr = fuse_lib_setattr,
4532 .access = fuse_lib_access,
4533 .readlink = fuse_lib_readlink,
4534 .mknod = fuse_lib_mknod,
4535 .mkdir = fuse_lib_mkdir,
4536 .unlink = fuse_lib_unlink,
4537 .rmdir = fuse_lib_rmdir,
4538 .symlink = fuse_lib_symlink,
4539 .rename = fuse_lib_rename,
4540 .link = fuse_lib_link,
4541 .create = fuse_lib_create,
4542 .open = fuse_lib_open,
4543 .read = fuse_lib_read,
4544 .write_buf = fuse_lib_write_buf,
4545 .flush = fuse_lib_flush,
4546 .release = fuse_lib_release,
4547 .fsync = fuse_lib_fsync,
4548 .opendir = fuse_lib_opendir,
4549 .readdir = fuse_lib_readdir,
4550 .readdirplus = fuse_lib_readdirplus,
4551 .releasedir = fuse_lib_releasedir,
4552 .fsyncdir = fuse_lib_fsyncdir,
4553 .statfs = fuse_lib_statfs,
4554 .setxattr = fuse_lib_setxattr,
4555 .getxattr = fuse_lib_getxattr,
4556 .listxattr = fuse_lib_listxattr,
4557 .removexattr = fuse_lib_removexattr,
4558 .getlk = fuse_lib_getlk,
4559 .setlk = fuse_lib_setlk,
4560 .flock = fuse_lib_flock,
4561 .bmap = fuse_lib_bmap,
4562 .ioctl = fuse_lib_ioctl,
4563 .poll = fuse_lib_poll,
4564 .fallocate = fuse_lib_fallocate,
4565 .copy_file_range = fuse_lib_copy_file_range,
4566 .lseek = fuse_lib_lseek,
4567#ifdef HAVE_STATX
4568 .statx = fuse_lib_statx,
4569#endif
4570};
4571
4572int fuse_notify_poll(struct fuse_pollhandle *ph)
4573{
4574 return fuse_lowlevel_notify_poll(ph);
4575}
4576
4577struct fuse_session *fuse_get_session(struct fuse *f)
4578{
4579 return f->se;
4580}
4581
4582static int fuse_session_loop_remember(struct fuse *f)
4583{
4584 struct fuse_session *se = f->se;
4585 int res = 0;
4586 struct timespec now;
4587 time_t next_clean;
4588 struct pollfd fds = {
4589 .fd = se->fd,
4590 .events = POLLIN
4591 };
4592 struct fuse_buf fbuf = {
4593 .mem = NULL,
4594 };
4595
4596 curr_time(&now);
4597 next_clean = now.tv_sec;
4598 while (!fuse_session_exited(se)) {
4599 unsigned timeout;
4600
4601 curr_time(&now);
4602 if (now.tv_sec < next_clean)
4603 timeout = next_clean - now.tv_sec;
4604 else
4605 timeout = 0;
4606
4607 res = poll(&fds, 1, timeout * 1000);
4608 if (res == -1) {
4609 if (errno == EINTR)
4610 continue;
4611 else
4612 break;
4613 } else if (res > 0) {
4614 res = fuse_session_receive_buf_internal(se, &fbuf,
4615 NULL);
4616 if (res == -EINTR)
4617 continue;
4618 if (res <= 0)
4619 break;
4620
4621 fuse_session_process_buf_internal(se, &fbuf, NULL);
4622 } else {
4623 timeout = fuse_clean_cache(f);
4624 curr_time(&now);
4625 next_clean = now.tv_sec + timeout;
4626 }
4627 }
4628
4629 fuse_buf_free(&fbuf);
4630 return res < 0 ? -1 : 0;
4631}
4632
4633int fuse_loop(struct fuse *f)
4634{
4635 if (!f)
4636 return -1;
4637
4638 if (lru_enabled(f))
4639 return fuse_session_loop_remember(f);
4640
4641 return fuse_session_loop(f->se);
4642}
4643
4644FUSE_SYMVER("fuse_loop_mt_312", "fuse_loop_mt@@FUSE_3.12")
4645int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config)
4646{
4647 if (f == NULL)
4648 return -1;
4649
4650 int res = fuse_start_cleanup_thread(f);
4651 if (res)
4652 return -1;
4653
4654 res = fuse_session_loop_mt_312(fuse_get_session(f), config);
4656 return res;
4657}
4658
4659int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1);
4660FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@FUSE_3.2")
4661int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1)
4662{
4663 struct fuse_loop_config *config = fuse_loop_cfg_create();
4664 if (config == NULL)
4665 return ENOMEM;
4666
4667 fuse_loop_cfg_convert(config, config_v1);
4668
4669 int res = fuse_loop_mt_312(f, config);
4670
4671 fuse_loop_cfg_destroy(config);
4672
4673 return res;
4674}
4675
4676int fuse_loop_mt_31(struct fuse *f, int clone_fd);
4677FUSE_SYMVER("fuse_loop_mt_31", "fuse_loop_mt@FUSE_3.0")
4678int fuse_loop_mt_31(struct fuse *f, int clone_fd)
4679{
4680 int err;
4681 struct fuse_loop_config *config = fuse_loop_cfg_create();
4682
4683 if (config == NULL)
4684 return ENOMEM;
4685
4686 fuse_loop_cfg_set_clone_fd(config, clone_fd);
4687
4688 err = fuse_loop_mt_312(f, config);
4689
4690 fuse_loop_cfg_destroy(config);
4691
4692 return err;
4693}
4694
4695void fuse_exit(struct fuse *f)
4696{
4697 fuse_session_exit(f->se);
4698}
4699
4700struct fuse_context *fuse_get_context(void)
4701{
4702 struct fuse_context_i *c = fuse_get_context_internal();
4703
4704 if (c)
4705 return &c->ctx;
4706 else
4707 return NULL;
4708}
4709
4710int fuse_getgroups(int size, gid_t list[])
4711{
4712 struct fuse_context_i *c = fuse_get_context_internal();
4713 if (!c)
4714 return -EINVAL;
4715
4716 return fuse_req_getgroups(c->req, size, list);
4717}
4718
4719int fuse_interrupted(void)
4720{
4721 struct fuse_context_i *c = fuse_get_context_internal();
4722
4723 if (c)
4724 return fuse_req_interrupted(c->req);
4725 else
4726 return 0;
4727}
4728
4729int fuse_invalidate_path(struct fuse *f, const char *path) {
4730 fuse_ino_t ino;
4731 int err = lookup_path_in_cache(f, path, &ino);
4732 if (err) {
4733 return err;
4734 }
4735
4736 return fuse_lowlevel_notify_inval_inode(f->se, ino, 0, 0);
4737}
4738
4739#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
4740
4741static const struct fuse_opt fuse_lib_opts[] = {
4744 FUSE_LIB_OPT("debug", debug, 1),
4745 FUSE_LIB_OPT("-d", debug, 1),
4746 FUSE_LIB_OPT("kernel_cache", kernel_cache, 1),
4747 FUSE_LIB_OPT("auto_cache", auto_cache, 1),
4748 FUSE_LIB_OPT("noauto_cache", auto_cache, 0),
4749 FUSE_LIB_OPT("no_rofd_flush", no_rofd_flush, 1),
4750 FUSE_LIB_OPT("umask=", set_mode, 1),
4751 FUSE_LIB_OPT("umask=%o", umask, 0),
4752 FUSE_LIB_OPT("fmask=", set_mode, 1),
4753 FUSE_LIB_OPT("fmask=%o", fmask, 0),
4754 FUSE_LIB_OPT("dmask=", set_mode, 1),
4755 FUSE_LIB_OPT("dmask=%o", dmask, 0),
4756 FUSE_LIB_OPT("uid=", set_uid, 1),
4757 FUSE_LIB_OPT("uid=%d", uid, 0),
4758 FUSE_LIB_OPT("gid=", set_gid, 1),
4759 FUSE_LIB_OPT("gid=%d", gid, 0),
4760 FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0),
4761 FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0),
4762 FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0),
4763 FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1),
4764 FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0),
4765 FUSE_LIB_OPT("noforget", remember, -1),
4766 FUSE_LIB_OPT("remember=%u", remember, 0),
4767 FUSE_LIB_OPT("modules=%s", modules, 0),
4768 FUSE_LIB_OPT("parallel_direct_write=%d", parallel_direct_writes, 0),
4770};
4771
4772static int fuse_lib_opt_proc(void *data, const char *arg, int key,
4773 struct fuse_args *outargs)
4774{
4775 (void) arg; (void) outargs; (void) data; (void) key;
4776
4777 /* Pass through unknown options */
4778 return 1;
4779}
4780
4781
4782static const struct fuse_opt fuse_help_opts[] = {
4783 FUSE_LIB_OPT("modules=%s", modules, 1),
4784 FUSE_OPT_KEY("modules=%s", FUSE_OPT_KEY_KEEP),
4786};
4787
4788static void print_module_help(const char *name,
4790{
4791 struct fuse_args a = FUSE_ARGS_INIT(0, NULL);
4792 if (fuse_opt_add_arg(&a, "") == -1 ||
4793 fuse_opt_add_arg(&a, "-h") == -1)
4794 return;
4795 printf("\nOptions for %s module:\n", name);
4796 (*fac)(&a, NULL);
4798}
4799
4800void fuse_lib_help(struct fuse_args *args)
4801{
4802 /* These are not all options, but only the ones that
4803 may be of interest to an end-user */
4804 printf(
4805" -o kernel_cache cache files in kernel\n"
4806" -o [no]auto_cache enable caching based on modification times (off)\n"
4807" -o no_rofd_flush disable flushing of read-only fd on close (off)\n"
4808" -o umask=M set file permissions (octal)\n"
4809" -o fmask=M set file permissions (octal)\n"
4810" -o dmask=M set dir permissions (octal)\n"
4811" -o uid=N set file owner\n"
4812" -o gid=N set file group\n"
4813" -o entry_timeout=T cache timeout for names (1.0s)\n"
4814" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
4815" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
4816" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n"
4817" -o noforget never forget cached inodes\n"
4818" -o remember=T remember cached inodes for T seconds (0s)\n"
4819" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n");
4820
4821
4822 /* Print low-level help */
4824
4825 /* Print help for builtin modules */
4826 print_module_help("subdir", &fuse_module_subdir_factory);
4827#ifdef HAVE_ICONV
4828 print_module_help("iconv", &fuse_module_iconv_factory);
4829#endif
4830
4831 /* Parse command line options in case we need to
4832 activate more modules */
4833 struct fuse_config conf = { .modules = NULL };
4834 if (fuse_opt_parse(args, &conf, fuse_help_opts,
4835 fuse_lib_opt_proc) == -1
4836 || !conf.modules)
4837 return;
4838
4839 char *module;
4840 char *next;
4841 struct fuse_module *m;
4842
4843 // Iterate over all modules
4844 for (module = conf.modules; module; module = next) {
4845 char *p;
4846 for (p = module; *p && *p != ':'; p++);
4847 next = *p ? p + 1 : NULL;
4848 *p = '\0';
4849
4850 m = fuse_get_module(module);
4851 if (m)
4852 print_module_help(module, &m->factory);
4853 }
4854}
4855
4856static int fuse_init_intr_signal(int signum, int *installed)
4857{
4858 struct sigaction old_sa;
4859
4860 if (sigaction(signum, NULL, &old_sa) == -1) {
4861 perror("fuse: cannot get old signal handler");
4862 return -1;
4863 }
4864
4865 if (old_sa.sa_handler == SIG_DFL) {
4866 struct sigaction sa;
4867
4868 memset(&sa, 0, sizeof(struct sigaction));
4869 sa.sa_handler = fuse_intr_sighandler;
4870 sigemptyset(&sa.sa_mask);
4871
4872 if (sigaction(signum, &sa, NULL) == -1) {
4873 perror("fuse: cannot set interrupt signal handler");
4874 return -1;
4875 }
4876 *installed = 1;
4877 }
4878 return 0;
4879}
4880
4881static void fuse_restore_intr_signal(int signum)
4882{
4883 struct sigaction sa;
4884
4885 memset(&sa, 0, sizeof(struct sigaction));
4886 sa.sa_handler = SIG_DFL;
4887 sigaction(signum, &sa, NULL);
4888}
4889
4890
4891static int fuse_push_module(struct fuse *f, const char *module,
4892 struct fuse_args *args)
4893{
4894 struct fuse_fs *fs[2] = { f->fs, NULL };
4895 struct fuse_fs *newfs;
4896 struct fuse_module *m = fuse_get_module(module);
4897
4898 if (!m)
4899 return -1;
4900
4901 newfs = m->factory(args, fs);
4902 if (!newfs) {
4903 fuse_put_module(m);
4904 return -1;
4905 }
4906 f->fs = newfs;
4907 return 0;
4908}
4909
4910struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
4911 void *user_data)
4912{
4913 struct fuse_fs *fs;
4914
4915 if (sizeof(struct fuse_operations) < op_size) {
4916 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not not work\n");
4917 op_size = sizeof(struct fuse_operations);
4918 }
4919
4920 fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
4921 if (!fs) {
4922 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse_fs object\n");
4923 return NULL;
4924 }
4925
4926 fs->user_data = user_data;
4927 if (op)
4928 memcpy(&fs->op, op, op_size);
4929 return fs;
4930}
4931
4932static int node_table_init(struct node_table *t)
4933{
4934 t->size = NODE_TABLE_MIN_SIZE;
4935 t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
4936 if (t->array == NULL) {
4937 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
4938 return -1;
4939 }
4940 t->use = 0;
4941 t->split = 0;
4942
4943 return 0;
4944}
4945
4946static void *fuse_prune_nodes(void *fuse)
4947{
4948 struct fuse *f = fuse;
4949 int sleep_time;
4950
4951 fuse_set_thread_name("fuse_prune_nodes");
4952
4953 while(1) {
4954 sleep_time = fuse_clean_cache(f);
4955 sleep(sleep_time);
4956 }
4957 return NULL;
4958}
4959
4960int fuse_start_cleanup_thread(struct fuse *f)
4961{
4962 if (lru_enabled(f))
4963 return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
4964
4965 return 0;
4966}
4967
4968void fuse_stop_cleanup_thread(struct fuse *f)
4969{
4970 if (lru_enabled(f)) {
4971 pthread_mutex_lock(&f->lock);
4972 pthread_cancel(f->prune_thread);
4973 pthread_mutex_unlock(&f->lock);
4974 pthread_join(f->prune_thread, NULL);
4975 }
4976}
4977
4978/*
4979 * Not supposed to be called directly, but supposed to be called
4980 * through the fuse_new macro
4981 */
4982struct fuse *_fuse_new_31(struct fuse_args *args,
4983 const struct fuse_operations *op, size_t op_size,
4984 struct libfuse_version *version, void *user_data)
4985{
4986 struct fuse *f;
4987 struct node *root;
4988 struct fuse_fs *fs;
4989 struct fuse_lowlevel_ops llop = fuse_path_ops;
4990
4991 f = (struct fuse *) calloc(1, sizeof(struct fuse));
4992 if (f == NULL) {
4993 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
4994 goto out;
4995 }
4996
4997 f->conf.entry_timeout = 1.0;
4998 f->conf.attr_timeout = 1.0;
4999 f->conf.negative_timeout = 0.0;
5000 f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
5001
5002 /* Parse options */
5003 if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
5004 fuse_lib_opt_proc) == -1)
5005 goto out_free;
5006
5007 pthread_mutex_lock(&fuse_context_lock);
5008 static int builtin_modules_registered = 0;
5009 /* Have the builtin modules already been registered? */
5010 if (builtin_modules_registered == 0) {
5011 /* If not, register them. */
5012 fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
5013#ifdef HAVE_ICONV
5014 fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
5015#endif
5016 builtin_modules_registered= 1;
5017 }
5018 pthread_mutex_unlock(&fuse_context_lock);
5019
5020 if (fuse_create_context_key() == -1)
5021 goto out_free;
5022
5023 fs = fuse_fs_new(op, op_size, user_data);
5024 if (!fs)
5025 goto out_delete_context_key;
5026
5027 f->fs = fs;
5028
5029 /* Oh f**k, this is ugly! */
5030 if (!fs->op.lock) {
5031 llop.getlk = NULL;
5032 llop.setlk = NULL;
5033 }
5034
5035 f->pagesize = getpagesize();
5036 init_list_head(&f->partial_slabs);
5037 init_list_head(&f->full_slabs);
5038 init_list_head(&f->lru_table);
5039
5040 if (f->conf.modules) {
5041 char *module;
5042 char *next;
5043
5044 for (module = f->conf.modules; module; module = next) {
5045 char *p;
5046 for (p = module; *p && *p != ':'; p++);
5047 next = *p ? p + 1 : NULL;
5048 *p = '\0';
5049 if (module[0] &&
5050 fuse_push_module(f, module, args) == -1)
5051 goto out_free_fs;
5052 }
5053 }
5054
5055 if (!f->conf.ac_attr_timeout_set)
5056 f->conf.ac_attr_timeout = f->conf.attr_timeout;
5057
5058#if defined(__FreeBSD__) || defined(__NetBSD__)
5059 /*
5060 * In FreeBSD, we always use these settings as inode numbers
5061 * are needed to make getcwd(3) work.
5062 */
5063 f->conf.readdir_ino = 1;
5064#endif
5065
5066 /* not declared globally, to restrict usage of this function */
5067 struct fuse_session *fuse_session_new_versioned(
5068 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
5069 size_t op_size, struct libfuse_version *version,
5070 void *userdata);
5071 f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version,
5072 f);
5073 if (f->se == NULL)
5074 goto out_free_fs;
5075
5076 /* Trace topmost layer by default */
5077 f->fs->debug = f->conf.debug;
5078 f->ctr = 0;
5079 f->generation = 0;
5080 if (node_table_init(&f->name_table) == -1)
5081 goto out_free_session;
5082
5083 if (node_table_init(&f->id_table) == -1)
5084 goto out_free_name_table;
5085
5086 pthread_mutex_init(&f->lock, NULL);
5087
5088 root = alloc_node(f);
5089 if (root == NULL) {
5090 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
5091 goto out_free_id_table;
5092 }
5093 if (lru_enabled(f)) {
5094 struct node_lru *lnode = node_lru(root);
5095 init_list_head(&lnode->lru);
5096 }
5097
5098 strcpy(root->inline_name, "/");
5099 root->name = root->inline_name;
5100 root->parent = NULL;
5101 root->nodeid = FUSE_ROOT_ID;
5102 inc_nlookup(root);
5103 hash_id(f, root);
5104
5105 return f;
5106
5107out_free_id_table:
5108 free(f->id_table.array);
5109out_free_name_table:
5110 free(f->name_table.array);
5111out_free_session:
5112 fuse_session_destroy(f->se);
5113out_free_fs:
5114 free(f->fs);
5115 free(f->conf.modules);
5116out_delete_context_key:
5117 fuse_delete_context_key();
5118out_free:
5119 free(f);
5120out:
5121 return NULL;
5122}
5123
5124/* Emulates 3.0-style fuse_new(), which processes --help */
5125FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
5126struct fuse *_fuse_new_30(struct fuse_args *args,
5127 const struct fuse_operations *op,
5128 size_t op_size,
5129 struct libfuse_version *version,
5130 void *user_data)
5131{
5132 struct fuse_config conf = {0};
5133
5134 const struct fuse_opt opts[] = {
5135 FUSE_LIB_OPT("-h", show_help, 1),
5136 FUSE_LIB_OPT("--help", show_help, 1),
5138 };
5139
5140 if (fuse_opt_parse(args, &conf, opts,
5141 fuse_lib_opt_proc) == -1)
5142 return NULL;
5143
5144 if (conf.show_help) {
5145 fuse_lib_help(args);
5146 return NULL;
5147 } else
5148 return _fuse_new_31(args, op, op_size, version, user_data);
5149}
5150
5151/* ABI compat version */
5152struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
5153 size_t op_size, void *user_data);
5154FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
5155struct fuse *fuse_new_31(struct fuse_args *args,
5156 const struct fuse_operations *op,
5157 size_t op_size, void *user_data)
5158{
5159 /* unknown version */
5160 struct libfuse_version version = { 0 };
5161
5162 return _fuse_new_31(args, op, op_size, &version, user_data);
5163}
5164
5165/*
5166 * ABI compat version
5167 * Emulates 3.0-style fuse_new(), which processes --help
5168 */
5169struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
5170 size_t op_size, void *user_data);
5171FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
5172struct fuse *fuse_new_30(struct fuse_args *args,
5173 const struct fuse_operations *op,
5174 size_t op_size, void *user_data)
5175{
5176 struct fuse_config conf = {0};
5177
5178 const struct fuse_opt opts[] = {
5179 FUSE_LIB_OPT("-h", show_help, 1),
5180 FUSE_LIB_OPT("--help", show_help, 1),
5182 };
5183
5184 if (fuse_opt_parse(args, &conf, opts,
5185 fuse_lib_opt_proc) == -1)
5186 return NULL;
5187
5188 if (conf.show_help) {
5189 fuse_lib_help(args);
5190 return NULL;
5191 } else
5192 return fuse_new_31(args, op, op_size, user_data);
5193}
5194
5195
5196void fuse_destroy(struct fuse *f)
5197{
5198 size_t i;
5199
5200 if (f->conf.intr && f->intr_installed)
5201 fuse_restore_intr_signal(f->conf.intr_signal);
5202
5203 if (f->fs) {
5204 fuse_create_context(f);
5205
5206 for (i = 0; i < f->id_table.size; i++) {
5207 struct node *node;
5208
5209 for (node = f->id_table.array[i]; node != NULL;
5210 node = node->id_next) {
5211 if (node->is_hidden) {
5212 char *path;
5213 if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
5214 fuse_fs_unlink(f->fs, path);
5215 free(path);
5216 }
5217 }
5218 }
5219 }
5220 }
5221 for (i = 0; i < f->id_table.size; i++) {
5222 struct node *node;
5223 struct node *next;
5224
5225 for (node = f->id_table.array[i]; node != NULL; node = next) {
5226 next = node->id_next;
5227 free_node(f, node);
5228 f->id_table.use--;
5229 }
5230 }
5231 assert(list_empty(&f->partial_slabs));
5232 assert(list_empty(&f->full_slabs));
5233
5234 while (fuse_modules) {
5235 fuse_put_module(fuse_modules);
5236 }
5237 free(f->id_table.array);
5238 free(f->name_table.array);
5239 pthread_mutex_destroy(&f->lock);
5240 fuse_session_destroy(f->se);
5241 free(f->fs);
5242 free(f->conf.modules);
5243 free(f);
5244 fuse_delete_context_key();
5245}
5246
5247int fuse_mount(struct fuse *f, const char *mountpoint) {
5248 return fuse_session_mount(fuse_get_session(f), mountpoint);
5249}
5250
5251
5252void fuse_unmount(struct fuse *f) {
5254}
5255
5256int fuse_version(void)
5257{
5258 return FUSE_VERSION;
5259}
5260
5261const char *fuse_pkgversion(void)
5262{
5263 return PACKAGE_VERSION;
5264}
int fuse_getgroups(int size, gid_t list[])
Definition fuse.c:4654
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
int fuse_interrupted(void)
Definition fuse.c:4663
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_start_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4906
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
Definition fuse.h:1383
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_exit(struct fuse *f)
Definition fuse.c:4639
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4433
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4914
fuse_fill_dir_flags
Definition fuse.h:58
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_SPLICE_READ
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_IS_FD
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
int fuse_version(void)
Definition fuse.c:5206
@ FUSE_BUF_SPLICE_MOVE
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_session_exited(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_reply_xattr(fuse_req_t req, size_t count)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
enum fuse_buf_flags flags
void * mem
size_t size
struct fuse_buf buf[1]
int32_t show_help
Definition fuse.h:279
uint32_t no_interrupt
void * private_data
Definition fuse.h:874
mode_t umask
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t noflush
Definition fuse_common.h:93
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
void(* init)(void *userdata, struct fuse_conn_info *conn)
void(* setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__i_8h_source.html0000644000175000017500000012410315156613430022576 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_i.h Source File
libfuse
fuse_i.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#ifndef LIB_FUSE_I_H_
10#define LIB_FUSE_I_H_
11
12#include "fuse.h"
13#include "fuse_lowlevel.h"
14#include "util.h"
15
16#include <pthread.h>
17#include <semaphore.h>
18#include <stdint.h>
19#include <stdbool.h>
20#include <errno.h>
21#include <stdatomic.h>
22
23#define MIN(a, b) \
24({ \
25 typeof(a) _a = (a); \
26 typeof(b) _b = (b); \
27 _a < _b ? _a : _b; \
28})
29
30struct mount_opts;
31struct fuse_ring_pool;
32
33struct fuse_req {
34 struct fuse_session *se;
35 uint64_t unique;
36 _Atomic int ref_cnt;
37 pthread_mutex_t lock;
38 struct fuse_ctx ctx;
39 struct fuse_chan *ch;
40 int interrupted;
41 struct {
42 unsigned int ioctl_64bit : 1;
43 unsigned int is_uring : 1;
44 unsigned int is_copy_file_range_64 : 1;
45 } flags;
46 union {
47 struct {
48 uint64_t unique;
49 } i;
50 struct {
52 void *data;
53 } ni;
54 } u;
55 struct fuse_req *next;
56 struct fuse_req *prev;
57};
58
59struct fuse_notify_req {
60 uint64_t unique;
61 void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
62 const void *, const struct fuse_buf *);
63 struct fuse_notify_req *next;
64 struct fuse_notify_req *prev;
65};
66
67struct fuse_session_uring {
68 bool enable;
69 unsigned int q_depth;
70 struct fuse_ring_pool *pool;
71};
72
73struct fuse_session {
74 _Atomic(char *)mountpoint;
75 int fd;
76 struct fuse_custom_io *io;
77 struct mount_opts *mo;
78 int debug;
79 int deny_others;
80 struct fuse_lowlevel_ops op;
81 int got_init;
82 struct cuse_data *cuse_data;
83 void *userdata;
84 uid_t owner;
85 struct fuse_conn_info conn;
86 struct fuse_req list;
87 struct fuse_req interrupts;
88 pthread_mutex_t lock;
89 int got_destroy;
90 pthread_key_t pipe_key;
91 int broken_splice_nonblock;
92 uint64_t notify_ctr;
93 struct fuse_notify_req notify_list;
94 _Atomic size_t bufsize;
95 int error;
96
97 /*
98 * This is useful if any kind of ABI incompatibility is found at
99 * a later version, to 'fix' it at run time.
100 */
101 struct libfuse_version version;
102
103 /* thread synchronization */
104 _Atomic bool mt_exited;
105 pthread_mutex_t mt_lock;
106 sem_t mt_finish;
107
108 /* true if reading requests from /dev/fuse are handled internally */
109 bool buf_reallocable;
110
111 /* io_uring */
112 struct fuse_session_uring uring;
113
114 /*
115 * conn->want and conn_want_ext options set by libfuse , needed
116 * to correctly convert want to want_ext
117 */
118 uint32_t conn_want;
119 uint64_t conn_want_ext;
120};
121
122struct fuse_chan {
123 pthread_mutex_t lock;
124 int ctr;
125 int fd;
126};
127
135struct fuse_module {
136 char *name;
137 fuse_module_factory_t factory;
138 struct fuse_module *next;
139 struct fusemod_so *so;
140 int ctr;
141};
142
151#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
152struct fuse_loop_config
153{
154 /* verififier that a correct struct was was passed. This is especially
155 * needed, as versions below (3, 12) were using a public struct
156 * (now called fuse_loop_config_v1), which was hard to extend with
157 * additional parameters, without risking that file system implementations
158 * would not have noticed and might either pass uninitialized members
159 * or even too small structs.
160 * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
161 * or 1. v2 or even higher version just need to set a value here
162 * which not conflicting and very unlikely as having been set by
163 * file system implementation.
164 */
165 int version_id;
166
171 int clone_fd;
184
190 unsigned int max_threads;
191};
192#endif
193
194/* ----------------------------------------------------------- *
195 * Channel interface (when using -o clone_fd) *
196 * ----------------------------------------------------------- */
197
204struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
205
211void fuse_chan_put(struct fuse_chan *ch);
212
213struct mount_opts *parse_mount_opts(struct fuse_args *args);
214void destroy_mount_opts(struct mount_opts *mo);
215void fuse_mount_version(void);
216unsigned get_max_read(struct mount_opts *o);
217void fuse_kern_unmount(const char *mountpoint, int fd);
218int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
219
220int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
221 int count);
222void fuse_free_req(fuse_req_t req);
223void list_init_req(struct fuse_req *req);
224
225void _cuse_lowlevel_init(fuse_req_t req, const fuse_ino_t nodeid,
226 const void *req_header, const void *req_payload);
227void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
228
229int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
230
231void fuse_buf_free(struct fuse_buf *buf);
232
233int fuse_session_receive_buf_internal(struct fuse_session *se,
234 struct fuse_buf *buf,
235 struct fuse_chan *ch);
236void fuse_session_process_buf_internal(struct fuse_session *se,
237 const struct fuse_buf *buf,
238 struct fuse_chan *ch);
239
240struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
241 size_t op_size, void *private_data);
242int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
243int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
244
250int fuse_loop_cfg_verify(struct fuse_loop_config *config);
251
252
253/*
254 * This can be changed dynamically on recent kernels through the
255 * /proc/sys/fs/fuse/max_pages_limit interface.
256 *
257 * Older kernels will always use the default value.
258 */
259#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
260#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
261
262/* room needed in buffer to accommodate header */
263#define FUSE_BUFFER_HEADER_SIZE 0x1000
264
265
266#endif /* LIB_FUSE_I_H_*/
struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
Definition fuse.h:1383
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
struct fuse_req * fuse_req_t
uint64_t fuse_ino_t
unsigned int max_threads
Definition fuse_i.h:166
unsigned int max_idle_threads
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__log_8c_source.html0000644000175000017500000003256515156613430023134 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_log.c Source File
libfuse
fuse_log.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2019 Red Hat, Inc.
4
5 Logging API.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt
9*/
10
11#include "fuse_log.h"
12
13#include <stdio.h>
14#include <stdbool.h>
15#include <syslog.h>
16#include <stdarg.h>
17
18#define MAX_SYSLOG_LINE_LEN 512
19
20static bool to_syslog = false;
21
22static void default_log_func(enum fuse_log_level level, const char *fmt, va_list ap)
23{
24 if (to_syslog)
25 vsyslog(level, fmt, ap);
26 else
27 vfprintf(stderr, fmt, ap);
28}
29
30static fuse_log_func_t log_func = default_log_func;
31
33{
34 if (!func)
35 func = default_log_func;
36
37 log_func = func;
38}
39
40void fuse_log(enum fuse_log_level level, const char *fmt, ...)
41{
42 va_list ap;
43
44 va_start(ap, fmt);
45 log_func(level, fmt, ap);
46 va_end(ap);
47}
48
49void fuse_log_enable_syslog(const char *ident, int option, int facility)
50{
51 to_syslog = true;
52
53 openlog(ident, option, facility);
54}
55
56void fuse_log_close_syslog(void)
57{
58 closelog();
59}
void fuse_log_close_syslog(void)
Definition fuse_log.c:93
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void(* fuse_log_func_t)(enum fuse_log_level level, const char *fmt, va_list ap)
Definition fuse_log.h:52
void fuse_log_enable_syslog(const char *ident, int option, int facility)
Definition fuse_log.c:86
fuse_log_level
Definition fuse_log.h:28
void fuse_set_log_func(fuse_log_func_t func)
Definition fuse_log.c:69
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__loop_8c_source.html0000644000175000017500000002617215156613430023321 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_loop.c Source File
libfuse
fuse_loop.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the single-threaded FUSE session loop.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt
9*/
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_i.h"
14#include "fuse_uring_i.h"
15#include <stdio.h>
16#include <stdlib.h>
17#include <errno.h>
18
19int fuse_session_loop(struct fuse_session *se)
20{
21 int res = 0;
22 struct fuse_buf fbuf = {
23 .mem = NULL,
24 };
25
26 while (!fuse_session_exited(se)) {
27 res = fuse_session_receive_buf_internal(se, &fbuf, NULL);
28
29 if (res == -EINTR)
30 continue;
31 if (res <= 0)
32 break;
33
34 fuse_session_process_buf(se, &fbuf);
35 }
36
37 fuse_buf_free(&fbuf);
38 if(res > 0)
39 /* No error, just the length of the most recently read
40 request */
41 res = 0;
42 if(se->error != 0)
43 res = se->error;
44
45 if (se->uring.pool)
46 fuse_uring_stop(se);
47 return res;
48}
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void * mem
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__loop__mt_8c_source.html0000644000175000017500000024526315156613430024164 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_loop_mt.c Source File
libfuse
fuse_loop_mt.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of the multi-threaded FUSE session loop.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#define _GNU_SOURCE
12
13#include "fuse_config.h"
14#include "fuse_lowlevel.h"
15#include "fuse_misc.h"
16#include "fuse_kernel.h"
17#include "fuse_i.h"
18#include "fuse_uring_i.h"
19#include "util.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25#include <signal.h>
26#include <semaphore.h>
27#include <errno.h>
28#include <sys/time.h>
29#include <sys/ioctl.h>
30#include <assert.h>
31#include <limits.h>
32
33/* Environment var controlling the thread stack size */
34#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
35
36#define FUSE_LOOP_MT_V2_IDENTIFIER INT_MAX - 2
37#define FUSE_LOOP_MT_DEF_CLONE_FD 0
38#define FUSE_LOOP_MT_DEF_MAX_THREADS 10
39#define FUSE_LOOP_MT_DEF_IDLE_THREADS -1 /* thread destruction is disabled
40 * by default */
41
42/* an arbitrary large value that cannot be valid */
43#define FUSE_LOOP_MT_MAX_THREADS (100U * 1000)
44
45struct fuse_worker {
46 struct fuse_worker *prev;
47 struct fuse_worker *next;
48 pthread_t thread_id;
49
50 // We need to include fuse_buf so that we can properly free
51 // it when a thread is terminated by pthread_cancel().
52 struct fuse_buf fbuf;
53 struct fuse_chan *ch;
54 struct fuse_mt *mt;
55};
56
57/* synchronization via se->mt_lock */
58struct fuse_mt {
59 int numworker;
60 int numavail;
61 struct fuse_session *se;
62 struct fuse_worker main;
63 int error;
64 int clone_fd;
65 int max_idle;
66 int max_threads;
67};
68
69static struct fuse_chan *fuse_chan_new(int fd)
70{
71 struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
72 if (ch == NULL) {
73 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate channel\n");
74 return NULL;
75 }
76
77 memset(ch, 0, sizeof(*ch));
78 ch->fd = fd;
79 ch->ctr = 1;
80 pthread_mutex_init(&ch->lock, NULL);
81
82 return ch;
83}
84
85struct fuse_chan *fuse_chan_get(struct fuse_chan *ch)
86{
87 assert(ch->ctr > 0);
88 pthread_mutex_lock(&ch->lock);
89 ch->ctr++;
90 pthread_mutex_unlock(&ch->lock);
91
92 return ch;
93}
94
95void fuse_chan_put(struct fuse_chan *ch)
96{
97 if (ch == NULL)
98 return;
99 pthread_mutex_lock(&ch->lock);
100 ch->ctr--;
101 if (!ch->ctr) {
102 pthread_mutex_unlock(&ch->lock);
103 close(ch->fd);
104 pthread_mutex_destroy(&ch->lock);
105 free(ch);
106 } else
107 pthread_mutex_unlock(&ch->lock);
108}
109
110static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
111{
112 struct fuse_worker *prev = next->prev;
113 w->next = next;
114 w->prev = prev;
115 prev->next = w;
116 next->prev = w;
117}
118
119static void list_del_worker(struct fuse_worker *w)
120{
121 struct fuse_worker *prev = w->prev;
122 struct fuse_worker *next = w->next;
123 prev->next = next;
124 next->prev = prev;
125}
126
127static int fuse_loop_start_thread(struct fuse_mt *mt);
128
129static void *fuse_do_work(void *data)
130{
131 struct fuse_worker *w = (struct fuse_worker *) data;
132 struct fuse_mt *mt = w->mt;
133 struct fuse_session *se = mt->se;
134
135 fuse_set_thread_name("fuse_worker");
136
137 while (!fuse_session_exited(se)) {
138 int isforget = 0;
139 int res;
140
141 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
142 res = fuse_session_receive_buf_internal(se, &w->fbuf, w->ch);
143 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
144 if (res == -EINTR)
145 continue;
146 if (res <= 0) {
147 if (res < 0) {
149 mt->error = res;
150 }
151 break;
152 }
153
154 pthread_mutex_lock(&se->mt_lock);
155 if (fuse_session_exited(se)) {
156 pthread_mutex_unlock(&se->mt_lock);
157 return NULL;
158 }
159
160 /*
161 * This disgusting hack is needed so that zillions of threads
162 * are not created on a burst of FORGET messages
163 */
164 if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) {
165 struct fuse_in_header *in = w->fbuf.mem;
166
167 if (in->opcode == FUSE_FORGET ||
168 in->opcode == FUSE_BATCH_FORGET)
169 isforget = 1;
170 }
171
172 if (!isforget)
173 mt->numavail--;
174 if (mt->numavail == 0 && mt->numworker < mt->max_threads &&
175 likely(se->got_init))
176 fuse_loop_start_thread(mt);
177 pthread_mutex_unlock(&se->mt_lock);
178
179 fuse_session_process_buf_internal(se, &w->fbuf, w->ch);
180
181 pthread_mutex_lock(&se->mt_lock);
182 if (!isforget)
183 mt->numavail++;
184
185 /* creating and destroying threads is rather expensive - and there is
186 * not much gain from destroying existing threads. It is therefore
187 * discouraged to set max_idle to anything else than -1. If there
188 * is indeed a good reason to destruct threads it should be done
189 * delayed, a moving average might be useful for that.
190 */
191 if (mt->max_idle != -1 && mt->numavail > mt->max_idle && mt->numworker > 1) {
192 if (fuse_session_exited(se)) {
193 pthread_mutex_unlock(&se->mt_lock);
194 return NULL;
195 }
196 list_del_worker(w);
197 mt->numavail--;
198 mt->numworker--;
199 pthread_mutex_unlock(&se->mt_lock);
200
201 pthread_detach(w->thread_id);
202 fuse_buf_free(&w->fbuf);
203 fuse_chan_put(w->ch);
204 free(w);
205 return NULL;
206 }
207 pthread_mutex_unlock(&se->mt_lock);
208 }
209
210 sem_post(&se->mt_finish);
211 return NULL;
212}
213
214int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
215{
216 sigset_t oldset;
217 sigset_t newset;
218 int res;
219 pthread_attr_t attr;
220 char *stack_size;
221
222 /* Override default stack size
223 * XXX: This should ideally be a parameter option. It is rather
224 * well hidden here.
225 */
226 pthread_attr_init(&attr);
227 stack_size = getenv(ENVNAME_THREAD_STACK);
228 if (stack_size) {
229 long size;
230
231 res = libfuse_strtol(stack_size, &size);
232 if (res)
233 fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n",
234 stack_size);
235 else if (pthread_attr_setstacksize(&attr, size))
236 fuse_log(FUSE_LOG_ERR, "fuse: could not set stack size: %ld\n",
237 size);
238 }
239
240 /* Disallow signal reception in worker threads */
241 sigemptyset(&newset);
242 sigaddset(&newset, SIGTERM);
243 sigaddset(&newset, SIGINT);
244 sigaddset(&newset, SIGHUP);
245 sigaddset(&newset, SIGQUIT);
246 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
247 res = pthread_create(thread_id, &attr, func, arg);
248 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
249 pthread_attr_destroy(&attr);
250 if (res != 0) {
251 fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n",
252 strerror(res));
253 return -1;
254 }
255
256 return 0;
257}
258
259static int fuse_clone_chan_fd_default(struct fuse_session *se)
260{
261 int res;
262 int clonefd;
263 uint32_t masterfd;
264 const char *devname = "/dev/fuse";
265
266#ifndef O_CLOEXEC
267#define O_CLOEXEC 0
268#endif
269 clonefd = open(devname, O_RDWR | O_CLOEXEC);
270 if (clonefd == -1) {
271 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname,
272 strerror(errno));
273 return -1;
274 }
275 if (!O_CLOEXEC) {
276 res = fcntl(clonefd, F_SETFD, FD_CLOEXEC);
277 if (res == -1) {
278 fuse_log(FUSE_LOG_ERR, "fuse: failed to set CLOEXEC: %s\n",
279 strerror(errno));
280 close(clonefd);
281 return -1;
282 }
283 }
284
285 masterfd = se->fd;
286 res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
287 if (res == -1) {
288 fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
289 strerror(errno));
290 close(clonefd);
291 return -1;
292 }
293 return clonefd;
294}
295
296static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
297{
298 int clonefd;
299 struct fuse_session *se = mt->se;
300 struct fuse_chan *newch;
301
302 if (se->io != NULL) {
303 if (se->io->clone_fd != NULL)
304 clonefd = se->io->clone_fd(se->fd);
305 else
306 return NULL;
307 } else {
308 clonefd = fuse_clone_chan_fd_default(se);
309 }
310 if (clonefd < 0)
311 return NULL;
312
313 newch = fuse_chan_new(clonefd);
314 if (newch == NULL)
315 close(clonefd);
316
317 return newch;
318}
319
320static int fuse_loop_start_thread(struct fuse_mt *mt)
321{
322 int res;
323
324 struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
325 if (!w) {
326 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
327 return -1;
328 }
329 memset(w, 0, sizeof(struct fuse_worker));
330 w->fbuf.mem = NULL;
331 w->mt = mt;
332
333 w->ch = NULL;
334 if (mt->clone_fd) {
335 w->ch = fuse_clone_chan(mt);
336 if(!w->ch) {
337 /* Don't attempt this again */
338 fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
339 "without -o clone_fd.\n");
340 mt->clone_fd = 0;
341 }
342 }
343
344 res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
345 if (res == -1) {
346 fuse_chan_put(w->ch);
347 free(w);
348 return -1;
349 }
350 list_add_worker(w, &mt->main);
351 mt->numavail ++;
352 mt->numworker ++;
353
354 return 0;
355}
356
357static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
358{
359 pthread_join(w->thread_id, NULL);
360 pthread_mutex_lock(&mt->se->mt_lock);
361 list_del_worker(w);
362 pthread_mutex_unlock(&mt->se->mt_lock);
363 fuse_buf_free(&w->fbuf);
364 fuse_chan_put(w->ch);
365 free(w);
366}
367
368int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
369FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
370int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
371{
372int err;
373 struct fuse_mt mt;
374 struct fuse_worker *w;
375 int created_config = 0;
376
377 if (config) {
378 err = fuse_loop_cfg_verify(config);
379 if (err)
380 return err;
381 } else {
382 /* The caller does not care about parameters - use the default */
383 config = fuse_loop_cfg_create();
384 created_config = 1;
385 }
386
387
388 memset(&mt, 0, sizeof(struct fuse_mt));
389 mt.se = se;
390 mt.clone_fd = config->clone_fd;
391 mt.error = 0;
392 mt.numworker = 0;
393 mt.numavail = 0;
394 mt.max_idle = config->max_idle_threads;
395 mt.max_threads = config->max_threads;
396 mt.main.thread_id = pthread_self();
397 mt.main.prev = mt.main.next = &mt.main;
398
399 pthread_mutex_lock(&se->mt_lock);
400 err = fuse_loop_start_thread(&mt);
401 pthread_mutex_unlock(&se->mt_lock);
402 if (!err) {
403 while (!fuse_session_exited(se))
404 sem_wait(&se->mt_finish);
405 if (se->debug)
406 fuse_log(FUSE_LOG_DEBUG,
407 "fuse: session exited, terminating workers\n");
408
409 pthread_mutex_lock(&se->mt_lock);
410 for (w = mt.main.next; w != &mt.main; w = w->next)
411 pthread_cancel(w->thread_id);
412 pthread_mutex_unlock(&se->mt_lock);
413
414 while (mt.main.next != &mt.main)
415 fuse_join_worker(&mt, mt.main.next);
416
417 err = mt.error;
418
419 if (se->uring.pool)
420 fuse_uring_stop(se);
421 }
422
423 pthread_mutex_destroy(&se->mt_lock);
424 if(se->error != 0)
425 err = se->error;
426
427
428 if (created_config) {
429 fuse_loop_cfg_destroy(config);
430 config = NULL;
431 }
432
433 return err;
434}
435
436int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
437FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
438int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
439{
440 int err;
441 struct fuse_loop_config *config = NULL;
442
443 if (config_v1 != NULL) {
444 /* convert the given v1 config */
445 config = fuse_loop_cfg_create();
446 if (config == NULL)
447 return ENOMEM;
448
449 fuse_loop_cfg_convert(config, config_v1);
450 }
451
452 err = fuse_session_loop_mt_312(se, config);
453
454 fuse_loop_cfg_destroy(config);
455
456 return err;
457}
458
459
460int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
461FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
462int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
463{
464 int err;
465 struct fuse_loop_config *config = fuse_loop_cfg_create();
466 if (clone_fd > 0)
467 fuse_loop_cfg_set_clone_fd(config, clone_fd);
468 err = fuse_session_loop_mt_312(se, config);
469
470 fuse_loop_cfg_destroy(config);
471
472 return err;
473}
474
475struct fuse_loop_config *fuse_loop_cfg_create(void)
476{
477 struct fuse_loop_config *config = calloc(1, sizeof(*config));
478 if (config == NULL)
479 return NULL;
480
481 config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
482 config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
483 config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
484 config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
485
486 return config;
487}
488
489void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
490{
491 free(config);
492}
493
494int fuse_loop_cfg_verify(struct fuse_loop_config *config)
495{
496 if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
497 return -EINVAL;
498
499 return 0;
500}
501
502void fuse_loop_cfg_convert(struct fuse_loop_config *config,
503 struct fuse_loop_config_v1 *v1_conf)
504{
505 fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
506
507 fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
508}
509
510void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
511 unsigned int value)
512{
513 if (value > FUSE_LOOP_MT_MAX_THREADS) {
514 if (value != UINT_MAX)
515 fuse_log(FUSE_LOG_ERR,
516 "Ignoring invalid max threads value "
517 "%u > max (%u).\n", value,
518 FUSE_LOOP_MT_MAX_THREADS);
519 return;
520 }
521 config->max_idle_threads = value;
522}
523
524void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
525 unsigned int value)
526{
527 config->max_threads = value;
528}
529
530void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
531 unsigned int value)
532{
533 config->clone_fd = value;
534}
535
@ FUSE_BUF_IS_FD
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
int fuse_session_exited(struct fuse_session *se)
unsigned int max_threads
Definition fuse_i.h:166
unsigned int max_idle_threads
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__lowlevel_8c_source.html0000644000175000017500000305673115156613430024210 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_lowlevel.c Source File
libfuse
fuse_lowlevel.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of (most of) the low-level FUSE API. The session loop
6 functions are implemented in separate files.
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_kernel.h"
17#include "fuse_opt.h"
18#include "fuse_misc.h"
19#include "mount_util.h"
20#include "util.h"
21#include "fuse_uring_i.h"
22
23#include <pthread.h>
24#include <stdatomic.h>
25#include <stdint.h>
26#include <inttypes.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <stddef.h>
31#include <stdalign.h>
32#include <string.h>
33#include <unistd.h>
34#include <limits.h>
35#include <errno.h>
36#include <assert.h>
37#include <sys/file.h>
38#include <sys/ioctl.h>
39#include <stdalign.h>
40
41#ifdef USDT_ENABLED
42#include "usdt.h"
43#endif
44
45#ifndef F_LINUX_SPECIFIC_BASE
46#define F_LINUX_SPECIFIC_BASE 1024
47#endif
48#ifndef F_SETPIPE_SZ
49#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
50#endif
51
52#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
53#define OFFSET_MAX 0x7fffffffffffffffLL
54
55struct fuse_pollhandle {
56 uint64_t kh;
57 struct fuse_session *se;
58};
59
60static size_t pagesize;
61
62static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
63{
64 pagesize = getpagesize();
65}
66
67#ifdef USDT_ENABLED
68/* tracepoints */
69static void trace_request_receive(int err)
70{
71 USDT(libfuse, request_receive, err);
72}
73
74static void trace_request_process(unsigned int opcode, unsigned int unique)
75{
76 USDT(libfuse, request_process, opcode, unique);
77}
78
79static void trace_request_reply(uint64_t unique, unsigned int len,
80 int error, int reply_err)
81{
82 USDT(libfuse, request_reply, unique, len, error, reply_err);
83}
84#else
85static void trace_request_receive(int err)
86{
87 (void)err;
88}
89
90static void trace_request_process(unsigned int opcode, unsigned int unique)
91{
92 (void)opcode;
93 (void)unique;
94}
95
96static void trace_request_reply(uint64_t unique, unsigned int len,
97 int error, int reply_err)
98{
99 (void)unique;
100 (void)len;
101 (void)error;
102 (void)reply_err;
103}
104#endif
105
106static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
107{
108 attr->ino = stbuf->st_ino;
109 attr->mode = stbuf->st_mode;
110 attr->nlink = stbuf->st_nlink;
111 attr->uid = stbuf->st_uid;
112 attr->gid = stbuf->st_gid;
113 attr->rdev = stbuf->st_rdev;
114 attr->size = stbuf->st_size;
115 attr->blksize = stbuf->st_blksize;
116 attr->blocks = stbuf->st_blocks;
117 attr->atime = stbuf->st_atime;
118 attr->mtime = stbuf->st_mtime;
119 attr->ctime = stbuf->st_ctime;
120 attr->atimensec = ST_ATIM_NSEC(stbuf);
121 attr->mtimensec = ST_MTIM_NSEC(stbuf);
122 attr->ctimensec = ST_CTIM_NSEC(stbuf);
123}
124
125static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
126{
127 stbuf->st_mode = attr->mode;
128 stbuf->st_uid = attr->uid;
129 stbuf->st_gid = attr->gid;
130 stbuf->st_size = attr->size;
131 stbuf->st_atime = attr->atime;
132 stbuf->st_mtime = attr->mtime;
133 stbuf->st_ctime = attr->ctime;
134 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
135 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
136 ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
137}
138
139static size_t iov_length(const struct iovec *iov, size_t count)
140{
141 size_t seg;
142 size_t ret = 0;
143
144 for (seg = 0; seg < count; seg++)
145 ret += iov[seg].iov_len;
146 return ret;
147}
148
149void list_init_req(struct fuse_req *req)
150{
151 req->next = req;
152 req->prev = req;
153}
154
155static void list_del_req(struct fuse_req *req)
156{
157 struct fuse_req *prev = req->prev;
158 struct fuse_req *next = req->next;
159 prev->next = next;
160 next->prev = prev;
161}
162
163static void list_add_req(struct fuse_req *req, struct fuse_req *next)
164{
165 struct fuse_req *prev = next->prev;
166 req->next = next;
167 req->prev = prev;
168 prev->next = req;
169 next->prev = req;
170}
171
172static void destroy_req(fuse_req_t req)
173{
174 if (req->flags.is_uring) {
175 fuse_log(FUSE_LOG_ERR, "Refusing to destruct uring req\n");
176 return;
177 }
178 assert(req->ch == NULL);
179 pthread_mutex_destroy(&req->lock);
180 free(req);
181}
182
183void fuse_free_req(fuse_req_t req)
184{
185 int ctr;
186 struct fuse_session *se = req->se;
187
188 /* XXX: for now no support for interrupts with io-uring
189 * It actually might work already, though. But then would add
190 * a lock across ring queues.
191 */
192 if (se->conn.no_interrupt || req->flags.is_uring) {
193 ctr = --req->ref_cnt;
194 fuse_chan_put(req->ch);
195 req->ch = NULL;
196 } else {
197 pthread_mutex_lock(&se->lock);
198 req->u.ni.func = NULL;
199 req->u.ni.data = NULL;
200 list_del_req(req);
201 ctr = --req->ref_cnt;
202 fuse_chan_put(req->ch);
203 req->ch = NULL;
204 pthread_mutex_unlock(&se->lock);
205 }
206 if (!ctr)
207 destroy_req(req);
208}
209
210static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
211{
212 struct fuse_req *req;
213
214 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
215 if (req == NULL) {
216 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
217 } else {
218 req->se = se;
219 req->ref_cnt = 1;
220 list_init_req(req);
221 pthread_mutex_init(&req->lock, NULL);
222 }
223
224 return req;
225}
226
227/*
228 * Send data to fuse-kernel using an fd of the fuse device.
229 */
230static int fuse_write_msg_dev(struct fuse_session *se, struct fuse_chan *ch,
231 struct iovec *iov, int count)
232{
233 ssize_t res;
234 int err;
235
236 if (se->io != NULL)
237
238 /* se->io->writev is never NULL if se->io is not NULL as
239 * specified by fuse_session_custom_io()
240 */
241 res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
242 se->userdata);
243 else
244 res = writev(ch ? ch->fd : se->fd, iov, count);
245
246 if (res == -1) {
247 /* ENOENT means the operation was interrupted */
248 err = errno;
249 if (!fuse_session_exited(se) && err != ENOENT)
250 perror("fuse: writing device");
251 return -err;
252 }
253
254 return 0;
255}
256
257static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
258 struct iovec *iov, int count, fuse_req_t req)
259{
260 struct fuse_out_header *out = iov[0].iov_base;
261 int err;
262 bool is_uring = req && req->flags.is_uring ? true : false;
263
264 if (!is_uring)
265 assert(se != NULL);
266 out->len = iov_length(iov, count);
267
268 if (se->debug) {
269 if (out->unique == 0) {
270 fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
271 out->error, out->len);
272 } else if (out->error) {
273 fuse_log(FUSE_LOG_DEBUG,
274 " unique: %llu, error: %i (%s), outsize: %i\n",
275 (unsigned long long) out->unique, out->error,
276 strerror(-out->error), out->len);
277 } else {
278 fuse_log(FUSE_LOG_DEBUG,
279 " unique: %llu, success, outsize: %i\n",
280 (unsigned long long) out->unique, out->len);
281 }
282 }
283
284 if (is_uring)
285 err = fuse_send_msg_uring(req, iov, count);
286 else
287 err = fuse_write_msg_dev(se, ch, iov, count);
288
289 trace_request_reply(out->unique, out->len, out->error, err);
290 return err;
291}
292
293int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
294 int count)
295{
296 struct fuse_out_header out;
297
298#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
299 const char *str = strerrordesc_np(error * -1);
300 if ((str == NULL && error != 0) || error > 0) {
301#else
302 if (error <= -1000 || error > 0) {
303#endif
304 fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
305 error = -ERANGE;
306 }
307
308 out.unique = req->unique;
309 out.error = error;
310
311 iov[0].iov_base = &out;
312 iov[0].iov_len = sizeof(struct fuse_out_header);
313
314 return fuse_send_msg(req->se, req->ch, iov, count, req);
315}
316
317static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
318 int count)
319{
320 int res;
321
322 res = fuse_send_reply_iov_nofree(req, error, iov, count);
323 fuse_free_req(req);
324 return res;
325}
326
327static int send_reply(fuse_req_t req, int error, const void *arg,
328 size_t argsize)
329{
330 if (req->flags.is_uring)
331 return send_reply_uring(req, error, arg, argsize);
332
333 struct iovec iov[2];
334 int count = 1;
335 if (argsize) {
336 iov[1].iov_base = (void *) arg;
337 iov[1].iov_len = argsize;
338 count++;
339 }
340 return send_reply_iov(req, error, iov, count);
341}
342
343int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
344{
345 int res;
346 struct iovec *padded_iov;
347
348 padded_iov = malloc((count + 1) * sizeof(struct iovec));
349 if (padded_iov == NULL)
350 return fuse_reply_err(req, ENOMEM);
351
352 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
353 count++;
354
355 res = send_reply_iov(req, 0, padded_iov, count);
356 free(padded_iov);
357
358 return res;
359}
360
361
362/* `buf` is allowed to be empty so that the proper size may be
363 allocated by the caller */
364size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
365 const char *name, const struct stat *stbuf, off_t off)
366{
367 (void)req;
368 size_t namelen;
369 size_t entlen;
370 size_t entlen_padded;
371 struct fuse_dirent *dirent;
372
373 namelen = strlen(name);
374 entlen = FUSE_NAME_OFFSET + namelen;
375 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
376
377 if ((buf == NULL) || (entlen_padded > bufsize))
378 return entlen_padded;
379
380 dirent = (struct fuse_dirent*) buf;
381 dirent->ino = stbuf->st_ino;
382 dirent->off = off;
383 dirent->namelen = namelen;
384 dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
385 memcpy(dirent->name, name, namelen);
386 memset(dirent->name + namelen, 0, entlen_padded - entlen);
387
388 return entlen_padded;
389}
390
391static void convert_statfs(const struct statvfs *stbuf,
392 struct fuse_kstatfs *kstatfs)
393{
394 kstatfs->bsize = stbuf->f_bsize;
395 kstatfs->frsize = stbuf->f_frsize;
396 kstatfs->blocks = stbuf->f_blocks;
397 kstatfs->bfree = stbuf->f_bfree;
398 kstatfs->bavail = stbuf->f_bavail;
399 kstatfs->files = stbuf->f_files;
400 kstatfs->ffree = stbuf->f_ffree;
401 kstatfs->namelen = stbuf->f_namemax;
402}
403
404static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
405{
406 return send_reply(req, 0, arg, argsize);
407}
408
409int fuse_reply_err(fuse_req_t req, int err)
410{
411 return send_reply(req, -err, NULL, 0);
412}
413
415{
416 fuse_free_req(req);
417}
418
419static unsigned long calc_timeout_sec(double t)
420{
421 if (t > (double) ULONG_MAX)
422 return ULONG_MAX;
423 else if (t < 0.0)
424 return 0;
425 else
426 return (unsigned long) t;
427}
428
429static unsigned int calc_timeout_nsec(double t)
430{
431 double f = t - (double) calc_timeout_sec(t);
432 if (f < 0.0)
433 return 0;
434 else if (f >= 0.999999999)
435 return 999999999;
436 else
437 return (unsigned int) (f * 1.0e9);
438}
439
440static void fill_entry(struct fuse_entry_out *arg,
441 const struct fuse_entry_param *e)
442{
443 arg->nodeid = e->ino;
444 arg->generation = e->generation;
445 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
446 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
447 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
448 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
449 convert_stat(&e->attr, &arg->attr);
450}
451
452/* `buf` is allowed to be empty so that the proper size may be
453 allocated by the caller */
454size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
455 const char *name,
456 const struct fuse_entry_param *e, off_t off)
457{
458 (void)req;
459 size_t namelen;
460 size_t entlen;
461 size_t entlen_padded;
462
463 namelen = strlen(name);
464 entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
465 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
466 if ((buf == NULL) || (entlen_padded > bufsize))
467 return entlen_padded;
468
469 struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
470 memset(&dp->entry_out, 0, sizeof(dp->entry_out));
471 fill_entry(&dp->entry_out, e);
472
473 struct fuse_dirent *dirent = &dp->dirent;
474 dirent->ino = e->attr.st_ino;
475 dirent->off = off;
476 dirent->namelen = namelen;
477 dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
478 memcpy(dirent->name, name, namelen);
479 memset(dirent->name + namelen, 0, entlen_padded - entlen);
480
481 return entlen_padded;
482}
483
484static void fill_open(struct fuse_open_out *arg,
485 const struct fuse_file_info *f)
486{
487 arg->fh = f->fh;
488 if (f->backing_id > 0) {
489 arg->backing_id = f->backing_id;
490 arg->open_flags |= FOPEN_PASSTHROUGH;
491 }
492 if (f->direct_io)
493 arg->open_flags |= FOPEN_DIRECT_IO;
494 if (f->keep_cache)
495 arg->open_flags |= FOPEN_KEEP_CACHE;
496 if (f->cache_readdir)
497 arg->open_flags |= FOPEN_CACHE_DIR;
498 if (f->nonseekable)
499 arg->open_flags |= FOPEN_NONSEEKABLE;
500 if (f->noflush)
501 arg->open_flags |= FOPEN_NOFLUSH;
503 arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
504}
505
506int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
507{
508 struct fuse_entry_out arg;
509 size_t size = req->se->conn.proto_minor < 9 ?
510 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
511
512 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
513 negative entry */
514 if (!e->ino && req->se->conn.proto_minor < 4)
515 return fuse_reply_err(req, ENOENT);
516
517 memset(&arg, 0, sizeof(arg));
518 fill_entry(&arg, e);
519 return send_reply_ok(req, &arg, size);
520}
521
522int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
523 const struct fuse_file_info *f)
524{
525 alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
526 size_t entrysize = req->se->conn.proto_minor < 9 ?
527 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
528 struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
529 struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
530
531 memset(buf, 0, sizeof(buf));
532 fill_entry(earg, e);
533 fill_open(oarg, f);
534 return send_reply_ok(req, buf,
535 entrysize + sizeof(struct fuse_open_out));
536}
537
538int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
539 double attr_timeout)
540{
541 struct fuse_attr_out arg;
542 size_t size = req->se->conn.proto_minor < 9 ?
543 FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
544
545 memset(&arg, 0, sizeof(arg));
546 arg.attr_valid = calc_timeout_sec(attr_timeout);
547 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
548 convert_stat(attr, &arg.attr);
549
550 return send_reply_ok(req, &arg, size);
551}
552
553int fuse_reply_readlink(fuse_req_t req, const char *linkname)
554{
555 return send_reply_ok(req, linkname, strlen(linkname));
556}
557
558int fuse_passthrough_open(fuse_req_t req, int fd)
559{
560 struct fuse_backing_map map = { .fd = fd };
561 int ret;
562
563 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
564 if (ret <= 0) {
565 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno));
566 return 0;
567 }
568
569 return ret;
570}
571
572int fuse_passthrough_close(fuse_req_t req, int backing_id)
573{
574 int ret;
575
576 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id);
577 if (ret < 0)
578 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno));
579
580 return ret;
581}
582
583int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
584{
585 struct fuse_open_out arg;
586
587 memset(&arg, 0, sizeof(arg));
588 fill_open(&arg, f);
589 return send_reply_ok(req, &arg, sizeof(arg));
590}
591
592static int do_fuse_reply_write(fuse_req_t req, size_t count)
593{
594 struct fuse_write_out arg;
595
596 memset(&arg, 0, sizeof(arg));
597 arg.size = count;
598
599 return send_reply_ok(req, &arg, sizeof(arg));
600}
601
602static int do_fuse_reply_copy(fuse_req_t req, size_t count)
603{
604 struct fuse_copy_file_range_out arg;
605
606 memset(&arg, 0, sizeof(arg));
607 arg.bytes_copied = count;
608
609 return send_reply_ok(req, &arg, sizeof(arg));
610}
611
612int fuse_reply_write(fuse_req_t req, size_t count)
613{
614 /*
615 * This function is also used by FUSE_COPY_FILE_RANGE and its 64-bit
616 * variant.
617 */
618 if (req->flags.is_copy_file_range_64)
619 return do_fuse_reply_copy(req, count);
620 else
621 return do_fuse_reply_write(req, count);
622}
623
624int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
625{
626 return send_reply_ok(req, buf, size);
627}
628
629static int fuse_send_data_iov_fallback(struct fuse_session *se,
630 struct fuse_chan *ch,
631 struct iovec *iov, int iov_count,
632 struct fuse_bufvec *buf,
633 size_t len, fuse_req_t req)
634{
635 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
636 void *mbuf;
637 int res;
638
639 /* Optimize common case */
640 if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
641 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
642 /* FIXME: also avoid memory copy if there are multiple buffers
643 but none of them contain an fd */
644
645 iov[iov_count].iov_base = buf->buf[0].mem;
646 iov[iov_count].iov_len = len;
647 iov_count++;
648 return fuse_send_msg(se, ch, iov, iov_count, req);
649 }
650
651 res = posix_memalign(&mbuf, pagesize, len);
652 if (res != 0)
653 return res;
654
655 mem_buf.buf[0].mem = mbuf;
656 res = fuse_buf_copy(&mem_buf, buf, 0);
657 if (res < 0) {
658 free(mbuf);
659 return -res;
660 }
661 len = res;
662
663 iov[iov_count].iov_base = mbuf;
664 iov[iov_count].iov_len = len;
665 iov_count++;
666 res = fuse_send_msg(se, ch, iov, iov_count, req);
667 free(mbuf);
668
669 return res;
670}
671
672struct fuse_ll_pipe {
673 size_t size;
674 int can_grow;
675 int pipe[2];
676};
677
678static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
679{
680 close(llp->pipe[0]);
681 close(llp->pipe[1]);
682 free(llp);
683}
684
685#ifdef HAVE_SPLICE
686#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
687static int fuse_pipe(int fds[2])
688{
689 int rv = pipe(fds);
690
691 if (rv == -1)
692 return rv;
693
694 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
695 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
696 fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
697 fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
698 close(fds[0]);
699 close(fds[1]);
700 rv = -1;
701 }
702 return rv;
703}
704#else
705static int fuse_pipe(int fds[2])
706{
707 return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
708}
709#endif
710
711static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
712{
713 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
714 if (llp == NULL) {
715 int res;
716
717 llp = malloc(sizeof(struct fuse_ll_pipe));
718 if (llp == NULL)
719 return NULL;
720
721 res = fuse_pipe(llp->pipe);
722 if (res == -1) {
723 free(llp);
724 return NULL;
725 }
726
727 /*
728 *the default size is 16 pages on linux
729 */
730 llp->size = pagesize * 16;
731 llp->can_grow = 1;
732
733 pthread_setspecific(se->pipe_key, llp);
734 }
735
736 return llp;
737}
738#endif
739
740static void fuse_ll_clear_pipe(struct fuse_session *se)
741{
742 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
743 if (llp) {
744 pthread_setspecific(se->pipe_key, NULL);
745 fuse_ll_pipe_free(llp);
746 }
747}
748
749#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
750static int read_back(int fd, char *buf, size_t len)
751{
752 int res;
753
754 res = read(fd, buf, len);
755 if (res == -1) {
756 fuse_log(FUSE_LOG_ERR,
757 "fuse: internal error: failed to read back from pipe: %s\n",
758 strerror(errno));
759 return -EIO;
760 }
761 if (res != len) {
762 fuse_log(FUSE_LOG_ERR,
763 "fuse: internal error: short read back from pipe: %i from %zd\n",
764 res, len);
765 return -EIO;
766 }
767 return 0;
768}
769
770static int grow_pipe_to_max(int pipefd)
771{
772 int res;
773 long max;
774 long maxfd;
775 char buf[32];
776
777 maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
778 if (maxfd < 0)
779 return -errno;
780
781 res = read(maxfd, buf, sizeof(buf) - 1);
782 if (res < 0) {
783 int saved_errno;
784
785 saved_errno = errno;
786 close(maxfd);
787 return -saved_errno;
788 }
789 close(maxfd);
790 buf[res] = '\0';
791
792 res = libfuse_strtol(buf, &max);
793 if (res)
794 return res;
795 res = fcntl(pipefd, F_SETPIPE_SZ, max);
796 if (res < 0)
797 return -errno;
798 return max;
799}
800
801static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
802 struct iovec *iov, int iov_count,
803 struct fuse_bufvec *buf, unsigned int flags,
804 fuse_req_t req)
805{
806 int res;
807 size_t len = fuse_buf_size(buf);
808 struct fuse_out_header *out = iov[0].iov_base;
809 struct fuse_ll_pipe *llp;
810 int splice_flags;
811 size_t pipesize;
812 size_t total_buf_size;
813 size_t idx;
814 size_t headerlen;
815 struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
816
817 if (se->broken_splice_nonblock)
818 goto fallback;
819
820 if (flags & FUSE_BUF_NO_SPLICE)
821 goto fallback;
822
823 total_buf_size = 0;
824 for (idx = buf->idx; idx < buf->count; idx++) {
825 total_buf_size += buf->buf[idx].size;
826 if (idx == buf->idx)
827 total_buf_size -= buf->off;
828 }
829 if (total_buf_size < 2 * pagesize)
830 goto fallback;
831
832 if (se->conn.proto_minor < 14 ||
833 !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
834 goto fallback;
835
836 llp = fuse_ll_get_pipe(se);
837 if (llp == NULL)
838 goto fallback;
839
840
841 headerlen = iov_length(iov, iov_count);
842
843 out->len = headerlen + len;
844
845 /*
846 * Heuristic for the required pipe size, does not work if the
847 * source contains less than page size fragments
848 */
849 pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
850
851 if (llp->size < pipesize) {
852 if (llp->can_grow) {
853 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
854 if (res == -1) {
855 res = grow_pipe_to_max(llp->pipe[0]);
856 if (res > 0)
857 llp->size = res;
858 llp->can_grow = 0;
859 goto fallback;
860 }
861 llp->size = res;
862 }
863 if (llp->size < pipesize)
864 goto fallback;
865 }
866
867
868 res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
869 if (res == -1)
870 goto fallback;
871
872 if (res != headerlen) {
873 res = -EIO;
874 fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
875 headerlen);
876 goto clear_pipe;
877 }
878
879 pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
880 pipe_buf.buf[0].fd = llp->pipe[1];
881
882 res = fuse_buf_copy(&pipe_buf, buf,
884 if (res < 0) {
885 if (res == -EAGAIN || res == -EINVAL) {
886 /*
887 * Should only get EAGAIN on kernels with
888 * broken SPLICE_F_NONBLOCK support (<=
889 * 2.6.35) where this error or a short read is
890 * returned even if the pipe itself is not
891 * full
892 *
893 * EINVAL might mean that splice can't handle
894 * this combination of input and output.
895 */
896 if (res == -EAGAIN)
897 se->broken_splice_nonblock = 1;
898
899 pthread_setspecific(se->pipe_key, NULL);
900 fuse_ll_pipe_free(llp);
901 goto fallback;
902 }
903 res = -res;
904 goto clear_pipe;
905 }
906
907 if (res != 0 && res < len) {
908 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
909 void *mbuf;
910 size_t now_len = res;
911 /*
912 * For regular files a short count is either
913 * 1) due to EOF, or
914 * 2) because of broken SPLICE_F_NONBLOCK (see above)
915 *
916 * For other inputs it's possible that we overflowed
917 * the pipe because of small buffer fragments.
918 */
919
920 res = posix_memalign(&mbuf, pagesize, len);
921 if (res != 0)
922 goto clear_pipe;
923
924 mem_buf.buf[0].mem = mbuf;
925 mem_buf.off = now_len;
926 res = fuse_buf_copy(&mem_buf, buf, 0);
927 if (res > 0) {
928 char *tmpbuf;
929 size_t extra_len = res;
930 /*
931 * Trickiest case: got more data. Need to get
932 * back the data from the pipe and then fall
933 * back to regular write.
934 */
935 tmpbuf = malloc(headerlen);
936 if (tmpbuf == NULL) {
937 free(mbuf);
938 res = ENOMEM;
939 goto clear_pipe;
940 }
941 res = read_back(llp->pipe[0], tmpbuf, headerlen);
942 free(tmpbuf);
943 if (res != 0) {
944 free(mbuf);
945 goto clear_pipe;
946 }
947 res = read_back(llp->pipe[0], mbuf, now_len);
948 if (res != 0) {
949 free(mbuf);
950 goto clear_pipe;
951 }
952 len = now_len + extra_len;
953 iov[iov_count].iov_base = mbuf;
954 iov[iov_count].iov_len = len;
955 iov_count++;
956 res = fuse_send_msg(se, ch, iov, iov_count, req);
957 free(mbuf);
958 return res;
959 }
960 free(mbuf);
961 res = now_len;
962 }
963 len = res;
964 out->len = headerlen + len;
965
966 if (se->debug) {
967 fuse_log(FUSE_LOG_DEBUG,
968 " unique: %llu, success, outsize: %i (splice)\n",
969 (unsigned long long) out->unique, out->len);
970 }
971
972 splice_flags = 0;
973 if ((flags & FUSE_BUF_SPLICE_MOVE) &&
974 (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
975 splice_flags |= SPLICE_F_MOVE;
976
977 if (se->io != NULL && se->io->splice_send != NULL) {
978 res = se->io->splice_send(llp->pipe[0], NULL,
979 ch ? ch->fd : se->fd, NULL, out->len,
980 splice_flags, se->userdata);
981 } else {
982 res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
983 out->len, splice_flags);
984 }
985 if (res == -1) {
986 res = -errno;
987 perror("fuse: splice from pipe");
988 goto clear_pipe;
989 }
990 if (res != out->len) {
991 res = -EIO;
992 fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
993 res, out->len);
994 goto clear_pipe;
995 }
996 return 0;
997
998clear_pipe:
999 fuse_ll_clear_pipe(se);
1000 return res;
1001
1002fallback:
1003 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len, req);
1004}
1005#else
1006static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
1007 struct iovec *iov, int iov_count,
1008 struct fuse_bufvec *req_data, unsigned int flags,
1009 fuse_req_t req)
1010{
1011 size_t len = fuse_buf_size(req_data);
1012 (void) flags;
1013
1014 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, req_data, len, req);
1015}
1016#endif
1017
1018int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
1019 enum fuse_buf_copy_flags flags)
1020{
1021 struct iovec iov[2];
1022 struct fuse_out_header out;
1023 int res;
1024
1025 if (req->flags.is_uring)
1026 return fuse_reply_data_uring(req, bufv, flags);
1027
1028 iov[0].iov_base = &out;
1029 iov[0].iov_len = sizeof(struct fuse_out_header);
1030
1031 out.unique = req->unique;
1032 out.error = 0;
1033
1034 res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags, req);
1035 if (res <= 0) {
1036 fuse_free_req(req);
1037 return res;
1038 } else {
1039 return fuse_reply_err(req, res);
1040 }
1041}
1042
1043int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
1044{
1045 struct fuse_statfs_out arg;
1046 size_t size = req->se->conn.proto_minor < 4 ?
1047 FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
1048
1049 memset(&arg, 0, sizeof(arg));
1050 convert_statfs(stbuf, &arg.st);
1051
1052 return send_reply_ok(req, &arg, size);
1053}
1054
1055int fuse_reply_xattr(fuse_req_t req, size_t count)
1056{
1057 struct fuse_getxattr_out arg;
1058
1059 memset(&arg, 0, sizeof(arg));
1060 arg.size = count;
1061
1062 return send_reply_ok(req, &arg, sizeof(arg));
1063}
1064
1065int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
1066{
1067 struct fuse_lk_out arg;
1068
1069 memset(&arg, 0, sizeof(arg));
1070 arg.lk.type = lock->l_type;
1071 if (lock->l_type != F_UNLCK) {
1072 arg.lk.start = lock->l_start;
1073 if (lock->l_len == 0)
1074 arg.lk.end = OFFSET_MAX;
1075 else
1076 arg.lk.end = lock->l_start + lock->l_len - 1;
1077 }
1078 arg.lk.pid = lock->l_pid;
1079 return send_reply_ok(req, &arg, sizeof(arg));
1080}
1081
1082int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
1083{
1084 struct fuse_bmap_out arg;
1085
1086 memset(&arg, 0, sizeof(arg));
1087 arg.block = idx;
1088
1089 return send_reply_ok(req, &arg, sizeof(arg));
1090}
1091
1092static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
1093 size_t count)
1094{
1095 struct fuse_ioctl_iovec *fiov;
1096 size_t i;
1097
1098 fiov = malloc(sizeof(fiov[0]) * count);
1099 if (!fiov)
1100 return NULL;
1101
1102 for (i = 0; i < count; i++) {
1103 fiov[i].base = (uintptr_t) iov[i].iov_base;
1104 fiov[i].len = iov[i].iov_len;
1105 }
1106
1107 return fiov;
1108}
1109
1111 const struct iovec *in_iov, size_t in_count,
1112 const struct iovec *out_iov, size_t out_count)
1113{
1114 struct fuse_ioctl_out arg;
1115 struct fuse_ioctl_iovec *in_fiov = NULL;
1116 struct fuse_ioctl_iovec *out_fiov = NULL;
1117 struct iovec iov[4];
1118 size_t count = 1;
1119 int res;
1120
1121 memset(&arg, 0, sizeof(arg));
1122 arg.flags |= FUSE_IOCTL_RETRY;
1123 arg.in_iovs = in_count;
1124 arg.out_iovs = out_count;
1125 iov[count].iov_base = &arg;
1126 iov[count].iov_len = sizeof(arg);
1127 count++;
1128
1129 if (req->se->conn.proto_minor < 16) {
1130 if (in_count) {
1131 iov[count].iov_base = (void *)in_iov;
1132 iov[count].iov_len = sizeof(in_iov[0]) * in_count;
1133 count++;
1134 }
1135
1136 if (out_count) {
1137 iov[count].iov_base = (void *)out_iov;
1138 iov[count].iov_len = sizeof(out_iov[0]) * out_count;
1139 count++;
1140 }
1141 } else {
1142 /* Can't handle non-compat 64bit ioctls on 32bit */
1143 if (sizeof(void *) == 4 && req->flags.ioctl_64bit) {
1144 res = fuse_reply_err(req, EINVAL);
1145 goto out;
1146 }
1147
1148 if (in_count) {
1149 in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
1150 if (!in_fiov)
1151 goto enomem;
1152
1153 iov[count].iov_base = (void *)in_fiov;
1154 iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
1155 count++;
1156 }
1157 if (out_count) {
1158 out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
1159 if (!out_fiov)
1160 goto enomem;
1161
1162 iov[count].iov_base = (void *)out_fiov;
1163 iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
1164 count++;
1165 }
1166 }
1167
1168 res = send_reply_iov(req, 0, iov, count);
1169out:
1170 free(in_fiov);
1171 free(out_fiov);
1172
1173 return res;
1174
1175enomem:
1176 res = fuse_reply_err(req, ENOMEM);
1177 goto out;
1178}
1179
1180int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
1181{
1182 struct fuse_ioctl_out arg;
1183 struct iovec iov[3];
1184 size_t count = 1;
1185
1186 memset(&arg, 0, sizeof(arg));
1187 arg.result = result;
1188 iov[count].iov_base = &arg;
1189 iov[count].iov_len = sizeof(arg);
1190 count++;
1191
1192 if (size) {
1193 iov[count].iov_base = (char *) buf;
1194 iov[count].iov_len = size;
1195 count++;
1196 }
1197
1198 return send_reply_iov(req, 0, iov, count);
1199}
1200
1201int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
1202 int count)
1203{
1204 struct iovec *padded_iov;
1205 struct fuse_ioctl_out arg;
1206 int res;
1207
1208 padded_iov = malloc((count + 2) * sizeof(struct iovec));
1209 if (padded_iov == NULL)
1210 return fuse_reply_err(req, ENOMEM);
1211
1212 memset(&arg, 0, sizeof(arg));
1213 arg.result = result;
1214 padded_iov[1].iov_base = &arg;
1215 padded_iov[1].iov_len = sizeof(arg);
1216
1217 memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
1218
1219 res = send_reply_iov(req, 0, padded_iov, count + 2);
1220 free(padded_iov);
1221
1222 return res;
1223}
1224
1225int fuse_reply_poll(fuse_req_t req, unsigned revents)
1226{
1227 struct fuse_poll_out arg;
1228
1229 memset(&arg, 0, sizeof(arg));
1230 arg.revents = revents;
1231
1232 return send_reply_ok(req, &arg, sizeof(arg));
1233}
1234
1235int fuse_reply_lseek(fuse_req_t req, off_t off)
1236{
1237 struct fuse_lseek_out arg;
1238
1239 memset(&arg, 0, sizeof(arg));
1240 arg.offset = off;
1241
1242 return send_reply_ok(req, &arg, sizeof(arg));
1243}
1244
1245#ifdef HAVE_STATX
1246int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx,
1247 double attr_timeout)
1248{
1249 struct fuse_statx_out arg;
1250
1251 memset(&arg, 0, sizeof(arg));
1252 arg.flags = flags;
1253 arg.attr_valid = calc_timeout_sec(attr_timeout);
1254 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
1255 memcpy(&arg.stat, statx, sizeof(arg.stat));
1256
1257 return send_reply_ok(req, &arg, sizeof(arg));
1258}
1259#else
1260int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx,
1261 double attr_timeout)
1262{
1263 (void)req;
1264 (void)flags;
1265 (void)statx;
1266 (void)attr_timeout;
1267
1268 return -ENOSYS;
1269}
1270#endif
1271
1272static void _do_lookup(fuse_req_t req, const fuse_ino_t nodeid,
1273 const void *op_in, const void *in_payload)
1274{
1275 (void)op_in;
1276
1277 char *name = (char *)in_payload;
1278
1279 if (req->se->op.lookup)
1280 req->se->op.lookup(req, nodeid, name);
1281 else
1282 fuse_reply_err(req, ENOSYS);
1283}
1284
1285static void do_lookup(fuse_req_t req, const fuse_ino_t nodeid,
1286 const void *inarg)
1287{
1288 _do_lookup(req, nodeid, NULL, inarg);
1289}
1290
1291static void _do_forget(fuse_req_t req, const fuse_ino_t nodeid,
1292 const void *op_in, const void *in_payload)
1293{
1294 (void)in_payload;
1295
1296 struct fuse_forget_in *arg = (struct fuse_forget_in *)op_in;
1297
1298 if (req->se->op.forget)
1299 req->se->op.forget(req, nodeid, arg->nlookup);
1300 else
1301 fuse_reply_none(req);
1302}
1303
1304static void do_forget(fuse_req_t req, const fuse_ino_t nodeid,
1305 const void *inarg)
1306{
1307 _do_forget(req, nodeid, inarg, NULL);
1308}
1309
1310static void _do_batch_forget(fuse_req_t req, const fuse_ino_t nodeid,
1311 const void *op_in, const void *in_payload)
1312{
1313 (void)nodeid;
1314 unsigned int i;
1315
1316 const struct fuse_batch_forget_in *arg = op_in;
1317 const struct fuse_forget_one *forgets = in_payload;
1318
1319 if (req->se->op.forget_multi) {
1320 req->se->op.forget_multi(req, arg->count,
1321 (struct fuse_forget_data *)in_payload);
1322 } else if (req->se->op.forget) {
1323 for (i = 0; i < arg->count; i++) {
1324 const struct fuse_forget_one *forget = &forgets[i];
1325 struct fuse_req *dummy_req;
1326
1327 dummy_req = fuse_ll_alloc_req(req->se);
1328 if (dummy_req == NULL)
1329 break;
1330
1331 dummy_req->unique = req->unique;
1332 dummy_req->ctx = req->ctx;
1333 dummy_req->ch = NULL;
1334
1335 req->se->op.forget(dummy_req, forget->nodeid,
1336 forget->nlookup);
1337 }
1338 fuse_reply_none(req);
1339 } else {
1340 fuse_reply_none(req);
1341 }
1342}
1343
1344static void do_batch_forget(fuse_req_t req, const fuse_ino_t nodeid,
1345 const void *inarg)
1346{
1347 struct fuse_batch_forget_in *arg = (void *)inarg;
1348 struct fuse_forget_one *param = (void *)PARAM(arg);
1349
1350 _do_batch_forget(req, nodeid, inarg, param);
1351}
1352
1353static void _do_getattr(fuse_req_t req, const fuse_ino_t nodeid,
1354 const void *op_in, const void *in_payload)
1355{
1356 struct fuse_getattr_in *arg = (struct fuse_getattr_in *)op_in;
1357 (void)in_payload;
1358
1359 struct fuse_file_info *fip = NULL;
1360 struct fuse_file_info fi;
1361
1362 if (req->se->conn.proto_minor >= 9) {
1363 if (arg->getattr_flags & FUSE_GETATTR_FH) {
1364 memset(&fi, 0, sizeof(fi));
1365 fi.fh = arg->fh;
1366 fip = &fi;
1367 }
1368 }
1369
1370 if (req->se->op.getattr)
1371 req->se->op.getattr(req, nodeid, fip);
1372 else
1373 fuse_reply_err(req, ENOSYS);
1374}
1375
1376static void do_getattr(fuse_req_t req, const fuse_ino_t nodeid,
1377 const void *inarg)
1378{
1379 _do_getattr(req, nodeid, inarg, NULL);
1380}
1381
1382static void _do_setattr(fuse_req_t req, const fuse_ino_t nodeid,
1383 const void *op_in, const void *in_payload)
1384{
1385 (void)in_payload;
1386 const struct fuse_setattr_in *arg = op_in;
1387 uint32_t valid = arg->valid;
1388
1389 if (req->se->op.setattr) {
1390 struct fuse_file_info *fi = NULL;
1391 struct fuse_file_info fi_store;
1392 struct stat stbuf;
1393 memset(&stbuf, 0, sizeof(stbuf));
1394 convert_attr(arg, &stbuf);
1395 if (arg->valid & FATTR_FH) {
1396 valid &= ~FATTR_FH;
1397 memset(&fi_store, 0, sizeof(fi_store));
1398 fi = &fi_store;
1399 fi->fh = arg->fh;
1400 }
1401 valid &= FUSE_SET_ATTR_MODE | FUSE_SET_ATTR_UID |
1402 FUSE_SET_ATTR_GID | FUSE_SET_ATTR_SIZE |
1403 FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME |
1404 FUSE_SET_ATTR_KILL_SUID | FUSE_SET_ATTR_KILL_SGID |
1405 FUSE_SET_ATTR_ATIME_NOW | FUSE_SET_ATTR_MTIME_NOW |
1406 FUSE_SET_ATTR_CTIME;
1407
1408 req->se->op.setattr(req, nodeid, &stbuf, valid, fi);
1409 } else
1410 fuse_reply_err(req, ENOSYS);
1411}
1412
1413static void do_setattr(fuse_req_t req, const fuse_ino_t nodeid,
1414 const void *inarg)
1415{
1416 _do_setattr(req, nodeid, inarg, NULL);
1417}
1418
1419static void _do_access(fuse_req_t req, const fuse_ino_t nodeid,
1420 const void *op_in, const void *in_payload)
1421{
1422 (void)in_payload;
1423 const struct fuse_access_in *arg = op_in;
1424
1425 if (req->se->op.access)
1426 req->se->op.access(req, nodeid, arg->mask);
1427 else
1428 fuse_reply_err(req, ENOSYS);
1429}
1430
1431static void do_access(fuse_req_t req, const fuse_ino_t nodeid,
1432 const void *inarg)
1433{
1434 _do_access(req, nodeid, inarg, NULL);
1435}
1436
1437static void _do_readlink(fuse_req_t req, const fuse_ino_t nodeid,
1438 const void *op_in, const void *in_payload)
1439{
1440 (void)op_in;
1441 (void)in_payload;
1442
1443 if (req->se->op.readlink)
1444 req->se->op.readlink(req, nodeid);
1445 else
1446 fuse_reply_err(req, ENOSYS);
1447}
1448
1449static void do_readlink(fuse_req_t req, const fuse_ino_t nodeid,
1450 const void *inarg)
1451{
1452 _do_readlink(req, nodeid, inarg, NULL);
1453}
1454
1455static void _do_mknod(fuse_req_t req, const fuse_ino_t nodeid,
1456 const void *op_in, const void *in_payload)
1457{
1458 const struct fuse_mknod_in *arg = (struct fuse_mknod_in *)op_in;
1459 const char *name = in_payload;
1460
1461 if (req->se->conn.proto_minor >= 12)
1462 req->ctx.umask = arg->umask;
1463
1464 if (req->se->op.mknod)
1465 req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
1466 else
1467 fuse_reply_err(req, ENOSYS);
1468}
1469
1470static void do_mknod(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1471{
1472 struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg;
1473 char *name = PARAM(arg);
1474
1475 if (req->se->conn.proto_minor < 12)
1476 name = (char *)inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
1477
1478 _do_mknod(req, nodeid, inarg, name);
1479}
1480
1481static void _do_mkdir(fuse_req_t req, const fuse_ino_t nodeid,
1482 const void *op_in, const void *in_payload)
1483{
1484 const char *name = in_payload;
1485 const struct fuse_mkdir_in *arg = op_in;
1486
1487 if (req->se->conn.proto_minor >= 12)
1488 req->ctx.umask = arg->umask;
1489
1490 if (req->se->op.mkdir)
1491 req->se->op.mkdir(req, nodeid, name, arg->mode);
1492 else
1493 fuse_reply_err(req, ENOSYS);
1494}
1495
1496static void do_mkdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1497{
1498 const struct fuse_mkdir_in *arg = inarg;
1499 const char *name = PARAM(arg);
1500
1501 _do_mkdir(req, nodeid, inarg, name);
1502}
1503
1504static void _do_unlink(fuse_req_t req, const fuse_ino_t nodeid,
1505 const void *op_in, const void *in_payload)
1506{
1507 (void)op_in;
1508 const char *name = in_payload;
1509
1510 if (req->se->op.unlink)
1511 req->se->op.unlink(req, nodeid, name);
1512 else
1513 fuse_reply_err(req, ENOSYS);
1514}
1515
1516static void do_unlink(fuse_req_t req, const fuse_ino_t nodeid,
1517 const void *inarg)
1518{
1519 _do_unlink(req, nodeid, NULL, inarg);
1520}
1521
1522static void _do_rmdir(fuse_req_t req, const fuse_ino_t nodeid,
1523 const void *op_in, const void *in_payload)
1524{
1525 (void)op_in;
1526 const char *name = in_payload;
1527
1528 if (req->se->op.rmdir)
1529 req->se->op.rmdir(req, nodeid, name);
1530 else
1531 fuse_reply_err(req, ENOSYS);
1532}
1533
1534static void do_rmdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1535{
1536 _do_rmdir(req, nodeid, NULL, inarg);
1537}
1538
1539static void _do_symlink(fuse_req_t req, const fuse_ino_t nodeid,
1540 const void *op_in, const void *in_payload)
1541{
1542 (void)op_in;
1543 const char *name = (char *)in_payload;
1544 const char *linkname = name + strlen(name) + 1;
1545
1546 if (req->se->op.symlink)
1547 req->se->op.symlink(req, linkname, nodeid, name);
1548 else
1549 fuse_reply_err(req, ENOSYS);
1550}
1551
1552static void do_symlink(fuse_req_t req, const fuse_ino_t nodeid,
1553 const void *inarg)
1554{
1555 _do_symlink(req, nodeid, NULL, inarg);
1556}
1557
1558static void _do_rename(fuse_req_t req, const fuse_ino_t nodeid,
1559 const void *op_in, const void *in_payload)
1560{
1561 const struct fuse_rename_in *arg = (struct fuse_rename_in *)op_in;
1562 const char *oldname = in_payload;
1563 const char *newname = oldname + strlen(oldname) + 1;
1564
1565 if (req->se->op.rename)
1566 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1567 0);
1568 else
1569 fuse_reply_err(req, ENOSYS);
1570}
1571
1572static void do_rename(fuse_req_t req, const fuse_ino_t nodeid,
1573 const void *inarg)
1574{
1575 const struct fuse_rename_in *arg = inarg;
1576 const void *payload = PARAM(arg);
1577
1578 _do_rename(req, nodeid, arg, payload);
1579}
1580
1581static void _do_rename2(fuse_req_t req, const fuse_ino_t nodeid,
1582 const void *op_in, const void *in_payload)
1583{
1584 const struct fuse_rename2_in *arg = op_in;
1585 const char *oldname = in_payload;
1586 const char *newname = oldname + strlen(oldname) + 1;
1587
1588 if (req->se->op.rename)
1589 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1590 arg->flags);
1591 else
1592 fuse_reply_err(req, ENOSYS);
1593}
1594
1595static void do_rename2(fuse_req_t req, const fuse_ino_t nodeid,
1596 const void *inarg)
1597{
1598 const struct fuse_rename2_in *arg = inarg;
1599 const void *payload = PARAM(arg);
1600
1601 _do_rename2(req, nodeid, arg, payload);
1602}
1603
1604static void _do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *op_in,
1605 const void *in_payload)
1606{
1607 (void)in_payload;
1608 const struct fuse_create_in *arg = op_in;
1609
1610 if (req->se->op.tmpfile) {
1611 struct fuse_file_info fi;
1612
1613 memset(&fi, 0, sizeof(fi));
1614 fi.flags = arg->flags;
1615
1616 if (req->se->conn.proto_minor >= 12)
1617 req->ctx.umask = arg->umask;
1618
1619 req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
1620 } else
1621 fuse_reply_err(req, ENOSYS);
1622}
1623
1624static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1625{
1626 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
1627
1628 _do_tmpfile(req, nodeid, arg, NULL);
1629}
1630
1631static void _do_link(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
1632 const void *in_payload)
1633{
1634 struct fuse_link_in *arg = (struct fuse_link_in *)op_in;
1635
1636 if (req->se->op.link)
1637 req->se->op.link(req, arg->oldnodeid, nodeid, in_payload);
1638 else
1639 fuse_reply_err(req, ENOSYS);
1640}
1641
1642static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1643{
1644 const struct fuse_link_in *arg = inarg;
1645 const void *name = PARAM(arg);
1646
1647 _do_link(req, nodeid, inarg, name);
1648}
1649
1650static void _do_create(fuse_req_t req, const fuse_ino_t nodeid,
1651 const void *op_in, const void *in_payload)
1652{
1653 const struct fuse_create_in *arg = op_in;
1654 const char *name = in_payload;
1655
1656 if (req->se->op.create) {
1657 struct fuse_file_info fi;
1658
1659 memset(&fi, 0, sizeof(fi));
1660 fi.flags = arg->flags;
1661
1662 if (req->se->conn.proto_minor >= 12)
1663 req->ctx.umask = arg->umask;
1664
1665 /* XXX: fuse_create_in::open_flags */
1666
1667 req->se->op.create(req, nodeid, name, arg->mode, &fi);
1668 } else {
1669 fuse_reply_err(req, ENOSYS);
1670 }
1671}
1672
1673static void do_create(fuse_req_t req, const fuse_ino_t nodeid,
1674 const void *inarg)
1675{
1676 const struct fuse_create_in *arg = (struct fuse_create_in *)inarg;
1677 void *payload = PARAM(arg);
1678
1679 if (req->se->conn.proto_minor < 12)
1680 payload = (char *)inarg + sizeof(struct fuse_open_in);
1681
1682 _do_create(req, nodeid, arg, payload);
1683}
1684
1685static void _do_open(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
1686 const void *in_payload)
1687{
1688 (void)in_payload;
1689 struct fuse_open_in *arg = (struct fuse_open_in *)op_in;
1690 struct fuse_file_info fi;
1691
1692 memset(&fi, 0, sizeof(fi));
1693 fi.flags = arg->flags;
1694
1695 /* XXX: fuse_open_in::open_flags */
1696
1697 if (req->se->op.open)
1698 req->se->op.open(req, nodeid, &fi);
1699 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
1700 fuse_reply_err(req, ENOSYS);
1701 else
1702 fuse_reply_open(req, &fi);
1703}
1704
1705static void do_open(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1706{
1707 _do_open(req, nodeid, inarg, NULL);
1708}
1709
1710static void _do_read(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
1711 const void *in_payload)
1712{
1713 (void)in_payload;
1714 struct fuse_read_in *arg = (struct fuse_read_in *)op_in;
1715
1716 if (req->se->op.read) {
1717 struct fuse_file_info fi;
1718
1719 memset(&fi, 0, sizeof(fi));
1720 fi.fh = arg->fh;
1721 if (req->se->conn.proto_minor >= 9) {
1722 fi.lock_owner = arg->lock_owner;
1723 fi.flags = arg->flags;
1724 }
1725 req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
1726 } else
1727 fuse_reply_err(req, ENOSYS);
1728}
1729
1730static void do_read(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1731{
1732 _do_read(req, nodeid, inarg, NULL);
1733}
1734
1735static void _do_write(fuse_req_t req, const fuse_ino_t nodeid,
1736 const void *op_in, const void *in_payload)
1737{
1738 struct fuse_write_in *arg = (struct fuse_write_in *)op_in;
1739 const char *buf = in_payload;
1740 struct fuse_file_info fi;
1741
1742 memset(&fi, 0, sizeof(fi));
1743 fi.fh = arg->fh;
1744 fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
1745
1746 if (req->se->conn.proto_minor >= 9) {
1747 fi.lock_owner = arg->lock_owner;
1748 fi.flags = arg->flags;
1749 }
1750
1751 if (req->se->op.write)
1752 req->se->op.write(req, nodeid, buf, arg->size, arg->offset,
1753 &fi);
1754 else
1755 fuse_reply_err(req, ENOSYS);
1756}
1757
1758static void do_write(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1759{
1760 struct fuse_write_in *arg = (struct fuse_write_in *)inarg;
1761 const void *payload;
1762
1763 if (req->se->conn.proto_minor < 9)
1764 payload = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1765 else
1766 payload = PARAM(arg);
1767
1768 _do_write(req, nodeid, arg, payload);
1769}
1770
1771static void _do_write_buf(fuse_req_t req, const fuse_ino_t nodeid,
1772 const void *op_in, struct fuse_bufvec *bufv)
1773{
1774 struct fuse_session *se = req->se;
1775 struct fuse_write_in *arg = (struct fuse_write_in *)op_in;
1776 struct fuse_file_info fi;
1777
1778 memset(&fi, 0, sizeof(fi));
1779 fi.fh = arg->fh;
1780 fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
1781
1782 if (se->conn.proto_minor >= 9) {
1783 fi.lock_owner = arg->lock_owner;
1784 fi.flags = arg->flags;
1785 }
1786
1787 se->op.write_buf(req, nodeid, bufv, arg->offset, &fi);
1788}
1789
1790static void do_write_buf(fuse_req_t req, const fuse_ino_t nodeid,
1791 const void *inarg, const struct fuse_buf *ibuf)
1792{
1793 struct fuse_session *se = req->se;
1794 struct fuse_bufvec bufv = {
1795 .buf[0] = *ibuf,
1796 .count = 1,
1797 };
1798 struct fuse_write_in *arg = (struct fuse_write_in *)inarg;
1799
1800 if (se->conn.proto_minor < 9) {
1801 bufv.buf[0].mem = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1802 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1803 FUSE_COMPAT_WRITE_IN_SIZE;
1804 assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
1805 } else {
1806 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
1807 bufv.buf[0].mem = PARAM(arg);
1808
1809 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1810 sizeof(struct fuse_write_in);
1811 }
1812 if (bufv.buf[0].size < arg->size) {
1813 fuse_log(FUSE_LOG_ERR,
1814 "fuse: %s: buffer size too small\n", __func__);
1815 fuse_reply_err(req, EIO);
1816 goto out;
1817 }
1818 bufv.buf[0].size = arg->size;
1819
1820 _do_write_buf(req, nodeid, inarg, &bufv);
1821
1822out:
1823 /* Need to reset the pipe if ->write_buf() didn't consume all data */
1824 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
1825 fuse_ll_clear_pipe(se);
1826}
1827
1828static void _do_flush(fuse_req_t req, const fuse_ino_t nodeid,
1829 const void *op_in, const void *in_payload)
1830{
1831 (void)in_payload;
1832 struct fuse_flush_in *arg = (struct fuse_flush_in *)op_in;
1833 struct fuse_file_info fi;
1834
1835 memset(&fi, 0, sizeof(fi));
1836 fi.fh = arg->fh;
1837 fi.flush = 1;
1838 if (req->se->conn.proto_minor >= 7)
1839 fi.lock_owner = arg->lock_owner;
1840
1841 if (req->se->op.flush)
1842 req->se->op.flush(req, nodeid, &fi);
1843 else
1844 fuse_reply_err(req, ENOSYS);
1845}
1846
1847static void do_flush(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1848{
1849 _do_flush(req, nodeid, inarg, NULL);
1850}
1851
1852static void _do_release(fuse_req_t req, const fuse_ino_t nodeid,
1853 const void *op_in, const void *in_payload)
1854{
1855 (void)in_payload;
1856 const struct fuse_release_in *arg = op_in;
1857 struct fuse_file_info fi;
1858
1859 memset(&fi, 0, sizeof(fi));
1860 fi.flags = arg->flags;
1861 fi.fh = arg->fh;
1862 if (req->se->conn.proto_minor >= 8) {
1863 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
1864 fi.lock_owner = arg->lock_owner;
1865 }
1866 if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
1867 fi.flock_release = 1;
1868 fi.lock_owner = arg->lock_owner;
1869 }
1870
1871 if (req->se->op.release)
1872 req->se->op.release(req, nodeid, &fi);
1873 else
1874 fuse_reply_err(req, 0);
1875}
1876
1877static void do_release(fuse_req_t req, const fuse_ino_t nodeid,
1878 const void *inarg)
1879{
1880 _do_release(req, nodeid, inarg, NULL);
1881}
1882
1883static void _do_fsync(fuse_req_t req, const fuse_ino_t nodeid,
1884 const void *op_in, const void *in_payload)
1885{
1886 (void)in_payload;
1887 const struct fuse_fsync_in *arg = op_in;
1888 struct fuse_file_info fi;
1889 int datasync = arg->fsync_flags & 1;
1890
1891 memset(&fi, 0, sizeof(fi));
1892 fi.fh = arg->fh;
1893
1894 if (req->se->op.fsync)
1895 req->se->op.fsync(req, nodeid, datasync, &fi);
1896 else
1897 fuse_reply_err(req, ENOSYS);
1898}
1899
1900static void do_fsync(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
1901{
1902 _do_fsync(req, nodeid, inarg, NULL);
1903}
1904
1905static void _do_opendir(fuse_req_t req, const fuse_ino_t nodeid,
1906 const void *op_in, const void *in_payload)
1907{
1908 (void)in_payload;
1909 const struct fuse_open_in *arg = op_in;
1910 struct fuse_file_info fi;
1911
1912 memset(&fi, 0, sizeof(fi));
1913 fi.flags = arg->flags;
1914 /* XXX: fuse_open_in::open_flags */
1915
1916 if (req->se->op.opendir)
1917 req->se->op.opendir(req, nodeid, &fi);
1918 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
1919 fuse_reply_err(req, ENOSYS);
1920 else
1921 fuse_reply_open(req, &fi);
1922}
1923
1924static void do_opendir(fuse_req_t req, const fuse_ino_t nodeid,
1925 const void *inarg)
1926{
1927 _do_opendir(req, nodeid, inarg, NULL);
1928}
1929
1930static void _do_readdir(fuse_req_t req, const fuse_ino_t nodeid,
1931 const void *op_in, const void *in_payload)
1932{
1933 (void)in_payload;
1934 struct fuse_read_in *arg = (struct fuse_read_in *)op_in;
1935 struct fuse_file_info fi;
1936
1937 memset(&fi, 0, sizeof(fi));
1938 fi.fh = arg->fh;
1939
1940 if (req->se->op.readdir)
1941 req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
1942 else
1943 fuse_reply_err(req, ENOSYS);
1944}
1945
1946static void do_readdir(fuse_req_t req, const fuse_ino_t nodeid,
1947 const void *inarg)
1948{
1949 _do_readdir(req, nodeid, inarg, NULL);
1950}
1951
1952static void _do_readdirplus(fuse_req_t req, const fuse_ino_t nodeid,
1953 const void *op_in, const void *in_payload)
1954{
1955 (void)in_payload;
1956 struct fuse_read_in *arg = (struct fuse_read_in *)op_in;
1957 struct fuse_file_info fi;
1958
1959 memset(&fi, 0, sizeof(fi));
1960 fi.fh = arg->fh;
1961
1962 if (req->se->op.readdirplus)
1963 req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
1964 else
1965 fuse_reply_err(req, ENOSYS);
1966}
1967
1968static void do_readdirplus(fuse_req_t req, const fuse_ino_t nodeid,
1969 const void *inarg)
1970{
1971 _do_readdirplus(req, nodeid, inarg, NULL);
1972}
1973
1974static void _do_releasedir(fuse_req_t req, const fuse_ino_t nodeid,
1975 const void *op_in, const void *in_payload)
1976{
1977 (void)in_payload;
1978 struct fuse_release_in *arg = (struct fuse_release_in *)op_in;
1979 struct fuse_file_info fi;
1980
1981 memset(&fi, 0, sizeof(fi));
1982 fi.flags = arg->flags;
1983 fi.fh = arg->fh;
1984
1985 if (req->se->op.releasedir)
1986 req->se->op.releasedir(req, nodeid, &fi);
1987 else
1988 fuse_reply_err(req, 0);
1989}
1990
1991static void do_releasedir(fuse_req_t req, const fuse_ino_t nodeid,
1992 const void *inarg)
1993{
1994 _do_releasedir(req, nodeid, inarg, NULL);
1995}
1996
1997static void _do_fsyncdir(fuse_req_t req, const fuse_ino_t nodeid,
1998 const void *op_in, const void *in_payload)
1999{
2000 (void)in_payload;
2001 struct fuse_fsync_in *arg = (struct fuse_fsync_in *)op_in;
2002 struct fuse_file_info fi;
2003 int datasync = arg->fsync_flags & 1;
2004
2005 memset(&fi, 0, sizeof(fi));
2006 fi.fh = arg->fh;
2007
2008 if (req->se->op.fsyncdir)
2009 req->se->op.fsyncdir(req, nodeid, datasync, &fi);
2010 else
2011 fuse_reply_err(req, ENOSYS);
2012}
2013static void do_fsyncdir(fuse_req_t req, const fuse_ino_t nodeid,
2014 const void *inarg)
2015{
2016 _do_fsyncdir(req, nodeid, inarg, NULL);
2017}
2018
2019static void _do_statfs(fuse_req_t req, const fuse_ino_t nodeid,
2020 const void *op_in, const void *in_payload)
2021{
2022 (void) nodeid;
2023 (void)op_in;
2024 (void)in_payload;
2025
2026 if (req->se->op.statfs)
2027 req->se->op.statfs(req, nodeid);
2028 else {
2029 struct statvfs buf = {
2030 .f_namemax = 255,
2031 .f_bsize = 512,
2032 };
2033 fuse_reply_statfs(req, &buf);
2034 }
2035}
2036static void do_statfs(fuse_req_t req, const fuse_ino_t nodeid,
2037 const void *inarg)
2038{
2039 _do_statfs(req, nodeid, inarg, NULL);
2040}
2041
2042static void _do_setxattr(fuse_req_t req, const fuse_ino_t nodeid,
2043 const void *op_in, const void *in_payload)
2044{
2045 struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *)op_in;
2046 const char *name = in_payload;
2047 const char *value = name + strlen(name) + 1;
2048
2049 /* XXX:The API should be extended to support extra_flags/setxattr_flags */
2050
2051 if (req->se->op.setxattr)
2052 req->se->op.setxattr(req, nodeid, name, value, arg->size,
2053 arg->flags);
2054 else
2055 fuse_reply_err(req, ENOSYS);
2056}
2057static void do_setxattr(fuse_req_t req, const fuse_ino_t nodeid,
2058 const void *inarg)
2059{
2060 struct fuse_session *se = req->se;
2061 unsigned int xattr_ext = !!(se->conn.want & FUSE_CAP_SETXATTR_EXT);
2062 const struct fuse_setxattr_in *arg = inarg;
2063 char *payload = xattr_ext ? PARAM(arg) :
2064 (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
2065
2066 _do_setxattr(req, nodeid, arg, payload);
2067}
2068
2069static void _do_getxattr(fuse_req_t req, const fuse_ino_t nodeid,
2070 const void *op_in, const void *in_payload)
2071{
2072 const struct fuse_getxattr_in *arg = op_in;
2073
2074 if (req->se->op.getxattr)
2075 req->se->op.getxattr(req, nodeid, in_payload, arg->size);
2076 else
2077 fuse_reply_err(req, ENOSYS);
2078}
2079
2080static void do_getxattr(fuse_req_t req, const fuse_ino_t nodeid,
2081 const void *inarg)
2082{
2083 const struct fuse_getxattr_in *arg = inarg;
2084 const void *payload = PARAM(arg);
2085
2086 _do_getxattr(req, nodeid, arg, payload);
2087}
2088
2089static void _do_listxattr(fuse_req_t req, const fuse_ino_t nodeid,
2090 const void *inarg, const void *in_payload)
2091{
2092 (void)in_payload;
2093 const struct fuse_getxattr_in *arg = inarg;
2094
2095 if (req->se->op.listxattr)
2096 req->se->op.listxattr(req, nodeid, arg->size);
2097 else
2098 fuse_reply_err(req, ENOSYS);
2099}
2100static void do_listxattr(fuse_req_t req, const fuse_ino_t nodeid,
2101 const void *inarg)
2102{
2103 _do_listxattr(req, nodeid, inarg, NULL);
2104}
2105
2106static void _do_removexattr(fuse_req_t req, const fuse_ino_t nodeid,
2107 const void *inarg, const void *in_payload)
2108{
2109 (void)inarg;
2110 const char *name = in_payload;
2111
2112 if (req->se->op.removexattr)
2113 req->se->op.removexattr(req, nodeid, name);
2114 else
2115 fuse_reply_err(req, ENOSYS);
2116}
2117static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2118{
2119 _do_removexattr(req, nodeid, NULL, inarg);
2120}
2121
2122static void convert_fuse_file_lock(const struct fuse_file_lock *fl,
2123 struct flock *flock)
2124{
2125 memset(flock, 0, sizeof(struct flock));
2126 flock->l_type = fl->type;
2127 flock->l_whence = SEEK_SET;
2128 flock->l_start = fl->start;
2129 if (fl->end == OFFSET_MAX)
2130 flock->l_len = 0;
2131 else
2132 flock->l_len = fl->end - fl->start + 1;
2133 flock->l_pid = fl->pid;
2134}
2135
2136static void _do_getlk(fuse_req_t req, const fuse_ino_t nodeid,
2137 const void *op_in, const void *in_payload)
2138{
2139 (void)in_payload;
2140 const struct fuse_lk_in *arg = op_in;
2141 struct fuse_file_info fi;
2142 struct flock flock;
2143
2144 memset(&fi, 0, sizeof(fi));
2145 fi.fh = arg->fh;
2146 fi.lock_owner = arg->owner;
2147
2148 convert_fuse_file_lock(&arg->lk, &flock);
2149 if (req->se->op.getlk)
2150 req->se->op.getlk(req, nodeid, &fi, &flock);
2151 else
2152 fuse_reply_err(req, ENOSYS);
2153}
2154static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2155{
2156 _do_getlk(req, nodeid, inarg, NULL);
2157}
2158
2159static void do_setlk_common(fuse_req_t req, const fuse_ino_t nodeid,
2160 const void *op_in, int sleep)
2161{
2162 const struct fuse_lk_in *arg = op_in;
2163 struct fuse_file_info fi;
2164 struct flock flock;
2165
2166 memset(&fi, 0, sizeof(fi));
2167 fi.fh = arg->fh;
2168 fi.lock_owner = arg->owner;
2169
2170 if (arg->lk_flags & FUSE_LK_FLOCK) {
2171 int op = 0;
2172
2173 switch (arg->lk.type) {
2174 case F_RDLCK:
2175 op = LOCK_SH;
2176 break;
2177 case F_WRLCK:
2178 op = LOCK_EX;
2179 break;
2180 case F_UNLCK:
2181 op = LOCK_UN;
2182 break;
2183 }
2184 if (!sleep)
2185 op |= LOCK_NB;
2186
2187 if (req->se->op.flock)
2188 req->se->op.flock(req, nodeid, &fi, op);
2189 else
2190 fuse_reply_err(req, ENOSYS);
2191 } else {
2192 convert_fuse_file_lock(&arg->lk, &flock);
2193 if (req->se->op.setlk)
2194 req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
2195 else
2196 fuse_reply_err(req, ENOSYS);
2197 }
2198}
2199
2200static void _do_setlk(fuse_req_t req, const fuse_ino_t nodeid,
2201 const void *op_in, const void *in_payload)
2202{
2203 (void)in_payload;
2204 do_setlk_common(req, nodeid, op_in, 0);
2205}
2206
2207static void do_setlk(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
2208{
2209 _do_setlk(req, nodeid, inarg, NULL);
2210}
2211
2212static void _do_setlkw(fuse_req_t req, const fuse_ino_t nodeid,
2213 const void *op_in, const void *in_payload)
2214{
2215 (void)in_payload;
2216 do_setlk_common(req, nodeid, op_in, 1);
2217}
2218static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2219{
2220 _do_setlkw(req, nodeid, inarg, NULL);
2221}
2222
2223static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
2224{
2225 struct fuse_req *curr;
2226
2227 for (curr = se->list.next; curr != &se->list; curr = curr->next) {
2228 if (curr->unique == req->u.i.unique) {
2230 void *data;
2231
2232 curr->ref_cnt++;
2233 pthread_mutex_unlock(&se->lock);
2234
2235 /* Ugh, ugly locking */
2236 pthread_mutex_lock(&curr->lock);
2237 pthread_mutex_lock(&se->lock);
2238 curr->interrupted = 1;
2239 func = curr->u.ni.func;
2240 data = curr->u.ni.data;
2241 pthread_mutex_unlock(&se->lock);
2242 if (func)
2243 func(curr, data);
2244 pthread_mutex_unlock(&curr->lock);
2245
2246 pthread_mutex_lock(&se->lock);
2247 curr->ref_cnt--;
2248 if (!curr->ref_cnt) {
2249 destroy_req(curr);
2250 }
2251
2252 return 1;
2253 }
2254 }
2255 for (curr = se->interrupts.next; curr != &se->interrupts;
2256 curr = curr->next) {
2257 if (curr->u.i.unique == req->u.i.unique)
2258 return 1;
2259 }
2260 return 0;
2261}
2262
2263static void _do_interrupt(fuse_req_t req, const fuse_ino_t nodeid,
2264 const void *op_in, const void *in_payload)
2265{
2266 (void)in_payload;
2267 const struct fuse_interrupt_in *arg = op_in;
2268 struct fuse_session *se = req->se;
2269
2270 (void) nodeid;
2271 if (se->debug)
2272 fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
2273 (unsigned long long) arg->unique);
2274
2275 req->u.i.unique = arg->unique;
2276
2277 pthread_mutex_lock(&se->lock);
2278 if (find_interrupted(se, req)) {
2279 fuse_chan_put(req->ch);
2280 req->ch = NULL;
2281 destroy_req(req);
2282 } else
2283 list_add_req(req, &se->interrupts);
2284 pthread_mutex_unlock(&se->lock);
2285}
2286static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2287{
2288 _do_interrupt(req, nodeid, inarg, NULL);
2289}
2290
2291static struct fuse_req *check_interrupt(struct fuse_session *se,
2292 struct fuse_req *req)
2293{
2294 struct fuse_req *curr;
2295
2296 for (curr = se->interrupts.next; curr != &se->interrupts;
2297 curr = curr->next) {
2298 if (curr->u.i.unique == req->unique) {
2299 req->interrupted = 1;
2300 list_del_req(curr);
2301 fuse_chan_put(curr->ch);
2302 curr->ch = NULL;
2303 destroy_req(curr);
2304 return NULL;
2305 }
2306 }
2307 curr = se->interrupts.next;
2308 if (curr != &se->interrupts) {
2309 list_del_req(curr);
2310 list_init_req(curr);
2311 return curr;
2312 } else
2313 return NULL;
2314}
2315
2316static void _do_bmap(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
2317 const void *in_payload)
2318{
2319 (void)in_payload;
2320 const struct fuse_bmap_in *arg = op_in;
2321
2322 if (req->se->op.bmap)
2323 req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
2324 else
2325 fuse_reply_err(req, ENOSYS);
2326}
2327static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2328{
2329 _do_bmap(req, nodeid, inarg, NULL);
2330}
2331
2332static void _do_ioctl(fuse_req_t req, const fuse_ino_t nodeid,
2333 const void *op_in, const void *in_payload)
2334{
2335 struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *)op_in;
2336 unsigned int flags = arg->flags;
2337 const void *in_buf = in_payload;
2338 struct fuse_file_info fi;
2339
2340 if (flags & FUSE_IOCTL_DIR &&
2341 !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
2342 fuse_reply_err(req, ENOTTY);
2343 return;
2344 }
2345
2346 memset(&fi, 0, sizeof(fi));
2347 fi.fh = arg->fh;
2348
2349 if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
2350 !(flags & FUSE_IOCTL_32BIT)) {
2351 req->flags.ioctl_64bit = 1;
2352 }
2353
2354 if (req->se->op.ioctl)
2355 req->se->op.ioctl(req, nodeid, arg->cmd,
2356 (void *)(uintptr_t)arg->arg, &fi, flags,
2357 in_buf, arg->in_size, arg->out_size);
2358 else
2359 fuse_reply_err(req, ENOSYS);
2360}
2361static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2362{
2363 const struct fuse_ioctl_in *arg = inarg;
2364 void *in_buf = arg->in_size ? PARAM(arg) : NULL;
2365
2366 _do_ioctl(req, nodeid, arg, in_buf);
2367}
2368
2369void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
2370{
2371 free(ph);
2372}
2373
2374static void _do_poll(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
2375 const void *in_payload)
2376{
2377 (void)in_payload;
2378 struct fuse_poll_in *arg = (struct fuse_poll_in *)op_in;
2379 struct fuse_file_info fi;
2380
2381 memset(&fi, 0, sizeof(fi));
2382 fi.fh = arg->fh;
2383 fi.poll_events = arg->events;
2384
2385 if (req->se->op.poll) {
2386 struct fuse_pollhandle *ph = NULL;
2387
2388 if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
2389 ph = malloc(sizeof(struct fuse_pollhandle));
2390 if (ph == NULL) {
2391 fuse_reply_err(req, ENOMEM);
2392 return;
2393 }
2394 ph->kh = arg->kh;
2395 ph->se = req->se;
2396 }
2397
2398 req->se->op.poll(req, nodeid, &fi, ph);
2399 } else {
2400 fuse_reply_err(req, ENOSYS);
2401 }
2402}
2403
2404static void do_poll(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
2405{
2406 _do_poll(req, nodeid, inarg, NULL);
2407}
2408
2409static void _do_fallocate(fuse_req_t req, const fuse_ino_t nodeid,
2410 const void *op_in, const void *in_payload)
2411{
2412 (void)in_payload;
2413 const struct fuse_fallocate_in *arg = op_in;
2414 struct fuse_file_info fi;
2415
2416 memset(&fi, 0, sizeof(fi));
2417 fi.fh = arg->fh;
2418
2419 if (req->se->op.fallocate)
2420 req->se->op.fallocate(req, nodeid, arg->mode, arg->offset,
2421 arg->length, &fi);
2422 else
2423 fuse_reply_err(req, ENOSYS);
2424}
2425
2426static void do_fallocate(fuse_req_t req, const fuse_ino_t nodeid,
2427 const void *inarg)
2428{
2429 _do_fallocate(req, nodeid, inarg, NULL);
2430}
2431
2432static void copy_file_range_common(fuse_req_t req, const fuse_ino_t nodeid_in,
2433 const struct fuse_copy_file_range_in *arg)
2434{
2435 struct fuse_file_info fi_in, fi_out;
2436
2437 memset(&fi_in, 0, sizeof(fi_in));
2438 fi_in.fh = arg->fh_in;
2439
2440 memset(&fi_out, 0, sizeof(fi_out));
2441 fi_out.fh = arg->fh_out;
2442
2443 if (req->se->op.copy_file_range)
2444 req->se->op.copy_file_range(req, nodeid_in, arg->off_in, &fi_in,
2445 arg->nodeid_out, arg->off_out,
2446 &fi_out, arg->len, arg->flags);
2447 else
2448 fuse_reply_err(req, ENOSYS);
2449}
2450
2451static void _do_copy_file_range(fuse_req_t req, const fuse_ino_t nodeid_in,
2452 const void *op_in, const void *in_payload)
2453{
2454 const struct fuse_copy_file_range_in *arg = op_in;
2455 struct fuse_copy_file_range_in arg_tmp;
2456
2457 (void) in_payload;
2458 /* fuse_write_out can only handle 32bit copy size */
2459 if (arg->len > 0xfffff000) {
2460 arg_tmp = *arg;
2461 arg_tmp.len = 0xfffff000;
2462 arg = &arg_tmp;
2463 }
2464 copy_file_range_common(req, nodeid_in, arg);
2465}
2466
2467static void do_copy_file_range(fuse_req_t req, const fuse_ino_t nodeid_in,
2468 const void *inarg)
2469{
2470 _do_copy_file_range(req, nodeid_in, inarg, NULL);
2471}
2472
2473static void _do_copy_file_range_64(fuse_req_t req, const fuse_ino_t nodeid_in,
2474 const void *op_in, const void *in_payload)
2475{
2476 (void) in_payload;
2477 req->flags.is_copy_file_range_64 = 1;
2478 /* Limit size on 32bit userspace to avoid conversion overflow */
2479 if (sizeof(size_t) == 4)
2480 _do_copy_file_range(req, nodeid_in, op_in, NULL);
2481 else
2482 copy_file_range_common(req, nodeid_in, op_in);
2483}
2484
2485static void do_copy_file_range_64(fuse_req_t req, const fuse_ino_t nodeid_in,
2486 const void *inarg)
2487{
2488 _do_copy_file_range_64(req, nodeid_in, inarg, NULL);
2489}
2490
2491/*
2492 * Note that the uint64_t offset in struct fuse_lseek_in is derived from
2493 * linux kernel loff_t and is therefore signed.
2494 */
2495static void _do_lseek(fuse_req_t req, const fuse_ino_t nodeid,
2496 const void *op_in, const void *in_payload)
2497{
2498 (void)in_payload;
2499 const struct fuse_lseek_in *arg = op_in;
2500 struct fuse_file_info fi;
2501
2502 memset(&fi, 0, sizeof(fi));
2503 fi.fh = arg->fh;
2504
2505 if (req->se->op.lseek)
2506 req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
2507 else
2508 fuse_reply_err(req, ENOSYS);
2509}
2510
2511static void do_lseek(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg)
2512{
2513 _do_lseek(req, nodeid, inarg, NULL);
2514}
2515
2516#ifdef HAVE_STATX
2517static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid,
2518 const void *op_in, const void *in_payload)
2519{
2520 (void)in_payload;
2521 const struct fuse_statx_in *arg = op_in;
2522 struct fuse_file_info *fip = NULL;
2523 struct fuse_file_info fi;
2524
2525 if (arg->getattr_flags & FUSE_GETATTR_FH) {
2526 memset(&fi, 0, sizeof(fi));
2527 fi.fh = arg->fh;
2528 fip = &fi;
2529 }
2530
2531 if (req->se->op.statx)
2532 req->se->op.statx(req, nodeid, arg->sx_flags, arg->sx_mask, fip);
2533 else
2534 fuse_reply_err(req, ENOSYS);
2535}
2536#else
2537static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid,
2538 const void *op_in, const void *in_payload)
2539{
2540 (void)in_payload;
2541 (void)req;
2542 (void)nodeid;
2543 (void)op_in;
2544}
2545#endif
2546
2547static void do_statx(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2548{
2549 _do_statx(req, nodeid, inarg, NULL);
2550}
2551
2552static bool want_flags_valid(uint64_t capable, uint64_t want)
2553{
2554 uint64_t unknown_flags = want & (~capable);
2555 if (unknown_flags != 0) {
2556 fuse_log(FUSE_LOG_ERR,
2557 "fuse: unknown connection 'want' flags: 0x%08llx\n",
2558 (unsigned long long)unknown_flags);
2559 return false;
2560 }
2561 return true;
2562}
2563
2568{
2569 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2570
2571 /*
2572 * Convert want to want_ext if necessary.
2573 * For the high level interface this function might be called
2574 * twice, once from the high level interface and once from the
2575 * low level interface. Both, with different want_ext_default and
2576 * want_default values. In order to suppress a failure for the
2577 * second call, we check if the lower 32 bits of want_ext are
2578 * already set to the value of want.
2579 */
2580 if (conn->want != se->conn_want &&
2581 fuse_lower_32_bits(conn->want_ext) != conn->want) {
2582 if (conn->want_ext != se->conn_want_ext) {
2583 fuse_log(FUSE_LOG_ERR,
2584 "%s: Both conn->want_ext and conn->want are set.\n"
2585 "want=%x want_ext=%llx, se->want=%x se->want_ext=%llx\n",
2586 __func__, conn->want,
2587 (unsigned long long)conn->want_ext,
2588 se->conn_want,
2589 (unsigned long long)se->conn_want_ext);
2590 return -EINVAL;
2591 }
2592
2593 /* high bits from want_ext, low bits from want */
2594 conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
2595 conn->want;
2596 }
2597
2598 /* ensure there won't be a second conversion */
2599 conn->want = fuse_lower_32_bits(conn->want_ext);
2600
2601 return 0;
2602}
2603
2604bool fuse_set_feature_flag(struct fuse_conn_info *conn,
2605 uint64_t flag)
2606{
2607 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2608
2609 if (conn->capable_ext & flag) {
2610 conn->want_ext |= flag;
2611 se->conn_want_ext |= flag;
2612 conn->want |= flag;
2613 se->conn_want |= flag;
2614 return true;
2615 }
2616 return false;
2617}
2618
2619void fuse_unset_feature_flag(struct fuse_conn_info *conn,
2620 uint64_t flag)
2621{
2622 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
2623
2624 conn->want_ext &= ~flag;
2625 se->conn_want_ext &= ~flag;
2626 conn->want &= ~flag;
2627 se->conn_want &= ~flag;
2628}
2629
2630bool fuse_get_feature_flag(struct fuse_conn_info *conn,
2631 uint64_t flag)
2632{
2633 return conn->capable_ext & flag ? true : false;
2634}
2635
2636/* Prevent bogus data races (bogus since "init" is called before
2637 * multi-threading becomes relevant */
2638static __attribute__((no_sanitize("thread"))) void
2639_do_init(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
2640 const void *in_payload)
2641{
2642 (void)in_payload;
2643 const struct fuse_init_in *arg = op_in;
2644 struct fuse_init_out outarg;
2645 struct fuse_session *se = req->se;
2646 size_t bufsize = se->bufsize;
2647 size_t outargsize = sizeof(outarg);
2648 uint64_t inargflags = 0;
2649 uint64_t outargflags = 0;
2650 bool buf_reallocable = se->buf_reallocable;
2651 (void) nodeid;
2652 bool enable_io_uring = false;
2653
2654 if (se->debug) {
2655 fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
2656 if (arg->major == 7 && arg->minor >= 6) {
2657 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
2658 fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
2659 arg->max_readahead);
2660 }
2661 }
2662 se->conn.proto_major = arg->major;
2663 se->conn.proto_minor = arg->minor;
2664 se->conn.capable_ext = 0;
2665 se->conn.want_ext = 0;
2666
2667 memset(&outarg, 0, sizeof(outarg));
2668 outarg.major = FUSE_KERNEL_VERSION;
2669 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
2670
2671 if (arg->major < 7) {
2672 fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
2673 arg->major, arg->minor);
2674 fuse_reply_err(req, EPROTO);
2675 return;
2676 }
2677
2678 if (arg->major > 7) {
2679 /* Wait for a second INIT request with a 7.X version */
2680 send_reply_ok(req, &outarg, sizeof(outarg));
2681 return;
2682 }
2683
2684 if (arg->minor >= 6) {
2685 if (arg->max_readahead < se->conn.max_readahead)
2686 se->conn.max_readahead = arg->max_readahead;
2687 inargflags = arg->flags;
2688 if (inargflags & FUSE_INIT_EXT)
2689 inargflags = inargflags | (uint64_t) arg->flags2 << 32;
2690 if (inargflags & FUSE_ASYNC_READ)
2691 se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
2692 if (inargflags & FUSE_POSIX_LOCKS)
2693 se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
2694 if (inargflags & FUSE_ATOMIC_O_TRUNC)
2695 se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
2696 if (inargflags & FUSE_EXPORT_SUPPORT)
2697 se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
2698 if (inargflags & FUSE_DONT_MASK)
2699 se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
2700 if (inargflags & FUSE_FLOCK_LOCKS)
2701 se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
2702 if (inargflags & FUSE_AUTO_INVAL_DATA)
2703 se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
2704 if (inargflags & FUSE_DO_READDIRPLUS)
2705 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
2706 if (inargflags & FUSE_READDIRPLUS_AUTO)
2707 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
2708 if (inargflags & FUSE_ASYNC_DIO)
2709 se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
2710 if (inargflags & FUSE_WRITEBACK_CACHE)
2711 se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
2712 if (inargflags & FUSE_NO_OPEN_SUPPORT)
2713 se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
2714 if (inargflags & FUSE_PARALLEL_DIROPS)
2715 se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
2716 if (inargflags & FUSE_POSIX_ACL)
2717 se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
2718 if (inargflags & FUSE_HANDLE_KILLPRIV)
2719 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
2720 if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
2721 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
2722 if (inargflags & FUSE_CACHE_SYMLINKS)
2723 se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
2724 if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
2725 se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
2726 if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
2727 se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
2728 if (inargflags & FUSE_SETXATTR_EXT)
2729 se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
2730 if (!(inargflags & FUSE_MAX_PAGES)) {
2731 size_t max_bufsize =
2732 FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
2733 + FUSE_BUFFER_HEADER_SIZE;
2734 if (bufsize > max_bufsize) {
2735 bufsize = max_bufsize;
2736 }
2737 buf_reallocable = false;
2738 }
2739 if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
2740 se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
2741 if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
2742 se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
2743 if (inargflags & FUSE_PASSTHROUGH)
2744 se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
2745 if (inargflags & FUSE_NO_EXPORT_SUPPORT)
2746 se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
2747 if (inargflags & FUSE_OVER_IO_URING)
2748 se->conn.capable_ext |= FUSE_CAP_OVER_IO_URING;
2749
2750 } else {
2751 se->conn.max_readahead = 0;
2752 }
2753
2754 if (se->conn.proto_minor >= 14) {
2755#ifdef HAVE_SPLICE
2756#ifdef HAVE_VMSPLICE
2757 if ((se->io == NULL) || (se->io->splice_send != NULL)) {
2758 se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
2760 }
2761#endif
2762 if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
2763 se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
2764 }
2765#endif
2766 }
2767 if (se->conn.proto_minor >= 18)
2768 se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
2769
2770 /* Default settings for modern filesystems.
2771 *
2772 * Most of these capabilities were disabled by default in
2773 * libfuse2 for backwards compatibility reasons. In libfuse3,
2774 * we can finally enable them by default (as long as they're
2775 * supported by the kernel).
2776 */
2777#define LL_SET_DEFAULT(cond, cap) \
2778 if ((cond)) \
2779 fuse_set_feature_flag(&se->conn, cap)
2780
2781 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
2782 LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
2783 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
2784 LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
2785 LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
2786 LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
2787 LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
2789 LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
2790 LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
2791 LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
2793 LL_SET_DEFAULT(1, FUSE_CAP_OVER_IO_URING);
2794
2795 /* This could safely become default, but libfuse needs an API extension
2796 * to support it
2797 * LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
2798 */
2799
2800 se->conn.time_gran = 1;
2801
2802 if (se->op.init) {
2803 // Apply the first 32 bits of capable_ext to capable
2804 se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
2805
2806 se->op.init(se->userdata, &se->conn);
2807
2808 /*
2809 * se->conn.want is 32-bit value and deprecated in favour of
2810 * se->conn.want_ext
2811 * Userspace might still use conn.want - we need to convert it
2812 */
2814 }
2815
2816 if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
2817 fuse_reply_err(req, EPROTO);
2818 se->error = -EPROTO;
2820 return;
2821 }
2822
2823 unsigned max_read_mo = get_max_read(se->mo);
2824 if (se->conn.max_read != max_read_mo) {
2825 fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
2826 "requested different maximum read size (%u vs %u)\n",
2827 se->conn.max_read, max_read_mo);
2828 fuse_reply_err(req, EPROTO);
2829 se->error = -EPROTO;
2831 return;
2832 }
2833
2834 if (bufsize < FUSE_MIN_READ_BUFFER) {
2835 fuse_log(FUSE_LOG_ERR,
2836 "fuse: warning: buffer size too small: %zu\n",
2837 bufsize);
2838 bufsize = FUSE_MIN_READ_BUFFER;
2839 }
2840
2841 if (buf_reallocable)
2842 bufsize = UINT_MAX;
2843 se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE);
2844 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
2845
2846 if (arg->flags & FUSE_MAX_PAGES) {
2847 outarg.flags |= FUSE_MAX_PAGES;
2848 outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
2849 }
2850 outargflags = outarg.flags;
2851 /* Always enable big writes, this is superseded
2852 by the max_write option */
2853 outargflags |= FUSE_BIG_WRITES;
2854
2855 if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
2856 outargflags |= FUSE_ASYNC_READ;
2857 if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
2858 outargflags |= FUSE_POSIX_LOCKS;
2859 if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
2860 outargflags |= FUSE_ATOMIC_O_TRUNC;
2861 if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
2862 outargflags |= FUSE_EXPORT_SUPPORT;
2863 if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
2864 outargflags |= FUSE_DONT_MASK;
2865 if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
2866 outargflags |= FUSE_FLOCK_LOCKS;
2867 if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
2868 outargflags |= FUSE_AUTO_INVAL_DATA;
2869 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
2870 outargflags |= FUSE_DO_READDIRPLUS;
2871 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
2872 outargflags |= FUSE_READDIRPLUS_AUTO;
2873 if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
2874 outargflags |= FUSE_ASYNC_DIO;
2875 if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
2876 outargflags |= FUSE_WRITEBACK_CACHE;
2877 if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
2878 outargflags |= FUSE_PARALLEL_DIROPS;
2879 if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
2880 outargflags |= FUSE_POSIX_ACL;
2881 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
2882 outargflags |= FUSE_HANDLE_KILLPRIV;
2883 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
2884 outargflags |= FUSE_HANDLE_KILLPRIV_V2;
2885 if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
2886 outargflags |= FUSE_CACHE_SYMLINKS;
2887 if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
2888 outargflags |= FUSE_EXPLICIT_INVAL_DATA;
2889 if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
2890 outargflags |= FUSE_SETXATTR_EXT;
2891 if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
2892 outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
2893 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
2894 outargflags |= FUSE_PASSTHROUGH;
2895 /*
2896 * outarg.max_stack_depth includes the fuse stack layer,
2897 * so it is one more than max_backing_stack_depth.
2898 */
2899 outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
2900 }
2901 if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
2902 outargflags |= FUSE_NO_EXPORT_SUPPORT;
2903 if (se->uring.enable && se->conn.want_ext & FUSE_CAP_OVER_IO_URING) {
2904 outargflags |= FUSE_OVER_IO_URING;
2905 enable_io_uring = true;
2906 }
2907
2908 if ((inargflags & FUSE_REQUEST_TIMEOUT) && se->conn.request_timeout) {
2909 outargflags |= FUSE_REQUEST_TIMEOUT;
2910 outarg.request_timeout = se->conn.request_timeout;
2911 }
2912
2913 outarg.max_readahead = se->conn.max_readahead;
2914 outarg.max_write = se->conn.max_write;
2915 if (se->conn.proto_minor >= 13) {
2916 if (se->conn.max_background >= (1 << 16))
2917 se->conn.max_background = (1 << 16) - 1;
2918 if (se->conn.congestion_threshold > se->conn.max_background)
2919 se->conn.congestion_threshold = se->conn.max_background;
2920 if (!se->conn.congestion_threshold) {
2921 se->conn.congestion_threshold =
2922 se->conn.max_background * 3 / 4;
2923 }
2924
2925 outarg.max_background = se->conn.max_background;
2926 outarg.congestion_threshold = se->conn.congestion_threshold;
2927 }
2928 if (se->conn.proto_minor >= 23)
2929 outarg.time_gran = se->conn.time_gran;
2930
2931 if (se->debug) {
2932 fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
2933 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
2934 fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
2935 outarg.max_readahead);
2936 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
2937 fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
2938 outarg.max_background);
2939 fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
2940 outarg.congestion_threshold);
2941 fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
2942 outarg.time_gran);
2943 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
2944 fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
2945 outarg.max_stack_depth);
2946 }
2947 if (arg->minor < 5)
2948 outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
2949 else if (arg->minor < 23)
2950 outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
2951
2952 /* XXX: Add an option to make non-available io-uring fatal */
2953 if (enable_io_uring) {
2954 int ring_rc = fuse_uring_start(se);
2955
2956 if (ring_rc != 0) {
2957 fuse_log(FUSE_LOG_INFO,
2958 "fuse: failed to start io-uring: %s\n",
2959 strerror(ring_rc));
2960 outargflags &= ~FUSE_OVER_IO_URING;
2961 enable_io_uring = false;
2962 }
2963 }
2964
2965 if (inargflags & FUSE_INIT_EXT) {
2966 outargflags |= FUSE_INIT_EXT;
2967 outarg.flags2 = outargflags >> 32;
2968 }
2969 outarg.flags = outargflags;
2970
2971 /*
2972 * Has to be set before replying, as new kernel requests might
2973 * immediately arrive and got_init is used for op-code sanity.
2974 * Especially with external handlers, where we have no control
2975 * over the thread scheduling.
2976 */
2977 se->got_init = 1;
2978 send_reply_ok(req, &outarg, outargsize);
2979 if (enable_io_uring)
2980 fuse_uring_wake_ring_threads(se);
2981}
2982
2983static __attribute__((no_sanitize("thread"))) void
2984do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2985{
2986 _do_init(req, nodeid, inarg, NULL);
2987}
2988
2989static void _do_destroy(fuse_req_t req, const fuse_ino_t nodeid,
2990 const void *op_in, const void *in_payload)
2991{
2992 struct fuse_session *se = req->se;
2993 char *mountpoint;
2994
2995 (void) nodeid;
2996 (void)op_in;
2997 (void)in_payload;
2998
2999 mountpoint = atomic_exchange(&se->mountpoint, NULL);
3000 free(mountpoint);
3001
3002 se->got_destroy = 1;
3003 se->got_init = 0;
3004 if (se->op.destroy)
3005 se->op.destroy(se->userdata);
3006
3007 send_reply_ok(req, NULL, 0);
3008}
3009
3010static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
3011{
3012 _do_destroy(req, nodeid, inarg, NULL);
3013}
3014
3015static void list_del_nreq(struct fuse_notify_req *nreq)
3016{
3017 struct fuse_notify_req *prev = nreq->prev;
3018 struct fuse_notify_req *next = nreq->next;
3019 prev->next = next;
3020 next->prev = prev;
3021}
3022
3023static void list_add_nreq(struct fuse_notify_req *nreq,
3024 struct fuse_notify_req *next)
3025{
3026 struct fuse_notify_req *prev = next->prev;
3027 nreq->next = next;
3028 nreq->prev = prev;
3029 prev->next = nreq;
3030 next->prev = nreq;
3031}
3032
3033static void list_init_nreq(struct fuse_notify_req *nreq)
3034{
3035 nreq->next = nreq;
3036 nreq->prev = nreq;
3037}
3038
3039static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
3040 const void *inarg, const struct fuse_buf *buf)
3041{
3042 struct fuse_session *se = req->se;
3043 struct fuse_notify_req *nreq;
3044 struct fuse_notify_req *head;
3045
3046 pthread_mutex_lock(&se->lock);
3047 head = &se->notify_list;
3048 for (nreq = head->next; nreq != head; nreq = nreq->next) {
3049 if (nreq->unique == req->unique) {
3050 list_del_nreq(nreq);
3051 break;
3052 }
3053 }
3054 pthread_mutex_unlock(&se->lock);
3055
3056 if (nreq != head)
3057 nreq->reply(nreq, req, nodeid, inarg, buf);
3058}
3059
3060static int send_notify_iov(struct fuse_session *se, int notify_code,
3061 struct iovec *iov, int count)
3062{
3063 struct fuse_out_header out;
3064 struct fuse_req *req = NULL;
3065
3066 if (!se->got_init)
3067 return -ENOTCONN;
3068
3069 out.unique = 0;
3070 out.error = notify_code;
3071 iov[0].iov_base = &out;
3072 iov[0].iov_len = sizeof(struct fuse_out_header);
3073
3074 return fuse_send_msg(se, NULL, iov, count, req);
3075}
3076
3077int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
3078{
3079 if (ph != NULL) {
3080 struct fuse_notify_poll_wakeup_out outarg;
3081 struct iovec iov[2];
3082
3083 outarg.kh = ph->kh;
3084
3085 iov[1].iov_base = &outarg;
3086 iov[1].iov_len = sizeof(outarg);
3087
3088 return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
3089 } else {
3090 return 0;
3091 }
3092}
3093
3094int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
3095 off_t off, off_t len)
3096{
3097 struct fuse_notify_inval_inode_out outarg;
3098 struct iovec iov[2];
3099
3100 if (!se)
3101 return -EINVAL;
3102
3103 if (se->conn.proto_minor < 12)
3104 return -ENOSYS;
3105
3106 outarg.ino = ino;
3107 outarg.off = off;
3108 outarg.len = len;
3109
3110 iov[1].iov_base = &outarg;
3111 iov[1].iov_len = sizeof(outarg);
3112
3113 return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
3114}
3115
3116int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
3117{
3118 struct iovec iov[1];
3119
3120 if (!se)
3121 return -EINVAL;
3122
3123 if (se->conn.proto_minor < 44)
3124 return -ENOSYS;
3125
3126 return send_notify_iov(se, FUSE_NOTIFY_INC_EPOCH, iov, 1);
3127}
3128
3148static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
3149 const char *name, size_t namelen,
3150 enum fuse_notify_entry_flags flags)
3151{
3152 struct fuse_notify_inval_entry_out outarg;
3153 struct iovec iov[3];
3154
3155 if (!se)
3156 return -EINVAL;
3157
3158 if (se->conn.proto_minor < 12)
3159 return -ENOSYS;
3160
3161 outarg.parent = parent;
3162 outarg.namelen = namelen;
3163 outarg.flags = 0;
3164 if (flags & FUSE_LL_EXPIRE_ONLY)
3165 outarg.flags |= FUSE_EXPIRE_ONLY;
3166
3167 iov[1].iov_base = &outarg;
3168 iov[1].iov_len = sizeof(outarg);
3169 iov[2].iov_base = (void *)name;
3170 iov[2].iov_len = namelen + 1;
3171
3172 return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
3173}
3174
3175int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
3176 const char *name, size_t namelen)
3177{
3178 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
3179}
3180
3181int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
3182 const char *name, size_t namelen)
3183{
3184 if (!se)
3185 return -EINVAL;
3186
3187 if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
3188 return -ENOSYS;
3189
3190 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
3191}
3192
3193
3194int fuse_lowlevel_notify_delete(struct fuse_session *se,
3195 fuse_ino_t parent, fuse_ino_t child,
3196 const char *name, size_t namelen)
3197{
3198 struct fuse_notify_delete_out outarg;
3199 struct iovec iov[3];
3200
3201 if (!se)
3202 return -EINVAL;
3203
3204 if (se->conn.proto_minor < 18)
3205 return -ENOSYS;
3206
3207 outarg.parent = parent;
3208 outarg.child = child;
3209 outarg.namelen = namelen;
3210 outarg.padding = 0;
3211
3212 iov[1].iov_base = &outarg;
3213 iov[1].iov_len = sizeof(outarg);
3214 iov[2].iov_base = (void *)name;
3215 iov[2].iov_len = namelen + 1;
3216
3217 return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
3218}
3219
3220int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
3221 off_t offset, struct fuse_bufvec *bufv,
3222 enum fuse_buf_copy_flags flags)
3223{
3224 struct fuse_out_header out;
3225 struct fuse_notify_store_out outarg;
3226 struct iovec iov[3];
3227 size_t size = fuse_buf_size(bufv);
3228 int res;
3229 struct fuse_req *req = NULL;
3230
3231 if (!se)
3232 return -EINVAL;
3233
3234 if (se->conn.proto_minor < 15)
3235 return -ENOSYS;
3236
3237 out.unique = 0;
3238 out.error = FUSE_NOTIFY_STORE;
3239
3240 outarg.nodeid = ino;
3241 outarg.offset = offset;
3242 outarg.size = size;
3243 outarg.padding = 0;
3244
3245 iov[0].iov_base = &out;
3246 iov[0].iov_len = sizeof(out);
3247 iov[1].iov_base = &outarg;
3248 iov[1].iov_len = sizeof(outarg);
3249
3250 res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags, req);
3251 if (res > 0)
3252 res = -res;
3253
3254 return res;
3255}
3256
3257struct fuse_retrieve_req {
3258 struct fuse_notify_req nreq;
3259 void *cookie;
3260};
3261
3262static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
3263 fuse_req_t req, fuse_ino_t ino,
3264 const void *inarg,
3265 const struct fuse_buf *ibuf)
3266{
3267 struct fuse_session *se = req->se;
3268 struct fuse_retrieve_req *rreq =
3269 container_of(nreq, struct fuse_retrieve_req, nreq);
3270 const struct fuse_notify_retrieve_in *arg = inarg;
3271 struct fuse_bufvec bufv = {
3272 .buf[0] = *ibuf,
3273 .count = 1,
3274 };
3275
3276 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
3277 bufv.buf[0].mem = PARAM(arg);
3278
3279 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
3280 sizeof(struct fuse_notify_retrieve_in);
3281
3282 if (bufv.buf[0].size < arg->size) {
3283 fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
3284 fuse_reply_none(req);
3285 goto out;
3286 }
3287 bufv.buf[0].size = arg->size;
3288
3289 if (se->op.retrieve_reply) {
3290 se->op.retrieve_reply(req, rreq->cookie, ino,
3291 arg->offset, &bufv);
3292 } else {
3293 fuse_reply_none(req);
3294 }
3295out:
3296 free(rreq);
3297 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
3298 fuse_ll_clear_pipe(se);
3299}
3300
3301int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
3302 size_t size, off_t offset, void *cookie)
3303{
3304 struct fuse_notify_retrieve_out outarg;
3305 struct iovec iov[2];
3306 struct fuse_retrieve_req *rreq;
3307 int err;
3308
3309 if (!se)
3310 return -EINVAL;
3311
3312 if (se->conn.proto_minor < 15)
3313 return -ENOSYS;
3314
3315 rreq = malloc(sizeof(*rreq));
3316 if (rreq == NULL)
3317 return -ENOMEM;
3318
3319 pthread_mutex_lock(&se->lock);
3320 rreq->cookie = cookie;
3321 rreq->nreq.unique = se->notify_ctr++;
3322 rreq->nreq.reply = fuse_ll_retrieve_reply;
3323 list_add_nreq(&rreq->nreq, &se->notify_list);
3324 pthread_mutex_unlock(&se->lock);
3325
3326 outarg.notify_unique = rreq->nreq.unique;
3327 outarg.nodeid = ino;
3328 outarg.offset = offset;
3329 outarg.size = size;
3330 outarg.padding = 0;
3331
3332 iov[1].iov_base = &outarg;
3333 iov[1].iov_len = sizeof(outarg);
3334
3335 err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
3336 if (err) {
3337 pthread_mutex_lock(&se->lock);
3338 list_del_nreq(&rreq->nreq);
3339 pthread_mutex_unlock(&se->lock);
3340 free(rreq);
3341 }
3342
3343 return err;
3344}
3345
3347{
3348 return req->se->userdata;
3349}
3350
3351const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
3352{
3353 return &req->ctx;
3354}
3355
3357 void *data)
3358{
3359 pthread_mutex_lock(&req->lock);
3360 pthread_mutex_lock(&req->se->lock);
3361 req->u.ni.func = func;
3362 req->u.ni.data = data;
3363 pthread_mutex_unlock(&req->se->lock);
3364 if (req->interrupted && func)
3365 func(req, data);
3366 pthread_mutex_unlock(&req->lock);
3367}
3368
3370{
3371 int interrupted;
3372
3373 pthread_mutex_lock(&req->se->lock);
3374 interrupted = req->interrupted;
3375 pthread_mutex_unlock(&req->se->lock);
3376
3377 return interrupted;
3378}
3379
3381{
3382 return req->flags.is_uring;
3383}
3384
3385#ifndef HAVE_URING
3386int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
3387 void **mr)
3388{
3389 (void)req;
3390 (void)payload;
3391 (void)payload_sz;
3392 (void)mr;
3393 return -ENOTSUP;
3394}
3395#endif
3396
3397static struct {
3398 void (*func)(fuse_req_t req, const fuse_ino_t node, const void *arg);
3399 const char *name;
3400} fuse_ll_ops[] = {
3401 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
3402 [FUSE_FORGET] = { do_forget, "FORGET" },
3403 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
3404 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
3405 [FUSE_READLINK] = { do_readlink, "READLINK" },
3406 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
3407 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
3408 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
3409 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
3410 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
3411 [FUSE_RENAME] = { do_rename, "RENAME" },
3412 [FUSE_LINK] = { do_link, "LINK" },
3413 [FUSE_OPEN] = { do_open, "OPEN" },
3414 [FUSE_READ] = { do_read, "READ" },
3415 [FUSE_WRITE] = { do_write, "WRITE" },
3416 [FUSE_STATFS] = { do_statfs, "STATFS" },
3417 [FUSE_RELEASE] = { do_release, "RELEASE" },
3418 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
3419 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
3420 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
3421 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
3422 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
3423 [FUSE_FLUSH] = { do_flush, "FLUSH" },
3424 [FUSE_INIT] = { do_init, "INIT" },
3425 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
3426 [FUSE_READDIR] = { do_readdir, "READDIR" },
3427 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
3428 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
3429 [FUSE_GETLK] = { do_getlk, "GETLK" },
3430 [FUSE_SETLK] = { do_setlk, "SETLK" },
3431 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
3432 [FUSE_ACCESS] = { do_access, "ACCESS" },
3433 [FUSE_CREATE] = { do_create, "CREATE" },
3434 [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
3435 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
3436 [FUSE_BMAP] = { do_bmap, "BMAP" },
3437 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
3438 [FUSE_POLL] = { do_poll, "POLL" },
3439 [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
3440 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
3441 [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
3442 [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
3443 [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
3444 [FUSE_RENAME2] = { do_rename2, "RENAME2" },
3445 [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
3446 [FUSE_COPY_FILE_RANGE_64] = { do_copy_file_range_64, "COPY_FILE_RANGE_64" },
3447 [FUSE_LSEEK] = { do_lseek, "LSEEK" },
3448 [FUSE_STATX] = { do_statx, "STATX" },
3449 [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
3450};
3451
3452static struct {
3453 void (*func)(fuse_req_t req, const fuse_ino_t ino, const void *op_in,
3454 const void *op_payload);
3455 const char *name;
3456} fuse_ll_ops2[] __attribute__((unused)) = {
3457 [FUSE_LOOKUP] = { _do_lookup, "LOOKUP" },
3458 [FUSE_FORGET] = { _do_forget, "FORGET" },
3459 [FUSE_GETATTR] = { _do_getattr, "GETATTR" },
3460 [FUSE_SETATTR] = { _do_setattr, "SETATTR" },
3461 [FUSE_READLINK] = { _do_readlink, "READLINK" },
3462 [FUSE_SYMLINK] = { _do_symlink, "SYMLINK" },
3463 [FUSE_MKNOD] = { _do_mknod, "MKNOD" },
3464 [FUSE_MKDIR] = { _do_mkdir, "MKDIR" },
3465 [FUSE_UNLINK] = { _do_unlink, "UNLINK" },
3466 [FUSE_RMDIR] = { _do_rmdir, "RMDIR" },
3467 [FUSE_RENAME] = { _do_rename, "RENAME" },
3468 [FUSE_LINK] = { _do_link, "LINK" },
3469 [FUSE_OPEN] = { _do_open, "OPEN" },
3470 [FUSE_READ] = { _do_read, "READ" },
3471 [FUSE_WRITE] = { _do_write, "WRITE" },
3472 [FUSE_STATFS] = { _do_statfs, "STATFS" },
3473 [FUSE_RELEASE] = { _do_release, "RELEASE" },
3474 [FUSE_FSYNC] = { _do_fsync, "FSYNC" },
3475 [FUSE_SETXATTR] = { _do_setxattr, "SETXATTR" },
3476 [FUSE_GETXATTR] = { _do_getxattr, "GETXATTR" },
3477 [FUSE_LISTXATTR] = { _do_listxattr, "LISTXATTR" },
3478 [FUSE_REMOVEXATTR] = { _do_removexattr, "REMOVEXATTR" },
3479 [FUSE_FLUSH] = { _do_flush, "FLUSH" },
3480 [FUSE_INIT] = { _do_init, "INIT" },
3481 [FUSE_OPENDIR] = { _do_opendir, "OPENDIR" },
3482 [FUSE_READDIR] = { _do_readdir, "READDIR" },
3483 [FUSE_RELEASEDIR] = { _do_releasedir, "RELEASEDIR" },
3484 [FUSE_FSYNCDIR] = { _do_fsyncdir, "FSYNCDIR" },
3485 [FUSE_GETLK] = { _do_getlk, "GETLK" },
3486 [FUSE_SETLK] = { _do_setlk, "SETLK" },
3487 [FUSE_SETLKW] = { _do_setlkw, "SETLKW" },
3488 [FUSE_ACCESS] = { _do_access, "ACCESS" },
3489 [FUSE_CREATE] = { _do_create, "CREATE" },
3490 [FUSE_TMPFILE] = { _do_tmpfile, "TMPFILE" },
3491 [FUSE_INTERRUPT] = { _do_interrupt, "INTERRUPT" },
3492 [FUSE_BMAP] = { _do_bmap, "BMAP" },
3493 [FUSE_IOCTL] = { _do_ioctl, "IOCTL" },
3494 [FUSE_POLL] = { _do_poll, "POLL" },
3495 [FUSE_FALLOCATE] = { _do_fallocate, "FALLOCATE" },
3496 [FUSE_DESTROY] = { _do_destroy, "DESTROY" },
3497 [FUSE_NOTIFY_REPLY] = { (void *)1, "NOTIFY_REPLY" },
3498 [FUSE_BATCH_FORGET] = { _do_batch_forget, "BATCH_FORGET" },
3499 [FUSE_READDIRPLUS] = { _do_readdirplus, "READDIRPLUS" },
3500 [FUSE_RENAME2] = { _do_rename2, "RENAME2" },
3501 [FUSE_COPY_FILE_RANGE] = { _do_copy_file_range, "COPY_FILE_RANGE" },
3502 [FUSE_COPY_FILE_RANGE_64] = { _do_copy_file_range_64, "COPY_FILE_RANGE_64" },
3503 [FUSE_LSEEK] = { _do_lseek, "LSEEK" },
3504 [FUSE_STATX] = { _do_statx, "STATX" },
3505 [CUSE_INIT] = { _cuse_lowlevel_init, "CUSE_INIT" },
3506};
3507
3508/*
3509 * For ABI compatibility we cannot allow higher values than CUSE_INIT.
3510 * Without ABI compatibility we could use the size of the array.
3511 * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
3512 */
3513#define FUSE_MAXOP (CUSE_INIT + 1)
3514
3515
3520static inline int
3521fuse_req_opcode_sanity_ok(struct fuse_session *se, enum fuse_opcode in_op)
3522{
3523 int err = EIO;
3524
3525 if (!se->got_init) {
3526 enum fuse_opcode expected;
3527
3528 expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
3529 if (in_op != expected)
3530 return err;
3531 } else if (in_op == FUSE_INIT || in_op == CUSE_INIT)
3532 return err;
3533
3534 return 0;
3535}
3536
3537static inline void
3538fuse_session_in2req(struct fuse_req *req, struct fuse_in_header *in)
3539{
3540 req->unique = in->unique;
3541 req->ctx.uid = in->uid;
3542 req->ctx.gid = in->gid;
3543 req->ctx.pid = in->pid;
3544}
3545
3549static inline int
3550fuse_req_check_allow_root(struct fuse_session *se, enum fuse_opcode in_op,
3551 uid_t in_uid)
3552{
3553 int err = EACCES;
3554
3555 if (se->deny_others && in_uid != se->owner && in_uid != 0 &&
3556 in_op != FUSE_INIT && in_op != FUSE_READ &&
3557 in_op != FUSE_WRITE && in_op != FUSE_FSYNC &&
3558 in_op != FUSE_RELEASE && in_op != FUSE_READDIR &&
3559 in_op != FUSE_FSYNCDIR && in_op != FUSE_RELEASEDIR &&
3560 in_op != FUSE_NOTIFY_REPLY &&
3561 in_op != FUSE_READDIRPLUS)
3562 return err;
3563
3564 return 0;
3565}
3566
3567static const char *opname(enum fuse_opcode opcode)
3568{
3569 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
3570 return "???";
3571 else
3572 return fuse_ll_ops[opcode].name;
3573}
3574
3575static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
3576 struct fuse_bufvec *src)
3577{
3578 ssize_t res = fuse_buf_copy(dst, src, 0);
3579 if (res < 0) {
3580 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
3581 return res;
3582 }
3583 if ((size_t)res < fuse_buf_size(dst)) {
3584 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
3585 return -1;
3586 }
3587 return 0;
3588}
3589
3590void fuse_session_process_buf(struct fuse_session *se,
3591 const struct fuse_buf *buf)
3592{
3593 fuse_session_process_buf_internal(se, buf, NULL);
3594}
3595
3596/* libfuse internal handler */
3597void fuse_session_process_buf_internal(struct fuse_session *se,
3598 const struct fuse_buf *buf, struct fuse_chan *ch)
3599{
3600 const size_t write_header_size = sizeof(struct fuse_in_header) +
3601 sizeof(struct fuse_write_in);
3602 struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
3603 struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
3604 struct fuse_in_header *in;
3605 const void *inarg;
3606 struct fuse_req *req;
3607 void *mbuf = NULL;
3608 int err;
3609 int res;
3610
3611 if (buf->flags & FUSE_BUF_IS_FD) {
3612 if (buf->size < tmpbuf.buf[0].size)
3613 tmpbuf.buf[0].size = buf->size;
3614
3615 mbuf = malloc(tmpbuf.buf[0].size);
3616 if (mbuf == NULL) {
3617 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
3618 goto clear_pipe;
3619 }
3620 tmpbuf.buf[0].mem = mbuf;
3621
3622 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
3623 if (res < 0)
3624 goto clear_pipe;
3625
3626 in = mbuf;
3627 } else {
3628 in = buf->mem;
3629 }
3630
3631 trace_request_process(in->opcode, in->unique);
3632
3633 if (se->debug) {
3634 fuse_log(FUSE_LOG_DEBUG,
3635 "dev unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
3636 (unsigned long long) in->unique,
3637 opname((enum fuse_opcode) in->opcode), in->opcode,
3638 (unsigned long long) in->nodeid, buf->size, in->pid);
3639 }
3640
3641 req = fuse_ll_alloc_req(se);
3642 if (req == NULL) {
3643 struct fuse_out_header out = {
3644 .unique = in->unique,
3645 .error = -ENOMEM,
3646 };
3647 struct iovec iov = {
3648 .iov_base = &out,
3649 .iov_len = sizeof(struct fuse_out_header),
3650 };
3651
3652 fuse_send_msg(se, ch, &iov, 1, NULL);
3653 goto clear_pipe;
3654 }
3655
3656 fuse_session_in2req(req, in);
3657 req->ch = ch ? fuse_chan_get(ch) : NULL;
3658
3659 err = fuse_req_opcode_sanity_ok(se, in->opcode);
3660 if (err)
3661 goto reply_err;
3662
3663 err = fuse_req_check_allow_root(se, in->opcode, in->uid);
3664 if (err)
3665 goto reply_err;
3666
3667 err = ENOSYS;
3668 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
3669 goto reply_err;
3670 /* Do not process interrupt request */
3671 if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) {
3672 if (se->debug)
3673 fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
3674 goto reply_err;
3675 }
3676 if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) {
3677 struct fuse_req *intr;
3678 pthread_mutex_lock(&se->lock);
3679 intr = check_interrupt(se, req);
3680 list_add_req(req, &se->list);
3681 pthread_mutex_unlock(&se->lock);
3682 if (intr)
3683 fuse_reply_err(intr, EAGAIN);
3684 }
3685
3686 if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
3687 (in->opcode != FUSE_WRITE || !se->op.write_buf) &&
3688 in->opcode != FUSE_NOTIFY_REPLY) {
3689 void *newmbuf;
3690
3691 err = ENOMEM;
3692 newmbuf = realloc(mbuf, buf->size);
3693 if (newmbuf == NULL)
3694 goto reply_err;
3695 mbuf = newmbuf;
3696
3697 tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
3698 tmpbuf.buf[0].mem = (char *)mbuf + write_header_size;
3699
3700 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
3701 err = -res;
3702 if (res < 0)
3703 goto reply_err;
3704
3705 in = mbuf;
3706 }
3707
3708 inarg = (void *) &in[1];
3709 if (in->opcode == FUSE_WRITE && se->op.write_buf)
3710 do_write_buf(req, in->nodeid, inarg, buf);
3711 else if (in->opcode == FUSE_NOTIFY_REPLY)
3712 do_notify_reply(req, in->nodeid, inarg, buf);
3713 else
3714 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
3715
3716out_free:
3717 free(mbuf);
3718 return;
3719
3720reply_err:
3721 fuse_reply_err(req, err);
3722clear_pipe:
3723 if (buf->flags & FUSE_BUF_IS_FD)
3724 fuse_ll_clear_pipe(se);
3725 goto out_free;
3726}
3727
3728void fuse_session_process_uring_cqe(struct fuse_session *se,
3729 struct fuse_req *req,
3730 struct fuse_in_header *in, void *op_in,
3731 void *op_payload, size_t payload_len)
3732{
3733 int err;
3734
3735 fuse_session_in2req(req, in);
3736
3737 err = fuse_req_opcode_sanity_ok(se, in->opcode);
3738 if (err)
3739 goto reply_err;
3740
3741 err = fuse_req_check_allow_root(se, in->opcode, in->uid);
3742 if (err)
3743 goto reply_err;
3744
3745 err = ENOSYS;
3746 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
3747 goto reply_err;
3748
3749 if (se->debug) {
3750 fuse_log(
3751 FUSE_LOG_DEBUG,
3752 "cqe unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
3753 (unsigned long long)in->unique,
3754 opname((enum fuse_opcode)in->opcode), in->opcode,
3755 (unsigned long long)in->nodeid, payload_len, in->pid);
3756 }
3757
3758 if (in->opcode == FUSE_WRITE && se->op.write_buf) {
3759 struct fuse_bufvec bufv = {
3760 .buf[0] = { .size = payload_len,
3761 .flags = 0,
3762 .mem = op_payload },
3763 .count = 1,
3764 };
3765 _do_write_buf(req, in->nodeid, op_in, &bufv);
3766 } else if (in->opcode == FUSE_NOTIFY_REPLY) {
3767 struct fuse_buf buf = { .size = payload_len,
3768 .mem = op_payload };
3769 do_notify_reply(req, in->nodeid, op_in, &buf);
3770 } else {
3771 fuse_ll_ops2[in->opcode].func(req, in->nodeid, op_in,
3772 op_payload);
3773 }
3774
3775 return;
3776
3777reply_err:
3778 fuse_reply_err(req, err);
3779}
3780
3781#define LL_OPTION(n,o,v) \
3782 { n, offsetof(struct fuse_session, o), v }
3783
3784static const struct fuse_opt fuse_ll_opts[] = {
3785 LL_OPTION("debug", debug, 1),
3786 LL_OPTION("-d", debug, 1),
3787 LL_OPTION("--debug", debug, 1),
3788 LL_OPTION("allow_root", deny_others, 1),
3789 LL_OPTION("io_uring", uring.enable, 1),
3790 LL_OPTION("io_uring_q_depth=%u", uring.q_depth, -1),
3792};
3793
3794void fuse_lowlevel_version(void)
3795{
3796 printf("using FUSE kernel interface version %i.%i\n",
3797 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
3798 fuse_mount_version();
3799}
3800
3801void fuse_lowlevel_help(void)
3802{
3803 /* These are not all options, but the ones that are
3804 potentially of interest to an end-user */
3805 printf(
3806" -o allow_other allow access by all users\n"
3807" -o allow_root allow access by root\n"
3808" -o auto_unmount auto unmount on process termination\n"
3809" -o io_uring enable io-uring\n"
3810" -o io_uring_q_depth=<n> io-uring queue depth\n"
3811);
3812}
3813
3814void fuse_session_destroy(struct fuse_session *se)
3815{
3816 struct fuse_ll_pipe *llp;
3817
3818 if (se->got_init && !se->got_destroy) {
3819 if (se->op.destroy)
3820 se->op.destroy(se->userdata);
3821 }
3822 llp = pthread_getspecific(se->pipe_key);
3823 if (llp != NULL)
3824 fuse_ll_pipe_free(llp);
3825 pthread_key_delete(se->pipe_key);
3826 sem_destroy(&se->mt_finish);
3827 pthread_mutex_destroy(&se->mt_lock);
3828 pthread_mutex_destroy(&se->lock);
3829 free(se->cuse_data);
3830 if (se->fd != -1)
3831 close(se->fd);
3832 if (se->io != NULL)
3833 free(se->io);
3834 destroy_mount_opts(se->mo);
3835 free(se);
3836}
3837
3838
3839static void fuse_ll_pipe_destructor(void *data)
3840{
3841 struct fuse_ll_pipe *llp = data;
3842 fuse_ll_pipe_free(llp);
3843}
3844
3845void fuse_buf_free(struct fuse_buf *buf)
3846{
3847 if (buf->mem == NULL)
3848 return;
3849
3850 size_t write_header_sz =
3851 sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
3852
3853 char *ptr = (char *)buf->mem - pagesize + write_header_sz;
3854 free(ptr);
3855 buf->mem = NULL;
3856}
3857
3858/*
3859 * This is used to allocate buffers that hold fuse requests
3860 */
3861static void *buf_alloc(size_t size, bool internal)
3862{
3863 /*
3864 * For libfuse internal caller add in alignment. That cannot be done
3865 * for an external caller, as it is not guaranteed that the external
3866 * caller frees the raw pointer.
3867 */
3868 if (internal) {
3869 size_t write_header_sz = sizeof(struct fuse_in_header) +
3870 sizeof(struct fuse_write_in);
3871 size_t new_size = ROUND_UP(size + write_header_sz, pagesize);
3872
3873 char *buf = aligned_alloc(pagesize, new_size);
3874 if (buf == NULL)
3875 return NULL;
3876
3877 buf += pagesize - write_header_sz;
3878
3879 return buf;
3880 } else {
3881 return malloc(size);
3882 }
3883}
3884
3885/*
3886 *@param internal true if called from libfuse internal code
3887 */
3888static int _fuse_session_receive_buf(struct fuse_session *se,
3889 struct fuse_buf *buf, struct fuse_chan *ch,
3890 bool internal)
3891{
3892 int err;
3893 ssize_t res;
3894 size_t bufsize;
3895#ifdef HAVE_SPLICE
3896 struct fuse_ll_pipe *llp;
3897 struct fuse_buf tmpbuf;
3898
3899pipe_retry:
3900 bufsize = se->bufsize;
3901
3902 if (se->conn.proto_minor < 14 ||
3903 !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
3904 goto fallback;
3905
3906 llp = fuse_ll_get_pipe(se);
3907 if (llp == NULL)
3908 goto fallback;
3909
3910 if (llp->size < bufsize) {
3911 if (llp->can_grow) {
3912 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
3913 if (res == -1) {
3914 llp->can_grow = 0;
3915 res = grow_pipe_to_max(llp->pipe[0]);
3916 if (res > 0)
3917 llp->size = res;
3918 goto fallback;
3919 }
3920 llp->size = res;
3921 }
3922 if (llp->size < bufsize)
3923 goto fallback;
3924 }
3925
3926 if (se->io != NULL && se->io->splice_receive != NULL) {
3927 res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
3928 llp->pipe[1], NULL, bufsize, 0,
3929 se->userdata);
3930 } else {
3931 res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
3932 bufsize, 0);
3933 }
3934 err = errno;
3935 trace_request_receive(err);
3936
3937 if (fuse_session_exited(se))
3938 return 0;
3939
3940 if (res == -1) {
3941 if (err == ENODEV) {
3942 /* Filesystem was unmounted, or connection was aborted
3943 via /sys/fs/fuse/connections */
3945 return 0;
3946 }
3947
3948 /* FUSE_INIT might have increased the required bufsize */
3949 if (err == EINVAL && bufsize < se->bufsize) {
3950 fuse_ll_clear_pipe(se);
3951 goto pipe_retry;
3952 }
3953
3954 if (err != EINTR && err != EAGAIN)
3955 perror("fuse: splice from device");
3956 return -err;
3957 }
3958
3959 if (res < sizeof(struct fuse_in_header)) {
3960 fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
3961 return -EIO;
3962 }
3963
3964 tmpbuf = (struct fuse_buf){
3965 .size = res,
3966 .flags = FUSE_BUF_IS_FD,
3967 .fd = llp->pipe[0],
3968 };
3969
3970 /*
3971 * Don't bother with zero copy for small requests.
3972 * fuse_loop_mt() needs to check for FORGET so this more than
3973 * just an optimization.
3974 */
3975 if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) +
3976 pagesize) {
3977 struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
3978 struct fuse_bufvec dst = { .count = 1 };
3979
3980 if (!buf->mem) {
3981 buf->mem = buf_alloc(bufsize, internal);
3982 if (!buf->mem) {
3983 fuse_log(
3984 FUSE_LOG_ERR,
3985 "fuse: failed to allocate read buffer\n");
3986 return -ENOMEM;
3987 }
3988 buf->mem_size = bufsize;
3989 }
3990 buf->size = bufsize;
3991 buf->flags = 0;
3992 dst.buf[0] = *buf;
3993
3994 res = fuse_buf_copy(&dst, &src, 0);
3995 if (res < 0) {
3996 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
3997 strerror(-res));
3998 fuse_ll_clear_pipe(se);
3999 return res;
4000 }
4001 if (res < tmpbuf.size) {
4002 fuse_log(FUSE_LOG_ERR,
4003 "fuse: copy from pipe: short read\n");
4004 fuse_ll_clear_pipe(se);
4005 return -EIO;
4006 }
4007 assert(res == tmpbuf.size);
4008
4009 } else {
4010 /* Don't overwrite buf->mem, as that would cause a leak */
4011 buf->fd = tmpbuf.fd;
4012 buf->flags = tmpbuf.flags;
4013 }
4014 buf->size = tmpbuf.size;
4015
4016 return res;
4017
4018fallback:
4019#endif
4020 bufsize = internal ? buf->mem_size : se->bufsize;
4021 if (!buf->mem) {
4022 bufsize = se->bufsize; /* might have changed */
4023 buf->mem = buf_alloc(bufsize, internal);
4024 if (!buf->mem) {
4025 fuse_log(FUSE_LOG_ERR,
4026 "fuse: failed to allocate read buffer\n");
4027 return -ENOMEM;
4028 }
4029
4030 if (internal)
4031 buf->mem_size = bufsize;
4032 }
4033
4034restart:
4035 if (se->io != NULL) {
4036 /* se->io->read is never NULL if se->io is not NULL as
4037 specified by fuse_session_custom_io()*/
4038 res = se->io->read(ch ? ch->fd : se->fd, buf->mem, bufsize,
4039 se->userdata);
4040 } else {
4041 res = read(ch ? ch->fd : se->fd, buf->mem, bufsize);
4042 }
4043 err = errno;
4044 trace_request_receive(err);
4045
4046 if (fuse_session_exited(se))
4047 return 0;
4048 if (res == -1) {
4049 if (err == EINVAL && internal && se->bufsize > bufsize) {
4050 /* FUSE_INIT might have increased the required bufsize */
4051 bufsize = se->bufsize;
4052 void *newbuf = buf_alloc(bufsize, internal);
4053 if (!newbuf) {
4054 fuse_log(
4055 FUSE_LOG_ERR,
4056 "fuse: failed to (re)allocate read buffer\n");
4057 return -ENOMEM;
4058 }
4059 fuse_buf_free(buf);
4060 buf->mem = newbuf;
4061 buf->mem_size = bufsize;
4062 goto restart;
4063 }
4064
4065 /* ENOENT means the operation was interrupted, it's safe
4066 to restart */
4067 if (err == ENOENT)
4068 goto restart;
4069
4070 if (err == ENODEV) {
4071 /* Filesystem was unmounted, or connection was aborted
4072 via /sys/fs/fuse/connections */
4074 return 0;
4075 }
4076 /* Errors occurring during normal operation: EINTR (read
4077 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
4078 umounted) */
4079 if (err != EINTR && err != EAGAIN)
4080 perror("fuse: reading device");
4081 return -err;
4082 }
4083 if ((size_t)res < sizeof(struct fuse_in_header)) {
4084 fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
4085 return -EIO;
4086 }
4087
4088 buf->size = res;
4089
4090 return res;
4091}
4092
4093int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
4094{
4095 return _fuse_session_receive_buf(se, buf, NULL, false);
4096}
4097
4098/* libfuse internal handler */
4099int fuse_session_receive_buf_internal(struct fuse_session *se,
4100 struct fuse_buf *buf,
4101 struct fuse_chan *ch)
4102{
4103 /*
4104 * if run internally thread buffers are from libfuse - we can
4105 * reallocate them
4106 */
4107 if (unlikely(!se->got_init) && !se->buf_reallocable)
4108 se->buf_reallocable = true;
4109
4110 return _fuse_session_receive_buf(se, buf, ch, true);
4111}
4112
4113struct fuse_session *
4114fuse_session_new_versioned(struct fuse_args *args,
4115 const struct fuse_lowlevel_ops *op, size_t op_size,
4116 struct libfuse_version *version, void *userdata)
4117{
4118 int err;
4119 struct fuse_session *se;
4120 struct mount_opts *mo;
4121
4122 if (op == NULL || op_size == 0) {
4123 fuse_log(FUSE_LOG_ERR,
4124 "fuse: warning: empty op list passed to fuse_session_new()\n");
4125 return NULL;
4126 }
4127
4128 if (version == NULL) {
4129 fuse_log(FUSE_LOG_ERR, "fuse: warning: version not passed to fuse_session_new()\n");
4130 return NULL;
4131 }
4132
4133 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
4134 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
4135 op_size = sizeof(struct fuse_lowlevel_ops);
4136 }
4137
4138 if (args == NULL || args->argc == 0) {
4139 fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
4140 return NULL;
4141 }
4142
4143 se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
4144 if (se == NULL) {
4145 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
4146 goto out1;
4147 }
4148 se->fd = -1;
4149 se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize();
4150 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
4151 se->conn.max_readahead = UINT_MAX;
4152
4153 /*
4154 * Allow overriding with env, mostly to avoid the need to modify
4155 * all tests. I.e. to test with and without io-uring being enabled.
4156 */
4157 se->uring.enable = getenv("FUSE_URING_ENABLE") ?
4158 atoi(getenv("FUSE_URING_ENABLE")) :
4159 SESSION_DEF_URING_ENABLE;
4160 se->uring.q_depth = getenv("FUSE_URING_QUEUE_DEPTH") ?
4161 atoi(getenv("FUSE_URING_QUEUE_DEPTH")) :
4162 SESSION_DEF_URING_Q_DEPTH;
4163
4164 /* Parse options */
4165 if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
4166 goto out2;
4167 if(se->deny_others) {
4168 /* Allowing access only by root is done by instructing
4169 * kernel to allow access by everyone, and then restricting
4170 * access to root and mountpoint owner in libfuse.
4171 */
4172 // We may be adding the option a second time, but
4173 // that doesn't hurt.
4174 if(fuse_opt_add_arg(args, "-oallow_other") == -1)
4175 goto out2;
4176 }
4177 mo = parse_mount_opts(args);
4178 if (mo == NULL)
4179 goto out3;
4180
4181 if(args->argc == 1 &&
4182 args->argv[0][0] == '-') {
4183 fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
4184 "will be ignored\n");
4185 } else if (args->argc != 1) {
4186 int i;
4187 fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
4188 for(i = 1; i < args->argc-1; i++)
4189 fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
4190 fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
4191 goto out4;
4192 }
4193
4194 if (se->debug)
4195 fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
4196
4197 list_init_req(&se->list);
4198 list_init_req(&se->interrupts);
4199 list_init_nreq(&se->notify_list);
4200 se->notify_ctr = 1;
4201 pthread_mutex_init(&se->lock, NULL);
4202 sem_init(&se->mt_finish, 0, 0);
4203 pthread_mutex_init(&se->mt_lock, NULL);
4204
4205 err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
4206 if (err) {
4207 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
4208 strerror(err));
4209 goto out5;
4210 }
4211
4212 memcpy(&se->op, op, op_size);
4213 se->owner = getuid();
4214 se->userdata = userdata;
4215
4216 se->mo = mo;
4217
4218 /* Fuse server application should pass the version it was compiled
4219 * against and pass it. If a libfuse version accidentally introduces an
4220 * ABI incompatibility, it might be possible to 'fix' that at run time,
4221 * by checking the version numbers.
4222 */
4223 se->version = *version;
4224
4225 return se;
4226
4227out5:
4228 sem_destroy(&se->mt_finish);
4229 pthread_mutex_destroy(&se->mt_lock);
4230 pthread_mutex_destroy(&se->lock);
4231out4:
4232 fuse_opt_free_args(args);
4233out3:
4234 if (mo != NULL)
4235 destroy_mount_opts(mo);
4236out2:
4237 free(se);
4238out1:
4239 return NULL;
4240}
4241
4242struct fuse_session *fuse_session_new_30(struct fuse_args *args,
4243 const struct fuse_lowlevel_ops *op,
4244 size_t op_size, void *userdata);
4245struct fuse_session *fuse_session_new_30(struct fuse_args *args,
4246 const struct fuse_lowlevel_ops *op,
4247 size_t op_size,
4248 void *userdata)
4249{
4250 struct fuse_lowlevel_ops null_ops = { 0 };
4251
4252 /* unknown version */
4253 struct libfuse_version version = { 0 };
4254
4255 /*
4256 * This function is the ABI interface function from fuse_session_new in
4257 * compat.c. External libraries like "fuser" might call fuse_session_new()
4258 * with NULL ops and then pass that session to fuse_session_mount().
4259 * The actual FUSE operations are handled in their own library.
4260 */
4261 if (op == NULL) {
4262 op = &null_ops;
4263 op_size = sizeof(null_ops);
4264 }
4265
4266 return fuse_session_new_versioned(args, op, op_size, &version,
4267 userdata);
4268}
4269
4270FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17")
4271int fuse_session_custom_io_317(struct fuse_session *se,
4272 const struct fuse_custom_io *io, size_t op_size, int fd)
4273{
4274 if (sizeof(struct fuse_custom_io) < op_size) {
4275 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
4276 op_size = sizeof(struct fuse_custom_io);
4277 }
4278
4279 if (fd < 0) {
4280 fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
4281 "fuse_session_custom_io()\n", fd);
4282 return -EBADF;
4283 }
4284 if (io == NULL) {
4285 fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
4286 "fuse_session_custom_io()\n");
4287 return -EINVAL;
4288 } else if (io->read == NULL || io->writev == NULL) {
4289 /* If the user provides their own file descriptor, we can't
4290 guarantee that the default behavior of the io operations made
4291 in libfuse will function properly. Therefore, we enforce the
4292 user to implement these io operations when using custom io. */
4293 fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
4294 "implement both io->read() and io->writev\n");
4295 return -EINVAL;
4296 }
4297
4298 se->io = calloc(1, sizeof(struct fuse_custom_io));
4299 if (se->io == NULL) {
4300 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
4301 "Error: %s\n", strerror(errno));
4302 return -errno;
4303 }
4304
4305 se->fd = fd;
4306 memcpy(se->io, io, op_size);
4307 return 0;
4308}
4309
4310int fuse_session_custom_io_30(struct fuse_session *se,
4311 const struct fuse_custom_io *io, int fd);
4312FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0")
4313int fuse_session_custom_io_30(struct fuse_session *se,
4314 const struct fuse_custom_io *io, int fd)
4315{
4316 return fuse_session_custom_io_317(se, io,
4317 offsetof(struct fuse_custom_io, clone_fd), fd);
4318}
4319
4320int fuse_session_mount(struct fuse_session *se, const char *_mountpoint)
4321{
4322 int fd;
4323 char *mountpoint;
4324
4325 if (_mountpoint == NULL) {
4326 fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
4327 return -1;
4328 }
4329
4330 mountpoint = strdup(_mountpoint);
4331 if (mountpoint == NULL) {
4332 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for mountpoint. Error: %s\n",
4333 strerror(errno));
4334 return -1;
4335 }
4336
4337 /*
4338 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
4339 * would ensue.
4340 */
4341 do {
4342 fd = open("/dev/null", O_RDWR);
4343 if (fd > 2)
4344 close(fd);
4345 } while (fd >= 0 && fd <= 2);
4346
4347 /*
4348 * To allow FUSE daemons to run without privileges, the caller may open
4349 * /dev/fuse before launching the file system and pass on the file
4350 * descriptor by specifying /dev/fd/N as the mount point. Note that the
4351 * parent process takes care of performing the mount in this case.
4352 */
4353 fd = fuse_mnt_parse_fuse_fd(mountpoint);
4354 if (fd != -1) {
4355 if (fcntl(fd, F_GETFD) == -1) {
4356 fuse_log(FUSE_LOG_ERR,
4357 "fuse: Invalid file descriptor /dev/fd/%u\n",
4358 fd);
4359 goto error_out;
4360 }
4361 se->fd = fd;
4362 return 0;
4363 }
4364
4365 /* Open channel */
4366 fd = fuse_kern_mount(mountpoint, se->mo);
4367 if (fd == -1)
4368 goto error_out;
4369 se->fd = fd;
4370
4371 /* Save mountpoint */
4372 se->mountpoint = mountpoint;
4373
4374 return 0;
4375
4376error_out:
4377 free(mountpoint);
4378 return -1;
4379}
4380
4381int fuse_session_fd(struct fuse_session *se)
4382{
4383 return se->fd;
4384}
4385
4386void fuse_session_unmount(struct fuse_session *se)
4387{
4388 if (se->mountpoint != NULL) {
4389 char *mountpoint = atomic_exchange(&se->mountpoint, NULL);
4390
4391 fuse_kern_unmount(mountpoint, se->fd);
4392 se->fd = -1;
4393 free(mountpoint);
4394 }
4395}
4396
4397#ifdef linux
4398int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
4399{
4400 char *buf;
4401 size_t bufsize = 1024;
4402 char path[128];
4403 int ret;
4404 int fd;
4405 unsigned long pid = req->ctx.pid;
4406 char *s;
4407
4408 sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
4409
4410retry:
4411 buf = malloc(bufsize);
4412 if (buf == NULL)
4413 return -ENOMEM;
4414
4415 ret = -EIO;
4416 fd = open(path, O_RDONLY);
4417 if (fd == -1)
4418 goto out_free;
4419
4420 ret = read(fd, buf, bufsize);
4421 close(fd);
4422 if (ret < 0) {
4423 ret = -EIO;
4424 goto out_free;
4425 }
4426
4427 if ((size_t)ret == bufsize) {
4428 free(buf);
4429 bufsize *= 4;
4430 goto retry;
4431 }
4432
4433 buf[ret] = '\0';
4434 ret = -EIO;
4435 s = strstr(buf, "\nGroups:");
4436 if (s == NULL)
4437 goto out_free;
4438
4439 s += 8;
4440 ret = 0;
4441 while (1) {
4442 char *end;
4443 unsigned long val = strtoul(s, &end, 0);
4444 if (end == s)
4445 break;
4446
4447 s = end;
4448 if (ret < size)
4449 list[ret] = val;
4450 ret++;
4451 }
4452
4453out_free:
4454 free(buf);
4455 return ret;
4456}
4457#else /* linux */
4458/*
4459 * This is currently not implemented on other than Linux...
4460 */
4461int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
4462{
4463 (void) req; (void) size; (void) list;
4464 return -ENOSYS;
4465}
4466#endif
4467
4468/* Prevent spurious data race warning - we don't care
4469 * about races for this flag */
4470__attribute__((no_sanitize_thread))
4471void fuse_session_exit(struct fuse_session *se)
4472{
4473 atomic_store_explicit(&se->mt_exited, 1, memory_order_relaxed);
4474 sem_post(&se->mt_finish);
4475}
4476
4477__attribute__((no_sanitize_thread))
4478void fuse_session_reset(struct fuse_session *se)
4479{
4480 se->mt_exited = false;
4481 se->error = 0;
4482}
4483
4484__attribute__((no_sanitize_thread))
4485int fuse_session_exited(struct fuse_session *se)
4486{
4487 bool exited =
4488 atomic_load_explicit(&se->mt_exited, memory_order_relaxed);
4489
4490 return exited ? 1 : 0;
4491}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
@ FUSE_BUF_IS_FD
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
fuse_buf_copy_flags
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
fuse_notify_entry_flags
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
int fuse_reply_poll(fuse_req_t req, unsigned revents)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf)
int fuse_session_exited(struct fuse_session *se)
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_session_reset(struct fuse_session *se)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_passthrough_open(fuse_req_t req, int fd)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_xattr(fuse_req_t req, size_t count)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_CAP_OVER_IO_URING
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
@ FUSE_BUF_IS_FD
bool fuse_req_is_uring(fuse_req_t req)
const struct fuse_ctx * fuse_req_ctx(fuse_req_t req)
int fuse_session_fd(struct fuse_session *se)
int fuse_req_interrupted(fuse_req_t req)
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
void fuse_session_unmount(struct fuse_session *se)
int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
size_t mem_size
void * mem
size_t size
struct fuse_buf buf[1]
uint64_t capable_ext
uint64_t want_ext
double entry_timeout
fuse_ino_t ino
uint64_t generation
double attr_timeout
struct stat attr
uint64_t lock_owner
uint32_t writepage
Definition fuse_common.h:60
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t nonseekable
Definition fuse_common.h:78
int32_t backing_id
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t noflush
Definition fuse_common.h:93
uint32_t flush
Definition fuse_common.h:74
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__misc_8h_source.html0000644000175000017500000002434215156613430023305 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_misc.h Source File
libfuse
fuse_misc.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#include <pthread.h>
10
11/*
12 Versioned symbols cannot be used in some cases because it
13 - not supported on MacOSX (in MachO binary format)
14
15 Note: "@@" denotes the default symbol, "@" is binary a compat version.
16
17*/
18#ifdef LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS
19# if HAVE_SYMVER_ATTRIBUTE
20# define FUSE_SYMVER(sym1, sym2) __attribute__ ((symver (sym2)))
21# else
22# define FUSE_SYMVER(sym1, sym2) __asm__("\t.symver " sym1 "," sym2);
23# endif
24#else
25#define FUSE_SYMVER(sym1, sym2)
26#endif
27
28#ifdef HAVE_STRUCT_STAT_ST_ATIM
29/* Linux */
30#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
31#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
32#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
33#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val)
34#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctim.tv_nsec = (val)
35#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val)
36#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
37/* FreeBSD */
38#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
39#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
40#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
41#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val)
42#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctimespec.tv_nsec = (val)
43#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val)
44#else
45#define ST_ATIM_NSEC(stbuf) 0
46#define ST_CTIM_NSEC(stbuf) 0
47#define ST_MTIM_NSEC(stbuf) 0
48#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0)
49#define ST_CTIM_NSEC_SET(stbuf, val) do { } while (0)
50#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0)
51#endif
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__opt_8c_source.html0000644000175000017500000023542115156613430023151 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_opt.c Source File
libfuse
fuse_opt.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Implementation of option parsing routines (dealing with `struct
6 fuse_args`).
7
8 This program can be distributed under the terms of the GNU LGPLv2.
9 See the file LGPL2.txt
10*/
11
12#include "fuse_config.h"
13#include "fuse_i.h"
14#include "fuse_opt.h"
15#include "fuse_misc.h"
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <assert.h>
21
22struct fuse_opt_context {
23 void *data;
24 const struct fuse_opt *opt;
25 fuse_opt_proc_t proc;
26 int argctr;
27 int argc;
28 char **argv;
29 struct fuse_args outargs;
30 char *opts;
31 int nonopt;
32};
33
34void fuse_opt_free_args(struct fuse_args *args)
35{
36 if (args) {
37 if (args->argv && args->allocated) {
38 int i;
39 for (i = 0; i < args->argc; i++)
40 free(args->argv[i]);
41 free(args->argv);
42 }
43 args->argc = 0;
44 args->argv = NULL;
45 args->allocated = 0;
46 }
47}
48
49static int alloc_failed(void)
50{
51 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
52 return -1;
53}
54
55int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
56{
57 char **newargv;
58 char *newarg;
59
60 assert(!args->argv || args->allocated);
61
62 newarg = strdup(arg);
63 if (!newarg)
64 return alloc_failed();
65
66 newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
67 if (!newargv) {
68 free(newarg);
69 return alloc_failed();
70 }
71
72 args->argv = newargv;
73 args->allocated = 1;
74 args->argv[args->argc++] = newarg;
75 args->argv[args->argc] = NULL;
76 return 0;
77}
78
79static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
80 const char *arg)
81{
82 assert(pos <= args->argc);
83 if (fuse_opt_add_arg(args, arg) == -1)
84 return -1;
85
86 if (pos != args->argc - 1) {
87 char *newarg = args->argv[args->argc - 1];
88 memmove(&args->argv[pos + 1], &args->argv[pos],
89 sizeof(char *) * (args->argc - pos - 1));
90 args->argv[pos] = newarg;
91 }
92 return 0;
93}
94
95int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
96{
97 return fuse_opt_insert_arg_common(args, pos, arg);
98}
99
100static int next_arg(struct fuse_opt_context *ctx, const char *opt)
101{
102 if (ctx->argctr + 1 >= ctx->argc) {
103 fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt);
104 return -1;
105 }
106 ctx->argctr++;
107 return 0;
108}
109
110static int add_arg(struct fuse_opt_context *ctx, const char *arg)
111{
112 return fuse_opt_add_arg(&ctx->outargs, arg);
113}
114
115static int add_opt_common(char **opts, const char *opt, int esc)
116{
117 unsigned oldlen = *opts ? strlen(*opts) : 0;
118 char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
119
120 if (!d)
121 return alloc_failed();
122
123 *opts = d;
124 if (oldlen) {
125 d += oldlen;
126 *d++ = ',';
127 }
128
129 for (; *opt; opt++) {
130 if (esc && (*opt == ',' || *opt == '\\'))
131 *d++ = '\\';
132 *d++ = *opt;
133 }
134 *d = '\0';
135
136 return 0;
137}
138
139int fuse_opt_add_opt(char **opts, const char *opt)
140{
141 return add_opt_common(opts, opt, 0);
142}
143
144int fuse_opt_add_opt_escaped(char **opts, const char *opt)
145{
146 return add_opt_common(opts, opt, 1);
147}
148
149static int add_opt(struct fuse_opt_context *ctx, const char *opt)
150{
151 return add_opt_common(&ctx->opts, opt, 1);
152}
153
154static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
155 int iso)
156{
157 if (key == FUSE_OPT_KEY_DISCARD)
158 return 0;
159
160 if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
161 int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
162 if (res == -1 || !res)
163 return res;
164 }
165 if (iso)
166 return add_opt(ctx, arg);
167 else
168 return add_arg(ctx, arg);
169}
170
171static int match_template(const char *t, const char *arg, unsigned *sepp)
172{
173 int arglen = strlen(arg);
174 const char *sep = strchr(t, '=');
175 sep = sep ? sep : strchr(t, ' ');
176 if (sep && (!sep[1] || sep[1] == '%')) {
177 int tlen = sep - t;
178 if (sep[0] == '=')
179 tlen ++;
180 if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
181 *sepp = sep - t;
182 return 1;
183 }
184 }
185 if (strcmp(t, arg) == 0) {
186 *sepp = 0;
187 return 1;
188 }
189 return 0;
190}
191
192static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
193 const char *arg, unsigned *sepp)
194{
195 for (; opt && opt->templ; opt++)
196 if (match_template(opt->templ, arg, sepp))
197 return opt;
198 return NULL;
199}
200
201int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
202{
203 unsigned dummy;
204 return find_opt(opts, opt, &dummy) ? 1 : 0;
205}
206
207static int process_opt_param(void *var, const char *format, const char *param,
208 const char *arg)
209{
210 assert(format[0] == '%');
211 if (format[1] == 's') {
212 char **s = var;
213 char *copy = strdup(param);
214 if (!copy)
215 return alloc_failed();
216
217 free(*s);
218 *s = copy;
219 } else {
220 if (sscanf(param, format, var) != 1) {
221 fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg);
222 return -1;
223 }
224 }
225 return 0;
226}
227
228static int process_opt(struct fuse_opt_context *ctx,
229 const struct fuse_opt *opt, unsigned sep,
230 const char *arg, int iso)
231{
232 if (opt->offset == -1U) {
233 if (call_proc(ctx, arg, opt->value, iso) == -1)
234 return -1;
235 } else {
236 void *var = (char *)ctx->data + opt->offset;
237 if (sep && opt->templ[sep + 1]) {
238 const char *param = arg + sep;
239 if (opt->templ[sep] == '=')
240 param ++;
241 if (process_opt_param(var, opt->templ + sep + 1,
242 param, arg) == -1)
243 return -1;
244 } else
245 *(int *)var = opt->value;
246 }
247 return 0;
248}
249
250static int process_opt_sep_arg(struct fuse_opt_context *ctx,
251 const struct fuse_opt *opt, unsigned sep,
252 const char *arg, int iso)
253{
254 int res;
255 char *newarg;
256 char *param;
257
258 if (next_arg(ctx, arg) == -1)
259 return -1;
260
261 param = ctx->argv[ctx->argctr];
262 newarg = malloc(sep + strlen(param) + 1);
263 if (!newarg)
264 return alloc_failed();
265
266 memcpy(newarg, arg, sep);
267 strcpy(newarg + sep, param);
268 res = process_opt(ctx, opt, sep, newarg, iso);
269 free(newarg);
270
271 return res;
272}
273
274static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
275{
276 unsigned sep;
277 const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
278 if (opt) {
279 for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
280 int res;
281 if (sep && opt->templ[sep] == ' ' && !arg[sep])
282 res = process_opt_sep_arg(ctx, opt, sep, arg,
283 iso);
284 else
285 res = process_opt(ctx, opt, sep, arg, iso);
286 if (res == -1)
287 return -1;
288 }
289 return 0;
290 } else
291 return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
292}
293
294static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
295{
296 char *s = opts;
297 char *d = s;
298 int end = 0;
299
300 while (!end) {
301 if (*s == '\0')
302 end = 1;
303 if (*s == ',' || end) {
304 int res;
305
306 *d = '\0';
307 res = process_gopt(ctx, opts, 1);
308 if (res == -1)
309 return -1;
310 d = opts;
311 } else {
312 if (s[0] == '\\' && s[1] != '\0') {
313 s++;
314 if (s[0] >= '0' && s[0] <= '3' &&
315 s[1] >= '0' && s[1] <= '7' &&
316 s[2] >= '0' && s[2] <= '7') {
317 *d++ = (s[0] - '0') * 0100 +
318 (s[1] - '0') * 0010 +
319 (s[2] - '0');
320 s += 2;
321 } else {
322 *d++ = *s;
323 }
324 } else {
325 *d++ = *s;
326 }
327 }
328 s++;
329 }
330
331 return 0;
332}
333
334static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
335{
336 int res;
337 char *copy = strdup(opts);
338
339 if (!copy) {
340 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
341 return -1;
342 }
343 res = process_real_option_group(ctx, copy);
344 free(copy);
345 return res;
346}
347
348static int process_one(struct fuse_opt_context *ctx, const char *arg)
349{
350 if (ctx->nonopt || arg[0] != '-')
351 return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
352 else if (arg[1] == 'o') {
353 if (arg[2])
354 return process_option_group(ctx, arg + 2);
355 else {
356 if (next_arg(ctx, arg) == -1)
357 return -1;
358
359 return process_option_group(ctx,
360 ctx->argv[ctx->argctr]);
361 }
362 } else if (arg[1] == '-' && !arg[2]) {
363 if (add_arg(ctx, arg) == -1)
364 return -1;
365 ctx->nonopt = ctx->outargs.argc;
366 return 0;
367 } else
368 return process_gopt(ctx, arg, 0);
369}
370
371static int opt_parse(struct fuse_opt_context *ctx)
372{
373 if (ctx->argc) {
374 if (add_arg(ctx, ctx->argv[0]) == -1)
375 return -1;
376 }
377
378 for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
379 if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
380 return -1;
381
382 if (ctx->opts) {
383 if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
384 fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
385 return -1;
386 }
387
388 /* If option separator ("--") is the last argument, remove it */
389 if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
390 strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
391 free(ctx->outargs.argv[ctx->outargs.argc - 1]);
392 ctx->outargs.argv[--ctx->outargs.argc] = NULL;
393 }
394
395 return 0;
396}
397
398int fuse_opt_parse(struct fuse_args *args, void *data,
399 const struct fuse_opt opts[], fuse_opt_proc_t proc)
400{
401 int res;
402 struct fuse_opt_context ctx = {
403 .data = data,
404 .opt = opts,
405 .proc = proc,
406 };
407
408 if (!args || !args->argv || !args->argc)
409 return 0;
410
411 ctx.argc = args->argc;
412 ctx.argv = args->argv;
413
414 res = opt_parse(&ctx);
415 if (res != -1) {
416 struct fuse_args tmp = *args;
417 *args = ctx.outargs;
418 ctx.outargs = tmp;
419 }
420 free(ctx.opts);
421 fuse_opt_free_args(&ctx.outargs);
422 return res;
423}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition fuse_opt.h:180
#define FUSE_OPT_KEY_OPT
Definition fuse_opt.h:129
#define FUSE_OPT_KEY_NONOPT
Definition fuse_opt.h:137
#define FUSE_OPT_KEY_DISCARD
Definition fuse_opt.h:153
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition fuse_opt.c:95
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
int allocated
Definition fuse_opt.h:117
char ** argv
Definition fuse_opt.h:114
unsigned long offset
Definition fuse_opt.h:85
const char * templ
Definition fuse_opt.h:79
int value
Definition fuse_opt.h:91
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__signals_8c_source.html0000644000175000017500000011025315156613430024002 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_signals.c Source File
libfuse
fuse_signals.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Utility functions for setting signal handlers.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt
9*/
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_i.h"
14
15#include <stdio.h>
16#include <string.h>
17#include <signal.h>
18#include <stdlib.h>
19#include <errno.h>
20
21#ifdef HAVE_BACKTRACE
22#include <execinfo.h>
23#endif
24
25/*
26 * Must not handle SIGCANCEL, as that is used to wake up threads from
27 * syscalls reading requests from /dev/fuse
28 */
29static int teardown_sigs[] = { SIGHUP, SIGINT, SIGTERM };
30
31static int ignore_sigs[] = { SIGPIPE};
32static int fail_sigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV };
33static struct fuse_session *fuse_instance;
34
35#ifdef HAVE_BACKTRACE
36#define BT_STACK_SZ (1024 * 1024)
37static void *backtrace_buffer[BT_STACK_SZ];
38#endif
39
40static void dump_stack(void)
41{
42#ifdef HAVE_BACKTRACE
43 char **strings;
44
45 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
46 strings = backtrace_symbols(backtrace_buffer, nptrs);
47
48 if (strings == NULL) {
49 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
50 strerror(errno));
51 return;
52 }
53
54 for (int idx = 0; idx < nptrs; idx++)
55 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
56
57 free(strings);
58#endif
59}
60
61static void exit_handler(int sig)
62{
63 if (fuse_instance == NULL) {
64 fuse_log(FUSE_LOG_ERR, "fuse_instance is NULL\n");
65 return;
66 }
67
68 if (fuse_instance->debug)
69 fuse_log(FUSE_LOG_ERR, "exit_handler called with sig %d\n",
70 sig);
71
72 fuse_session_exit(fuse_instance);
73
74 if (sig < 0) {
75 fuse_log(FUSE_LOG_ERR,
76 "assertion error: signal value <= 0\n");
77 dump_stack();
78 abort();
79 fuse_instance->error = sig;
80 }
81
82 fuse_instance->error = sig;
83}
84
85static void exit_backtrace(int sig)
86{
87 if (fuse_instance == NULL)
88 return;
89
90 fuse_session_exit(fuse_instance);
91
92 fuse_remove_signal_handlers(fuse_instance);
93 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
94 dump_stack();
95 abort();
96}
97
98
99static void do_nothing(int sig)
100{
101 (void) sig;
102}
103
104static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
105{
106 struct sigaction sa;
107 struct sigaction old_sa;
108
109 memset(&sa, 0, sizeof(struct sigaction));
110 sa.sa_handler = remove ? SIG_DFL : handler;
111 sigemptyset(&(sa.sa_mask));
112 sa.sa_flags = 0;
113
114 if (sigaction(sig, NULL, &old_sa) == -1) {
115 perror("fuse: cannot get old signal handler");
116 return -1;
117 }
118
119 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
120 sigaction(sig, &sa, NULL) == -1) {
121 perror("fuse: cannot set signal handler");
122 return -1;
123 }
124 return 0;
125}
126
127static int _fuse_set_signal_handlers(int signals[], int nr_signals,
128 void (*handler)(int))
129{
130 for (int idx = 0; idx < nr_signals; idx++) {
131 int signal = signals[idx];
132
133 /*
134 * If we used SIG_IGN instead of the do_nothing function,
135 * then we would be unable to tell if we set SIG_IGN (and
136 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
137 * or if it was already set to SIG_IGN (and should be left
138 * untouched.
139 */
140 if (set_one_signal_handler(signal, handler, 0) == -1) {
141 fuse_log(FUSE_LOG_ERR,
142 "Failed to install signal handler for sig %d\n",
143 signal);
144 return -1;
145 }
146 }
147
148 return 0;
149}
150
151int fuse_set_signal_handlers(struct fuse_session *se)
152{
153 size_t nr_signals;
154 int rc;
155
156 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
157 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
158 if (rc < 0)
159 return rc;
160
161 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
162 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
163 if (rc < 0)
164 return rc;
165
166 /*
167 * needs to be set independently if already set, as some applications
168 * may have multiple sessions and might rely on traditional behavior
169 * that the last session is used.
170 */
171 fuse_instance = se;
172
173 return 0;
174}
175
176int fuse_set_fail_signal_handlers(struct fuse_session *se)
177{
178 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
179
180 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
181 exit_backtrace);
182 if (rc < 0)
183 return rc;
184
185 /* See fuse_set_signal_handlers, why set unconditionally */
186 fuse_instance = se;
187
188 return 0;
189}
190
191static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
192 void (*handler)(int))
193{
194 for (int idx = 0; idx < nr_signals; idx++)
195 set_one_signal_handler(signals[idx], handler, 1);
196}
197
198void fuse_remove_signal_handlers(struct fuse_session *se)
199{
200 size_t nr_signals;
201
202 if (fuse_instance != se)
203 fuse_log(FUSE_LOG_ERR,
204 "fuse: fuse_remove_signal_handlers: unknown session\n");
205 else
206 fuse_instance = NULL;
207
208 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
209 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
210
211 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
212 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
213
214 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
215 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
216}
int fuse_set_fail_signal_handlers(struct fuse_session *se)
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__uring_8c_source.html0000644000175000017500000043702415156613430023476 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_uring.c Source File
libfuse
fuse_uring.c
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2025 Bernd Schubert <bschubert@ddn.com>
4 *
5 * Implementation of (most of) FUSE-over-io-uring.
6 *
7 * This program can be distributed under the terms of the GNU LGPLv2.
8 * See the file LGPL2.txt
9 */
10
11#define _GNU_SOURCE
12
13#include "fuse_i.h"
14#include "fuse_kernel.h"
15#include "fuse_uring_i.h"
16
17#include <stdlib.h>
18#include <liburing.h>
19#include <sys/sysinfo.h>
20#include <stdint.h>
21#include <inttypes.h>
22#include <stdbool.h>
23#include <string.h>
24#include <unistd.h>
25#include <numa.h>
26#include <pthread.h>
27#include <stdio.h>
28#include <linux/sched.h>
29#include <poll.h>
30#include <sys/eventfd.h>
31
32/* Size of command data area in SQE when IORING_SETUP_SQE128 is used */
33#define FUSE_URING_MAX_SQE128_CMD_DATA 80
34
35struct fuse_ring_ent {
36 struct fuse_ring_queue *ring_queue; /* back pointer */
37 struct fuse_req req;
38
39 struct fuse_uring_req_header *req_header;
40 void *op_payload;
41 size_t req_payload_sz;
42
43 /* commit id of a fuse request */
44 uint64_t req_commit_id;
45
46 enum fuse_uring_cmd last_cmd;
47
48 /* header and payload */
49 struct iovec iov[2];
50};
51
52struct fuse_ring_queue {
53 /* back pointer */
54 struct fuse_ring_pool *ring_pool;
55 int qid;
56 int numa_node;
57 pthread_t tid;
58 int eventfd;
59 size_t req_header_sz;
60 struct io_uring ring;
61
62 pthread_mutex_t ring_lock;
63 bool cqe_processing;
64
65 /* size depends on queue depth */
66 struct fuse_ring_ent ent[];
67};
68
72struct fuse_ring_pool {
73 struct fuse_session *se;
74
75 /* number of queues */
76 size_t nr_queues;
77
78 /* number of per queue entries */
79 size_t queue_depth;
80
81 /* max payload size for fuse requests*/
82 size_t max_req_payload_sz;
83
84 /* size of a single queue */
85 size_t queue_mem_size;
86
87 unsigned int started_threads;
88 unsigned int failed_threads;
89
90 /* Avoid sending queue entries before FUSE_INIT reply*/
91 sem_t init_sem;
92
93 pthread_cond_t thread_start_cond;
94 pthread_mutex_t thread_start_mutex;
95
96 /* pointer to the first queue */
97 struct fuse_ring_queue *queues;
98};
99
100static size_t
101fuse_ring_queue_size(const size_t q_depth)
102{
103 const size_t req_size = sizeof(struct fuse_ring_ent) * q_depth;
104
105 return sizeof(struct fuse_ring_queue) + req_size;
106}
107
108static struct fuse_ring_queue *
109fuse_uring_get_queue(struct fuse_ring_pool *fuse_ring, int qid)
110{
111 void *ptr =
112 ((char *)fuse_ring->queues) + (qid * fuse_ring->queue_mem_size);
113
114 return ptr;
115}
116
120static void *fuse_uring_get_sqe_cmd(struct io_uring_sqe *sqe)
121{
122 return (void *)&sqe->cmd[0];
123}
124
125static void fuse_uring_sqe_set_req_data(struct fuse_uring_cmd_req *req,
126 const unsigned int qid,
127 const uint64_t commit_id)
128{
129 req->qid = qid;
130 req->commit_id = commit_id;
131 req->flags = 0;
132}
133
134static void
135fuse_uring_sqe_prepare(struct io_uring_sqe *sqe, struct fuse_ring_ent *req,
136 __u32 cmd_op)
137{
138 /* These fields should be written once, never change */
139 sqe->opcode = IORING_OP_URING_CMD;
140
141 /*
142 * IOSQE_FIXED_FILE: fd is the index to the fd *array*
143 * given to io_uring_register_files()
144 */
145 sqe->flags = IOSQE_FIXED_FILE;
146 sqe->fd = 0;
147
148 sqe->rw_flags = 0;
149 sqe->ioprio = 0;
150 sqe->off = 0;
151
152 io_uring_sqe_set_data(sqe, req);
153
154 sqe->cmd_op = cmd_op;
155 sqe->__pad1 = 0;
156}
157
158static int fuse_uring_commit_sqe(struct fuse_ring_pool *ring_pool,
159 struct fuse_ring_queue *queue,
160 struct fuse_ring_ent *ring_ent)
161{
162 bool locked = false;
163 struct fuse_session *se = ring_pool->se;
164 struct fuse_uring_req_header *rrh = ring_ent->req_header;
165 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
166 struct fuse_uring_ent_in_out *ent_in_out =
167 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
168 struct io_uring_sqe *sqe;
169
170 if (pthread_self() != queue->tid) {
171 pthread_mutex_lock(&queue->ring_lock);
172 locked = true;
173 }
174
175 sqe = io_uring_get_sqe(&queue->ring);
176
177 if (sqe == NULL) {
178 /* This is an impossible condition, unless there is a bug.
179 * The kernel sent back an SQEs, which is assigned to a request.
180 * There is no way to get out of SQEs, as the number of
181 * SQEs matches the number tof requests.
182 */
183
184 se->error = -EIO;
185 fuse_log(FUSE_LOG_ERR, "Failed to get a ring SQEs\n");
186
187 return -EIO;
188 }
189
190 ring_ent->last_cmd = FUSE_IO_URING_CMD_COMMIT_AND_FETCH;
191 fuse_uring_sqe_prepare(sqe, ring_ent, ring_ent->last_cmd);
192 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid,
193 ring_ent->req_commit_id);
194
195 if (se->debug) {
196 fuse_log(FUSE_LOG_DEBUG, " unique: %" PRIu64 ", result=%d\n",
197 out->unique, ent_in_out->payload_sz);
198 }
199
200 if (!queue->cqe_processing)
201 io_uring_submit(&queue->ring);
202
203 if (locked)
204 pthread_mutex_unlock(&queue->ring_lock);
205
206 return 0;
207}
208
209int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
210 void **mr)
211{
212 struct fuse_ring_ent *ring_ent;
213
214 /* Not possible without io-uring interface */
215 if (!req->flags.is_uring)
216 return -EINVAL;
217
218 ring_ent = container_of(req, struct fuse_ring_ent, req);
219
220 *payload = ring_ent->op_payload;
221 *payload_sz = ring_ent->req_payload_sz;
222
223 /*
224 * For now unused, but will be used later when the application can
225 * allocate the buffers itself and register them for rdma.
226 */
227 if (mr)
228 *mr = NULL;
229
230 return 0;
231}
232
233int send_reply_uring(fuse_req_t req, int error, const void *arg, size_t argsize)
234{
235 int res;
236 struct fuse_ring_ent *ring_ent =
237 container_of(req, struct fuse_ring_ent, req);
238 struct fuse_uring_req_header *rrh = ring_ent->req_header;
239 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
240 struct fuse_uring_ent_in_out *ent_in_out =
241 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
242
243 struct fuse_ring_queue *queue = ring_ent->ring_queue;
244 struct fuse_ring_pool *ring_pool = queue->ring_pool;
245 size_t max_payload_sz = ring_pool->max_req_payload_sz;
246
247 if (argsize > max_payload_sz) {
248 fuse_log(FUSE_LOG_ERR, "argsize %zu exceeds buffer size %zu",
249 argsize, max_payload_sz);
250 error = -EINVAL;
251 } else if (argsize) {
252 if (arg != ring_ent->op_payload)
253 memcpy(ring_ent->op_payload, arg, argsize);
254 }
255 ent_in_out->payload_sz = argsize;
256
257 out->error = error;
258 out->unique = req->unique;
259
260 res = fuse_uring_commit_sqe(ring_pool, queue, ring_ent);
261
262 fuse_free_req(req);
263
264 return res;
265}
266
267int fuse_reply_data_uring(fuse_req_t req, struct fuse_bufvec *bufv,
268 enum fuse_buf_copy_flags flags)
269{
270 struct fuse_ring_ent *ring_ent =
271 container_of(req, struct fuse_ring_ent, req);
272
273 struct fuse_ring_queue *queue = ring_ent->ring_queue;
274 struct fuse_ring_pool *ring_pool = queue->ring_pool;
275 struct fuse_uring_req_header *rrh = ring_ent->req_header;
276 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
277 struct fuse_uring_ent_in_out *ent_in_out =
278 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
279 size_t max_payload_sz = ring_ent->req_payload_sz;
280 struct fuse_bufvec dest_vec = FUSE_BUFVEC_INIT(max_payload_sz);
281 int res;
282
283 dest_vec.buf[0].mem = ring_ent->op_payload;
284 dest_vec.buf[0].size = max_payload_sz;
285
286 res = fuse_buf_copy(&dest_vec, bufv, flags);
287
288 out->error = res < 0 ? res : 0;
289 out->unique = req->unique;
290
291 ent_in_out->payload_sz = res > 0 ? res : 0;
292
293 res = fuse_uring_commit_sqe(ring_pool, queue, ring_ent);
294
295 fuse_free_req(req);
296
297 return res;
298}
299
303int fuse_send_msg_uring(fuse_req_t req, struct iovec *iov, int count)
304{
305 struct fuse_ring_ent *ring_ent =
306 container_of(req, struct fuse_ring_ent, req);
307
308 struct fuse_ring_queue *queue = ring_ent->ring_queue;
309 struct fuse_ring_pool *ring_pool = queue->ring_pool;
310 struct fuse_uring_req_header *rrh = ring_ent->req_header;
311 struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out;
312 struct fuse_uring_ent_in_out *ent_in_out =
313 (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out;
314 size_t max_buf = ring_pool->max_req_payload_sz;
315 size_t len = 0;
316 int res = 0;
317
318 /* copy iov into the payload, idx=0 is the header section */
319 for (int idx = 1; idx < count; idx++) {
320 struct iovec *cur = &iov[idx];
321
322 if (len + cur->iov_len > max_buf) {
323 fuse_log(FUSE_LOG_ERR,
324 "iov[%d] exceeds buffer size %zu",
325 idx, max_buf);
326 res = -EINVAL; /* Gracefully handle this? */
327 break;
328 }
329
330 memcpy(ring_ent->op_payload + len, cur->iov_base, cur->iov_len);
331 len += cur->iov_len;
332 }
333
334 ent_in_out->payload_sz = len;
335
336 out->error = res;
337 out->unique = req->unique;
338 out->len = len;
339
340 return fuse_uring_commit_sqe(ring_pool, queue, ring_ent);
341}
342
343static int fuse_queue_setup_io_uring(struct io_uring *ring, size_t qid,
344 size_t depth, int fd, int evfd)
345{
346 int rc;
347 struct io_uring_params params = {0};
348 int files[2] = { fd, evfd };
349
350 depth += 1; /* for the eventfd poll SQE */
351
352 params.flags = IORING_SETUP_SQE128;
353
354 /* Avoid cq overflow */
355 params.flags |= IORING_SETUP_CQSIZE;
356 params.cq_entries = depth * 2;
357
358 /* These flags should help to increase performance, but actually
359 * make it a bit slower - reason should get investigated.
360 */
361 if (0) {
362 /* Has the main slow down effect */
363 params.flags |= IORING_SETUP_SINGLE_ISSUER;
364
365 // params.flags |= IORING_SETUP_DEFER_TASKRUN;
366 params.flags |= IORING_SETUP_TASKRUN_FLAG;
367
368 /* Second main effect to make it slower */
369 params.flags |= IORING_SETUP_COOP_TASKRUN;
370 }
371
372 rc = io_uring_queue_init_params(depth, ring, &params);
373 if (rc != 0) {
374 fuse_log(FUSE_LOG_ERR, "Failed to setup qid %zu: %d (%s)\n",
375 qid, rc, strerror(-rc));
376 return rc;
377 }
378
379 rc = io_uring_register_files(ring, files, 1);
380 if (rc != 0) {
381 rc = -errno;
382 fuse_log(FUSE_LOG_ERR,
383 "Failed to register files for ring idx %zu: %s",
384 qid, strerror(errno));
385 return rc;
386 }
387
388 return 0;
389}
390
391static void fuse_session_destruct_uring(struct fuse_ring_pool *fuse_ring)
392{
393 for (size_t qid = 0; qid < fuse_ring->nr_queues; qid++) {
394 struct fuse_ring_queue *queue =
395 fuse_uring_get_queue(fuse_ring, qid);
396
397 if (queue->tid != 0) {
398 uint64_t value = 1ULL;
399 int rc;
400
401 rc = write(queue->eventfd, &value, sizeof(value));
402 if (rc != sizeof(value))
403 fprintf(stderr,
404 "Wrote to eventfd=%d err=%s: rc=%d\n",
405 queue->eventfd, strerror(errno), rc);
406 pthread_cancel(queue->tid);
407 pthread_join(queue->tid, NULL);
408 queue->tid = 0;
409 }
410
411 if (queue->eventfd >= 0) {
412 close(queue->eventfd);
413 queue->eventfd = -1;
414 }
415
416 if (queue->ring.ring_fd != -1)
417 io_uring_queue_exit(&queue->ring);
418
419 for (size_t idx = 0; idx < fuse_ring->queue_depth; idx++) {
420 struct fuse_ring_ent *ent = &queue->ent[idx];
421
422 numa_free(ent->op_payload, ent->req_payload_sz);
423 numa_free(ent->req_header, queue->req_header_sz);
424 }
425
426 pthread_mutex_destroy(&queue->ring_lock);
427 }
428
429 free(fuse_ring->queues);
430 pthread_cond_destroy(&fuse_ring->thread_start_cond);
431 pthread_mutex_destroy(&fuse_ring->thread_start_mutex);
432 free(fuse_ring);
433}
434
435static int fuse_uring_register_ent(struct fuse_ring_queue *queue,
436 struct fuse_ring_ent *ent)
437{
438 struct io_uring_sqe *sqe;
439
440 sqe = io_uring_get_sqe(&queue->ring);
441 if (sqe == NULL) {
442 /*
443 * All SQEs are idle here - no good reason this
444 * could fail
445 */
446 fuse_log(FUSE_LOG_ERR, "Failed to get all ring SQEs");
447 return -EIO;
448 }
449
450 ent->last_cmd = FUSE_IO_URING_CMD_REGISTER;
451 fuse_uring_sqe_prepare(sqe, ent, ent->last_cmd);
452
453 /* only needed for fetch */
454 ent->iov[0].iov_base = ent->req_header;
455 ent->iov[0].iov_len = queue->req_header_sz;
456
457 ent->iov[1].iov_base = ent->op_payload;
458 ent->iov[1].iov_len = ent->req_payload_sz;
459
460 sqe->addr = (uint64_t)(ent->iov);
461 sqe->len = 2;
462
463 /* this is a fetch, kernel does not read commit id */
464 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid, 0);
465
466 return 0;
467
468}
469
470static int fuse_uring_register_queue(struct fuse_ring_queue *queue)
471{
472 struct fuse_ring_pool *ring_pool = queue->ring_pool;
473 unsigned int sq_ready;
474 struct io_uring_sqe *sqe;
475 int res;
476
477 for (size_t idx = 0; idx < ring_pool->queue_depth; idx++) {
478 struct fuse_ring_ent *ent = &queue->ent[idx];
479
480 res = fuse_uring_register_ent(queue, ent);
481 if (res != 0)
482 return res;
483 }
484
485 sq_ready = io_uring_sq_ready(&queue->ring);
486 if (sq_ready != ring_pool->queue_depth) {
487 fuse_log(FUSE_LOG_ERR,
488 "SQE ready mismatch, expected %zu got %u\n",
489 ring_pool->queue_depth, sq_ready);
490 return -EINVAL;
491 }
492
493 /* Poll SQE for the eventfd to wake up on teardown */
494 sqe = io_uring_get_sqe(&queue->ring);
495 if (sqe == NULL) {
496 fuse_log(FUSE_LOG_ERR, "Failed to get eventfd SQE");
497 return -EIO;
498 }
499
500 io_uring_prep_poll_add(sqe, queue->eventfd, POLLIN);
501 io_uring_sqe_set_data(sqe, (void *)(uintptr_t)queue->eventfd);
502
503 /* Only preparation until here, no submission yet */
504
505 return 0;
506}
507
508static struct fuse_ring_pool *fuse_create_ring(struct fuse_session *se)
509{
510 struct fuse_ring_pool *fuse_ring = NULL;
511 const size_t nr_queues = get_nprocs_conf();
512 size_t payload_sz = se->bufsize - FUSE_BUFFER_HEADER_SIZE;
513 size_t queue_sz;
514
515 if (se->debug)
516 fuse_log(FUSE_LOG_DEBUG, "starting io-uring q-depth=%d\n",
517 se->uring.q_depth);
518
519 fuse_ring = calloc(1, sizeof(*fuse_ring));
520 if (fuse_ring == NULL) {
521 fuse_log(FUSE_LOG_ERR, "Allocating the ring failed\n");
522 goto err;
523 }
524
525 queue_sz = fuse_ring_queue_size(se->uring.q_depth);
526 fuse_ring->queues = calloc(1, queue_sz * nr_queues);
527 if (fuse_ring->queues == NULL) {
528 fuse_log(FUSE_LOG_ERR, "Allocating the queues failed\n");
529 goto err;
530 }
531
532 fuse_ring->se = se;
533 fuse_ring->nr_queues = nr_queues;
534 fuse_ring->queue_depth = se->uring.q_depth;
535 fuse_ring->max_req_payload_sz = payload_sz;
536 fuse_ring->queue_mem_size = queue_sz;
537
538 /*
539 * very basic queue initialization, that cannot fail and will
540 * allow easy cleanup if something (like mmap) fails in the middle
541 * below
542 */
543 for (size_t qid = 0; qid < nr_queues; qid++) {
544 struct fuse_ring_queue *queue =
545 fuse_uring_get_queue(fuse_ring, qid);
546
547 queue->ring.ring_fd = -1;
548 queue->numa_node = numa_node_of_cpu(qid);
549 queue->qid = qid;
550 queue->ring_pool = fuse_ring;
551 queue->eventfd = -1;
552 pthread_mutex_init(&queue->ring_lock, NULL);
553 }
554
555 pthread_cond_init(&fuse_ring->thread_start_cond, NULL);
556 pthread_mutex_init(&fuse_ring->thread_start_mutex, NULL);
557 sem_init(&fuse_ring->init_sem, 0, 0);
558
559 return fuse_ring;
560
561err:
562 if (fuse_ring)
563 fuse_session_destruct_uring(fuse_ring);
564
565 return NULL;
566}
567
568static void fuse_uring_resubmit(struct fuse_ring_queue *queue,
569 struct fuse_ring_ent *ent)
570{
571 struct io_uring_sqe *sqe;
572
573 sqe = io_uring_get_sqe(&queue->ring);
574 if (sqe == NULL) {
575 /* This is an impossible condition, unless there is a bug.
576 * The kernel sent back an SQEs, which is assigned to a request.
577 * There is no way to get out of SQEs, as the number of
578 * SQEs matches the number tof requests.
579 */
580
581 queue->ring_pool->se->error = -EIO;
582 fuse_log(FUSE_LOG_ERR, "Failed to get a ring SQEs\n");
583
584 return;
585 }
586
587 fuse_uring_sqe_prepare(sqe, ent, ent->last_cmd);
588
589 switch (ent->last_cmd) {
590 case FUSE_IO_URING_CMD_REGISTER:
591 sqe->addr = (uint64_t)(ent->iov);
592 sqe->len = 2;
593 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe),
594 queue->qid, 0);
595 break;
596 case FUSE_IO_URING_CMD_COMMIT_AND_FETCH:
597 fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe),
598 queue->qid, ent->req_commit_id);
599 break;
600 default:
601 fuse_log(FUSE_LOG_ERR, "Unknown command type: %d\n",
602 ent->last_cmd);
603 queue->ring_pool->se->error = -EINVAL;
604 break;
605 }
606
607 /* caller submits */
608}
609
610static void fuse_uring_handle_cqe(struct fuse_ring_queue *queue,
611 struct io_uring_cqe *cqe)
612{
613 struct fuse_ring_ent *ent = io_uring_cqe_get_data(cqe);
614
615 if (!ent) {
616 fuse_log(FUSE_LOG_ERR,
617 "cqe=%p io_uring_cqe_get_data returned NULL\n", cqe);
618 return;
619 }
620
621 struct fuse_req *req = &ent->req;
622 struct fuse_ring_pool *fuse_ring = queue->ring_pool;
623 struct fuse_uring_req_header *rrh = ent->req_header;
624
625 struct fuse_in_header *in = (struct fuse_in_header *)&rrh->in_out;
626 struct fuse_uring_ent_in_out *ent_in_out = &rrh->ring_ent_in_out;
627
628 ent->req_commit_id = ent_in_out->commit_id;
629 if (unlikely(ent->req_commit_id == 0)) {
630 /*
631 * If this happens kernel will not find the response - it will
632 * be stuck forever - better to abort immediately.
633 */
634 fuse_log(FUSE_LOG_ERR, "Received invalid commit_id=0\n");
635 abort();
636 }
637
638 memset(&req->flags, 0, sizeof(req->flags));
639 memset(&req->u, 0, sizeof(req->u));
640 req->flags.is_uring = 1;
641 req->ref_cnt++;
642 req->ch = NULL; /* not needed for uring */
643 req->interrupted = 0;
644 list_init_req(req);
645
646 fuse_session_process_uring_cqe(fuse_ring->se, req, in, &rrh->op_in,
647 ent->op_payload, ent_in_out->payload_sz);
648}
649
650static int fuse_uring_queue_handle_cqes(struct fuse_ring_queue *queue)
651{
652 struct fuse_ring_pool *ring_pool = queue->ring_pool;
653 struct fuse_session *se = ring_pool->se;
654 size_t num_completed = 0;
655 struct io_uring_cqe *cqe;
656 unsigned int head;
657 struct fuse_ring_ent *ent;
658 int ret = 0;
659
660 io_uring_for_each_cqe(&queue->ring, head, cqe) {
661 int err = 0;
662
663 num_completed++;
664
665 err = cqe->res;
666 if (unlikely(err != 0)) {
667 if (err > 0 && ((uintptr_t)io_uring_cqe_get_data(cqe) ==
668 (unsigned int)queue->eventfd)) {
669 /* teardown from eventfd */
670 return -ENOTCONN;
671 }
672
673
674 switch (err) {
675 case -EAGAIN:
676 fallthrough;
677 case -EINTR:
678 ent = io_uring_cqe_get_data(cqe);
679 fuse_uring_resubmit(queue, ent);
680 continue;
681 default:
682 break;
683 }
684
685 /* -ENOTCONN is ok on umount */
686 if (err != -ENOTCONN) {
687 se->error = cqe->res;
688
689 /* return first error */
690 if (ret == 0)
691 ret = err;
692 }
693
694 } else {
695 fuse_uring_handle_cqe(queue, cqe);
696 }
697 }
698
699 if (num_completed)
700 io_uring_cq_advance(&queue->ring, num_completed);
701
702 return ret == 0 ? 0 : num_completed;
703}
704
709static void fuse_uring_set_thread_core(int qid)
710{
711 cpu_set_t mask;
712 int rc;
713
714 CPU_ZERO(&mask);
715 CPU_SET(qid, &mask);
716 rc = sched_setaffinity(0, sizeof(cpu_set_t), &mask);
717 if (rc != 0)
718 fuse_log(FUSE_LOG_ERR, "Failed to bind qid=%d to its core: %s\n",
719 qid, strerror(errno));
720
721 if (0) {
722 const int policy = SCHED_IDLE;
723 const struct sched_param param = {
724 .sched_priority = sched_get_priority_min(policy),
725 };
726
727 /* Set the lowest possible priority, so that the application
728 * submitting requests is not moved away from the current core.
729 */
730 rc = sched_setscheduler(0, policy, &param);
731 if (rc != 0)
732 fuse_log(FUSE_LOG_ERR, "Failed to set scheduler: %s\n",
733 strerror(errno));
734 }
735}
736
737/*
738 * @return negative error code or io-uring file descriptor
739 */
740static int fuse_uring_init_queue(struct fuse_ring_queue *queue)
741{
742 struct fuse_ring_pool *ring = queue->ring_pool;
743 struct fuse_session *se = ring->se;
744 int res;
745 size_t page_sz = sysconf(_SC_PAGESIZE);
746
747 queue->eventfd = eventfd(0, EFD_CLOEXEC);
748 if (queue->eventfd < 0) {
749 res = -errno;
750 fuse_log(FUSE_LOG_ERR,
751 "Failed to create eventfd for qid %d: %s\n",
752 queue->qid, strerror(errno));
753 return res;
754 }
755
756 res = fuse_queue_setup_io_uring(&queue->ring, queue->qid,
757 ring->queue_depth, se->fd,
758 queue->eventfd);
759 if (res != 0) {
760 fuse_log(FUSE_LOG_ERR, "qid=%d io_uring init failed\n",
761 queue->qid);
762 return res;
763 }
764
765 queue->req_header_sz = ROUND_UP(sizeof(struct fuse_ring_ent),
766 page_sz);
767
768 for (size_t idx = 0; idx < ring->queue_depth; idx++) {
769 struct fuse_ring_ent *ring_ent = &queue->ent[idx];
770 struct fuse_req *req = &ring_ent->req;
771
772 ring_ent->ring_queue = queue;
773
774 /*
775 * Also allocate the header to have it page aligned, which
776 * is a requirement for page pinning
777 */
778 ring_ent->req_header =
779 numa_alloc_local(queue->req_header_sz);
780 if (!ring_ent->req_header)
781 return -ENOMEM;
782 ring_ent->req_payload_sz = ring->max_req_payload_sz;
783
784 ring_ent->op_payload =
785 numa_alloc_local(ring_ent->req_payload_sz);
786 if (!ring_ent->op_payload)
787 return -ENOMEM;
788
789 req->se = se;
790 pthread_mutex_init(&req->lock, NULL);
791 req->flags.is_uring = 1;
792 req->ref_cnt = 1; /* extra ref to avoid destruction */
793 list_init_req(req);
794 }
795
796 res = fuse_uring_register_queue(queue);
797 if (res != 0) {
798 fuse_log(
799 FUSE_LOG_ERR,
800 "Grave fuse-uring error on preparing SQEs, aborting\n");
801 se->error = -EIO;
803 return res;
804 }
805
806 return queue->ring.ring_fd;
807}
808
809static void *fuse_uring_thread(void *arg)
810{
811 struct fuse_ring_queue *queue = arg;
812 struct fuse_ring_pool *ring_pool = queue->ring_pool;
813 struct fuse_session *se = ring_pool->se;
814 int err;
815 char thread_name[16] = { 0 };
816
817 snprintf(thread_name, 16, "fuse-ring-%d", queue->qid);
818 thread_name[15] = '\0';
819 fuse_set_thread_name(thread_name);
820
821 fuse_uring_set_thread_core(queue->qid);
822
823 err = fuse_uring_init_queue(queue);
824 pthread_mutex_lock(&ring_pool->thread_start_mutex);
825 if (err < 0)
826 ring_pool->failed_threads++;
827 ring_pool->started_threads++;
828 pthread_cond_broadcast(&ring_pool->thread_start_cond);
829 pthread_mutex_unlock(&ring_pool->thread_start_mutex);
830
831 if (err < 0) {
832 fuse_log(FUSE_LOG_ERR, "qid=%d queue setup failed\n",
833 queue->qid);
834 goto err_non_fatal;
835 }
836
837 sem_wait(&ring_pool->init_sem);
838
839 /* Not using fuse_session_exited(se), as that cannot be inlined */
840 while (!atomic_load_explicit(&se->mt_exited, memory_order_relaxed)) {
841 io_uring_submit_and_wait(&queue->ring, 1);
842
843 pthread_mutex_lock(&queue->ring_lock);
844 queue->cqe_processing = true;
845 err = fuse_uring_queue_handle_cqes(queue);
846 queue->cqe_processing = false;
847 pthread_mutex_unlock(&queue->ring_lock);
848 if (err < 0)
849 goto err;
850 }
851
852 return NULL;
853
854err:
856err_non_fatal:
857 return NULL;
858}
859
860static int fuse_uring_start_ring_threads(struct fuse_ring_pool *ring)
861{
862 int rc = 0;
863
864 for (size_t qid = 0; qid < ring->nr_queues; qid++) {
865 struct fuse_ring_queue *queue = fuse_uring_get_queue(ring, qid);
866
867 rc = pthread_create(&queue->tid, NULL, fuse_uring_thread, queue);
868 if (rc != 0)
869 break;
870 }
871
872 return rc;
873}
874
875static int fuse_uring_sanity_check(struct fuse_session *se)
876{
877 if (se->uring.q_depth == 0) {
878 fuse_log(FUSE_LOG_ERR, "io-uring queue depth must be > 0\n");
879 return -EINVAL;
880 }
881
882 _Static_assert(sizeof(struct fuse_uring_cmd_req) <=
883 FUSE_URING_MAX_SQE128_CMD_DATA,
884 "SQE128_CMD_DATA has 80B cmd data");
885
886 return 0;
887}
888
889int fuse_uring_start(struct fuse_session *se)
890{
891 int err = 0;
892 struct fuse_ring_pool *fuse_ring;
893
894 fuse_uring_sanity_check(se);
895
896 fuse_ring = fuse_create_ring(se);
897 if (fuse_ring == NULL) {
898 err = -EADDRNOTAVAIL;
899 goto err;
900 }
901
902 se->uring.pool = fuse_ring;
903
904 /* Hold off threads from send fuse ring entries (SQEs) */
905 sem_init(&fuse_ring->init_sem, 0, 0);
906 pthread_cond_init(&fuse_ring->thread_start_cond, NULL);
907 pthread_mutex_init(&fuse_ring->thread_start_mutex, NULL);
908
909 err = fuse_uring_start_ring_threads(fuse_ring);
910 if (err)
911 goto err;
912
913 /*
914 * Wait for all threads to start or to fail
915 */
916 pthread_mutex_lock(&fuse_ring->thread_start_mutex);
917 while (fuse_ring->started_threads < fuse_ring->nr_queues)
918 pthread_cond_wait(&fuse_ring->thread_start_cond,
919 &fuse_ring->thread_start_mutex);
920
921 if (fuse_ring->failed_threads != 0)
922 err = -EADDRNOTAVAIL;
923 pthread_mutex_unlock(&fuse_ring->thread_start_mutex);
924
925err:
926 if (err) {
927 /* Note all threads need to have been started */
928 if (fuse_ring)
929 fuse_session_destruct_uring(fuse_ring);
930 se->uring.pool = NULL;
931 }
932 return err;
933}
934
935int fuse_uring_stop(struct fuse_session *se)
936{
937 struct fuse_ring_pool *ring = se->uring.pool;
938
939 if (ring == NULL)
940 return 0;
941
942 fuse_session_destruct_uring(ring);
943
944 return 0;
945}
946
947void fuse_uring_wake_ring_threads(struct fuse_session *se)
948{
949 struct fuse_ring_pool *ring = se->uring.pool;
950
951 /* Wake up the threads to let them send SQEs */
952 for (size_t qid = 0; qid < ring->nr_queues; qid++)
953 sem_post(&ring->init_sem);
954}
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
fuse_buf_copy_flags
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_exit(struct fuse_session *se)
struct fuse_req * fuse_req_t
int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
void * mem
size_t size
struct fuse_buf buf[1]
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2fuse__uring__i_8h_source.html0000644000175000017500000004213215156613430024142 0ustar berndbernd libfuse: fuse-3.18.2/lib/fuse_uring_i.h Source File
libfuse
fuse_uring_i.h
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2025 Bernd Schubert <bschubert@ddn.com>
4 * This program can be distributed under the terms of the GNU LGPLv2.
5 * See the file LGPL2.txt
6 */
7
8#ifndef FUSE_URING_I_H_
9#define FUSE_URING_I_H_
10
11#include "fuse_config.h"
12#include "fuse_lowlevel.h"
13#include "fuse_kernel.h"
14
15#ifndef HAVE_URING
16#include "util.h"
17#endif
18
19#include <errno.h> // IWYU pragma: keep
20
21/* io-uring defaults */
22#define SESSION_DEF_URING_ENABLE (0)
23#define SESSION_DEF_URING_Q_DEPTH (8)
24
25void fuse_session_process_uring_cqe(struct fuse_session *se,
26 struct fuse_req *req,
27 struct fuse_in_header *in, void *in_header,
28 void *in_payload, size_t payload_len);
29
30#ifdef HAVE_URING
31
32struct fuse_in_header;
33
34int fuse_uring_start(struct fuse_session *se);
35void fuse_uring_wake_ring_threads(struct fuse_session *se);
36int fuse_uring_stop(struct fuse_session *se);
37int send_reply_uring(fuse_req_t req, int error, const void *arg,
38 size_t argsize);
39
40int fuse_reply_data_uring(fuse_req_t req, struct fuse_bufvec *bufv,
41 enum fuse_buf_copy_flags flags);
42int fuse_send_msg_uring(fuse_req_t req, struct iovec *iov, int count);
43
44#else // HAVE_URING
45
46static inline int fuse_uring_start(struct fuse_session *se FUSE_VAR_UNUSED)
47{
48 return -ENOTSUP;
49}
50
51static inline void
52fuse_uring_wake_ring_threads(struct fuse_session *se FUSE_VAR_UNUSED)
53{
54}
55
56static inline int fuse_uring_stop(struct fuse_session *se FUSE_VAR_UNUSED)
57{
58 return -ENOTSUP;
59}
60
61static inline int send_reply_uring(fuse_req_t req FUSE_VAR_UNUSED,
62 int error FUSE_VAR_UNUSED,
63 const void *arg FUSE_VAR_UNUSED,
64 size_t argsize FUSE_VAR_UNUSED)
65{
66 return -ENOTSUP;
67}
68
69static inline int
70fuse_reply_data_uring(fuse_req_t req FUSE_VAR_UNUSED,
71 struct fuse_bufvec *bufv FUSE_VAR_UNUSED,
72 enum fuse_buf_copy_flags flags FUSE_VAR_UNUSED)
73{
74 return -ENOTSUP;
75}
76
77static inline int fuse_send_msg_uring(fuse_req_t req FUSE_VAR_UNUSED,
78 struct iovec *iov FUSE_VAR_UNUSED,
79 int count FUSE_VAR_UNUSED)
80{
81 return -ENOTSUP;
82}
83
84#endif // HAVE_URING
85
86#endif // FUSE_URING_I_H_
fuse_buf_copy_flags
struct fuse_req * fuse_req_t
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2helper_8c_source.html0000644000175000017500000032441015156613430022442 0ustar berndbernd libfuse: fuse-3.18.2/lib/helper.c Source File
libfuse
helper.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Helper functions to create (simple) standalone programs. With the
6 aid of these functions it should be possible to create full FUSE
7 file system by implementing nothing but the request handlers.
8
9 This program can be distributed under the terms of the GNU LGPLv2.
10 See the file LGPL2.txt.
11*/
12
13#include "fuse_config.h"
14#include "fuse_i.h"
15#include "fuse_misc.h"
16#include "fuse_opt.h"
17#include "fuse_lowlevel.h"
18#include "mount_util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <stddef.h>
23#include <unistd.h>
24#include <string.h>
25#include <limits.h>
26#include <errno.h>
27#include <sys/param.h>
28
29#define FUSE_HELPER_OPT(t, p) \
30 { t, offsetof(struct fuse_cmdline_opts, p), 1 }
31
32static const struct fuse_opt fuse_helper_opts[] = {
33 FUSE_HELPER_OPT("-h", show_help),
34 FUSE_HELPER_OPT("--help", show_help),
35 FUSE_HELPER_OPT("-V", show_version),
36 FUSE_HELPER_OPT("--version", show_version),
37 FUSE_HELPER_OPT("-d", debug),
38 FUSE_HELPER_OPT("debug", debug),
39 FUSE_HELPER_OPT("-d", foreground),
40 FUSE_HELPER_OPT("debug", foreground),
43 FUSE_HELPER_OPT("-f", foreground),
44 FUSE_HELPER_OPT("-s", singlethread),
45 FUSE_HELPER_OPT("fsname=", nodefault_subtype),
47#ifndef __FreeBSD__
48 FUSE_HELPER_OPT("subtype=", nodefault_subtype),
50#endif
51 FUSE_HELPER_OPT("clone_fd", clone_fd),
52 FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads),
53 FUSE_HELPER_OPT("max_threads=%u", max_threads),
55};
56
57struct fuse_conn_info_opts {
58 int atomic_o_trunc;
59 int no_remote_posix_lock;
60 int no_remote_flock;
61 int splice_write;
62 int splice_move;
63 int splice_read;
64 int no_splice_write;
65 int no_splice_move;
66 int no_splice_read;
67 int auto_inval_data;
68 int no_auto_inval_data;
69 int no_readdirplus;
70 int no_readdirplus_auto;
71 int async_dio;
72 int no_async_dio;
73 int writeback_cache;
74 int no_writeback_cache;
75 int async_read;
76 int sync_read;
77 unsigned max_write;
78 unsigned max_readahead;
79 unsigned max_background;
80 unsigned congestion_threshold;
81 unsigned time_gran;
82 int set_max_write;
83 int set_max_readahead;
84 int set_max_background;
85 int set_congestion_threshold;
86 int set_time_gran;
87};
88
89#define CONN_OPTION(t, p, v) \
90 { t, offsetof(struct fuse_conn_info_opts, p), v }
91static const struct fuse_opt conn_info_opt_spec[] = {
92 CONN_OPTION("max_write=%u", max_write, 0),
93 CONN_OPTION("max_write=", set_max_write, 1),
94 CONN_OPTION("max_readahead=%u", max_readahead, 0),
95 CONN_OPTION("max_readahead=", set_max_readahead, 1),
96 CONN_OPTION("max_background=%u", max_background, 0),
97 CONN_OPTION("max_background=", set_max_background, 1),
98 CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0),
99 CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1),
100 CONN_OPTION("sync_read", sync_read, 1),
101 CONN_OPTION("async_read", async_read, 1),
102 CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1),
103 CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1),
104 CONN_OPTION("no_remote_lock", no_remote_flock, 1),
105 CONN_OPTION("no_remote_flock", no_remote_flock, 1),
106 CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1),
107 CONN_OPTION("splice_write", splice_write, 1),
108 CONN_OPTION("no_splice_write", no_splice_write, 1),
109 CONN_OPTION("splice_move", splice_move, 1),
110 CONN_OPTION("no_splice_move", no_splice_move, 1),
111 CONN_OPTION("splice_read", splice_read, 1),
112 CONN_OPTION("no_splice_read", no_splice_read, 1),
113 CONN_OPTION("auto_inval_data", auto_inval_data, 1),
114 CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1),
115 CONN_OPTION("readdirplus=no", no_readdirplus, 1),
116 CONN_OPTION("readdirplus=yes", no_readdirplus, 0),
117 CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1),
118 CONN_OPTION("readdirplus=auto", no_readdirplus, 0),
119 CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0),
120 CONN_OPTION("async_dio", async_dio, 1),
121 CONN_OPTION("no_async_dio", no_async_dio, 1),
122 CONN_OPTION("writeback_cache", writeback_cache, 1),
123 CONN_OPTION("no_writeback_cache", no_writeback_cache, 1),
124 CONN_OPTION("time_gran=%u", time_gran, 0),
125 CONN_OPTION("time_gran=", set_time_gran, 1),
127};
128
129
130void fuse_cmdline_help(void)
131{
132 printf(" -h --help print help\n"
133 " -V --version print version\n"
134 " -d -o debug enable debug output (implies -f)\n"
135 " -f foreground operation\n"
136 " -s disable multi-threaded operation\n"
137 " -o clone_fd use separate fuse device fd for each thread\n"
138 " (may improve performance)\n"
139 " -o max_idle_threads the maximum number of idle worker threads\n"
140 " allowed (default: -1)\n"
141 " -o max_threads the maximum number of worker threads\n"
142 " allowed (default: 10)\n");
143}
144
145static int fuse_helper_opt_proc(void *data, const char *arg, int key,
146 struct fuse_args *outargs)
147{
148 (void) outargs;
149 struct fuse_cmdline_opts *opts = data;
150
151 switch (key) {
153 if (!opts->mountpoint) {
154 if (fuse_mnt_parse_fuse_fd(arg) != -1) {
155 return fuse_opt_add_opt(&opts->mountpoint, arg);
156 }
157
158 char mountpoint[PATH_MAX] = "";
159 if (realpath(arg, mountpoint) == NULL) {
160 fuse_log(FUSE_LOG_ERR,
161 "fuse: bad mount point `%s': %s\n",
162 arg, strerror(errno));
163 return -1;
164 }
165 return fuse_opt_add_opt(&opts->mountpoint, mountpoint);
166 } else {
167 fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg);
168 return -1;
169 }
170
171 default:
172 /* Pass through unknown options */
173 return 1;
174 }
175}
176
177/* Under FreeBSD, there is no subtype option so this
178 function actually sets the fsname */
179static int add_default_subtype(const char *progname, struct fuse_args *args)
180{
181 int res;
182 char *subtype_opt;
183
184 const char *basename = strrchr(progname, '/');
185 if (basename == NULL)
186 basename = progname;
187 else if (basename[1] != '\0')
188 basename++;
189
190 subtype_opt = (char *) malloc(strlen(basename) + 64);
191 if (subtype_opt == NULL) {
192 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
193 return -1;
194 }
195#ifdef __FreeBSD__
196 sprintf(subtype_opt, "-ofsname=%s", basename);
197#else
198 sprintf(subtype_opt, "-osubtype=%s", basename);
199#endif
200 res = fuse_opt_add_arg(args, subtype_opt);
201 free(subtype_opt);
202 return res;
203}
204
205int fuse_parse_cmdline_312(struct fuse_args *args,
206 struct fuse_cmdline_opts *opts);
207FUSE_SYMVER("fuse_parse_cmdline_312", "fuse_parse_cmdline@@FUSE_3.12")
208int fuse_parse_cmdline_312(struct fuse_args *args,
209 struct fuse_cmdline_opts *opts)
210{
211 memset(opts, 0, sizeof(struct fuse_cmdline_opts));
212
213 opts->max_idle_threads = UINT_MAX; /* new default in fuse version 3.12 */
214 opts->max_threads = 10;
215
216 if (fuse_opt_parse(args, opts, fuse_helper_opts,
217 fuse_helper_opt_proc) == -1)
218 return -1;
219
220 /* *Linux*: if neither -o subtype nor -o fsname are specified,
221 set subtype to program's basename.
222 *FreeBSD*: if fsname is not specified, set to program's
223 basename. */
224 if (!opts->nodefault_subtype)
225 if (add_default_subtype(args->argv[0], args) == -1)
226 return -1;
227
228 return 0;
229}
230
234int fuse_parse_cmdline_30(struct fuse_args *args,
235 struct fuse_cmdline_opts *opts);
236FUSE_SYMVER("fuse_parse_cmdline_30", "fuse_parse_cmdline@FUSE_3.0")
237int fuse_parse_cmdline_30(struct fuse_args *args,
238 struct fuse_cmdline_opts *out_opts)
239{
240 struct fuse_cmdline_opts opts;
241
242 int rc = fuse_parse_cmdline_312(args, &opts);
243 if (rc == 0) {
244 /* copy up to the size of the old pre 3.12 struct */
245 memcpy(out_opts, &opts,
246 offsetof(struct fuse_cmdline_opts, max_idle_threads) +
247 sizeof(opts.max_idle_threads));
248 }
249
250 return rc;
251}
252
253int fuse_daemonize(int foreground)
254{
255 if (!foreground) {
256 int nullfd;
257 int waiter[2];
258 char completed;
259
260 if (pipe(waiter)) {
261 perror("fuse_daemonize: pipe");
262 return -1;
263 }
264
265 /*
266 * demonize current process by forking it and killing the
267 * parent. This makes current process as a child of 'init'.
268 */
269 switch(fork()) {
270 case -1:
271 perror("fuse_daemonize: fork");
272 return -1;
273 case 0:
274 break;
275 default:
276 (void) read(waiter[0], &completed, sizeof(completed));
277 _exit(0);
278 }
279
280 if (setsid() == -1) {
281 perror("fuse_daemonize: setsid");
282 return -1;
283 }
284
285 (void) chdir("/");
286
287 nullfd = open("/dev/null", O_RDWR, 0);
288 if (nullfd != -1) {
289 (void) dup2(nullfd, 0);
290 (void) dup2(nullfd, 1);
291 (void) dup2(nullfd, 2);
292 if (nullfd > 2)
293 close(nullfd);
294 }
295
296 /* Propagate completion of daemon initialization */
297 completed = 1;
298 (void) write(waiter[1], &completed, sizeof(completed));
299 close(waiter[0]);
300 close(waiter[1]);
301 } else {
302 (void) chdir("/");
303 }
304 return 0;
305}
306
307int fuse_main_real_versioned(int argc, char *argv[],
308 const struct fuse_operations *op, size_t op_size,
309 struct libfuse_version *version, void *user_data)
310{
311 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
312 struct fuse *fuse;
313 struct fuse_cmdline_opts opts;
314 int res;
315 struct fuse_loop_config *loop_config = NULL;
316
317 if (fuse_parse_cmdline(&args, &opts) != 0)
318 return 1;
319
320 if (opts.show_version) {
321 printf("FUSE library version %s\n", PACKAGE_VERSION);
323 res = 0;
324 goto out1;
325 }
326
327 if (opts.show_help) {
328 if(args.argv[0][0] != '\0')
329 printf("usage: %s [options] <mountpoint>\n\n",
330 args.argv[0]);
331 printf("FUSE options:\n");
333 fuse_lib_help(&args);
334 res = 0;
335 goto out1;
336 }
337
338 if (!opts.show_help &&
339 !opts.mountpoint) {
340 fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n");
341 res = 2;
342 goto out1;
343 }
344
345 struct fuse *_fuse_new_31(struct fuse_args *args,
346 const struct fuse_operations *op, size_t op_size,
347 struct libfuse_version *version,
348 void *user_data);
349 fuse = _fuse_new_31(&args, op, op_size, version, user_data);
350 if (fuse == NULL) {
351 res = 3;
352 goto out1;
353 }
354
355 if (fuse_mount(fuse,opts.mountpoint) != 0) {
356 res = 4;
357 goto out2;
358 }
359
360 if (fuse_daemonize(opts.foreground) != 0) {
361 res = 5;
362 goto out3;
363 }
364
365 struct fuse_session *se = fuse_get_session(fuse);
366 if (fuse_set_signal_handlers(se) != 0) {
367 res = 6;
368 goto out3;
369 }
370
371 if (opts.singlethread)
372 res = fuse_loop(fuse);
373 else {
374 loop_config = fuse_loop_cfg_create();
375 if (loop_config == NULL) {
376 res = 7;
377 goto out3;
378 }
379
380 fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
381
382 fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);
383 fuse_loop_cfg_set_max_threads(loop_config, opts.max_threads);
384 res = fuse_loop_mt(fuse, loop_config);
385 }
386 if (res)
387 res = 8;
388
390out3:
391 fuse_unmount(fuse);
392out2:
393 fuse_destroy(fuse);
394out1:
395 fuse_loop_cfg_destroy(loop_config);
396 free(opts.mountpoint);
397 fuse_opt_free_args(&args);
398 return res;
399}
400
401/* Not symboled, as not part of the official API */
402int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
403 size_t op_size, void *user_data);
404int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
405 size_t op_size, void *user_data)
406{
407 struct libfuse_version version = { 0 };
408 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
409 user_data);
410}
411
412void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
413 struct fuse_conn_info *conn)
414{
415 if(opts->set_max_write)
416 conn->max_write = opts->max_write;
417 if(opts->set_max_background)
418 conn->max_background = opts->max_background;
419 if(opts->set_congestion_threshold)
420 conn->congestion_threshold = opts->congestion_threshold;
421 if(opts->set_time_gran)
422 conn->time_gran = opts->time_gran;
423 if(opts->set_max_readahead)
424 conn->max_readahead = opts->max_readahead;
425
426#define LL_ENABLE(cond, cap) \
427 do { \
428 if (cond) \
429 fuse_set_feature_flag(conn, cap); \
430 } while (0)
431
432#define LL_DISABLE(cond, cap) \
433 do { \
434 if (cond) \
435 fuse_unset_feature_flag(conn, cap); \
436 } while (0)
437
438 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
439 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
440
441 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
442 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
443
444 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
445 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
446
447 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
448 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
449
450 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
451 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
452
453 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
454 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
455
456 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
457 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
458
459 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
460 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
461
462 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
463 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
464}
465
466struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
467{
468 struct fuse_conn_info_opts *opts;
469
470 opts = calloc(1, sizeof(struct fuse_conn_info_opts));
471 if(opts == NULL) {
472 fuse_log(FUSE_LOG_ERR, "calloc failed\n");
473 return NULL;
474 }
475 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
476 free(opts);
477 return NULL;
478 }
479 return opts;
480}
481
482int fuse_open_channel(const char *mountpoint, const char* options)
483{
484 struct mount_opts *opts = NULL;
485 int fd = -1;
486 const char *argv[] = { "", "-o", options };
487 int argc = sizeof(argv) / sizeof(argv[0]);
488 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
489
490 opts = parse_mount_opts(&args);
491 if (opts == NULL)
492 return -1;
493
494 fd = fuse_kern_mount(mountpoint, opts);
495 destroy_mount_opts(opts);
496
497 return fd;
498}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
Definition helper.c:307
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_READDIRPLUS_AUTO
struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
Definition helper.c:466
#define FUSE_CAP_ASYNC_DIO
#define FUSE_CAP_READDIRPLUS
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_KEY_NONOPT
Definition fuse_opt.h:137
#define FUSE_OPT_KEY_KEEP
Definition fuse_opt.h:145
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
Definition helper.c:412
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
char ** argv
Definition fuse_opt.h:114
uint32_t time_gran
uint32_t congestion_threshold
uint32_t max_write
uint32_t max_readahead
uint32_t max_background
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2modules_2iconv_8c_source.html0000644000175000017500000035537415156613430024130 0ustar berndbernd libfuse: fuse-3.18.2/lib/modules/iconv.c Source File
libfuse
iconv.c
1/*
2 fuse iconv module: file name charset conversion
3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#include <fuse_config.h>
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17#include <iconv.h>
18#include <pthread.h>
19#include <locale.h>
20#include <langinfo.h>
21
22struct iconv {
23 struct fuse_fs *next;
24 pthread_mutex_t lock;
25 char *from_code;
26 char *to_code;
27 iconv_t tofs;
28 iconv_t fromfs;
29};
30
31struct iconv_dh {
32 struct iconv *ic;
33 void *prev_buf;
34 fuse_fill_dir_t prev_filler;
35};
36
37static struct iconv *iconv_get(void)
38{
40}
41
42static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
43 int fromfs)
44{
45 size_t pathlen;
46 size_t newpathlen;
47 char *newpath;
48 size_t plen;
49 char *p;
50 size_t res;
51 int err;
52
53 if (path == NULL) {
54 *newpathp = NULL;
55 return 0;
56 }
57
58 pathlen = strlen(path);
59 newpathlen = pathlen * 4;
60 newpath = malloc(newpathlen + 1);
61 if (!newpath)
62 return -ENOMEM;
63
64 plen = newpathlen;
65 p = newpath;
66 pthread_mutex_lock(&ic->lock);
67 do {
68 res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path,
69 &pathlen, &p, &plen);
70 if (res == (size_t) -1) {
71 char *tmp;
72 size_t inc;
73
74 err = -EILSEQ;
75 if (errno != E2BIG)
76 goto err;
77
78 inc = (pathlen + 1) * 4;
79 newpathlen += inc;
80 int dp = p - newpath;
81 tmp = realloc(newpath, newpathlen + 1);
82 err = -ENOMEM;
83 if (!tmp)
84 goto err;
85
86 p = tmp + dp;
87 plen += inc;
88 newpath = tmp;
89 }
90 } while (res == (size_t) -1);
91 pthread_mutex_unlock(&ic->lock);
92 *p = '\0';
93 *newpathp = newpath;
94 return 0;
95
96err:
97 iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL);
98 pthread_mutex_unlock(&ic->lock);
99 free(newpath);
100 return err;
101}
102
103static int iconv_getattr(const char *path, struct stat *stbuf,
104 struct fuse_file_info *fi)
105{
106 struct iconv *ic = iconv_get();
107 char *newpath;
108 int err = iconv_convpath(ic, path, &newpath, 0);
109 if (!err) {
110 err = fuse_fs_getattr(ic->next, newpath, stbuf, fi);
111 free(newpath);
112 }
113 return err;
114}
115
116static int iconv_access(const char *path, int mask)
117{
118 struct iconv *ic = iconv_get();
119 char *newpath;
120 int err = iconv_convpath(ic, path, &newpath, 0);
121 if (!err) {
122 err = fuse_fs_access(ic->next, newpath, mask);
123 free(newpath);
124 }
125 return err;
126}
127
128static int iconv_readlink(const char *path, char *buf, size_t size)
129{
130 struct iconv *ic = iconv_get();
131 char *newpath;
132 int err = iconv_convpath(ic, path, &newpath, 0);
133 if (!err) {
134 err = fuse_fs_readlink(ic->next, newpath, buf, size);
135 if (!err) {
136 char *newlink;
137 err = iconv_convpath(ic, buf, &newlink, 1);
138 if (!err) {
139 strncpy(buf, newlink, size - 1);
140 buf[size - 1] = '\0';
141 free(newlink);
142 }
143 }
144 free(newpath);
145 }
146 return err;
147}
148
149static int iconv_opendir(const char *path, struct fuse_file_info *fi)
150{
151 struct iconv *ic = iconv_get();
152 char *newpath;
153 int err = iconv_convpath(ic, path, &newpath, 0);
154 if (!err) {
155 err = fuse_fs_opendir(ic->next, newpath, fi);
156 free(newpath);
157 }
158 return err;
159}
160
161static int iconv_dir_fill(void *buf, const char *name,
162 const struct stat *stbuf, off_t off,
163 enum fuse_fill_dir_flags flags)
164{
165 struct iconv_dh *dh = buf;
166 char *newname;
167 int res = 0;
168 if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
169 res = dh->prev_filler(dh->prev_buf, newname, stbuf, off, flags);
170 free(newname);
171 }
172 return res;
173}
174
175static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
176 off_t offset, struct fuse_file_info *fi,
177 enum fuse_readdir_flags flags)
178{
179 struct iconv *ic = iconv_get();
180 char *newpath;
181 int err = iconv_convpath(ic, path, &newpath, 0);
182 if (!err) {
183 struct iconv_dh dh;
184 dh.ic = ic;
185 dh.prev_buf = buf;
186 dh.prev_filler = filler;
187 err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill,
188 offset, fi, flags);
189 free(newpath);
190 }
191 return err;
192}
193
194static int iconv_releasedir(const char *path, struct fuse_file_info *fi)
195{
196 struct iconv *ic = iconv_get();
197 char *newpath;
198 int err = iconv_convpath(ic, path, &newpath, 0);
199 if (!err) {
200 err = fuse_fs_releasedir(ic->next, newpath, fi);
201 free(newpath);
202 }
203 return err;
204}
205
206static int iconv_mknod(const char *path, mode_t mode, dev_t rdev)
207{
208 struct iconv *ic = iconv_get();
209 char *newpath;
210 int err = iconv_convpath(ic, path, &newpath, 0);
211 if (!err) {
212 err = fuse_fs_mknod(ic->next, newpath, mode, rdev);
213 free(newpath);
214 }
215 return err;
216}
217
218static int iconv_mkdir(const char *path, mode_t mode)
219{
220 struct iconv *ic = iconv_get();
221 char *newpath;
222 int err = iconv_convpath(ic, path, &newpath, 0);
223 if (!err) {
224 err = fuse_fs_mkdir(ic->next, newpath, mode);
225 free(newpath);
226 }
227 return err;
228}
229
230static int iconv_unlink(const char *path)
231{
232 struct iconv *ic = iconv_get();
233 char *newpath;
234 int err = iconv_convpath(ic, path, &newpath, 0);
235 if (!err) {
236 err = fuse_fs_unlink(ic->next, newpath);
237 free(newpath);
238 }
239 return err;
240}
241
242static int iconv_rmdir(const char *path)
243{
244 struct iconv *ic = iconv_get();
245 char *newpath;
246 int err = iconv_convpath(ic, path, &newpath, 0);
247 if (!err) {
248 err = fuse_fs_rmdir(ic->next, newpath);
249 free(newpath);
250 }
251 return err;
252}
253
254static int iconv_symlink(const char *from, const char *to)
255{
256 struct iconv *ic = iconv_get();
257 char *newfrom;
258 char *newto;
259 int err = iconv_convpath(ic, from, &newfrom, 0);
260 if (!err) {
261 err = iconv_convpath(ic, to, &newto, 0);
262 if (!err) {
263 err = fuse_fs_symlink(ic->next, newfrom, newto);
264 free(newto);
265 }
266 free(newfrom);
267 }
268 return err;
269}
270
271static int iconv_rename(const char *from, const char *to, unsigned int flags)
272{
273 struct iconv *ic = iconv_get();
274 char *newfrom;
275 char *newto;
276 int err = iconv_convpath(ic, from, &newfrom, 0);
277 if (!err) {
278 err = iconv_convpath(ic, to, &newto, 0);
279 if (!err) {
280 err = fuse_fs_rename(ic->next, newfrom, newto, flags);
281 free(newto);
282 }
283 free(newfrom);
284 }
285 return err;
286}
287
288static int iconv_link(const char *from, const char *to)
289{
290 struct iconv *ic = iconv_get();
291 char *newfrom;
292 char *newto;
293 int err = iconv_convpath(ic, from, &newfrom, 0);
294 if (!err) {
295 err = iconv_convpath(ic, to, &newto, 0);
296 if (!err) {
297 err = fuse_fs_link(ic->next, newfrom, newto);
298 free(newto);
299 }
300 free(newfrom);
301 }
302 return err;
303}
304
305static int iconv_chmod(const char *path, mode_t mode,
306 struct fuse_file_info *fi)
307{
308 struct iconv *ic = iconv_get();
309 char *newpath;
310 int err = iconv_convpath(ic, path, &newpath, 0);
311 if (!err) {
312 err = fuse_fs_chmod(ic->next, newpath, mode, fi);
313 free(newpath);
314 }
315 return err;
316}
317
318static int iconv_chown(const char *path, uid_t uid, gid_t gid,
319 struct fuse_file_info *fi)
320{
321 struct iconv *ic = iconv_get();
322 char *newpath;
323 int err = iconv_convpath(ic, path, &newpath, 0);
324 if (!err) {
325 err = fuse_fs_chown(ic->next, newpath, uid, gid, fi);
326 free(newpath);
327 }
328 return err;
329}
330
331static int iconv_truncate(const char *path, off_t size,
332 struct fuse_file_info *fi)
333{
334 struct iconv *ic = iconv_get();
335 char *newpath;
336 int err = iconv_convpath(ic, path, &newpath, 0);
337 if (!err) {
338 err = fuse_fs_truncate(ic->next, newpath, size, fi);
339 free(newpath);
340 }
341 return err;
342}
343
344static int iconv_utimens(const char *path, const struct timespec ts[2],
345 struct fuse_file_info *fi)
346{
347 struct iconv *ic = iconv_get();
348 char *newpath;
349 int err = iconv_convpath(ic, path, &newpath, 0);
350 if (!err) {
351 err = fuse_fs_utimens(ic->next, newpath, ts, fi);
352 free(newpath);
353 }
354 return err;
355}
356
357static int iconv_create(const char *path, mode_t mode,
358 struct fuse_file_info *fi)
359{
360 struct iconv *ic = iconv_get();
361 char *newpath;
362 int err = iconv_convpath(ic, path, &newpath, 0);
363 if (!err) {
364 err = fuse_fs_create(ic->next, newpath, mode, fi);
365 free(newpath);
366 }
367 return err;
368}
369
370static int iconv_open_file(const char *path, struct fuse_file_info *fi)
371{
372 struct iconv *ic = iconv_get();
373 char *newpath;
374 int err = iconv_convpath(ic, path, &newpath, 0);
375 if (!err) {
376 err = fuse_fs_open(ic->next, newpath, fi);
377 free(newpath);
378 }
379 return err;
380}
381
382static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
383 size_t size, off_t offset, struct fuse_file_info *fi)
384{
385 struct iconv *ic = iconv_get();
386 char *newpath;
387 int err = iconv_convpath(ic, path, &newpath, 0);
388 if (!err) {
389 err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi);
390 free(newpath);
391 }
392 return err;
393}
394
395static int iconv_write_buf(const char *path, struct fuse_bufvec *buf,
396 off_t offset, struct fuse_file_info *fi)
397{
398 struct iconv *ic = iconv_get();
399 char *newpath;
400 int err = iconv_convpath(ic, path, &newpath, 0);
401 if (!err) {
402 err = fuse_fs_write_buf(ic->next, newpath, buf, offset, fi);
403 free(newpath);
404 }
405 return err;
406}
407
408static int iconv_statfs(const char *path, struct statvfs *stbuf)
409{
410 struct iconv *ic = iconv_get();
411 char *newpath;
412 int err = iconv_convpath(ic, path, &newpath, 0);
413 if (!err) {
414 err = fuse_fs_statfs(ic->next, newpath, stbuf);
415 free(newpath);
416 }
417 return err;
418}
419
420static int iconv_flush(const char *path, struct fuse_file_info *fi)
421{
422 struct iconv *ic = iconv_get();
423 char *newpath;
424 int err = iconv_convpath(ic, path, &newpath, 0);
425 if (!err) {
426 err = fuse_fs_flush(ic->next, newpath, fi);
427 free(newpath);
428 }
429 return err;
430}
431
432static int iconv_release(const char *path, struct fuse_file_info *fi)
433{
434 struct iconv *ic = iconv_get();
435 char *newpath;
436 int err = iconv_convpath(ic, path, &newpath, 0);
437 if (!err) {
438 err = fuse_fs_release(ic->next, newpath, fi);
439 free(newpath);
440 }
441 return err;
442}
443
444static int iconv_fsync(const char *path, int isdatasync,
445 struct fuse_file_info *fi)
446{
447 struct iconv *ic = iconv_get();
448 char *newpath;
449 int err = iconv_convpath(ic, path, &newpath, 0);
450 if (!err) {
451 err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi);
452 free(newpath);
453 }
454 return err;
455}
456
457static int iconv_fsyncdir(const char *path, int isdatasync,
458 struct fuse_file_info *fi)
459{
460 struct iconv *ic = iconv_get();
461 char *newpath;
462 int err = iconv_convpath(ic, path, &newpath, 0);
463 if (!err) {
464 err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi);
465 free(newpath);
466 }
467 return err;
468}
469
470static int iconv_setxattr(const char *path, const char *name,
471 const char *value, size_t size, int flags)
472{
473 struct iconv *ic = iconv_get();
474 char *newpath;
475 int err = iconv_convpath(ic, path, &newpath, 0);
476 if (!err) {
477 err = fuse_fs_setxattr(ic->next, newpath, name, value, size,
478 flags);
479 free(newpath);
480 }
481 return err;
482}
483
484static int iconv_getxattr(const char *path, const char *name, char *value,
485 size_t size)
486{
487 struct iconv *ic = iconv_get();
488 char *newpath;
489 int err = iconv_convpath(ic, path, &newpath, 0);
490 if (!err) {
491 err = fuse_fs_getxattr(ic->next, newpath, name, value, size);
492 free(newpath);
493 }
494 return err;
495}
496
497static int iconv_listxattr(const char *path, char *list, size_t size)
498{
499 struct iconv *ic = iconv_get();
500 char *newpath;
501 int err = iconv_convpath(ic, path, &newpath, 0);
502 if (!err) {
503 err = fuse_fs_listxattr(ic->next, newpath, list, size);
504 free(newpath);
505 }
506 return err;
507}
508
509static int iconv_removexattr(const char *path, const char *name)
510{
511 struct iconv *ic = iconv_get();
512 char *newpath;
513 int err = iconv_convpath(ic, path, &newpath, 0);
514 if (!err) {
515 err = fuse_fs_removexattr(ic->next, newpath, name);
516 free(newpath);
517 }
518 return err;
519}
520
521static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd,
522 struct flock *lock)
523{
524 struct iconv *ic = iconv_get();
525 char *newpath;
526 int err = iconv_convpath(ic, path, &newpath, 0);
527 if (!err) {
528 err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock);
529 free(newpath);
530 }
531 return err;
532}
533
534static int iconv_flock(const char *path, struct fuse_file_info *fi, int op)
535{
536 struct iconv *ic = iconv_get();
537 char *newpath;
538 int err = iconv_convpath(ic, path, &newpath, 0);
539 if (!err) {
540 err = fuse_fs_flock(ic->next, newpath, fi, op);
541 free(newpath);
542 }
543 return err;
544}
545
546static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
547{
548 struct iconv *ic = iconv_get();
549 char *newpath;
550 int err = iconv_convpath(ic, path, &newpath, 0);
551 if (!err) {
552 err = fuse_fs_bmap(ic->next, newpath, blocksize, idx);
553 free(newpath);
554 }
555 return err;
556}
557
558static off_t iconv_lseek(const char *path, off_t off, int whence,
559 struct fuse_file_info *fi)
560{
561 struct iconv *ic = iconv_get();
562 char *newpath;
563 int res = iconv_convpath(ic, path, &newpath, 0);
564 if (!res) {
565 res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
566 free(newpath);
567 }
568 return res;
569}
570
571#ifdef HAVE_STATX
572static int iconv_statx(const char *path, int flags, int mask, struct statx *stxbuf,
573 struct fuse_file_info *fi)
574{
575 struct iconv *ic = iconv_get();
576 char *newpath;
577 int res = iconv_convpath(ic, path, &newpath, 0);
578
579 if (!res) {
580 res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi);
581 free(newpath);
582 }
583 return res;
584}
585#endif
586
587static void *iconv_init(struct fuse_conn_info *conn,
588 struct fuse_config *cfg)
589{
590 struct iconv *ic = iconv_get();
591 fuse_fs_init(ic->next, conn, cfg);
592 /* Don't touch cfg->nullpath_ok, we can work with
593 either */
594 return ic;
595}
596
597static void iconv_destroy(void *data)
598{
599 struct iconv *ic = data;
600 fuse_fs_destroy(ic->next);
601 iconv_close(ic->tofs);
602 iconv_close(ic->fromfs);
603 pthread_mutex_destroy(&ic->lock);
604 free(ic->from_code);
605 free(ic->to_code);
606 free(ic);
607}
608
609static const struct fuse_operations iconv_oper = {
610 .destroy = iconv_destroy,
611 .init = iconv_init,
612 .getattr = iconv_getattr,
613 .access = iconv_access,
614 .readlink = iconv_readlink,
615 .opendir = iconv_opendir,
616 .readdir = iconv_readdir,
617 .releasedir = iconv_releasedir,
618 .mknod = iconv_mknod,
619 .mkdir = iconv_mkdir,
620 .symlink = iconv_symlink,
621 .unlink = iconv_unlink,
622 .rmdir = iconv_rmdir,
623 .rename = iconv_rename,
624 .link = iconv_link,
625 .chmod = iconv_chmod,
626 .chown = iconv_chown,
627 .truncate = iconv_truncate,
628 .utimens = iconv_utimens,
629 .create = iconv_create,
630 .open = iconv_open_file,
631 .read_buf = iconv_read_buf,
632 .write_buf = iconv_write_buf,
633 .statfs = iconv_statfs,
634 .flush = iconv_flush,
635 .release = iconv_release,
636 .fsync = iconv_fsync,
637 .fsyncdir = iconv_fsyncdir,
638 .setxattr = iconv_setxattr,
639 .getxattr = iconv_getxattr,
640 .listxattr = iconv_listxattr,
641 .removexattr = iconv_removexattr,
642 .lock = iconv_lock,
643 .flock = iconv_flock,
644 .bmap = iconv_bmap,
645 .lseek = iconv_lseek,
646#ifdef HAVE_STATX
647 .statx = iconv_statx,
648#endif
649};
650
651static const struct fuse_opt iconv_opts[] = {
652 FUSE_OPT_KEY("-h", 0),
653 FUSE_OPT_KEY("--help", 0),
654 { "from_code=%s", offsetof(struct iconv, from_code), 0 },
655 { "to_code=%s", offsetof(struct iconv, to_code), 1 },
657};
658
659static void iconv_help(void)
660{
661 char *charmap;
662 const char *old = setlocale(LC_CTYPE, "");
663
664 charmap = strdup(nl_langinfo(CODESET));
665 if (old)
666 setlocale(LC_CTYPE, old);
667 else
668 perror("setlocale");
669
670 printf(
671" -o from_code=CHARSET original encoding of file names (default: UTF-8)\n"
672" -o to_code=CHARSET new encoding of the file names (default: %s)\n",
673 charmap);
674 free(charmap);
675}
676
677static int iconv_opt_proc(void *data, const char *arg, int key,
678 struct fuse_args *outargs)
679{
680 (void) data; (void) arg; (void) outargs;
681
682 if (!key) {
683 iconv_help();
684 return -1;
685 }
686
687 return 1;
688}
689
690static struct fuse_fs *iconv_new(struct fuse_args *args,
691 struct fuse_fs *next[])
692{
693 struct fuse_fs *fs;
694 struct iconv *ic;
695 const char *old = NULL;
696 const char *from;
697 const char *to;
698
699 ic = calloc(1, sizeof(struct iconv));
700 if (ic == NULL) {
701 fuse_log(FUSE_LOG_ERR, "fuse-iconv: memory allocation failed\n");
702 return NULL;
703 }
704
705 if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
706 goto out_free;
707
708 if (!next[0] || next[1]) {
709 fuse_log(FUSE_LOG_ERR, "fuse-iconv: exactly one next filesystem required\n");
710 goto out_free;
711 }
712
713 from = ic->from_code ? ic->from_code : "UTF-8";
714 to = ic->to_code ? ic->to_code : "";
715 /* FIXME: detect charset equivalence? */
716 if (!to[0])
717 old = setlocale(LC_CTYPE, "");
718 ic->tofs = iconv_open(from, to);
719 if (ic->tofs == (iconv_t) -1) {
720 fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
721 to, from);
722 goto out_free;
723 }
724 ic->fromfs = iconv_open(to, from);
725 if (ic->tofs == (iconv_t) -1) {
726 fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
727 from, to);
728 goto out_iconv_close_to;
729 }
730 if (old) {
731 setlocale(LC_CTYPE, old);
732 old = NULL;
733 }
734
735 ic->next = next[0];
736 fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
737 if (!fs)
738 goto out_iconv_close_from;
739
740 return fs;
741
742out_iconv_close_from:
743 iconv_close(ic->fromfs);
744out_iconv_close_to:
745 iconv_close(ic->tofs);
746out_free:
747 free(ic->from_code);
748 free(ic->to_code);
749 free(ic);
750 if (old) {
751 setlocale(LC_CTYPE, old);
752 }
753 return NULL;
754}
755
756FUSE_REGISTER_MODULE(iconv, iconv_new);
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
fuse_fill_dir_flags
Definition fuse.h:58
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition fuse.h:1395
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
void * private_data
Definition fuse.h:874
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2modules_2subdir_8c_source.html0000644000175000017500000034101215156613430024262 0ustar berndbernd libfuse: fuse-3.18.2/lib/modules/subdir.c Source File
libfuse
subdir.c
1/*
2 fuse subdir module: offset paths with a base directory
3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt
7*/
8
9#include <fuse_config.h>
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17
18struct subdir {
19 char *base;
20 size_t baselen;
21 int rellinks;
22 struct fuse_fs *next;
23};
24
25static struct subdir *subdir_get(void)
26{
28}
29
30static int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
31{
32 char *newpath = NULL;
33
34 if (path != NULL) {
35 unsigned newlen = d->baselen + strlen(path);
36
37 newpath = malloc(newlen + 2);
38 if (!newpath)
39 return -ENOMEM;
40
41 if (path[0] == '/')
42 path++;
43 strcpy(newpath, d->base);
44 strcpy(newpath + d->baselen, path);
45 if (!newpath[0])
46 strcpy(newpath, ".");
47 }
48 *newpathp = newpath;
49
50 return 0;
51}
52
53static int subdir_getattr(const char *path, struct stat *stbuf,
54 struct fuse_file_info *fi)
55{
56 struct subdir *d = subdir_get();
57 char *newpath;
58 int err = subdir_addpath(d, path, &newpath);
59 if (!err) {
60 err = fuse_fs_getattr(d->next, newpath, stbuf, fi);
61 free(newpath);
62 }
63 return err;
64}
65
66static int subdir_access(const char *path, int mask)
67{
68 struct subdir *d = subdir_get();
69 char *newpath;
70 int err = subdir_addpath(d, path, &newpath);
71 if (!err) {
72 err = fuse_fs_access(d->next, newpath, mask);
73 free(newpath);
74 }
75 return err;
76}
77
78
79static int count_components(const char *p)
80{
81 int ctr;
82
83 for (; *p == '/'; p++);
84 for (ctr = 0; *p; ctr++) {
85 for (; *p && *p != '/'; p++);
86 for (; *p == '/'; p++);
87 }
88 return ctr;
89}
90
91static void strip_common(const char **sp, const char **tp)
92{
93 const char *s = *sp;
94 const char *t = *tp;
95 do {
96 for (; *s == '/'; s++);
97 for (; *t == '/'; t++);
98 *tp = t;
99 *sp = s;
100 for (; *s == *t && *s && *s != '/'; s++, t++);
101 } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
102}
103
104static void transform_symlink(struct subdir *d, const char *path,
105 char *buf, size_t size)
106{
107 const char *l = buf;
108 size_t llen;
109 char *s;
110 int dotdots;
111 int i;
112
113 if (l[0] != '/' || d->base[0] != '/')
114 return;
115
116 strip_common(&l, &path);
117 if (l - buf < (long) d->baselen)
118 return;
119
120 dotdots = count_components(path);
121 if (!dotdots)
122 return;
123 dotdots--;
124
125 llen = strlen(l);
126 if (dotdots * 3 + llen + 2 > size)
127 return;
128
129 s = buf + dotdots * 3;
130 if (llen)
131 memmove(s, l, llen + 1);
132 else if (!dotdots)
133 strcpy(s, ".");
134 else
135 *s = '\0';
136
137 for (s = buf, i = 0; i < dotdots; i++, s += 3)
138 memcpy(s, "../", 3);
139}
140
141
142static int subdir_readlink(const char *path, char *buf, size_t size)
143{
144 struct subdir *d = subdir_get();
145 char *newpath;
146 int err = subdir_addpath(d, path, &newpath);
147 if (!err) {
148 err = fuse_fs_readlink(d->next, newpath, buf, size);
149 if (!err && d->rellinks)
150 transform_symlink(d, newpath, buf, size);
151 free(newpath);
152 }
153 return err;
154}
155
156static int subdir_opendir(const char *path, struct fuse_file_info *fi)
157{
158 struct subdir *d = subdir_get();
159 char *newpath;
160 int err = subdir_addpath(d, path, &newpath);
161 if (!err) {
162 err = fuse_fs_opendir(d->next, newpath, fi);
163 free(newpath);
164 }
165 return err;
166}
167
168static int subdir_readdir(const char *path, void *buf,
169 fuse_fill_dir_t filler, off_t offset,
170 struct fuse_file_info *fi,
171 enum fuse_readdir_flags flags)
172{
173 struct subdir *d = subdir_get();
174 char *newpath;
175 int err = subdir_addpath(d, path, &newpath);
176 if (!err) {
177 err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
178 fi, flags);
179 free(newpath);
180 }
181 return err;
182}
183
184static int subdir_releasedir(const char *path, struct fuse_file_info *fi)
185{
186 struct subdir *d = subdir_get();
187 char *newpath;
188 int err = subdir_addpath(d, path, &newpath);
189 if (!err) {
190 err = fuse_fs_releasedir(d->next, newpath, fi);
191 free(newpath);
192 }
193 return err;
194}
195
196static int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
197{
198 struct subdir *d = subdir_get();
199 char *newpath;
200 int err = subdir_addpath(d, path, &newpath);
201 if (!err) {
202 err = fuse_fs_mknod(d->next, newpath, mode, rdev);
203 free(newpath);
204 }
205 return err;
206}
207
208static int subdir_mkdir(const char *path, mode_t mode)
209{
210 struct subdir *d = subdir_get();
211 char *newpath;
212 int err = subdir_addpath(d, path, &newpath);
213 if (!err) {
214 err = fuse_fs_mkdir(d->next, newpath, mode);
215 free(newpath);
216 }
217 return err;
218}
219
220static int subdir_unlink(const char *path)
221{
222 struct subdir *d = subdir_get();
223 char *newpath;
224 int err = subdir_addpath(d, path, &newpath);
225 if (!err) {
226 err = fuse_fs_unlink(d->next, newpath);
227 free(newpath);
228 }
229 return err;
230}
231
232static int subdir_rmdir(const char *path)
233{
234 struct subdir *d = subdir_get();
235 char *newpath;
236 int err = subdir_addpath(d, path, &newpath);
237 if (!err) {
238 err = fuse_fs_rmdir(d->next, newpath);
239 free(newpath);
240 }
241 return err;
242}
243
244static int subdir_symlink(const char *from, const char *path)
245{
246 struct subdir *d = subdir_get();
247 char *newpath;
248 int err = subdir_addpath(d, path, &newpath);
249 if (!err) {
250 err = fuse_fs_symlink(d->next, from, newpath);
251 free(newpath);
252 }
253 return err;
254}
255
256static int subdir_rename(const char *from, const char *to, unsigned int flags)
257{
258 struct subdir *d = subdir_get();
259 char *newfrom;
260 char *newto;
261 int err = subdir_addpath(d, from, &newfrom);
262 if (!err) {
263 err = subdir_addpath(d, to, &newto);
264 if (!err) {
265 err = fuse_fs_rename(d->next, newfrom, newto, flags);
266 free(newto);
267 }
268 free(newfrom);
269 }
270 return err;
271}
272
273static int subdir_link(const char *from, const char *to)
274{
275 struct subdir *d = subdir_get();
276 char *newfrom;
277 char *newto;
278 int err = subdir_addpath(d, from, &newfrom);
279 if (!err) {
280 err = subdir_addpath(d, to, &newto);
281 if (!err) {
282 err = fuse_fs_link(d->next, newfrom, newto);
283 free(newto);
284 }
285 free(newfrom);
286 }
287 return err;
288}
289
290static int subdir_chmod(const char *path, mode_t mode,
291 struct fuse_file_info *fi)
292{
293 struct subdir *d = subdir_get();
294 char *newpath;
295 int err = subdir_addpath(d, path, &newpath);
296 if (!err) {
297 err = fuse_fs_chmod(d->next, newpath, mode, fi);
298 free(newpath);
299 }
300 return err;
301}
302
303static int subdir_chown(const char *path, uid_t uid, gid_t gid,
304 struct fuse_file_info *fi)
305{
306 struct subdir *d = subdir_get();
307 char *newpath;
308 int err = subdir_addpath(d, path, &newpath);
309 if (!err) {
310 err = fuse_fs_chown(d->next, newpath, uid, gid, fi);
311 free(newpath);
312 }
313 return err;
314}
315
316static int subdir_truncate(const char *path, off_t size,
317 struct fuse_file_info *fi)
318{
319 struct subdir *d = subdir_get();
320 char *newpath;
321 int err = subdir_addpath(d, path, &newpath);
322 if (!err) {
323 err = fuse_fs_truncate(d->next, newpath, size, fi);
324 free(newpath);
325 }
326 return err;
327}
328
329static int subdir_utimens(const char *path, const struct timespec ts[2],
330 struct fuse_file_info *fi)
331{
332 struct subdir *d = subdir_get();
333 char *newpath;
334 int err = subdir_addpath(d, path, &newpath);
335 if (!err) {
336 err = fuse_fs_utimens(d->next, newpath, ts, fi);
337 free(newpath);
338 }
339 return err;
340}
341
342static int subdir_create(const char *path, mode_t mode,
343 struct fuse_file_info *fi)
344{
345 struct subdir *d = subdir_get();
346 char *newpath;
347 int err = subdir_addpath(d, path, &newpath);
348 if (!err) {
349 err = fuse_fs_create(d->next, newpath, mode, fi);
350 free(newpath);
351 }
352 return err;
353}
354
355static int subdir_open(const char *path, struct fuse_file_info *fi)
356{
357 struct subdir *d = subdir_get();
358 char *newpath;
359 int err = subdir_addpath(d, path, &newpath);
360 if (!err) {
361 err = fuse_fs_open(d->next, newpath, fi);
362 free(newpath);
363 }
364 return err;
365}
366
367static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
368 size_t size, off_t offset, struct fuse_file_info *fi)
369{
370 struct subdir *d = subdir_get();
371 char *newpath;
372 int err = subdir_addpath(d, path, &newpath);
373 if (!err) {
374 err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
375 free(newpath);
376 }
377 return err;
378}
379
380static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
381 off_t offset, struct fuse_file_info *fi)
382{
383 struct subdir *d = subdir_get();
384 char *newpath;
385 int err = subdir_addpath(d, path, &newpath);
386 if (!err) {
387 err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
388 free(newpath);
389 }
390 return err;
391}
392
393static int subdir_statfs(const char *path, struct statvfs *stbuf)
394{
395 struct subdir *d = subdir_get();
396 char *newpath;
397 int err = subdir_addpath(d, path, &newpath);
398 if (!err) {
399 err = fuse_fs_statfs(d->next, newpath, stbuf);
400 free(newpath);
401 }
402 return err;
403}
404
405static int subdir_flush(const char *path, struct fuse_file_info *fi)
406{
407 struct subdir *d = subdir_get();
408 char *newpath;
409 int err = subdir_addpath(d, path, &newpath);
410 if (!err) {
411 err = fuse_fs_flush(d->next, newpath, fi);
412 free(newpath);
413 }
414 return err;
415}
416
417static int subdir_release(const char *path, struct fuse_file_info *fi)
418{
419 struct subdir *d = subdir_get();
420 char *newpath;
421 int err = subdir_addpath(d, path, &newpath);
422 if (!err) {
423 err = fuse_fs_release(d->next, newpath, fi);
424 free(newpath);
425 }
426 return err;
427}
428
429static int subdir_fsync(const char *path, int isdatasync,
430 struct fuse_file_info *fi)
431{
432 struct subdir *d = subdir_get();
433 char *newpath;
434 int err = subdir_addpath(d, path, &newpath);
435 if (!err) {
436 err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
437 free(newpath);
438 }
439 return err;
440}
441
442static int subdir_fsyncdir(const char *path, int isdatasync,
443 struct fuse_file_info *fi)
444{
445 struct subdir *d = subdir_get();
446 char *newpath;
447 int err = subdir_addpath(d, path, &newpath);
448 if (!err) {
449 err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
450 free(newpath);
451 }
452 return err;
453}
454
455static int subdir_setxattr(const char *path, const char *name,
456 const char *value, size_t size, int flags)
457{
458 struct subdir *d = subdir_get();
459 char *newpath;
460 int err = subdir_addpath(d, path, &newpath);
461 if (!err) {
462 err = fuse_fs_setxattr(d->next, newpath, name, value, size,
463 flags);
464 free(newpath);
465 }
466 return err;
467}
468
469static int subdir_getxattr(const char *path, const char *name, char *value,
470 size_t size)
471{
472 struct subdir *d = subdir_get();
473 char *newpath;
474 int err = subdir_addpath(d, path, &newpath);
475 if (!err) {
476 err = fuse_fs_getxattr(d->next, newpath, name, value, size);
477 free(newpath);
478 }
479 return err;
480}
481
482static int subdir_listxattr(const char *path, char *list, size_t size)
483{
484 struct subdir *d = subdir_get();
485 char *newpath;
486 int err = subdir_addpath(d, path, &newpath);
487 if (!err) {
488 err = fuse_fs_listxattr(d->next, newpath, list, size);
489 free(newpath);
490 }
491 return err;
492}
493
494static int subdir_removexattr(const char *path, const char *name)
495{
496 struct subdir *d = subdir_get();
497 char *newpath;
498 int err = subdir_addpath(d, path, &newpath);
499 if (!err) {
500 err = fuse_fs_removexattr(d->next, newpath, name);
501 free(newpath);
502 }
503 return err;
504}
505
506static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
507 struct flock *lock)
508{
509 struct subdir *d = subdir_get();
510 char *newpath;
511 int err = subdir_addpath(d, path, &newpath);
512 if (!err) {
513 err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
514 free(newpath);
515 }
516 return err;
517}
518
519static int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
520{
521 struct subdir *d = subdir_get();
522 char *newpath;
523 int err = subdir_addpath(d, path, &newpath);
524 if (!err) {
525 err = fuse_fs_flock(d->next, newpath, fi, op);
526 free(newpath);
527 }
528 return err;
529}
530
531static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
532{
533 struct subdir *d = subdir_get();
534 char *newpath;
535 int err = subdir_addpath(d, path, &newpath);
536 if (!err) {
537 err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
538 free(newpath);
539 }
540 return err;
541}
542
543static off_t subdir_lseek(const char *path, off_t off, int whence,
544 struct fuse_file_info *fi)
545{
546 struct subdir *ic = subdir_get();
547 char *newpath;
548 int res = subdir_addpath(ic, path, &newpath);
549 if (!res) {
550 res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
551 free(newpath);
552 }
553 return res;
554}
555
556#ifdef HAVE_STATX
557static int subdir_statx(const char *path, int flags, int mask, struct statx *stxbuf,
558 struct fuse_file_info *fi)
559{
560 struct subdir *ic = subdir_get();
561 char *newpath;
562 int res = subdir_addpath(ic, path, &newpath);
563
564 if (!res) {
565 res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi);
566 free(newpath);
567 }
568 return res;
569}
570#endif
571
572static void *subdir_init(struct fuse_conn_info *conn,
573 struct fuse_config *cfg)
574{
575 struct subdir *d = subdir_get();
576 fuse_fs_init(d->next, conn, cfg);
577 /* Don't touch cfg->nullpath_ok, we can work with
578 either */
579 return d;
580}
581
582static void subdir_destroy(void *data)
583{
584 struct subdir *d = data;
585 fuse_fs_destroy(d->next);
586 free(d->base);
587 free(d);
588}
589
590static const struct fuse_operations subdir_oper = {
591 .destroy = subdir_destroy,
592 .init = subdir_init,
593 .getattr = subdir_getattr,
594 .access = subdir_access,
595 .readlink = subdir_readlink,
596 .opendir = subdir_opendir,
597 .readdir = subdir_readdir,
598 .releasedir = subdir_releasedir,
599 .mknod = subdir_mknod,
600 .mkdir = subdir_mkdir,
601 .symlink = subdir_symlink,
602 .unlink = subdir_unlink,
603 .rmdir = subdir_rmdir,
604 .rename = subdir_rename,
605 .link = subdir_link,
606 .chmod = subdir_chmod,
607 .chown = subdir_chown,
608 .truncate = subdir_truncate,
609 .utimens = subdir_utimens,
610 .create = subdir_create,
611 .open = subdir_open,
612 .read_buf = subdir_read_buf,
613 .write_buf = subdir_write_buf,
614 .statfs = subdir_statfs,
615 .flush = subdir_flush,
616 .release = subdir_release,
617 .fsync = subdir_fsync,
618 .fsyncdir = subdir_fsyncdir,
619 .setxattr = subdir_setxattr,
620 .getxattr = subdir_getxattr,
621 .listxattr = subdir_listxattr,
622 .removexattr = subdir_removexattr,
623 .lock = subdir_lock,
624 .flock = subdir_flock,
625 .bmap = subdir_bmap,
626 .lseek = subdir_lseek,
627#ifdef HAVE_STATX
628 .statx = subdir_statx,
629#endif
630};
631
632static const struct fuse_opt subdir_opts[] = {
633 FUSE_OPT_KEY("-h", 0),
634 FUSE_OPT_KEY("--help", 0),
635 { "subdir=%s", offsetof(struct subdir, base), 0 },
636 { "rellinks", offsetof(struct subdir, rellinks), 1 },
637 { "norellinks", offsetof(struct subdir, rellinks), 0 },
639};
640
641static void subdir_help(void)
642{
643 printf(
644" -o subdir=DIR prepend this directory to all paths (mandatory)\n"
645" -o [no]rellinks transform absolute symlinks to relative\n");
646}
647
648static int subdir_opt_proc(void *data, const char *arg, int key,
649 struct fuse_args *outargs)
650{
651 (void) data; (void) arg; (void) outargs;
652
653 if (!key) {
654 subdir_help();
655 return -1;
656 }
657
658 return 1;
659}
660
661static struct fuse_fs *subdir_new(struct fuse_args *args,
662 struct fuse_fs *next[])
663{
664 struct fuse_fs *fs;
665 struct subdir *d;
666
667 d = calloc(1, sizeof(struct subdir));
668 if (d == NULL) {
669 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
670 return NULL;
671 }
672
673 if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
674 goto out_free;
675
676 if (!next[0] || next[1]) {
677 fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n");
678 goto out_free;
679 }
680
681 if (!d->base) {
682 fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n");
683 goto out_free;
684 }
685
686 if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
687 char *tmp = realloc(d->base, strlen(d->base) + 2);
688 if (!tmp) {
689 fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
690 goto out_free;
691 }
692 d->base = tmp;
693 strcat(d->base, "/");
694 }
695 d->baselen = strlen(d->base);
696 d->next = next[0];
697 fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
698 if (!fs)
699 goto out_free;
700 return fs;
701
702out_free:
703 free(d->base);
704 free(d);
705 return NULL;
706}
707
708FUSE_REGISTER_MODULE(subdir, subdir_new);
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4854
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition fuse.h:1395
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_OPT_END
Definition fuse_opt.h:104
void * private_data
Definition fuse.h:874
void(* destroy)(void *private_data)
Definition fuse.h:649
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2mount_8c_source.html0000644000175000017500000035411715156613430022334 0ustar berndbernd libfuse: fuse-3.18.2/lib/mount.c Source File
libfuse
mount.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture specific file system mounting (Linux).
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11/* For environ */
12#define _GNU_SOURCE
13
14#include "fuse_config.h"
15#include "fuse_i.h"
16#include "fuse_misc.h"
17#include "fuse_opt.h"
18#include "mount_util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <stddef.h>
24#include <string.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <poll.h>
28#include <spawn.h>
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <sys/wait.h>
32
33#include "fuse_mount_compat.h"
34
35#ifdef __NetBSD__
36#include <perfuse.h>
37
38#define MS_RDONLY MNT_RDONLY
39#define MS_NOSUID MNT_NOSUID
40#define MS_NODEV MNT_NODEV
41#define MS_NOEXEC MNT_NOEXEC
42#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
43#define MS_NOATIME MNT_NOATIME
44#define MS_NOSYMFOLLOW MNT_NOSYMFOLLOW
45
46#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
47#endif
48
49#define FUSERMOUNT_PROG "fusermount3"
50#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
51#define FUSE_COMMFD2_ENV "_FUSE_COMMFD2"
52#define FUSE_KERN_DEVICE_ENV "FUSE_KERN_DEVICE"
53
54#ifndef MS_DIRSYNC
55#define MS_DIRSYNC 128
56#endif
57
58enum {
59 KEY_KERN_FLAG,
60 KEY_KERN_OPT,
61 KEY_FUSERMOUNT_OPT,
62 KEY_SUBTYPE_OPT,
63 KEY_MTAB_OPT,
64 KEY_ALLOW_OTHER,
65 KEY_RO,
66};
67
68struct mount_opts {
69 int allow_other;
70 int flags;
71 int auto_unmount;
72 int blkdev;
73 char *fsname;
74 char *subtype;
75 char *subtype_opt;
76 char *mtab_opts;
77 char *fusermount_opts;
78 char *kernel_opts;
79 unsigned max_read;
80};
81
82#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
83
84static const struct fuse_opt fuse_mount_opts[] = {
85 FUSE_MOUNT_OPT("allow_other", allow_other),
86 FUSE_MOUNT_OPT("blkdev", blkdev),
87 FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
88 FUSE_MOUNT_OPT("fsname=%s", fsname),
89 FUSE_MOUNT_OPT("max_read=%u", max_read),
90 FUSE_MOUNT_OPT("subtype=%s", subtype),
91 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
92 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
93 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
94 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
95 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
96 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
97 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
98 FUSE_OPT_KEY("context=", KEY_KERN_OPT),
99 FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
100 FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
101 FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
102 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
103 FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
104 FUSE_OPT_KEY("-n", KEY_MTAB_OPT),
105 FUSE_OPT_KEY("-r", KEY_RO),
106 FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
107 FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
108 FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
109 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
110 FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
111 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
112 FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
113 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
114 FUSE_OPT_KEY("async", KEY_KERN_FLAG),
115 FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
116 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
117 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
118 FUSE_OPT_KEY("nodiratime", KEY_KERN_FLAG),
119 FUSE_OPT_KEY("nostrictatime", KEY_KERN_FLAG),
120 FUSE_OPT_KEY("symfollow", KEY_KERN_FLAG),
121 FUSE_OPT_KEY("nosymfollow", KEY_KERN_FLAG),
123};
124
125/*
126 * Running fusermount by calling 'posix_spawn'
127 *
128 * @param out_pid might be NULL
129 */
130static int fusermount_posix_spawn(posix_spawn_file_actions_t *action,
131 char const * const argv[], pid_t *out_pid)
132{
133 const char *full_path = FUSERMOUNT_DIR "/" FUSERMOUNT_PROG;
134 pid_t pid;
135
136 /* See man 7 environ for the global environ pointer */
137
138 /* first try the install path */
139 int status = posix_spawn(&pid, full_path, action, NULL,
140 (char * const *) argv, environ);
141 if (status != 0) {
142 /* if that fails, try a system install */
143 status = posix_spawnp(&pid, FUSERMOUNT_PROG, action, NULL,
144 (char * const *) argv, environ);
145 }
146
147 if (status != 0) {
148 fuse_log(FUSE_LOG_ERR, "Failed to call '%s': %s\n",
149 FUSERMOUNT_PROG, strerror(status));
150 return -status;
151 }
152
153 if (out_pid)
154 *out_pid = pid;
155 else
156 waitpid(pid, NULL, 0); /* FIXME: check exit code and return error if any */
157
158 return 0;
159}
160
161void fuse_mount_version(void)
162{
163 char const *const argv[] = {FUSERMOUNT_PROG, "--version", NULL};
164 int status = fusermount_posix_spawn(NULL, argv, NULL);
165
166 if(status != 0)
167 fuse_log(FUSE_LOG_ERR, "Running '%s --version' failed",
168 FUSERMOUNT_PROG);
169}
170
171struct mount_flags {
172 const char *opt;
173 unsigned long flag;
174 int on;
175};
176
177static const struct mount_flags mount_flags[] = {
178 {"rw", MS_RDONLY, 0},
179 {"ro", MS_RDONLY, 1},
180 {"suid", MS_NOSUID, 0},
181 {"nosuid", MS_NOSUID, 1},
182 {"dev", MS_NODEV, 0},
183 {"nodev", MS_NODEV, 1},
184 {"exec", MS_NOEXEC, 0},
185 {"noexec", MS_NOEXEC, 1},
186 {"async", MS_SYNCHRONOUS, 0},
187 {"sync", MS_SYNCHRONOUS, 1},
188 {"noatime", MS_NOATIME, 1},
189 {"nodiratime", MS_NODIRATIME, 1},
190 {"norelatime", MS_RELATIME, 0},
191 {"nostrictatime", MS_STRICTATIME, 0},
192 {"symfollow", MS_NOSYMFOLLOW, 0},
193 {"nosymfollow", MS_NOSYMFOLLOW, 1},
194#ifndef __NetBSD__
195 {"dirsync", MS_DIRSYNC, 1},
196#endif
197 {NULL, 0, 0}
198};
199
200unsigned get_max_read(struct mount_opts *o)
201{
202 return o->max_read;
203}
204
205static void set_mount_flag(const char *s, int *flags)
206{
207 int i;
208
209 for (i = 0; mount_flags[i].opt != NULL; i++) {
210 const char *opt = mount_flags[i].opt;
211 if (strcmp(opt, s) == 0) {
212 if (mount_flags[i].on)
213 *flags |= mount_flags[i].flag;
214 else
215 *flags &= ~mount_flags[i].flag;
216 return;
217 }
218 }
219 fuse_log(FUSE_LOG_ERR, "fuse: internal error, can't find mount flag\n");
220 abort();
221}
222
223static int fuse_mount_opt_proc(void *data, const char *arg, int key,
224 struct fuse_args *outargs)
225{
226 (void) outargs;
227 struct mount_opts *mo = data;
228
229 switch (key) {
230 case KEY_RO:
231 arg = "ro";
232 /* fall through */
233 case KEY_KERN_FLAG:
234 set_mount_flag(arg, &mo->flags);
235 return 0;
236
237 case KEY_KERN_OPT:
238 return fuse_opt_add_opt(&mo->kernel_opts, arg);
239
240 case KEY_FUSERMOUNT_OPT:
241 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
242
243 case KEY_SUBTYPE_OPT:
244 return fuse_opt_add_opt(&mo->subtype_opt, arg);
245
246 case KEY_MTAB_OPT:
247 return fuse_opt_add_opt(&mo->mtab_opts, arg);
248
249 /* Third party options like 'x-gvfs-notrash' */
250 case FUSE_OPT_KEY_OPT:
251 return (strncmp("x-", arg, 2) == 0) ?
252 fuse_opt_add_opt(&mo->mtab_opts, arg) :
253 1;
254 }
255
256 /* Pass through unknown options */
257 return 1;
258}
259
260/* return value:
261 * >= 0 => fd
262 * -1 => error
263 */
264static int receive_fd(int fd)
265{
266 struct msghdr msg;
267 struct iovec iov;
268 char buf[1];
269 int rv;
270 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
271 struct cmsghdr *cmsg;
272
273 iov.iov_base = buf;
274 iov.iov_len = 1;
275
276 memset(&msg, 0, sizeof(msg));
277 msg.msg_name = 0;
278 msg.msg_namelen = 0;
279 msg.msg_iov = &iov;
280 msg.msg_iovlen = 1;
281 /* old BSD implementations should use msg_accrights instead of
282 * msg_control; the interface is different. */
283 msg.msg_control = ccmsg;
284 msg.msg_controllen = sizeof(ccmsg);
285
286 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
287 if (rv == -1) {
288 fuse_log(FUSE_LOG_ERR, "recvmsg failed: %s", strerror(errno));
289 return -1;
290 }
291 if(!rv) {
292 /* EOF */
293 return -1;
294 }
295
296 cmsg = CMSG_FIRSTHDR(&msg);
297 if (cmsg->cmsg_type != SCM_RIGHTS) {
298 fuse_log(FUSE_LOG_ERR, "got control message of unknown type %d\n",
299 cmsg->cmsg_type);
300 return -1;
301 }
302 return *(int*)CMSG_DATA(cmsg);
303}
304
305void fuse_kern_unmount(const char *mountpoint, int fd)
306{
307 int res;
308
309 if (fd != -1) {
310 struct pollfd pfd;
311
312 pfd.fd = fd;
313 pfd.events = 0;
314 res = poll(&pfd, 1, 0);
315
316 /* Need to close file descriptor, otherwise synchronous umount
317 would recurse into filesystem, and deadlock.
318
319 Caller expects fuse_kern_unmount to close the fd, so close it
320 anyway. */
321 close(fd);
322
323 /* If file poll returns POLLERR on the device file descriptor,
324 then the filesystem is already unmounted or the connection
325 was severed via /sys/fs/fuse/connections/NNN/abort */
326 if (res == 1 && (pfd.revents & POLLERR))
327 return;
328 }
329
330 if (geteuid() == 0) {
331 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
332 return;
333 }
334
335 res = umount2(mountpoint, 2);
336 if (res == 0)
337 return;
338
339 char const * const argv[] =
340 { FUSERMOUNT_PROG, "--unmount", "--quiet", "--lazy",
341 "--", mountpoint, NULL };
342 int status = fusermount_posix_spawn(NULL, argv, NULL);
343 if(status != 0) {
344 fuse_log(FUSE_LOG_ERR, "Spawning %s to unmount failed: %s",
345 FUSERMOUNT_PROG, strerror(-status));
346 return;
347 }
348}
349
350static int setup_auto_unmount(const char *mountpoint, int quiet)
351{
352 int fds[2];
353 pid_t pid;
354 int res;
355
356 if (!mountpoint) {
357 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
358 return -1;
359 }
360
361 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
362 if(res == -1) {
363 fuse_log(FUSE_LOG_ERR, "Setting up auto-unmount socketpair() failed: %s\n",
364 strerror(errno));
365 return -1;
366 }
367
368 char arg_fd_entry[30];
369 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
370 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
371 /*
372 * This helps to identify the FD hold by parent process.
373 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
374 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
375 * One potential use case is to satisfy FD-Leak checks.
376 */
377 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
378 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
379
380 char const *const argv[] = {
381 FUSERMOUNT_PROG,
382 "--auto-unmount",
383 "--",
384 mountpoint,
385 NULL,
386 };
387
388 // TODO: add error handling for all manipulations of action.
389 posix_spawn_file_actions_t action;
390 posix_spawn_file_actions_init(&action);
391
392 if (quiet) {
393 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
394 posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
395 }
396 posix_spawn_file_actions_addclose(&action, fds[1]);
397
398 /*
399 * auto-umount runs in the background - it is not waiting for the
400 * process
401 */
402 int status = fusermount_posix_spawn(&action, argv, &pid);
403
404 posix_spawn_file_actions_destroy(&action);
405
406 if(status != 0) {
407 close(fds[0]);
408 close(fds[1]);
409 fuse_log(FUSE_LOG_ERR, "fuse: Setting up auto-unmount failed (spawn): %s",
410 strerror(-status));
411 return -1;
412 }
413 // passed to child now, so can close here.
414 close(fds[0]);
415
416 // Now fusermount3 will only exit when fds[1] closes automatically when our
417 // process exits.
418 return 0;
419 // Note: fds[1] is leakend and doesn't get FD_CLOEXEC
420}
421
422static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
423 const char *opts, int quiet)
424{
425 int fds[2];
426 pid_t pid;
427 int res;
428
429 if (!mountpoint) {
430 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
431 return -1;
432 }
433
434 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
435 if(res == -1) {
436 fuse_log(FUSE_LOG_ERR, "Running %s: socketpair() failed: %s\n",
437 FUSERMOUNT_PROG, strerror(errno));
438 return -1;
439 }
440
441 char arg_fd_entry[30];
442 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
443 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
444 /*
445 * This helps to identify the FD hold by parent process.
446 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
447 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
448 * One potential use case is to satisfy FD-Leak checks.
449 */
450 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
451 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
452
453 char const *const argv[] = {
454 FUSERMOUNT_PROG,
455 "-o", opts ? opts : "",
456 "--",
457 mountpoint,
458 NULL,
459 };
460
461
462 posix_spawn_file_actions_t action;
463 posix_spawn_file_actions_init(&action);
464
465 if (quiet) {
466 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
467 posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
468 }
469 posix_spawn_file_actions_addclose(&action, fds[1]);
470
471 int status = fusermount_posix_spawn(&action, argv, &pid);
472
473 posix_spawn_file_actions_destroy(&action);
474
475 if(status != 0) {
476 close(fds[0]);
477 close(fds[1]);
478 fuse_log(FUSE_LOG_ERR, "posix_spawn(p)() for %s failed: %s",
479 FUSERMOUNT_PROG, strerror(-status));
480 return -1;
481 }
482
483 // passed to child now, so can close here.
484 close(fds[0]);
485
486 int fd = receive_fd(fds[1]);
487
488 if (!mo->auto_unmount) {
489 /* with auto_unmount option fusermount3 will not exit until
490 this socket is closed */
491 close(fds[1]);
492 waitpid(pid, NULL, 0); /* bury zombie */
493 }
494
495 if (fd >= 0)
496 fcntl(fd, F_SETFD, FD_CLOEXEC);
497
498 return fd;
499}
500
501#ifndef O_CLOEXEC
502#define O_CLOEXEC 0
503#endif
504
505static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
506 const char *mnt_opts)
507{
508 char tmp[128];
509 const char *devname = getenv(FUSE_KERN_DEVICE_ENV) ?: "/dev/fuse";
510 char *source = NULL;
511 char *type = NULL;
512 struct stat stbuf;
513 int fd;
514 int res;
515
516 if (!mnt) {
517 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
518 return -1;
519 }
520
521 res = stat(mnt, &stbuf);
522 if (res == -1) {
523 fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n",
524 mnt, strerror(errno));
525 return -1;
526 }
527
528 fd = open(devname, O_RDWR | O_CLOEXEC);
529 if (fd == -1) {
530 if (errno == ENODEV || errno == ENOENT)
531 fuse_log(FUSE_LOG_ERR,
532 "fuse: device %s not found. Kernel module not loaded?\n",
533 devname);
534 else
535 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n",
536 devname, strerror(errno));
537 return -1;
538 }
539 if (!O_CLOEXEC)
540 fcntl(fd, F_SETFD, FD_CLOEXEC);
541
542 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
543 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
544
545 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
546 if (res == -1)
547 goto out_close;
548
549 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
550 (mo->subtype ? strlen(mo->subtype) : 0) +
551 strlen(devname) + 32);
552
553 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
554 if (!type || !source) {
555 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n");
556 goto out_close;
557 }
558
559 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
560 if (mo->subtype) {
561 strcat(type, ".");
562 strcat(type, mo->subtype);
563 }
564 strcpy(source,
565 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
566
567 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
568 if (res == -1 && errno == ENODEV && mo->subtype) {
569 /* Probably missing subtype support */
570 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
571 if (mo->fsname) {
572 if (!mo->blkdev)
573 sprintf(source, "%s#%s", mo->subtype,
574 mo->fsname);
575 } else {
576 strcpy(source, type);
577 }
578 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
579 }
580 if (res == -1) {
581 /*
582 * Maybe kernel doesn't support unprivileged mounts, in this
583 * case try falling back to fusermount3
584 */
585 if (errno == EPERM) {
586 res = -2;
587 } else {
588 int errno_save = errno;
589 if (mo->blkdev && errno == ENODEV &&
590 !fuse_mnt_check_fuseblk())
591 fuse_log(FUSE_LOG_ERR,
592 "fuse: 'fuseblk' support missing\n");
593 else
594 fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n",
595 strerror(errno_save));
596 }
597
598 goto out_close;
599 }
600
601#ifndef IGNORE_MTAB
602 if (geteuid() == 0) {
603 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
604 res = -1;
605 if (!newmnt)
606 goto out_umount;
607
608 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
609 mnt_opts);
610 free(newmnt);
611 if (res == -1)
612 goto out_umount;
613 }
614#endif /* IGNORE_MTAB */
615 free(type);
616 free(source);
617
618 return fd;
619
620out_umount:
621 umount2(mnt, 2); /* lazy umount */
622out_close:
623 free(type);
624 free(source);
625 close(fd);
626 return res;
627}
628
629static int get_mnt_flag_opts(char **mnt_optsp, int flags)
630{
631 int i;
632
633 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
634 return -1;
635
636 for (i = 0; mount_flags[i].opt != NULL; i++) {
637 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
638 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
639 return -1;
640 }
641 return 0;
642}
643
644struct mount_opts *parse_mount_opts(struct fuse_args *args)
645{
646 struct mount_opts *mo;
647
648 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
649 if (mo == NULL)
650 return NULL;
651
652 memset(mo, 0, sizeof(struct mount_opts));
653 mo->flags = MS_NOSUID | MS_NODEV;
654
655 if (args &&
656 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
657 goto err_out;
658
659 return mo;
660
661err_out:
662 destroy_mount_opts(mo);
663 return NULL;
664}
665
666void destroy_mount_opts(struct mount_opts *mo)
667{
668 free(mo->fsname);
669 free(mo->subtype);
670 free(mo->fusermount_opts);
671 free(mo->subtype_opt);
672 free(mo->kernel_opts);
673 free(mo->mtab_opts);
674 free(mo);
675}
676
677
678int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
679{
680 int res = -1;
681 char *mnt_opts = NULL;
682
683 res = -1;
684 if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
685 goto out;
686 if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1)
687 goto out;
688 if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1)
689 goto out;
690
691 res = fuse_mount_sys(mountpoint, mo, mnt_opts);
692 if (res >= 0 && mo->auto_unmount) {
693 if(0 > setup_auto_unmount(mountpoint, 0)) {
694 // Something went wrong, let's umount like in fuse_mount_sys.
695 umount2(mountpoint, MNT_DETACH); /* lazy umount */
696 res = -1;
697 }
698 } else if (res == -2) {
699 if (mo->fusermount_opts &&
700 fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
701 goto out;
702
703 if (mo->subtype) {
704 char *tmp_opts = NULL;
705
706 res = -1;
707 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
708 fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
709 free(tmp_opts);
710 goto out;
711 }
712
713 res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
714 free(tmp_opts);
715 if (res == -1)
716 res = fuse_mount_fusermount(mountpoint, mo,
717 mnt_opts, 0);
718 } else {
719 res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
720 }
721 }
722out:
723 free(mnt_opts);
724 return res;
725}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
#define FUSE_OPT_KEY_OPT
Definition fuse_opt.h:129
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition fuse_opt.c:144
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2mount__bsd_8c_source.html0000644000175000017500000013220015156613430023306 0ustar berndbernd libfuse: fuse-3.18.2/lib/mount_bsd.c Source File
libfuse
mount_bsd.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
4
5 Architecture specific file system mounting (FreeBSD).
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#include "fuse_config.h"
12#include "fuse_i.h"
13#include "fuse_misc.h"
14#include "fuse_opt.h"
15#include "util.h"
16
17#include <sys/param.h>
18#include "fuse_mount_compat.h"
19
20#include <sys/wait.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <stddef.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <string.h>
28
29#define FUSERMOUNT_PROG "mount_fusefs"
30#define FUSE_DEV_TRUNK "/dev/fuse"
31
32enum {
33 KEY_RO,
34 KEY_KERN
35};
36
37struct mount_opts {
38 int allow_other;
39 char *kernel_opts;
40 unsigned max_read;
41};
42
43#define FUSE_DUAL_OPT_KEY(templ, key) \
44 FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
45
46static const struct fuse_opt fuse_mount_opts[] = {
47 { "allow_other", offsetof(struct mount_opts, allow_other), 1 },
48 { "max_read=%u", offsetof(struct mount_opts, max_read), 1 },
49 FUSE_OPT_KEY("-r", KEY_RO),
50 /* standard FreeBSD mount options */
51 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
52 FUSE_DUAL_OPT_KEY("async", KEY_KERN),
53 FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
54 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
55 FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
56 FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
57 FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
58 FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
59 FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
60 FUSE_DUAL_OPT_KEY("union", KEY_KERN),
61 FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
62 FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
63 FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
64 FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
65 FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
66 FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
67 FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
68 FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
69 FUSE_DUAL_OPT_KEY("force", KEY_KERN),
70 FUSE_DUAL_OPT_KEY("update", KEY_KERN),
71 FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
72 FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
73 FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
74 FUSE_DUAL_OPT_KEY("automounted", KEY_KERN),
75 /* options supported under both Linux and FBSD */
76 FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
77 FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
78 FUSE_OPT_KEY("max_read=", KEY_KERN),
79 FUSE_OPT_KEY("subtype=", KEY_KERN),
80 /* FBSD FUSE specific mount options */
81 FUSE_DUAL_OPT_KEY("private", KEY_KERN),
82 FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
83 FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
84#if __FreeBSD_version >= 1200519
85 FUSE_DUAL_OPT_KEY("intr", KEY_KERN),
86#endif
87 /* stock FBSD mountopt parsing routine lets anything be negated... */
88 /*
89 * Linux specific mount options, but let just the mount util
90 * handle them
91 */
92 FUSE_OPT_KEY("fsname=", KEY_KERN),
94};
95
96void fuse_mount_version(void)
97{
98 system(FUSERMOUNT_PROG " --version");
99}
100
101unsigned get_max_read(struct mount_opts *o)
102{
103 return o->max_read;
104}
105
106static int fuse_mount_opt_proc(void *data, const char *arg, int key,
107 struct fuse_args *outargs)
108{
109 (void) outargs;
110 struct mount_opts *mo = data;
111
112 switch (key) {
113 case KEY_RO:
114 arg = "ro";
115 /* fall through */
116
117 case KEY_KERN:
118 return fuse_opt_add_opt(&mo->kernel_opts, arg);
119 }
120
121 /* Pass through unknown options */
122 return 1;
123}
124
125void fuse_kern_unmount(const char *mountpoint, int fd)
126{
127 if (close(fd) < 0)
128 fuse_log(FUSE_LOG_ERR, "closing FD %d failed: %s", fd, strerror(errno));
129 if (unmount(mountpoint, MNT_FORCE) < 0)
130 fuse_log(FUSE_LOG_ERR, "unmounting %s failed: %s",
131 mountpoint, strerror(errno));
132}
133
134static int fuse_mount_core(const char *mountpoint, const char *opts)
135{
136 const char *mountprog = FUSERMOUNT_PROG;
137 long fd;
138 char *fdnam, *dev;
139 pid_t pid, cpid;
140 int status;
141 int err;
142
143 fdnam = getenv("FUSE_DEV_FD");
144
145 if (fdnam) {
146 err = libfuse_strtol(fdnam, &fd);
147 if (err || fd < 0) {
148 fuse_log(FUSE_LOG_ERR, "invalid value given in FUSE_DEV_FD\n");
149 return -1;
150 }
151
152 goto mount;
153 }
154
155 dev = getenv("FUSE_DEV_NAME");
156
157 if (! dev)
158 dev = (char *)FUSE_DEV_TRUNK;
159
160 if ((fd = open(dev, O_RDWR)) < 0) {
161 perror("fuse: failed to open fuse device");
162 return -1;
163 }
164
165mount:
166 if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
167 goto out;
168
169 pid = fork();
170 cpid = pid;
171
172 if (pid == -1) {
173 perror("fuse: fork() failed");
174 close(fd);
175 return -1;
176 }
177
178 if (pid == 0) {
179 pid = fork();
180
181 if (pid == -1) {
182 perror("fuse: fork() failed");
183 close(fd);
184 _exit(EXIT_FAILURE);
185 }
186
187 if (pid == 0) {
188 const char *argv[32];
189 int a = 0;
190 int ret = -1;
191
192 if (! fdnam)
193 {
194 ret = asprintf(&fdnam, "%ld", fd);
195 if(ret == -1)
196 {
197 perror("fuse: failed to assemble mount arguments");
198 close(fd);
199 _exit(EXIT_FAILURE);
200 }
201 }
202
203 argv[a++] = mountprog;
204 if (opts) {
205 argv[a++] = "-o";
206 argv[a++] = opts;
207 }
208 argv[a++] = fdnam;
209 argv[a++] = mountpoint;
210 argv[a++] = NULL;
211 execvp(mountprog, (char **) argv);
212 perror("fuse: failed to exec mount program");
213 free(fdnam);
214 _exit(EXIT_FAILURE);
215 }
216
217 waitpid(pid, &status, 0);
218 if (!WIFEXITED(status))
219 _exit(EXIT_FAILURE);
220 _exit(WEXITSTATUS(status));
221 }
222
223 if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
224 perror("fuse: failed to mount file system");
225 if (close(fd) < 0)
226 perror("fuse: closing FD");
227 return -1;
228 }
229
230out:
231 return fd;
232}
233
234struct mount_opts *parse_mount_opts(struct fuse_args *args)
235{
236 struct mount_opts *mo;
237
238 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
239 if (mo == NULL)
240 return NULL;
241
242 memset(mo, 0, sizeof(struct mount_opts));
243
244 if (args &&
245 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
246 goto err_out;
247
248 return mo;
249
250err_out:
251 destroy_mount_opts(mo);
252 return NULL;
253}
254
255void destroy_mount_opts(struct mount_opts *mo)
256{
257 free(mo->kernel_opts);
258 free(mo);
259}
260
261int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
262{
263 /* mount util should not try to spawn the daemon */
264 setenv("MOUNT_FUSEFS_SAFE", "1", 1);
265 /* to notify the mount util it's called from lib */
266 setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
267
268 return fuse_mount_core(mountpoint, mo->kernel_opts);
269}
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
int fuse_opt_add_opt(char **opts, const char *opt)
Definition fuse_opt.c:139
#define FUSE_OPT_END
Definition fuse_opt.h:104
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2mount__util_8c_source.html0000644000175000017500000016025115156613430023522 0ustar berndbernd libfuse: fuse-3.18.2/lib/mount_util.c Source File
libfuse
mount_util.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture-independent mounting code.
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file LGPL2.txt.
9*/
10
11#include "fuse_config.h"
12#include "mount_util.h"
13
14#include <stdio.h>
15#include <unistd.h>
16#include <stdlib.h>
17#include <string.h>
18#include <signal.h>
19#include <dirent.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <limits.h>
23#include <paths.h>
24#if !defined( __NetBSD__) && !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__)
25#include <mntent.h>
26#else
27#define IGNORE_MTAB
28#endif
29#include <sys/stat.h>
30#include <sys/wait.h>
31
32#include "fuse_mount_compat.h"
33
34#include <sys/param.h>
35
36#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
37#define umount2(mnt, flags) unmount(mnt, ((flags) == 2) ? MNT_FORCE : 0)
38#endif
39
40#ifdef IGNORE_MTAB
41#define mtab_needs_update(mnt) 0
42#else
43static int mtab_needs_update(const char *mnt)
44{
45 int res;
46 struct stat stbuf;
47
48 /* If mtab is within new mount, don't touch it */
49 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
50 _PATH_MOUNTED[strlen(mnt)] == '/')
51 return 0;
52
53 /*
54 * Skip mtab update if /etc/mtab:
55 *
56 * - doesn't exist,
57 * - is on a read-only filesystem.
58 */
59 res = lstat(_PATH_MOUNTED, &stbuf);
60 if (res == -1) {
61 if (errno == ENOENT)
62 return 0;
63 } else {
64 uid_t ruid;
65 int err;
66
67 ruid = getuid();
68 if (ruid != 0)
69 setreuid(0, -1);
70
71 res = access(_PATH_MOUNTED, W_OK);
72 err = (res == -1) ? errno : 0;
73 if (ruid != 0)
74 setreuid(ruid, -1);
75
76 if (err == EROFS)
77 return 0;
78
79 res = access("/run/mount/utab", F_OK);
80 if (res == -1)
81 return 0;
82 }
83
84 return 1;
85}
86#endif /* IGNORE_MTAB */
87
88static int add_mount(const char *progname, const char *fsname,
89 const char *mnt, const char *type, const char *opts)
90{
91 int res;
92 int status;
93 sigset_t blockmask;
94 sigset_t oldmask;
95
96 sigemptyset(&blockmask);
97 sigaddset(&blockmask, SIGCHLD);
98 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
99 if (res == -1) {
100 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
101 return -1;
102 }
103
104 res = fork();
105 if (res == -1) {
106 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
107 goto out_restore;
108 }
109 if (res == 0) {
110 char *env = NULL;
111
112 sigprocmask(SIG_SETMASK, &oldmask, NULL);
113
114 if(setuid(geteuid()) == -1) {
115 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
116 res = -1;
117 goto out_restore;
118 }
119
120 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
121 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
122 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
123 progname, strerror(errno));
124 exit(1);
125 }
126 res = waitpid(res, &status, 0);
127 if (res == -1)
128 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
129
130 if (status != 0)
131 res = -1;
132
133 out_restore:
134 sigprocmask(SIG_SETMASK, &oldmask, NULL);
135
136 return res;
137}
138
139int fuse_mnt_add_mount(const char *progname, const char *fsname,
140 const char *mnt, const char *type, const char *opts)
141{
142 if (!mtab_needs_update(mnt))
143 return 0;
144
145 return add_mount(progname, fsname, mnt, type, opts);
146}
147
148static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
149{
150 int res;
151 int status;
152 sigset_t blockmask;
153 sigset_t oldmask;
154
155 sigemptyset(&blockmask);
156 sigaddset(&blockmask, SIGCHLD);
157 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
158 if (res == -1) {
159 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
160 return -1;
161 }
162
163 res = fork();
164 if (res == -1) {
165 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
166 goto out_restore;
167 }
168 if (res == 0) {
169 char *env = NULL;
170
171 sigprocmask(SIG_SETMASK, &oldmask, NULL);
172
173 if(setuid(geteuid()) == -1) {
174 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
175 res = -1;
176 goto out_restore;
177 }
178
179 if (lazy) {
180 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
181 "-l", NULL, &env);
182 } else {
183 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
184 NULL, &env);
185 }
186 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
187 progname, strerror(errno));
188 exit(1);
189 }
190 res = waitpid(res, &status, 0);
191 if (res == -1)
192 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
193
194 if (status != 0) {
195 res = -1;
196 }
197
198 out_restore:
199 sigprocmask(SIG_SETMASK, &oldmask, NULL);
200 return res;
201
202}
203
204int fuse_mnt_umount(const char *progname, const char *abs_mnt,
205 const char *rel_mnt, int lazy)
206{
207 int res;
208
209 if (!mtab_needs_update(abs_mnt)) {
210 res = umount2(rel_mnt, lazy ? 2 : 0);
211 if (res == -1)
212 fprintf(stderr, "%s: failed to unmount %s: %s\n",
213 progname, abs_mnt, strerror(errno));
214 return res;
215 }
216
217 return exec_umount(progname, rel_mnt, lazy);
218}
219
220static int remove_mount(const char *progname, const char *mnt)
221{
222 int res;
223 int status;
224 sigset_t blockmask;
225 sigset_t oldmask;
226
227 sigemptyset(&blockmask);
228 sigaddset(&blockmask, SIGCHLD);
229 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
230 if (res == -1) {
231 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
232 return -1;
233 }
234
235 res = fork();
236 if (res == -1) {
237 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
238 goto out_restore;
239 }
240 if (res == 0) {
241 char *env = NULL;
242
243 sigprocmask(SIG_SETMASK, &oldmask, NULL);
244
245 if(setuid(geteuid()) == -1) {
246 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
247 res = -1;
248 goto out_restore;
249 }
250
251 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
252 "--fake", mnt, NULL, &env);
253 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
254 progname, strerror(errno));
255 exit(1);
256 }
257 res = waitpid(res, &status, 0);
258 if (res == -1)
259 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
260
261 if (status != 0)
262 res = -1;
263
264 out_restore:
265 sigprocmask(SIG_SETMASK, &oldmask, NULL);
266 return res;
267}
268
269int fuse_mnt_remove_mount(const char *progname, const char *mnt)
270{
271 if (!mtab_needs_update(mnt))
272 return 0;
273
274 return remove_mount(progname, mnt);
275}
276
277char *fuse_mnt_resolve_path(const char *progname, const char *orig)
278{
279 char buf[PATH_MAX];
280 char *copy;
281 char *dst;
282 char *end;
283 char *lastcomp;
284 const char *toresolv;
285
286 if (!orig[0]) {
287 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
288 orig);
289 return NULL;
290 }
291
292 copy = strdup(orig);
293 if (copy == NULL) {
294 fprintf(stderr, "%s: failed to allocate memory\n", progname);
295 return NULL;
296 }
297
298 toresolv = copy;
299 lastcomp = NULL;
300 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
301 if (end[0] != '/') {
302 char *tmp;
303 end[1] = '\0';
304 tmp = strrchr(copy, '/');
305 if (tmp == NULL) {
306 lastcomp = copy;
307 toresolv = ".";
308 } else {
309 lastcomp = tmp + 1;
310 if (tmp == copy)
311 toresolv = "/";
312 }
313 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
314 lastcomp = NULL;
315 toresolv = copy;
316 }
317 else if (tmp)
318 tmp[0] = '\0';
319 }
320 if (realpath(toresolv, buf) == NULL) {
321 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
322 strerror(errno));
323 free(copy);
324 return NULL;
325 }
326 if (lastcomp == NULL)
327 dst = strdup(buf);
328 else {
329 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
330 if (dst) {
331 unsigned buflen = strlen(buf);
332 if (buflen && buf[buflen-1] == '/')
333 sprintf(dst, "%s%s", buf, lastcomp);
334 else
335 sprintf(dst, "%s/%s", buf, lastcomp);
336 }
337 }
338 free(copy);
339 if (dst == NULL)
340 fprintf(stderr, "%s: failed to allocate memory\n", progname);
341 return dst;
342}
343
344int fuse_mnt_check_fuseblk(void)
345{
346 char buf[256];
347 FILE *f = fopen("/proc/filesystems", "r");
348 if (!f)
349 return 1;
350
351 while (fgets(buf, sizeof(buf), f))
352 if (strstr(buf, "fuseblk\n")) {
353 fclose(f);
354 return 1;
355 }
356
357 fclose(f);
358 return 0;
359}
360
361int fuse_mnt_parse_fuse_fd(const char *mountpoint)
362{
363 int fd = -1;
364 int len = 0;
365
366 if (mountpoint == NULL) {
367 fprintf(stderr, "Invalid null-ptr mount-point!\n");
368 return -1;
369 }
370
371 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
372 len == strlen(mountpoint)) {
373 return fd;
374 }
375
376 return -1;
377}
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2mount__util_8h_source.html0000644000175000017500000001363215156613430023527 0ustar berndbernd libfuse: fuse-3.18.2/lib/mount_util.h Source File
libfuse
mount_util.h
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file LGPL2.txt.
7*/
8
9#include <sys/types.h>
10
11int fuse_mnt_add_mount(const char *progname, const char *fsname,
12 const char *mnt, const char *type, const char *opts);
13int fuse_mnt_remove_mount(const char *progname, const char *mnt);
14int fuse_mnt_umount(const char *progname, const char *abs_mnt,
15 const char *rel_mnt, int lazy);
16char *fuse_mnt_resolve_path(const char *progname, const char *orig);
17int fuse_mnt_check_fuseblk(void);
18int fuse_mnt_parse_fuse_fd(const char *mountpoint);
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2usdt_8h_source.html0000644000175000017500000027402315156613430022153 0ustar berndbernd libfuse: fuse-3.18.2/lib/usdt.h Source File
libfuse
usdt.h
1/*
2 * Copied from https://github.com/libbpf/usdt/
3 */
4
5// SPDX-License-Identifier: BSD-2-Clause
6/*
7 * This single-header library defines a collection of variadic macros for
8 * defining and triggering USDTs (User Statically-Defined Tracepoints):
9 *
10 * - For USDTs without associated semaphore:
11 * USDT(group, name, args...)
12 *
13 * - For USDTs with implicit (transparent to the user) semaphore:
14 * USDT_WITH_SEMA(group, name, args...)
15 * USDT_IS_ACTIVE(group, name)
16 *
17 * - For USDTs with explicit (user-defined and provided) semaphore:
18 * USDT_WITH_EXPLICIT_SEMA(sema, group, name, args...)
19 * USDT_SEMA_IS_ACTIVE(sema)
20 *
21 * all of which emit a NOP instruction into the instruction stream, and so
22 * have *zero* overhead for the surrounding code. USDTs are identified by
23 * a combination of `group` and `name` identifiers, which is used by external
24 * tracing tooling (tracers) for identifying exact USDTs of interest.
25 *
26 * USDTs can have an associated (2-byte) activity counter (USDT semaphore),
27 * automatically maintained by Linux kernel whenever any correctly written
28 * BPF-based tracer is attached to the USDT. This USDT semaphore can be used
29 * to check whether there is a need to do any extra data collection and
30 * processing for a given USDT (if necessary), and otherwise avoid extra work
31 * for a common case of USDT not being traced ("active").
32 *
33 * See documentation for USDT_WITH_SEMA()/USDT_IS_ACTIVE() or
34 * USDT_WITH_EXPLICIT_SEMA()/USDT_SEMA_IS_ACTIVE() APIs below for details on
35 * working with USDTs with implicitly or explicitly associated
36 * USDT semaphores, respectively.
37 *
38 * There is also some additional data recorded into an auxiliary note
39 * section. The data in the note section describes the operands, in terms of
40 * size and location, used by tracing tooling to know where to find USDT
41 * arguments. Each location is encoded as an assembler operand string.
42 * Tracing tools (bpftrace and BPF-based tracers, systemtap, etc) insert
43 * breakpoints on top of the nop, and decode the location operand-strings,
44 * like an assembler, to find the values being passed.
45 *
46 * The operand strings are selected by the compiler for each operand.
47 * They are constrained by inline-assembler codes.The default is:
48 *
49 * #define USDT_ARG_CONSTRAINT nor
50 *
51 * This is a good default if the operands tend to be integral and
52 * moderate in number (smaller than number of registers). In other
53 * cases, the compiler may report "'asm' requires impossible reload" or
54 * similar. In this case, consider simplifying the macro call (fewer
55 * and simpler operands), reduce optimization, or override the default
56 * constraints string via:
57 *
58 * #define USDT_ARG_CONSTRAINT g
59 * #include <usdt.h>
60 *
61 * For some historical description of USDT v3 format (the one used by this
62 * library and generally recognized and assumed by BPF-based tracing tools)
63 * see [0]. The more formal specification can be found at [1]. Additional
64 * argument constraints information can be found at [2].
65 *
66 * Original SystemTap's sys/sdt.h implementation ([3]) was used as a base for
67 * this USDT library implementation. Current implementation differs *a lot* in
68 * terms of exposed user API and general usability, which was the main goal
69 * and focus of the reimplementation work. Nevertheless, underlying recorded
70 * USDT definitions are fully binary compatible and any USDT-based tooling
71 * should work equally well with USDTs defined by either SystemTap's or this
72 * library's USDT implementation.
73 *
74 * [0] https://ecos.sourceware.org/ml/systemtap/2010-q3/msg00145.html
75 * [1] https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation
76 * [2] https://gcc.gnu.org/onlinedocs/gcc/Constraints.html
77 * [3] https://sourceware.org/git/?p=systemtap.git;a=blob;f=includes/sys/sdt.h
78 */
79#ifndef __USDT_H
80#define __USDT_H
81
82/*
83 * Changelog:
84 *
85 * 0.1.0
86 * -----
87 * - Initial release
88 */
89#define USDT_MAJOR_VERSION 0
90#define USDT_MINOR_VERSION 1
91#define USDT_PATCH_VERSION 0
92
93/* C++20 and C23 added __VA_OPT__ as a standard replacement for non-standard `##__VA_ARGS__` extension */
94#if (defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L) || (defined(__cplusplus) && __cplusplus > 201703L)
95#define __usdt_va_opt 1
96#define __usdt_va_args(...) __VA_OPT__(,) __VA_ARGS__
97#else
98#define __usdt_va_args(...) , ##__VA_ARGS__
99#endif
100
101/*
102 * Trigger USDT with `group`:`name` identifier and pass through `args` as its
103 * arguments. Zero arguments are acceptable as well. No USDT semaphore is
104 * associated with this USDT.
105 *
106 * Such "semaphoreless" USDTs are commonly used when there is no extra data
107 * collection or processing needed to collect and prepare USDT arguments and
108 * they are just available in the surrounding code. USDT() macro will just
109 * record their locations in CPU registers or in memory for tracing tooling to
110 * be able to access them, if necessary.
111 */
112#ifdef __usdt_va_opt
113#define USDT(group, name, ...) \
114 __usdt_probe(group, name, __usdt_sema_none, 0 __VA_OPT__(,) __VA_ARGS__)
115#else
116#define USDT(group, name, ...) \
117 __usdt_probe(group, name, __usdt_sema_none, 0, ##__VA_ARGS__)
118#endif
119
120/*
121 * Trigger USDT with `group`:`name` identifier and pass through `args` as its
122 * arguments. Zero arguments are acceptable as well. USDT also get an
123 * implicitly-defined associated USDT semaphore, which will be "activated" by
124 * tracing tooling and can be used to check whether USDT is being actively
125 * observed.
126 *
127 * USDTs with semaphore are commonly used when there is a need to perform
128 * additional data collection and processing to prepare USDT arguments, which
129 * otherwise might not be necessary for the rest of application logic. In such
130 * case, USDT semaphore can be used to avoid unnecessary extra work. If USDT
131 * is not traced (which is presumed to be a common situation), the associated
132 * USDT semaphore is "inactive", and so there is no need to waste resources to
133 * prepare USDT arguments. Use USDT_IS_ACTIVE(group, name) to check whether
134 * USDT is "active".
135 *
136 * N.B. There is an inherent (albeit short) gap between checking whether USDT
137 * is active and triggering corresponding USDT, in which external tracer can
138 * be attached to an USDT and activate USDT semaphore after the activity check.
139 * If such a race occurs, tracers might miss one USDT execution. Tracers are
140 * expected to accommodate such possibility and this is expected to not be
141 * a problem for applications and tracers.
142 *
143 * N.B. Implicit USDT semaphore defined by USDT_WITH_SEMA() is contained
144 * within a single executable or shared library and is not shared outside
145 * them. I.e., if you use USDT_WITH_SEMA() with the same USDT group and name
146 * identifier across executable and shared library, it will work and won't
147 * conflict, per se, but will define independent USDT semaphores, one for each
148 * shared library/executable in which USDT_WITH_SEMA(group, name) is used.
149 * That is, if you attach to this USDT in one shared library (or executable),
150 * then only USDT semaphore within that shared library (or executable) will be
151 * updated by the kernel, while other libraries (or executable) will not see
152 * activated USDT semaphore. In short, it's best to use unique USDT group:name
153 * identifiers across different shared libraries (and, equivalently, between
154 * executable and shared library). This is advanced consideration and is
155 * rarely (if ever) seen in practice, but just to avoid surprises this is
156 * called out here. (Static libraries become a part of final executable, once
157 * linked by linker, so the above considerations don't apply to them.)
158 */
159#ifdef __usdt_va_opt
160#define USDT_WITH_SEMA(group, name, ...) \
161 __usdt_probe(group, name, \
162 __usdt_sema_implicit, __usdt_sema_name(group, name) \
163 __VA_OPT__(,) __VA_ARGS__)
164#else
165#define USDT_WITH_SEMA(group, name, ...) \
166 __usdt_probe(group, name, \
167 __usdt_sema_implicit, __usdt_sema_name(group, name), \
168 ##__VA_ARGS__)
169#endif
170
171struct usdt_sema { volatile unsigned short active; };
172
173/*
174 * Check if USDT with `group`:`name` identifier is "active" (i.e., whether it
175 * is attached to by external tracing tooling and is actively observed).
176 *
177 * This macro can be used to decide whether any additional and potentially
178 * expensive data collection or processing should be done to pass extra
179 * information into the given USDT. It is assumed that USDT is triggered with
180 * USDT_WITH_SEMA() macro which will implicitly define associated USDT
181 * semaphore. (If one needs more control over USDT semaphore, see
182 * USDT_DEFINE_SEMA() and USDT_WITH_EXPLICIT_SEMA() macros below.)
183 *
184 * N.B. Such checks are necessarily racy and speculative. Between checking
185 * whether USDT is active and triggering the USDT itself, tracer can be
186 * detached with no notification. This race should be extremely rare and worst
187 * case should result in one-time wasted extra data collection and processing.
188 */
189#define USDT_IS_ACTIVE(group, name) ({ \
190 extern struct usdt_sema __usdt_sema_name(group, name) \
191 __usdt_asm_name(__usdt_sema_name(group, name)); \
192 __usdt_sema_implicit(__usdt_sema_name(group, name)); \
193 __usdt_sema_name(group, name).active > 0; \
194})
195
196/*
197 * APIs for working with user-defined explicit USDT semaphores.
198 *
199 * This is a less commonly used advanced API for use cases in which user needs
200 * an explicit control over (potentially shared across multiple USDTs) USDT
201 * semaphore instance. This can be used when there is a group of logically
202 * related USDTs that all need extra data collection and processing whenever
203 * any of a family of related USDTs are "activated" (i.e., traced). In such
204 * a case, all such related USDTs will be associated with the same shared USDT
205 * semaphore defined with USDT_DEFINE_SEMA() and the USDTs themselves will be
206 * triggered with USDT_WITH_EXPLICIT_SEMA() macros, taking an explicit extra
207 * USDT semaphore identifier as an extra parameter.
208 */
209
215#define USDT_SEMA(sema) __usdt_sema_##sema
216
217/*
218 * Define storage for user-defined USDT semaphore `sema`.
219 *
220 * Should be used only once in non-header source file to let compiler allocate
221 * space for the semaphore variable. Just like with any other global variable.
222 *
223 * This macro can be used anywhere where global variable declaration is
224 * allowed. Just like with global variable definitions, there should be only
225 * one definition of user-defined USDT semaphore with given `sema` identifier,
226 * otherwise compiler or linker will complain about duplicate variable
227 * definition.
228 *
229 * For C++, it is allowed to use USDT_DEFINE_SEMA() both in global namespace
230 * and inside namespaces (including nested namespaces). Just make sure that
231 * USDT_DECLARE_SEMA() is placed within the namespace where this semaphore is
232 * referenced, or any of its parent namespaces, so the C++ language-level
233 * identifier is visible to the code that needs to reference the semaphore.
234 * At the lowest layer, USDT semaphores have global naming and visibility
235 * (they have a corresponding `__usdt_sema_<name>` symbol, which can be linked
236 * against from C or C++ code, if necessary). To keep it simple, putting
237 * USDT_DECLARE_SEMA() declarations into global namespaces is the simplest
238 * no-brainer solution. All these aspects are irrelevant for plain C, because
239 * C doesn't have namespaces and everything is always in the global namespace.
240 *
241 * N.B. Due to USDT metadata being recorded in non-allocatable ELF note
242 * section, it has limitations when it comes to relocations, which, in
243 * practice, means that it's not possible to correctly share USDT semaphores
244 * between main executable and shared libraries, or even between multiple
245 * shared libraries. USDT semaphore has to be contained to individual shared
246 * library or executable to avoid unpleasant surprises with half-working USDT
247 * semaphores. We enforce this by marking semaphore ELF symbols as having
248 * a hidden visibility. This is quite an advanced use case and consideration
249 * and for most users this should have no consequences whatsoever.
250 */
251#define USDT_DEFINE_SEMA(sema) \
252 struct usdt_sema __usdt_sema_sec USDT_SEMA(sema) \
253 __usdt_asm_name(USDT_SEMA(sema)) \
254 __attribute__((visibility("hidden"))) = { 0 }
255
256/*
257 * Declare extern reference to user-defined USDT semaphore `sema`.
258 *
259 * Refers to a variable defined in another compilation unit by
260 * USDT_DEFINE_SEMA() and allows to use the same USDT semaphore across
261 * multiple compilation units (i.e., .c and .cpp files).
262 *
263 * See USDT_DEFINE_SEMA() notes above for C++ language usage peculiarities.
264 */
265#define USDT_DECLARE_SEMA(sema) \
266 extern struct usdt_sema USDT_SEMA(sema) __usdt_asm_name(USDT_SEMA(sema))
267
268/*
269 * Check if user-defined USDT semaphore `sema` is "active" (i.e., whether it
270 * is attached to by external tracing tooling and is actively observed).
271 *
272 * This macro can be used to decide whether any additional and potentially
273 * expensive data collection or processing should be done to pass extra
274 * information into USDT(s) associated with USDT semaphore `sema`.
275 *
276 * N.B. Such checks are necessarily racy. Between checking the state of USDT
277 * semaphore and triggering associated USDT(s), the active tracer might attach
278 * or detach. This race should be extremely rare and worst case should result
279 * in one-time missed USDT event or wasted extra data collection and
280 * processing. USDT-using tracers should be written with this in mind and is
281 * not a concern of the application defining USDTs with associated semaphore.
282 */
283#define USDT_SEMA_IS_ACTIVE(sema) (USDT_SEMA(sema).active > 0)
284
285/*
286 * Invoke USDT specified by `group` and `name` identifiers and associate
287 * explicitly user-defined semaphore `sema` with it. Pass through `args` as
288 * USDT arguments. `args` are optional and zero arguments are acceptable.
289 *
290 * Semaphore is defined with the help of USDT_DEFINE_SEMA() macro and can be
291 * checked whether active with USDT_SEMA_IS_ACTIVE().
292 */
293#ifdef __usdt_va_opt
294#define USDT_WITH_EXPLICIT_SEMA(sema, group, name, ...) \
295 __usdt_probe(group, name, __usdt_sema_explicit, USDT_SEMA(sema), ##__VA_ARGS__)
296#else
297#define USDT_WITH_EXPLICIT_SEMA(sema, group, name, ...) \
298 __usdt_probe(group, name, __usdt_sema_explicit, USDT_SEMA(sema) __VA_OPT__(,) __VA_ARGS__)
299#endif
300
301/*
302 * Adjustable implementation aspects
303 */
304#ifndef USDT_ARG_CONSTRAINT
305#if defined __powerpc__
306#define USDT_ARG_CONSTRAINT nZr
307#elif defined __arm__
308#define USDT_ARG_CONSTRAINT g
309#elif defined __loongarch__
310#define USDT_ARG_CONSTRAINT nmr
311#else
312#define USDT_ARG_CONSTRAINT nor
313#endif
314#endif /* USDT_ARG_CONSTRAINT */
315
316#ifndef USDT_NOP
317#if defined(__ia64__) || defined(__s390__) || defined(__s390x__)
318#define USDT_NOP nop 0
319#else
320#define USDT_NOP nop
321#endif
322#endif /* USDT_NOP */
323
324/*
325 * Implementation details
326 */
327/* USDT name for implicitly-defined USDT semaphore, derived from group:name */
328#define __usdt_sema_name(group, name) __usdt_sema_##group##__##name
329/* ELF section into which USDT semaphores are put */
330#define __usdt_sema_sec __attribute__((section(".probes")))
331
332#define __usdt_concat(a, b) a ## b
333#define __usdt_apply(fn, n) __usdt_concat(fn, n)
334
335#ifndef __usdt_nth
336#define __usdt_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, N, ...) N
337#endif
338
339#ifndef __usdt_narg
340#ifdef __usdt_va_opt
341#define __usdt_narg(...) __usdt_nth(_ __VA_OPT__(,) __VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
342#else
343#define __usdt_narg(...) __usdt_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
344#endif
345#endif /* __usdt_narg */
346
347#define __usdt_hash #
348#define __usdt_str_(x) #x
349#define __usdt_str(x) __usdt_str_(x)
350
351#ifndef __usdt_asm_name
352#define __usdt_asm_name(name) __asm__(__usdt_str(name))
353#endif
354
355#define __usdt_asm1(a) __usdt_str(a) "\n"
356#define __usdt_asm2(a,b) __usdt_str(a) "," __usdt_str(b) "\n"
357#define __usdt_asm3(a,b,c) __usdt_str(a) "," __usdt_str(b) "," __usdt_str(c) "\n"
358#define __usdt_asm5(a,b,c,d,e) __usdt_str(a) "," __usdt_str(b) "," __usdt_str(c) "," \
359 __usdt_str(d) "," __usdt_str(e) "\n"
360
361#ifdef __LP64__
362#define __usdt_asm_addr .8byte
363#else
364#define __usdt_asm_addr .4byte
365#endif
366
367#define __usdt_asm_strz_(x) __usdt_asm1(.asciz #x)
368#define __usdt_asm_strz(x) __usdt_asm_strz_(x)
369#define __usdt_asm_str_(x) __usdt_asm1(.ascii #x)
370#define __usdt_asm_str(x) __usdt_asm_str_(x)
371
372/* "semaphoreless" USDT case */
373#ifndef __usdt_sema_none
374#define __usdt_sema_none(sema)
375#endif
376
377/* implicitly defined __usdt_sema__group__name semaphore (using weak symbols) */
378#ifndef __usdt_sema_implicit
379#define __usdt_sema_implicit(sema) \
380 __asm__ __volatile__ ( \
381 __usdt_asm1(.ifndef sema) \
382 __usdt_asm3( .pushsection .probes, "aw", "progbits") \
383 __usdt_asm1( .weak sema) \
384 __usdt_asm1( .hidden sema) \
385 __usdt_asm1( .align 2) \
386 __usdt_asm1(sema:) \
387 __usdt_asm1( .zero 2) \
388 __usdt_asm2( .type sema, @object) \
389 __usdt_asm2( .size sema, 2) \
390 __usdt_asm1( .popsection) \
391 __usdt_asm1(.endif) \
392 );
393#endif
394
395/* externally defined semaphore using USDT_DEFINE_SEMA() and passed explicitly by user */
396#ifndef __usdt_sema_explicit
397#define __usdt_sema_explicit(sema) \
398 __asm__ __volatile__ ("" :: "m" (sema));
399#endif
400
401/* main USDT definition (nop and .note.stapsdt metadata) */
402#define __usdt_probe(group, name, sema_def, sema, ...) do { \
403 sema_def(sema) \
404 __asm__ __volatile__ ( \
405 __usdt_asm1(990: USDT_NOP) \
406 __usdt_asm3( .pushsection .note.stapsdt, "", "note") \
407 __usdt_asm1( .balign 4) \
408 __usdt_asm3( .4byte 992f-991f,994f-993f,3) \
409 __usdt_asm1(991: .asciz "stapsdt") \
410 __usdt_asm1(992: .balign 4) \
411 __usdt_asm1(993: __usdt_asm_addr 990b) \
412 __usdt_asm1( __usdt_asm_addr _.stapsdt.base) \
413 __usdt_asm1( __usdt_asm_addr sema) \
414 __usdt_asm_strz(group) \
415 __usdt_asm_strz(name) \
416 __usdt_asm_args(__VA_ARGS__) \
417 __usdt_asm1( .ascii "\0") \
418 __usdt_asm1(994: .balign 4) \
419 __usdt_asm1( .popsection) \
420 __usdt_asm1(.ifndef _.stapsdt.base) \
421 __usdt_asm5( .pushsection .stapsdt.base,"aG","progbits",.stapsdt.base,comdat)\
422 __usdt_asm1( .weak _.stapsdt.base) \
423 __usdt_asm1( .hidden _.stapsdt.base) \
424 __usdt_asm1(_.stapsdt.base:) \
425 __usdt_asm1( .space 1) \
426 __usdt_asm2( .size _.stapsdt.base, 1) \
427 __usdt_asm1( .popsection) \
428 __usdt_asm1(.endif) \
429 :: __usdt_asm_ops(__VA_ARGS__) \
430 ); \
431} while (0)
432
433/*
434 * NB: gdb PR24541 highlighted an unspecified corner of the sdt.h
435 * operand note format.
436 *
437 * The named register may be a longer or shorter (!) alias for the
438 * storage where the value in question is found. For example, on
439 * i386, 64-bit value may be put in register pairs, and a register
440 * name stored would identify just one of them. Previously, gcc was
441 * asked to emit the %w[id] (16-bit alias of some registers holding
442 * operands), even when a wider 32-bit value was used.
443 *
444 * Bottom line: the byte-width given before the @ sign governs. If
445 * there is a mismatch between that width and that of the named
446 * register, then a sys/sdt.h note consumer may need to employ
447 * architecture-specific heuristics to figure out where the compiler
448 * has actually put the complete value.
449 */
450#if defined(__powerpc__) || defined(__powerpc64__)
451#define __usdt_argref(id) %I[id]%[id]
452#elif defined(__i386__)
453#define __usdt_argref(id) %k[id] /* gcc.gnu.org/PR80115 sourceware.org/PR24541 */
454#else
455#define __usdt_argref(id) %[id]
456#endif
457
458#define __usdt_asm_arg(n) __usdt_asm_str(%c[__usdt_asz##n]) \
459 __usdt_asm1(.ascii "@") \
460 __usdt_asm_str(__usdt_argref(__usdt_aval##n))
461
462#define __usdt_asm_args0 /* no arguments */
463#define __usdt_asm_args1 __usdt_asm_arg(1)
464#define __usdt_asm_args2 __usdt_asm_args1 __usdt_asm1(.ascii " ") __usdt_asm_arg(2)
465#define __usdt_asm_args3 __usdt_asm_args2 __usdt_asm1(.ascii " ") __usdt_asm_arg(3)
466#define __usdt_asm_args4 __usdt_asm_args3 __usdt_asm1(.ascii " ") __usdt_asm_arg(4)
467#define __usdt_asm_args5 __usdt_asm_args4 __usdt_asm1(.ascii " ") __usdt_asm_arg(5)
468#define __usdt_asm_args6 __usdt_asm_args5 __usdt_asm1(.ascii " ") __usdt_asm_arg(6)
469#define __usdt_asm_args7 __usdt_asm_args6 __usdt_asm1(.ascii " ") __usdt_asm_arg(7)
470#define __usdt_asm_args8 __usdt_asm_args7 __usdt_asm1(.ascii " ") __usdt_asm_arg(8)
471#define __usdt_asm_args9 __usdt_asm_args8 __usdt_asm1(.ascii " ") __usdt_asm_arg(9)
472#define __usdt_asm_args10 __usdt_asm_args9 __usdt_asm1(.ascii " ") __usdt_asm_arg(10)
473#define __usdt_asm_args11 __usdt_asm_args10 __usdt_asm1(.ascii " ") __usdt_asm_arg(11)
474#define __usdt_asm_args12 __usdt_asm_args11 __usdt_asm1(.ascii " ") __usdt_asm_arg(12)
475#define __usdt_asm_args(...) __usdt_apply(__usdt_asm_args, __usdt_narg(__VA_ARGS__))
476
477#define __usdt_is_arr(x) (__builtin_classify_type(x) == 14 || __builtin_classify_type(x) == 5)
478#define __usdt_arg_size(x) (__usdt_is_arr(x) ? sizeof(void *) : sizeof(x))
479
480/*
481 * We can't use __builtin_choose_expr() in C++, so fall back to table-based
482 * signedness determination for known types, utilizing templates magic.
483 */
484#ifdef __cplusplus
485
486#define __usdt_is_signed(x) (!__usdt_is_arr(x) && __usdt_t<__typeof(x)>::is_signed)
487
488#include <cstddef>
489
490template<typename T> struct __usdt_t { static const bool is_signed = false; };
491template<typename A> struct __usdt_t<A[]> : public __usdt_t<A *> {};
492template<typename A, size_t N> struct __usdt_t<A[N]> : public __usdt_t<A *> {};
493
494#define __usdt_def_signed(T) \
495template<> struct __usdt_t<T> { static const bool is_signed = true; }; \
496template<> struct __usdt_t<const T> { static const bool is_signed = true; }; \
497template<> struct __usdt_t<volatile T> { static const bool is_signed = true; }; \
498template<> struct __usdt_t<const volatile T> { static const bool is_signed = true; }
499#define __usdt_maybe_signed(T) \
500template<> struct __usdt_t<T> { static const bool is_signed = (T)-1 < (T)1; }; \
501template<> struct __usdt_t<const T> { static const bool is_signed = (T)-1 < (T)1; }; \
502template<> struct __usdt_t<volatile T> { static const bool is_signed = (T)-1 < (T)1; }; \
503template<> struct __usdt_t<const volatile T> { static const bool is_signed = (T)-1 < (T)1; }
504
505__usdt_def_signed(signed char);
506__usdt_def_signed(short);
507__usdt_def_signed(int);
508__usdt_def_signed(long);
509__usdt_def_signed(long long);
510__usdt_maybe_signed(char);
511__usdt_maybe_signed(wchar_t);
512
513#else /* !__cplusplus */
514
515#define __usdt_is_inttype(x) (__builtin_classify_type(x) >= 1 && __builtin_classify_type(x) <= 4)
516#define __usdt_inttype(x) __typeof(__builtin_choose_expr(__usdt_is_inttype(x), (x), 0U))
517#define __usdt_is_signed(x) ((__usdt_inttype(x))-1 < (__usdt_inttype(x))1)
518
519#endif /* __cplusplus */
520
521#define __usdt_asm_op(n, x) \
522 [__usdt_asz##n] "n" ((__usdt_is_signed(x) ? (int)-1 : 1) * (int)__usdt_arg_size(x)), \
523 [__usdt_aval##n] __usdt_str(USDT_ARG_CONSTRAINT)(x)
524
525#define __usdt_asm_ops0() [__usdt_dummy] "g" (0)
526#define __usdt_asm_ops1(x) __usdt_asm_op(1, x)
527#define __usdt_asm_ops2(a,x) __usdt_asm_ops1(a), __usdt_asm_op(2, x)
528#define __usdt_asm_ops3(a,b,x) __usdt_asm_ops2(a,b), __usdt_asm_op(3, x)
529#define __usdt_asm_ops4(a,b,c,x) __usdt_asm_ops3(a,b,c), __usdt_asm_op(4, x)
530#define __usdt_asm_ops5(a,b,c,d,x) __usdt_asm_ops4(a,b,c,d), __usdt_asm_op(5, x)
531#define __usdt_asm_ops6(a,b,c,d,e,x) __usdt_asm_ops5(a,b,c,d,e), __usdt_asm_op(6, x)
532#define __usdt_asm_ops7(a,b,c,d,e,f,x) __usdt_asm_ops6(a,b,c,d,e,f), __usdt_asm_op(7, x)
533#define __usdt_asm_ops8(a,b,c,d,e,f,g,x) __usdt_asm_ops7(a,b,c,d,e,f,g), __usdt_asm_op(8, x)
534#define __usdt_asm_ops9(a,b,c,d,e,f,g,h,x) __usdt_asm_ops8(a,b,c,d,e,f,g,h), __usdt_asm_op(9, x)
535#define __usdt_asm_ops10(a,b,c,d,e,f,g,h,i,x) __usdt_asm_ops9(a,b,c,d,e,f,g,h,i), __usdt_asm_op(10, x)
536#define __usdt_asm_ops11(a,b,c,d,e,f,g,h,i,j,x) __usdt_asm_ops10(a,b,c,d,e,f,g,h,i,j), __usdt_asm_op(11, x)
537#define __usdt_asm_ops12(a,b,c,d,e,f,g,h,i,j,k,x) __usdt_asm_ops11(a,b,c,d,e,f,g,h,i,j,k), __usdt_asm_op(12, x)
538#define __usdt_asm_ops(...) __usdt_apply(__usdt_asm_ops, __usdt_narg(__VA_ARGS__))(__VA_ARGS__)
539
540#endif /* __USDT_H */
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2util_8c_source.html0000644000175000017500000002220715156613430022137 0ustar berndbernd libfuse: fuse-3.18.2/lib/util.c Source File
libfuse
util.c
1
2#include "fuse_config.h"
3
4#ifdef HAVE_PTHREAD_SETNAME_NP
5#define _GNU_SOURCE
6#include <pthread.h>
7#endif
8
9#include <errno.h>
10#include <stdlib.h>
11#include <errno.h>
12
13#ifndef FUSE_USE_VERSION
14#define FUSE_USE_VERSION (FUSE_MAKE_VERSION(3, 18))
15#endif
16
17#include "util.h"
18#include "fuse_log.h"
19#include "fuse_lowlevel.h"
20#include <stdio.h>
21
22int libfuse_strtol(const char *str, long *res)
23{
24 char *endptr;
25 int base = 10;
26 long val;
27
28 errno = 0;
29
30 if (!str)
31 return -EINVAL;
32
33 val = strtol(str, &endptr, base);
34
35 if (errno)
36 return -errno;
37
38 if (endptr == str || *endptr != '\0')
39 return -EINVAL;
40
41 *res = val;
42 return 0;
43}
44
45void fuse_set_thread_name(const char *name)
46{
47#ifdef HAVE_PTHREAD_SETNAME_NP
48 pthread_setname_np(pthread_self(), name);
49#else
50 (void)name;
51#endif
52}
53
fuse-3.18.2/doc/html/fuse-3_818_82_2lib_2util_8h_source.html0000644000175000017500000002134415156613430022145 0ustar berndbernd libfuse: fuse-3.18.2/lib/util.h Source File
libfuse
util.h
1#ifndef FUSE_UTIL_H_
2#define FUSE_UTIL_H_
3
4#include <stdint.h>
5#include <stdbool.h>
6
7#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
8
9#define likely(x) __builtin_expect(!!(x), 1)
10#define unlikely(x) __builtin_expect(!!(x), 0)
11
12struct fuse_conn_info;
13
14int libfuse_strtol(const char *str, long *res);
15void fuse_set_thread_name(const char *name);
16
20static inline uint32_t fuse_lower_32_bits(uint64_t nr)
21{
22 return (uint32_t)(nr & 0xffffffff);
23}
24
28static inline uint64_t fuse_higher_32_bits(uint64_t nr)
29{
30 return nr & ~0xffffffffULL;
31}
32
33#ifndef FUSE_VAR_UNUSED
34#define FUSE_VAR_UNUSED __attribute__((__unused__))
35#endif
36
37#define container_of(ptr, type, member) \
38 ({ \
39 unsigned long __mptr = (unsigned long)(ptr); \
40 ((type *)(__mptr - offsetof(type, member))); \
41 })
42
43#if __has_attribute(__fallthrough__)
44#define fallthrough __attribute__((__fallthrough__))
45#else
46#define fallthrough do {} while (0)
47#endif
48
49#endif /* FUSE_UTIL_H_ */
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2readdir__inode_8c_source.html0000644000175000017500000002531415156613430024324 0ustar berndbernd libfuse: fuse-3.18.2/test/readdir_inode.c Source File
libfuse
readdir_inode.c
1/*
2 * Prints each directory entry, its inode and d_type as returned by 'readdir'.
3 * Skips '.' and '..' because readdir is not required to return them and
4 * some of our examples don't. However if they are returned, their d_type
5 * should be valid.
6 */
7
8#include <stdio.h>
9#include <string.h>
10#include <sys/types.h>
11#include <dirent.h>
12#include <errno.h>
13
14int main(int argc, char* argv[])
15{
16 DIR* dirp;
17 struct dirent* dent;
18
19 if (argc != 2) {
20 fprintf(stderr, "Usage: readdir_inode dir\n");
21 return 1;
22 }
23
24 dirp = opendir(argv[1]);
25 if (dirp == NULL) {
26 perror("failed to open directory");
27 return 2;
28 }
29
30 errno = 0;
31 dent = readdir(dirp);
32 while (dent != NULL) {
33 if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
34 printf("%llu %d %s\n", (unsigned long long)dent->d_ino,
35 (int)dent->d_type, dent->d_name);
36 if ((long long)dent->d_ino < 0)
37 fprintf(stderr,"%s : bad d_ino %llu\n",
38 dent->d_name, (unsigned long long)dent->d_ino);
39 if ((dent->d_type < 1) || (dent->d_type > 15))
40 fprintf(stderr,"%s : bad d_type %d\n",
41 dent->d_name, (int)dent->d_type);
42 } else {
43 if (dent->d_type != DT_DIR)
44 fprintf(stderr,"%s : bad d_type %d\n",
45 dent->d_name, (int)dent->d_type);
46 }
47 dent = readdir(dirp);
48 }
49 if (errno != 0) {
50 perror("failed to read directory entry");
51 return 3;
52 }
53
54 closedir(dirp);
55
56 return 0;
57}
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2release__unlink__race_8c_source.html0000644000175000017500000005417515156613430025674 0ustar berndbernd libfuse: fuse-3.18.2/test/release_unlink_race.c Source File
libfuse
release_unlink_race.c
1/*
2 This program can be distributed under the terms of the GNU GPLv2.
3 See the file GPL2.txt.
4*/
5
6#define FUSE_USE_VERSION 31
7
8#define _GNU_SOURCE
9
10#include <fuse.h>
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <errno.h>
16
17static void *xmp_init(struct fuse_conn_info *conn,
18 struct fuse_config *cfg)
19{
20 (void) conn;
21
22 cfg->use_ino = 1;
23 cfg->nullpath_ok = 1;
24 cfg->entry_timeout = 0;
25 cfg->attr_timeout = 0;
26 cfg->negative_timeout = 0;
27
28 return NULL;
29}
30
31static int xmp_getattr(const char *path, struct stat *stbuf,
32 struct fuse_file_info *fi)
33{
34 int res;
35
36 (void) path;
37
38 if(fi)
39 res = fstat(fi->fh, stbuf);
40 else
41 res = lstat(path, stbuf);
42 if (res == -1)
43 return -errno;
44
45 return 0;
46}
47
48static int xmp_unlink(const char *path)
49{
50 int res;
51
52 res = unlink(path);
53 if (res == -1)
54 return -errno;
55
56 return 0;
57}
58
59static int xmp_rename(const char *from, const char *to, unsigned int flags)
60{
61 int res;
62
63 if (flags)
64 return -EINVAL;
65
66 if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
67
68 res = rename(from, to);
69 if (res == -1)
70 return -errno;
71
72 return 0;
73}
74
75static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
76{
77 int fd;
78
79 fd = open(path, fi->flags, mode);
80 if (fd == -1)
81 return -errno;
82
83 fi->fh = fd;
84 return 0;
85}
86
87static int xmp_release(const char *path, struct fuse_file_info *fi)
88{
89 (void) path;
90
91 if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
92
93 close(fi->fh);
94
95 return 0;
96}
97
98static const struct fuse_operations xmp_oper = {
99 .init = xmp_init,
100 .getattr = xmp_getattr,
101 .unlink = xmp_unlink,
102 .rename = xmp_rename,
103 .create = xmp_create,
104 .release = xmp_release,
105};
106
107int main(int argc, char *argv[])
108{
109 umask(0);
110 return fuse_main(argc, argv, &xmp_oper, NULL);
111}
int32_t nullpath_ok
Definition fuse.h:273
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2stracedecode_8c_source.html0000644000175000017500000010622215156613430024020 0ustar berndbernd libfuse: fuse-3.18.2/test/stracedecode.c Source File
libfuse
stracedecode.c
1#include <stdio.h>
2#include <string.h>
3#include "fuse_kernel.h"
4
5static struct {
6 const char *name;
7} fuse_ll_ops[] = {
8 [FUSE_LOOKUP] = { "LOOKUP" },
9 [FUSE_FORGET] = { "FORGET" },
10 [FUSE_GETATTR] = { "GETATTR" },
11 [FUSE_SETATTR] = { "SETATTR" },
12 [FUSE_READLINK] = { "READLINK" },
13 [FUSE_SYMLINK] = { "SYMLINK" },
14 [FUSE_MKNOD] = { "MKNOD" },
15 [FUSE_MKDIR] = { "MKDIR" },
16 [FUSE_UNLINK] = { "UNLINK" },
17 [FUSE_RMDIR] = { "RMDIR" },
18 [FUSE_RENAME] = { "RENAME" },
19 [FUSE_LINK] = { "LINK" },
20 [FUSE_OPEN] = { "OPEN" },
21 [FUSE_READ] = { "READ" },
22 [FUSE_WRITE] = { "WRITE" },
23 [FUSE_STATFS] = { "STATFS" },
24 [FUSE_RELEASE] = { "RELEASE" },
25 [FUSE_FSYNC] = { "FSYNC" },
26 [FUSE_SETXATTR] = { "SETXATTR" },
27 [FUSE_GETXATTR] = { "GETXATTR" },
28 [FUSE_LISTXATTR] = { "LISTXATTR" },
29 [FUSE_REMOVEXATTR] = { "REMOVEXATTR" },
30 [FUSE_FLUSH] = { "FLUSH" },
31 [FUSE_INIT] = { "INIT" },
32 [FUSE_OPENDIR] = { "OPENDIR" },
33 [FUSE_READDIR] = { "READDIR" },
34 [FUSE_RELEASEDIR] = { "RELEASEDIR" },
35 [FUSE_FSYNCDIR] = { "FSYNCDIR" },
36 [FUSE_GETLK] = { "GETLK" },
37 [FUSE_SETLK] = { "SETLK" },
38 [FUSE_SETLKW] = { "SETLKW" },
39 [FUSE_ACCESS] = { "ACCESS" },
40 [FUSE_CREATE] = { "CREATE" },
41 [FUSE_TMPFILE] = { "TMPFILE" },
42 [FUSE_INTERRUPT] = { "INTERRUPT" },
43 [FUSE_BMAP] = { "BMAP" },
44 [FUSE_DESTROY] = { "DESTROY" },
45 [FUSE_READDIRPLUS] = { "READDIRPLUS" },
46};
47
48#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
49
50static const char *opname(enum fuse_opcode opcode)
51{
52 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
53 return "???";
54 else
55 return fuse_ll_ops[opcode].name;
56}
57
58
59static void process_buf(int dir, char *buf, int len)
60{
61 static unsigned long long prevuniq = -1;
62 static int prevopcode;
63
64 if (!dir) {
65 struct fuse_in_header *in = (struct fuse_in_header *) buf;
66 buf += sizeof(struct fuse_in_header);
67
68 printf("unique: %llu, opcode: %s (%i), nodeid: %lu, len: %i, insize: %i\n",
69 (unsigned long long) in->unique,
70 opname((enum fuse_opcode) in->opcode), in->opcode,
71 (unsigned long) in->nodeid, in->len, len);
72
73 switch (in->opcode) {
74 case FUSE_READ: {
75 struct fuse_read_in *arg = (struct fuse_read_in *) buf;
76 printf("-READ fh:%llu off:%llu siz:%u rfl:%u own:%llu fl:%u\n",
77 arg->fh, arg->offset, arg->size, arg->read_flags,
78 arg->lock_owner, arg->flags);
79 break;
80 }
81 case FUSE_WRITE: {
82 struct fuse_write_in *arg = (struct fuse_write_in *) buf;
83 printf("-WRITE fh:%llu off:%llu siz:%u wfl:%u own:%llu fl:%u\n",
84 arg->fh, arg->offset, arg->size, arg->write_flags,
85 arg->lock_owner, arg->flags);
86 break;
87 }
88 }
89 prevuniq = in->unique;
90 prevopcode = in->opcode;
91 } else {
92 struct fuse_out_header *out = (struct fuse_out_header *) buf;
93 buf += sizeof(struct fuse_out_header);
94
95 printf(" unique: %llu, error: %i (%s), len: %i, outsize: %i\n",
96 (unsigned long long) out->unique, out->error,
97 strerror(-out->error), out->len, len);
98
99 if (out->unique == prevuniq) {
100 switch (prevopcode) {
101 case FUSE_GETATTR: {
102 struct fuse_attr_out *arg = (struct fuse_attr_out *) buf;
103 printf("+ATTR v:%llu.%09u i:%llu s:%llu b:%llu\n",
104 arg->attr_valid, arg->attr_valid_nsec,
105 arg->attr.ino, arg->attr.size, arg->attr.blocks);
106 break;
107 }
108 case FUSE_LOOKUP: {
109 struct fuse_entry_out *arg = (struct fuse_entry_out *) buf;
110 printf("+ENTRY nodeid:%llu v:%llu.%09u i:%llu s:%llu b:%llu\n",
111 arg->nodeid, arg->attr_valid, arg->attr_valid_nsec,
112 arg->attr.ino, arg->attr.size, arg->attr.blocks);
113 break;
114 }
115 }
116 }
117 }
118
119}
120
121int main(void)
122{
123 FILE *in = stdin;
124 while (1) {
125 int dir;
126 int res;
127 char buf[1048576];
128 unsigned len = 0;
129
130 memset(buf, 0, sizeof(buf));
131 while (1) {
132 char str[32];
133
134 res = fscanf(in, "%30s", str);
135 if (res != 1 && feof(in))
136 return 0;
137
138 if (res == 0)
139 continue;
140
141 if (strncmp(str, "read(", 5) == 0) {
142 dir = 0;
143 break;
144 } else if (strncmp(str, "writev(", 7) == 0) {
145 dir = 1;
146 break;
147 }
148 }
149
150 while (1) {
151 int c = getc(in);
152 if (c == '"') {
153 while (1) {
154 int val;
155
156 c = getc(in);
157 if (c == EOF) {
158 fprintf(stderr, "eof in string\n");
159 break;
160 }
161 if (c == '\n') {
162 fprintf(stderr, "eol in string\n");
163 break;
164 }
165 if (c == '"')
166 break;
167 if (c != '\\') {
168 val = c;
169 } else {
170 c = getc(in);
171 switch (c) {
172 case 'n': val = '\n'; break;
173 case 'r': val = '\r'; break;
174 case 't': val = '\t'; break;
175 case '"': val = '"'; break;
176 case '\\': val = '\\'; break;
177 case 'x':
178 res = scanf("%x", &val);
179 if (res != 1) {
180 fprintf(stderr, "parse error\n");
181 continue;
182 }
183 break;
184 default:
185 fprintf(stderr, "unknown sequence: '\\%c'\n", c);
186 continue;
187 }
188 }
189 buf[len++] = val;
190 }
191 }
192 if (c == '\n')
193 break;
194 }
195 process_buf(dir, buf, len);
196 memset(buf, 0, len);
197 len = 0;
198 }
199}
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2test__abi_8c_source.html0000644000175000017500000001276515156613430023334 0ustar berndbernd libfuse: fuse-3.18.2/test/test_abi.c Source File
libfuse
test_abi.c
1#define FUSE_USE_VERSION 30
2
3#include "fuse.h"
4
5#include <stdio.h>
6#include <stdlib.h>
7
8int main(void)
9{
10 if (sizeof(struct fuse_file_info) != 64) {
11 fprintf(stderr, "struct fuse_file_info size mismatch\n");
12 exit(1);
13 }
14 if (sizeof(struct fuse_conn_info) != 128) {
15 fprintf(stderr, "struct fuse_conn_info size mismatch\n");
16 exit(1);
17 }
18}
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2test__setattr_8c_source.html0000644000175000017500000012116615156613430024263 0ustar berndbernd libfuse: fuse-3.18.2/test/test_setattr.c Source File
libfuse
test_setattr.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
9
10#define FUSE_USE_VERSION 30
11
12/* Not really needed - just to test build with FUSE_USE_VERSION == 30 */
13#include <fuse.h>
14
15#include <fuse_config.h>
16#include <fuse_lowlevel.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <assert.h>
23#include <stddef.h>
24#include <unistd.h>
25#include <pthread.h>
26
27#ifndef __linux__
28#include <limits.h>
29#else
30#include <linux/limits.h>
31#endif
32
33#define FILE_INO 2
34#define FILE_NAME "truncate_me"
35
36static int got_fh;
37static mode_t file_mode = S_IFREG | 0644;
38
39static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
40 stbuf->st_ino = ino;
41 if (ino == FUSE_ROOT_ID) {
42 stbuf->st_mode = S_IFDIR | 0755;
43 stbuf->st_nlink = 1;
44 }
45
46 else if (ino == FILE_INO) {
47 stbuf->st_mode = file_mode;
48 stbuf->st_nlink = 1;
49 stbuf->st_size = 0;
50 }
51
52 else
53 return -1;
54
55 return 0;
56}
57
58static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
59 const char *name) {
60 struct fuse_entry_param e;
61 memset(&e, 0, sizeof(e));
62
63 if (parent != FUSE_ROOT_ID)
64 goto err_out;
65 else if (strcmp(name, FILE_NAME) == 0)
66 e.ino = FILE_INO;
67 else
68 goto err_out;
69
70 if (tfs_stat(e.ino, &e.attr) != 0)
71 goto err_out;
72 fuse_reply_entry(req, &e);
73 return;
74
75err_out:
76 fuse_reply_err(req, ENOENT);
77}
78
79static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
80 struct fuse_file_info *fi) {
81 struct stat stbuf;
82
83 (void) fi;
84
85 memset(&stbuf, 0, sizeof(stbuf));
86 if (tfs_stat(ino, &stbuf) != 0)
87 fuse_reply_err(req, ENOENT);
88 else
89 fuse_reply_attr(req, &stbuf, 5);
90}
91
92static void tfs_open(fuse_req_t req, fuse_ino_t ino,
93 struct fuse_file_info *fi) {
94 if (ino == FUSE_ROOT_ID)
95 fuse_reply_err(req, EISDIR);
96 else {
97 assert(ino == FILE_INO);
98 fi->fh = FILE_INO;
99 fuse_reply_open(req, fi);
100 }
101}
102
103static void tfs_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
104 int to_set, struct fuse_file_info *fi) {
105 if(ino != FILE_INO ||
106 !(to_set & FUSE_SET_ATTR_MODE)) {
107 fuse_reply_err(req, EINVAL);
108 return;
109 }
110
111 if(fi == NULL)
112 fprintf(stderr, "setattr with fi == NULL\n");
113 else if (fi->fh != FILE_INO)
114 fprintf(stderr, "setattr with wrong fi->fh\n");
115 else {
116 fprintf(stderr, "setattr ok\n");
117 got_fh = 1;
118 file_mode = attr->st_mode;
119 }
120
121 tfs_getattr(req, ino, fi);
122}
123
124static struct fuse_lowlevel_ops tfs_oper = {
125 .lookup = tfs_lookup,
126 .getattr = tfs_getattr,
127 .open = tfs_open,
128 .setattr = tfs_setattr,
129};
130
131static void* run_fs(void *data) {
132 struct fuse_session *se = (struct fuse_session*) data;
133 assert(fuse_session_loop(se) == 0);
134 return NULL;
135}
136
137static void test_fs(char *mountpoint) {
138 char fname[PATH_MAX];
139 int fd;
140
141 assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME,
142 mountpoint) > 0);
143 fd = open(fname, O_WRONLY);
144 if (fd == -1) {
145 perror(fname);
146 assert(0);
147 }
148
149 assert(fchmod(fd, 0600) == 0);
150 close(fd);
151}
152
153int main(int argc, char *argv[]) {
154 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
155 struct fuse_session *se;
156 struct fuse_cmdline_opts fuse_opts;
157 pthread_t fs_thread;
158
159 assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
160#ifndef __FreeBSD__
161 assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
162#endif
163 se = fuse_session_new(&args, &tfs_oper,
164 sizeof(tfs_oper), NULL);
165 assert (se != NULL);
166 assert(fuse_set_signal_handlers(se) == 0);
167 assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
168
169 /* Start file-system thread */
170 assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
171
172 /* Do test */
173 test_fs(fuse_opts.mountpoint);
174
175 /* Stop file system */
176 assert(pthread_cancel(fs_thread) == 0);
177
179 assert(got_fh == 1);
182
183 printf("Test completed successfully.\n");
184 return 0;
185}
186
187
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
fuse_ino_t ino
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2test__signals_8c_source.html0000644000175000017500000011602015156613430024226 0ustar berndbernd libfuse: fuse-3.18.2/test/test_signals.c Source File
libfuse
test_signals.c
1/*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2025 Bernd Schubert <bernd@bsbernd.com>
4 *
5 * Test for signal handling in libfuse.
6 *
7 * This program can be distributed under the terms of the GNU LGPLv2.
8 * See the file GPL2.txt
9 */
10
11#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
12
13#include "fuse_config.h"
14#include "fuse_lowlevel.h"
15#include "fuse_i.h"
16
17#include <pthread.h>
18#include <stdatomic.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <signal.h>
24#include <errno.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27
28static void test_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
29{
30 (void)parent;
31 (void)name;
32 /* Simulate slow lookup to test signal interruption */
33 sleep(2);
34 fuse_reply_err(req, ENOENT);
35}
36
37static void test_ll_getattr(fuse_req_t req, fuse_ino_t ino,
38 struct fuse_file_info *fi)
39{
40 (void)ino;
41 (void)fi;
42 /* Simulate slow getattr to test signal interruption */
43 sleep(2);
44 fuse_reply_err(req, ENOENT);
45}
46
47static const struct fuse_lowlevel_ops test_ll_ops = {
48 .lookup = test_ll_lookup,
49 .getattr = test_ll_getattr,
50};
51
52static void *signal_sender_thread(void *arg)
53{
54 (void)arg;
55
56 usleep(2 * 1000 * 1000);
57
58 /* Send SIGTERM to the process */
59 kill(getpid(), SIGTERM);
60 return NULL;
61}
62
63static void fork_child(void)
64{
65 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
66 struct fuse_session *se;
67 struct fuse_loop_config *loop_config;
68 pthread_t sig_thread;
69 char *mountpoint = NULL;
70 int ret = -1;
71
72 /* Add the program name to arg[0] */
73 if (fuse_opt_add_arg(&args, "test_signals")) {
74 fprintf(stderr, "Failed to add argument\n");
75 goto out_free_mountpoint;
76 }
77
78 /* Add debug flag to see more output */
79 fuse_opt_add_arg(&args, "-d");
80
81 /* Create temporary mount point */
82 mountpoint = strdup("/tmp/fuse_test_XXXXXX");
83 if (!mountpoint || !mkdtemp(mountpoint)) {
84 fprintf(stderr, "Failed to create temp dir\n");
85 goto out_free_args;
86 }
87
88 /* Create session */
89 se = fuse_session_new(&args, &test_ll_ops, sizeof(test_ll_ops), NULL);
90 if (!se) {
91 fprintf(stderr, "Failed to create FUSE session\n");
92 goto out_free_mountpoint;
93 }
94
95 /* Mount filesystem */
96 if (fuse_session_mount(se, mountpoint)) {
97 fprintf(stderr, "Failed to mount FUSE filesystem\n");
98 goto out_destroy_session;
99 }
100
101 /* Create loop config */
102 loop_config = fuse_loop_cfg_create();
103 if (!loop_config) {
104 fprintf(stderr, "Failed to create loop config\n");
105 goto out_unmount;
106 }
107 fuse_loop_cfg_set_clone_fd(loop_config, 0);
108 fuse_loop_cfg_set_max_threads(loop_config, 2);
109
110 /* Set up signal handlers */
111 if (fuse_set_signal_handlers(se)) {
112 fprintf(stderr, "Failed to set up signal handlers\n");
113 goto out_destroy_config;
114 }
115
116 /* Create thread that will send signals */
117 if (pthread_create(&sig_thread, NULL, signal_sender_thread, NULL)) {
118 fprintf(stderr, "Failed to create signal sender thread\n");
119 goto out_remove_handlers;
120 }
121
122 /* Enter FUSE loop */
123 ret = fuse_session_loop_mt_312(se, loop_config);
124
125 printf("Debug: fuse_session_loop_mt_312 returned %d\n", ret);
126 printf("Debug: session exited state: %d\n", fuse_session_exited(se));
127 printf("Debug: session status: %d\n", se->error);
128
129 /* Check exit status before cleanup */
130 int clean_exit = (fuse_session_exited(se) && se->error == SIGTERM);
131
132 /* Clean up */
133 pthread_join(sig_thread, NULL);
137 fuse_loop_cfg_destroy(loop_config);
138 rmdir(mountpoint);
139 free(mountpoint);
140 fuse_opt_free_args(&args);
141
142 /* Use saved exit status */
143 if (clean_exit) {
144 printf("Debug: Clean shutdown via SIGTERM\n");
145 exit(0);
146 }
147 printf("Debug: Exiting with status %d\n", ret != 0);
148 exit(ret != 0);
149
150out_remove_handlers:
152out_destroy_config:
153 fuse_loop_cfg_destroy(loop_config);
154out_unmount:
156out_destroy_session:
158out_free_mountpoint:
159 rmdir(mountpoint);
160 free(mountpoint);
161out_free_args:
162 fuse_opt_free_args(&args);
163 exit(1);
164}
165
166static void run_test_in_child(void)
167{
168 pid_t child;
169 int status;
170
171 child = fork();
172 if (child == -1) {
173 perror("fork");
174 exit(1);
175 }
176
177 if (child == 0)
178 fork_child();
179
180 /* In parent process */
181 if (waitpid(child, &status, 0) == -1) {
182 perror("waitpid");
183 exit(1);
184 }
185
186 /* Check if child exited due to SIGTERM - this is expected */
187 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM) {
188 printf("Child process terminated by SIGTERM as expected\n");
189 exit(0);
190 }
191
192 /* For any other type of exit, maintain existing behavior */
193 exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
194}
195
196int main(void)
197{
198 printf("Testing SIGTERM handling in libfuse\n");
199 run_test_in_child();
200 printf("SIGTERM handling test passed\n");
201 return 0;
202}
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_exited(struct fuse_session *se)
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2test__syscalls_8c_source.html0000644000175000017500000115750615156613430024442 0ustar berndbernd libfuse: fuse-3.18.2/test/test_syscalls.c Source File
libfuse
test_syscalls.c
1#define _GNU_SOURCE
2#include "fuse_config.h"
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <stdarg.h>
7#include <string.h>
8#include <unistd.h>
9#include <fcntl.h>
10#include <dirent.h>
11#include <utime.h>
12#include <errno.h>
13#include <assert.h>
14#include <time.h>
15#include <sys/socket.h>
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <sys/un.h>
19
20#ifndef ALLPERMS
21# define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)/* 07777 */
22#endif
23
24
25static const char *basepath;
26static const char *basepath_r;
27static char testfile[1024];
28static char testfile2[1024];
29static char testdir[1024];
30static char testdir2[1024];
31static char testsock[1024];
32static char subfile[1280];
33
34static char testfile_r[1024];
35static char testfile2_r[1024];
36static char testdir_r[1024];
37static char testdir2_r[1024];
38static char subfile_r[1280];
39
40static char testname[256];
41static char testdata[] = "abcdefghijklmnopqrstuvwxyz";
42static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./";
43static const char *testdir_files[] = { "f1", "f2", NULL};
44static long seekdir_offsets[4];
45static char zerodata[4096];
46static int testdatalen = sizeof(testdata) - 1;
47static int testdata2len = sizeof(testdata2) - 1;
48static unsigned int testnum = 0;
49static unsigned int select_test = 0;
50static unsigned int skip_test = 0;
51static unsigned int unlinked_test = 0;
52
53#define MAX_ENTRIES 1024
54#define MAX_TESTS 100
55
56static struct test {
57 int fd;
58 struct stat stat;
59} tests[MAX_TESTS];
60
61static void test_perror(const char *func, const char *msg)
62{
63 fprintf(stderr, "%s %s() - %s: %s\n", testname, func, msg,
64 strerror(errno));
65}
66
67static void test_error(const char *func, const char *msg, ...)
68 __attribute__ ((format (printf, 2, 3)));
69
70static void __start_test(const char *fmt, ...)
71 __attribute__ ((format (printf, 1, 2)));
72
73static void test_error(const char *func, const char *msg, ...)
74{
75 va_list ap;
76 fprintf(stderr, "%s %s() - ", testname, func);
77 va_start(ap, msg);
78 vfprintf(stderr, msg, ap);
79 va_end(ap);
80 fprintf(stderr, "\n");
81}
82
83static int is_dot_or_dotdot(const char *name) {
84 return name[0] == '.' &&
85 (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
86}
87
88static void success(void)
89{
90 fprintf(stderr, "%s OK\n", testname);
91}
92
93#define this_test (&tests[testnum-1])
94#define next_test (&tests[testnum])
95
96static void __start_test(const char *fmt, ...)
97{
98 unsigned int n;
99 va_list ap;
100 n = sprintf(testname, "%3i [", testnum);
101 va_start(ap, fmt);
102 n += vsprintf(testname + n, fmt, ap);
103 va_end(ap);
104 sprintf(testname + n, "]");
105 // Use dedicated testfile per test
106 sprintf(testfile, "%s/testfile.%d", basepath, testnum);
107 sprintf(testfile_r, "%s/testfile.%d", basepath_r, testnum);
108 if (testnum > MAX_TESTS) {
109 fprintf(stderr, "%s - too many tests\n", testname);
110 exit(1);
111 }
112 this_test->fd = -1;
113}
114
115#define start_test(msg, args...) { \
116 testnum++; \
117 if ((select_test && testnum != select_test) || \
118 (testnum == skip_test)) { \
119 return 0; \
120 } \
121 __start_test(msg, ##args); \
122}
123
124#define PERROR(msg) test_perror(__FUNCTION__, msg)
125#define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args)
126
127#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
128
129static int st_check_size(struct stat *st, int len)
130{
131 if (st->st_size != len) {
132 ERROR("length %u instead of %u", (int) st->st_size,
133 (int) len);
134 return -1;
135 }
136 return 0;
137}
138
139static int check_size(const char *path, int len)
140{
141 struct stat stbuf;
142 int res = stat(path, &stbuf);
143 if (res == -1) {
144 PERROR("stat");
145 return -1;
146 }
147 return st_check_size(&stbuf, len);
148}
149
150static int check_testfile_size(const char *path, int len)
151{
152 this_test->stat.st_size = len;
153 return check_size(path, len);
154}
155
156static int st_check_type(struct stat *st, mode_t type)
157{
158 if ((st->st_mode & S_IFMT) != type) {
159 ERROR("type 0%o instead of 0%o", st->st_mode & S_IFMT, type);
160 return -1;
161 }
162 return 0;
163}
164
165static int check_type(const char *path, mode_t type)
166{
167 struct stat stbuf;
168 int res = lstat(path, &stbuf);
169 if (res == -1) {
170 PERROR("lstat");
171 return -1;
172 }
173 return st_check_type(&stbuf, type);
174}
175
176static int st_check_mode(struct stat *st, mode_t mode)
177{
178 if ((st->st_mode & ALLPERMS) != mode) {
179 ERROR("mode 0%o instead of 0%o", st->st_mode & ALLPERMS,
180 mode);
181 return -1;
182 }
183 return 0;
184}
185
186static int check_mode(const char *path, mode_t mode)
187{
188 struct stat stbuf;
189 int res = lstat(path, &stbuf);
190 if (res == -1) {
191 PERROR("lstat");
192 return -1;
193 }
194 return st_check_mode(&stbuf, mode);
195}
196
197static int check_testfile_mode(const char *path, mode_t mode)
198{
199 this_test->stat.st_mode &= ~ALLPERMS;
200 this_test->stat.st_mode |= mode;
201 return check_mode(path, mode);
202}
203
204static int check_times(const char *path, time_t atime, time_t mtime)
205{
206 int err = 0;
207 struct stat stbuf;
208 int res = lstat(path, &stbuf);
209 if (res == -1) {
210 PERROR("lstat");
211 return -1;
212 }
213 if (stbuf.st_atime != atime) {
214 ERROR("atime %li instead of %li", stbuf.st_atime, atime);
215 err--;
216 }
217 if (stbuf.st_mtime != mtime) {
218 ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
219 err--;
220 }
221 if (err)
222 return -1;
223
224 return 0;
225}
226
227#if 0
228static int fcheck_times(int fd, time_t atime, time_t mtime)
229{
230 int err = 0;
231 struct stat stbuf;
232 int res = fstat(fd, &stbuf);
233 if (res == -1) {
234 PERROR("fstat");
235 return -1;
236 }
237 if (stbuf.st_atime != atime) {
238 ERROR("atime %li instead of %li", stbuf.st_atime, atime);
239 err--;
240 }
241 if (stbuf.st_mtime != mtime) {
242 ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
243 err--;
244 }
245 if (err)
246 return -1;
247
248 return 0;
249}
250#endif
251
252static int st_check_nlink(struct stat *st, nlink_t nlink)
253{
254 if (st->st_nlink != nlink) {
255 ERROR("nlink %li instead of %li", (long) st->st_nlink,
256 (long) nlink);
257 return -1;
258 }
259 return 0;
260}
261
262static int check_nlink(const char *path, nlink_t nlink)
263{
264 struct stat stbuf;
265 int res = lstat(path, &stbuf);
266 if (res == -1) {
267 PERROR("lstat");
268 return -1;
269 }
270 return st_check_nlink(&stbuf, nlink);
271}
272
273static int fcheck_stat(int fd, int flags, struct stat *st)
274{
275 struct stat stbuf;
276 int res = fstat(fd, &stbuf);
277 if (res == -1) {
278 if (flags & O_PATH) {
279 // With O_PATH fd, the server does not have to keep
280 // the inode alive so FUSE inode may be stale or bad
281 if (errno == ESTALE || errno == EIO ||
282 errno == ENOENT || errno == EBADF)
283 return 0;
284 }
285 PERROR("fstat");
286 return -1;
287 }
288
289 int err = 0;
290 err += st_check_type(&stbuf, st->st_mode & S_IFMT);
291 err += st_check_mode(&stbuf, st->st_mode & ALLPERMS);
292 err += st_check_size(&stbuf, st->st_size);
293 err += st_check_nlink(&stbuf, st->st_nlink);
294
295 return err;
296}
297
298static int check_nonexist(const char *path)
299{
300 struct stat stbuf;
301 int res = lstat(path, &stbuf);
302 if (res == 0) {
303 ERROR("file should not exist");
304 return -1;
305 }
306 if (errno != ENOENT) {
307 ERROR("file should not exist: %s", strerror(errno));
308 return -1;
309 }
310 return 0;
311}
312
313static int check_buffer(const char *buf, const char *data, unsigned len)
314{
315 if (memcmp(buf, data, len) != 0) {
316 ERROR("data mismatch");
317 return -1;
318 }
319 return 0;
320}
321
322static int check_data(const char *path, const char *data, int offset,
323 unsigned len)
324{
325 char buf[4096];
326 int res;
327 int fd = open(path, O_RDONLY);
328 if (fd == -1) {
329 PERROR("open");
330 return -1;
331 }
332 if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
333 PERROR("lseek");
334 close(fd);
335 return -1;
336 }
337 while (len) {
338 int rdlen = len < sizeof(buf) ? len : sizeof(buf);
339 res = read(fd, buf, rdlen);
340 if (res == -1) {
341 PERROR("read");
342 close(fd);
343 return -1;
344 }
345 if (res != rdlen) {
346 ERROR("short read: %u instead of %u", res, rdlen);
347 close(fd);
348 return -1;
349 }
350 if (check_buffer(buf, data, rdlen) != 0) {
351 close(fd);
352 return -1;
353 }
354 data += rdlen;
355 len -= rdlen;
356 }
357 res = close(fd);
358 if (res == -1) {
359 PERROR("close");
360 return -1;
361 }
362 return 0;
363}
364
365static int fcheck_data(int fd, const char *data, int offset,
366 unsigned len)
367{
368 char buf[4096];
369 int res;
370 if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
371 PERROR("lseek");
372 return -1;
373 }
374 while (len) {
375 int rdlen = len < sizeof(buf) ? len : sizeof(buf);
376 res = read(fd, buf, rdlen);
377 if (res == -1) {
378 PERROR("read");
379 return -1;
380 }
381 if (res != rdlen) {
382 ERROR("short read: %u instead of %u", res, rdlen);
383 return -1;
384 }
385 if (check_buffer(buf, data, rdlen) != 0) {
386 return -1;
387 }
388 data += rdlen;
389 len -= rdlen;
390 }
391 return 0;
392}
393
394static int check_dir_contents(const char *path, const char **contents)
395{
396 int i;
397 int res;
398 int err = 0;
399 int found[MAX_ENTRIES];
400 const char *cont[MAX_ENTRIES];
401 DIR *dp;
402
403 for (i = 0; contents[i]; i++) {
404 assert(i < MAX_ENTRIES - 3);
405 found[i] = 0;
406 cont[i] = contents[i];
407 }
408 cont[i] = NULL;
409
410 dp = opendir(path);
411 if (dp == NULL) {
412 PERROR("opendir");
413 return -1;
414 }
415 memset(found, 0, sizeof(found));
416 while(1) {
417 struct dirent *de;
418 errno = 0;
419 de = readdir(dp);
420 if (de == NULL) {
421 if (errno) {
422 PERROR("readdir");
423 closedir(dp);
424 return -1;
425 }
426 break;
427 }
428 if (is_dot_or_dotdot(de->d_name))
429 continue;
430 for (i = 0; cont[i] != NULL; i++) {
431 assert(i < MAX_ENTRIES);
432 if (strcmp(cont[i], de->d_name) == 0) {
433 if (found[i]) {
434 ERROR("duplicate entry <%s>",
435 de->d_name);
436 err--;
437 } else
438 found[i] = 1;
439 break;
440 }
441 }
442 if (!cont[i]) {
443 ERROR("unexpected entry <%s>", de->d_name);
444 err --;
445 }
446 }
447 for (i = 0; cont[i] != NULL; i++) {
448 if (!found[i]) {
449 ERROR("missing entry <%s>", cont[i]);
450 err--;
451 }
452 }
453 res = closedir(dp);
454 if (res == -1) {
455 PERROR("closedir");
456 return -1;
457 }
458 if (err)
459 return -1;
460
461 return 0;
462}
463
464static int create_file(const char *path, const char *data, int len)
465{
466 int res;
467 int fd;
468
469 unlink(path);
470 fd = creat(path, 0644);
471 if (fd == -1) {
472 PERROR("creat");
473 return -1;
474 }
475 if (len) {
476 res = write(fd, data, len);
477 if (res == -1) {
478 PERROR("write");
479 close(fd);
480 return -1;
481 }
482 if (res != len) {
483 ERROR("write is short: %u instead of %u", res, len);
484 close(fd);
485 return -1;
486 }
487 }
488 res = close(fd);
489 if (res == -1) {
490 PERROR("close");
491 return -1;
492 }
493 res = check_type(path, S_IFREG);
494 if (res == -1)
495 return -1;
496 res = check_mode(path, 0644);
497 if (res == -1)
498 return -1;
499 res = check_nlink(path, 1);
500 if (res == -1)
501 return -1;
502 res = check_size(path, len);
503 if (res == -1)
504 return -1;
505
506 if (len) {
507 res = check_data(path, data, 0, len);
508 if (res == -1)
509 return -1;
510 }
511
512 return 0;
513}
514
515static int create_path_fd(const char *path, const char *data, int len)
516{
517 int path_fd;
518 int res;
519
520 res = create_file(path, data, len);
521 if (res == -1)
522 return -1;
523
524 path_fd = open(path, O_PATH);
525 if (path_fd == -1)
526 PERROR("open(O_PATH)");
527
528 return path_fd;
529}
530
531// Can be called once per test
532static int create_testfile(const char *path, const char *data, int len)
533{
534 struct test *t = this_test;
535 struct stat *st = &t->stat;
536 int res, fd;
537
538 if (t->fd > 0) {
539 ERROR("testfile already created");
540 return -1;
541 }
542
543 fd = create_path_fd(path, data, len);
544 if (fd == -1)
545 return -1;
546
547 t->fd = fd;
548
549 res = fstat(fd, st);
550 if (res == -1) {
551 PERROR("fstat");
552 return -1;
553 }
554
555 return 0;
556}
557
558static int check_unlinked_testfile(int fd)
559{
560 struct stat *st = &this_test->stat;
561
562 st->st_nlink = 0;
563 return fcheck_stat(fd, O_PATH, st);
564}
565
566// Check recorded testfiles after all tests completed
567static int check_unlinked_testfiles(void)
568{
569 int fd;
570 int res, err = 0;
571 int num = testnum;
572
573 if (!unlinked_test)
574 return 0;
575
576 testnum = 0;
577 while (testnum < num) {
578 fd = next_test->fd;
579 start_test("check_unlinked_testfile");
580 if (fd == -1)
581 continue;
582
583 err += check_unlinked_testfile(fd);
584 res = close(fd);
585 if (res == -1) {
586 PERROR("close(test_fd)");
587 err--;
588 }
589 }
590
591 if (err) {
592 fprintf(stderr, "%i unlinked testfile checks failed\n", -err);
593 return 1;
594 }
595
596 return err;
597}
598
599static int cleanup_dir(const char *path, const char **dir_files, int quiet)
600{
601 int i;
602 int err = 0;
603
604 for (i = 0; dir_files[i]; i++) {
605 int res;
606 char fpath[1280];
607 sprintf(fpath, "%s/%s", path, dir_files[i]);
608 res = unlink(fpath);
609 if (res == -1 && !quiet) {
610 PERROR("unlink");
611 err --;
612 }
613 }
614 if (err)
615 return -1;
616
617 return 0;
618}
619
620static int create_dir(const char *path, const char **dir_files)
621{
622 int res;
623 int i;
624
625 rmdir(path);
626 res = mkdir(path, 0755);
627 if (res == -1) {
628 PERROR("mkdir");
629 return -1;
630 }
631 res = check_type(path, S_IFDIR);
632 if (res == -1)
633 return -1;
634 res = check_mode(path, 0755);
635 if (res == -1)
636 return -1;
637
638 for (i = 0; dir_files[i]; i++) {
639 char fpath[1280];
640 sprintf(fpath, "%s/%s", path, dir_files[i]);
641 res = create_file(fpath, "", 0);
642 if (res == -1) {
643 cleanup_dir(path, dir_files, 1);
644 return -1;
645 }
646 }
647 res = check_dir_contents(path, dir_files);
648 if (res == -1) {
649 cleanup_dir(path, dir_files, 1);
650 return -1;
651 }
652
653 return 0;
654}
655
656static int test_truncate(int len)
657{
658 const char *data = testdata;
659 int datalen = testdatalen;
660 int res;
661
662 start_test("truncate(%u)", (int) len);
663 res = create_testfile(testfile, data, datalen);
664 if (res == -1)
665 return -1;
666
667 res = truncate(testfile, len);
668 if (res == -1) {
669 PERROR("truncate");
670 return -1;
671 }
672 res = check_testfile_size(testfile, len);
673 if (res == -1)
674 return -1;
675
676 if (len > 0) {
677 if (len <= datalen) {
678 res = check_data(testfile, data, 0, len);
679 if (res == -1)
680 return -1;
681 } else {
682 res = check_data(testfile, data, 0, datalen);
683 if (res == -1)
684 return -1;
685 res = check_data(testfile, zerodata, datalen,
686 len - datalen);
687 if (res == -1)
688 return -1;
689 }
690 }
691 res = unlink(testfile);
692 if (res == -1) {
693 PERROR("unlink");
694 return -1;
695 }
696 res = check_nonexist(testfile);
697 if (res == -1)
698 return -1;
699
700 success();
701 return 0;
702}
703
704static int test_ftruncate(int len, int mode)
705{
706 const char *data = testdata;
707 int datalen = testdatalen;
708 int res;
709 int fd;
710
711 start_test("ftruncate(%u) mode: 0%03o", len, mode);
712 res = create_testfile(testfile, data, datalen);
713 if (res == -1)
714 return -1;
715
716 fd = open(testfile, O_WRONLY);
717 if (fd == -1) {
718 PERROR("open");
719 return -1;
720 }
721
722 res = fchmod(fd, mode);
723 if (res == -1) {
724 PERROR("fchmod");
725 close(fd);
726 return -1;
727 }
728 res = check_testfile_mode(testfile, mode);
729 if (res == -1) {
730 close(fd);
731 return -1;
732 }
733 res = ftruncate(fd, len);
734 if (res == -1) {
735 PERROR("ftruncate");
736 close(fd);
737 return -1;
738 }
739 close(fd);
740 res = check_testfile_size(testfile, len);
741 if (res == -1)
742 return -1;
743
744 if (len > 0) {
745 if (len <= datalen) {
746 res = check_data(testfile, data, 0, len);
747 if (res == -1)
748 return -1;
749 } else {
750 res = check_data(testfile, data, 0, datalen);
751 if (res == -1)
752 return -1;
753 res = check_data(testfile, zerodata, datalen,
754 len - datalen);
755 if (res == -1)
756 return -1;
757 }
758 }
759 res = unlink(testfile);
760 if (res == -1) {
761 PERROR("unlink");
762 return -1;
763 }
764 res = check_nonexist(testfile);
765 if (res == -1)
766 return -1;
767
768 success();
769 return 0;
770}
771
772static int test_seekdir(void)
773{
774 int i;
775 int res;
776 DIR *dp;
777 struct dirent *de = NULL;
778
779 start_test("seekdir");
780 res = create_dir(testdir, testdir_files);
781 if (res == -1)
782 return res;
783
784 dp = opendir(testdir);
785 if (dp == NULL) {
786 PERROR("opendir");
787 return -1;
788 }
789
790 /* Remember dir offsets */
791 for (i = 0; i < ARRAY_SIZE(seekdir_offsets); i++) {
792 seekdir_offsets[i] = telldir(dp);
793 errno = 0;
794 de = readdir(dp);
795 if (de == NULL) {
796 if (errno) {
797 PERROR("readdir");
798 goto fail;
799 }
800 break;
801 }
802 }
803
804 /* Walk until the end of directory */
805 while (de)
806 de = readdir(dp);
807
808 /* Start from the last valid dir offset and seek backwards */
809 for (i--; i >= 0; i--) {
810 seekdir(dp, seekdir_offsets[i]);
811 de = readdir(dp);
812 if (de == NULL) {
813 ERROR("Unexpected end of directory after seekdir()");
814 goto fail;
815 }
816 }
817
818 closedir(dp);
819 res = cleanup_dir(testdir, testdir_files, 0);
820 if (!res)
821 success();
822 return res;
823fail:
824 closedir(dp);
825 cleanup_dir(testdir, testdir_files, 1);
826 return -1;
827}
828
829#ifdef HAVE_COPY_FILE_RANGE
830static int test_copy_file_range(void)
831{
832 const char *data = testdata;
833 int datalen = testdatalen;
834 int err = 0;
835 int res;
836 int fd_in, fd_out;
837 off_t pos_in = 0, pos_out = 0;
838
839 start_test("copy_file_range");
840 unlink(testfile);
841 fd_in = open(testfile, O_CREAT | O_RDWR, 0644);
842 if (fd_in == -1) {
843 PERROR("creat");
844 return -1;
845 }
846 res = write(fd_in, data, datalen);
847 if (res == -1) {
848 PERROR("write");
849 close(fd_in);
850 return -1;
851 }
852 if (res != datalen) {
853 ERROR("write is short: %u instead of %u", res, datalen);
854 close(fd_in);
855 return -1;
856 }
857
858 unlink(testfile2);
859 fd_out = creat(testfile2, 0644);
860 if (fd_out == -1) {
861 PERROR("creat");
862 close(fd_in);
863 return -1;
864 }
865 res = copy_file_range(fd_in, &pos_in, fd_out, &pos_out, datalen, 0);
866 if (res == -1) {
867 PERROR("copy_file_range");
868 close(fd_in);
869 close(fd_out);
870 return -1;
871 }
872 if (res != datalen) {
873 ERROR("copy is short: %u instead of %u", res, datalen);
874 close(fd_in);
875 close(fd_out);
876 return -1;
877 }
878
879 res = close(fd_in);
880 if (res == -1) {
881 PERROR("close");
882 close(fd_out);
883 return -1;
884 }
885 res = close(fd_out);
886 if (res == -1) {
887 PERROR("close");
888 return -1;
889 }
890
891 err = check_data(testfile2, data, 0, datalen);
892
893 res = unlink(testfile);
894 if (res == -1) {
895 PERROR("unlink");
896 return -1;
897 }
898 res = check_nonexist(testfile);
899 if (res == -1)
900 return -1;
901 if (err)
902 return -1;
903
904 res = unlink(testfile2);
905 if (res == -1) {
906 PERROR("unlink");
907 return -1;
908 }
909 res = check_nonexist(testfile2);
910 if (res == -1)
911 return -1;
912 if (err)
913 return -1;
914
915 success();
916 return 0;
917}
918#else
919static int test_copy_file_range(void)
920{
921 return 0;
922}
923#endif
924
925#ifdef HAVE_STATX
926static int test_statx(void)
927{
928 struct statx sb;
929 char msg[] = "hi";
930 size_t msg_size = sizeof(msg);
931 struct timespec tp;
932 int res;
933
934 memset(&sb, 0, sizeof(sb));
935 unlink(testfile);
936
937 start_test("statx");
938
939 res = create_testfile(testfile, msg, msg_size);
940 if (res == -1)
941 return -1;
942
943 res = statx(-1, testfile, AT_EMPTY_PATH,
944 STATX_BASIC_STATS | STATX_BTIME, &sb);
945 if (res == -1)
946 return -1;
947
948 if (sb.stx_size != msg_size)
949 return -1;
950
951 clock_gettime(CLOCK_REALTIME, &tp);
952
953 if (sb.stx_btime.tv_sec > tp.tv_sec)
954 return -1;
955
956 if (sb.stx_btime.tv_sec == tp.tv_sec &&
957 sb.stx_btime.tv_nsec >= tp.tv_nsec)
958 return -1;
959
960 unlink(testfile);
961
962 success();
963 return 0;
964}
965#else
966static int test_statx(void)
967{
968 return 0;
969}
970#endif
971
972static int test_utime(void)
973{
974 struct utimbuf utm;
975 time_t atime = 987631200;
976 time_t mtime = 123116400;
977 int res;
978
979 start_test("utime");
980 res = create_testfile(testfile, NULL, 0);
981 if (res == -1)
982 return -1;
983
984 utm.actime = atime;
985 utm.modtime = mtime;
986 res = utime(testfile, &utm);
987 if (res == -1) {
988 PERROR("utime");
989 return -1;
990 }
991 res = check_times(testfile, atime, mtime);
992 if (res == -1) {
993 return -1;
994 }
995 res = unlink(testfile);
996 if (res == -1) {
997 PERROR("unlink");
998 return -1;
999 }
1000 res = check_nonexist(testfile);
1001 if (res == -1)
1002 return -1;
1003
1004 success();
1005 return 0;
1006}
1007
1008static int test_create(void)
1009{
1010 const char *data = testdata;
1011 int datalen = testdatalen;
1012 int err = 0;
1013 int res;
1014 int fd;
1015
1016 start_test("create");
1017 unlink(testfile);
1018 fd = creat(testfile, 0644);
1019 if (fd == -1) {
1020 PERROR("creat");
1021 return -1;
1022 }
1023 res = write(fd, data, datalen);
1024 if (res == -1) {
1025 PERROR("write");
1026 close(fd);
1027 return -1;
1028 }
1029 if (res != datalen) {
1030 ERROR("write is short: %u instead of %u", res, datalen);
1031 close(fd);
1032 return -1;
1033 }
1034 res = close(fd);
1035 if (res == -1) {
1036 PERROR("close");
1037 return -1;
1038 }
1039 res = check_type(testfile, S_IFREG);
1040 if (res == -1)
1041 return -1;
1042 err += check_mode(testfile, 0644);
1043 err += check_nlink(testfile, 1);
1044 err += check_size(testfile, datalen);
1045 err += check_data(testfile, data, 0, datalen);
1046 res = unlink(testfile);
1047 if (res == -1) {
1048 PERROR("unlink");
1049 return -1;
1050 }
1051 res = check_nonexist(testfile);
1052 if (res == -1)
1053 return -1;
1054 if (err)
1055 return -1;
1056
1057 success();
1058 return 0;
1059}
1060
1061static int test_create_unlink(void)
1062{
1063 const char *data = testdata;
1064 int datalen = testdatalen;
1065 int err = 0;
1066 int res;
1067 int fd;
1068
1069 start_test("create+unlink");
1070 unlink(testfile);
1071 fd = open(testfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
1072 if (fd == -1) {
1073 PERROR("creat");
1074 return -1;
1075 }
1076 res = unlink(testfile);
1077 if (res == -1) {
1078 PERROR("unlink");
1079 close(fd);
1080 return -1;
1081 }
1082 res = check_nonexist(testfile);
1083 if (res == -1) {
1084 close(fd);
1085 return -1;
1086 }
1087 res = write(fd, data, datalen);
1088 if (res == -1) {
1089 PERROR("write");
1090 close(fd);
1091 return -1;
1092 }
1093 if (res != datalen) {
1094 ERROR("write is short: %u instead of %u", res, datalen);
1095 close(fd);
1096 return -1;
1097 }
1098 struct stat st = {
1099 .st_mode = S_IFREG | 0644,
1100 .st_size = datalen,
1101 };
1102 err = fcheck_stat(fd, O_RDWR, &st);
1103 err += fcheck_data(fd, data, 0, datalen);
1104 res = close(fd);
1105 if (res == -1) {
1106 PERROR("close");
1107 err--;
1108 }
1109 if (err)
1110 return -1;
1111
1112 success();
1113 return 0;
1114}
1115
1116static int test_mknod(void)
1117{
1118 int err = 0;
1119 int res;
1120
1121 start_test("mknod");
1122 unlink(testfile);
1123 res = mknod(testfile, 0644, 0);
1124 if (res == -1) {
1125 PERROR("mknod");
1126 return -1;
1127 }
1128 res = check_type(testfile, S_IFREG);
1129 if (res == -1)
1130 return -1;
1131 err += check_mode(testfile, 0644);
1132 err += check_nlink(testfile, 1);
1133 err += check_size(testfile, 0);
1134 res = unlink(testfile);
1135 if (res == -1) {
1136 PERROR("unlink");
1137 return -1;
1138 }
1139 res = check_nonexist(testfile);
1140 if (res == -1)
1141 return -1;
1142 if (err)
1143 return -1;
1144
1145 success();
1146 return 0;
1147}
1148
1149#define test_open(exist, flags, mode) do_test_open(exist, flags, #flags, mode)
1150
1151static int do_test_open(int exist, int flags, const char *flags_str, int mode)
1152{
1153 char buf[4096];
1154 const char *data = testdata;
1155 int datalen = testdatalen;
1156 unsigned currlen = 0;
1157 int err = 0;
1158 int res;
1159 int fd;
1160 off_t off;
1161
1162 start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode);
1163 unlink(testfile);
1164 if (exist) {
1165 res = create_file(testfile_r, testdata2, testdata2len);
1166 if (res == -1)
1167 return -1;
1168
1169 currlen = testdata2len;
1170 }
1171
1172 fd = open(testfile, flags, mode);
1173 if ((flags & O_CREAT) && (flags & O_EXCL) && exist) {
1174 if (fd != -1) {
1175 ERROR("open should have failed");
1176 close(fd);
1177 return -1;
1178 } else if (errno == EEXIST)
1179 goto succ;
1180 }
1181 if (!(flags & O_CREAT) && !exist) {
1182 if (fd != -1) {
1183 ERROR("open should have failed");
1184 close(fd);
1185 return -1;
1186 } else if (errno == ENOENT)
1187 goto succ;
1188 }
1189 if (fd == -1) {
1190 PERROR("open");
1191 return -1;
1192 }
1193
1194 if (flags & O_TRUNC)
1195 currlen = 0;
1196
1197 err += check_type(testfile, S_IFREG);
1198 if (exist)
1199 err += check_mode(testfile, 0644);
1200 else
1201 err += check_mode(testfile, mode);
1202 err += check_nlink(testfile, 1);
1203 err += check_size(testfile, currlen);
1204 if (exist && !(flags & O_TRUNC) && (mode & S_IRUSR))
1205 err += check_data(testfile, testdata2, 0, testdata2len);
1206
1207 res = write(fd, data, datalen);
1208 if ((flags & O_ACCMODE) != O_RDONLY) {
1209 if (res == -1) {
1210 PERROR("write");
1211 err --;
1212 } else if (res != datalen) {
1213 ERROR("write is short: %u instead of %u", res, datalen);
1214 err --;
1215 } else {
1216 if (datalen > (int) currlen)
1217 currlen = datalen;
1218
1219 err += check_size(testfile, currlen);
1220
1221 if (mode & S_IRUSR) {
1222 err += check_data(testfile, data, 0, datalen);
1223 if (exist && !(flags & O_TRUNC) &&
1224 testdata2len > datalen)
1225 err += check_data(testfile,
1226 testdata2 + datalen,
1227 datalen,
1228 testdata2len - datalen);
1229 }
1230 }
1231 } else {
1232 if (res != -1) {
1233 ERROR("write should have failed");
1234 err --;
1235 } else if (errno != EBADF) {
1236 PERROR("write");
1237 err --;
1238 }
1239 }
1240 off = lseek(fd, SEEK_SET, 0);
1241 if (off == (off_t) -1) {
1242 PERROR("lseek");
1243 err--;
1244 } else if (off != 0) {
1245 ERROR("offset should have returned 0");
1246 err --;
1247 }
1248 res = read(fd, buf, sizeof(buf));
1249 if ((flags & O_ACCMODE) != O_WRONLY) {
1250 if (res == -1) {
1251 PERROR("read");
1252 err--;
1253 } else {
1254 int readsize =
1255 currlen < sizeof(buf) ? currlen : sizeof(buf);
1256 if (res != readsize) {
1257 ERROR("read is short: %i instead of %u",
1258 res, readsize);
1259 err--;
1260 } else {
1261 if ((flags & O_ACCMODE) != O_RDONLY) {
1262 err += check_buffer(buf, data, datalen);
1263 if (exist && !(flags & O_TRUNC) &&
1264 testdata2len > datalen)
1265 err += check_buffer(buf + datalen,
1266 testdata2 + datalen,
1267 testdata2len - datalen);
1268 } else if (exist)
1269 err += check_buffer(buf, testdata2,
1270 testdata2len);
1271 }
1272 }
1273 } else {
1274 if (res != -1) {
1275 ERROR("read should have failed");
1276 err --;
1277 } else if (errno != EBADF) {
1278 PERROR("read");
1279 err --;
1280 }
1281 }
1282
1283 res = close(fd);
1284 if (res == -1) {
1285 PERROR("close");
1286 return -1;
1287 }
1288 res = unlink(testfile);
1289 if (res == -1) {
1290 PERROR("unlink");
1291 return -1;
1292 }
1293 res = check_nonexist(testfile);
1294 if (res == -1)
1295 return -1;
1296 res = check_nonexist(testfile_r);
1297 if (res == -1)
1298 return -1;
1299 if (err)
1300 return -1;
1301
1302succ:
1303 success();
1304 return 0;
1305}
1306
1307#define test_open_acc(flags, mode, err) \
1308 do_test_open_acc(flags, #flags, mode, err)
1309
1310static int do_test_open_acc(int flags, const char *flags_str, int mode, int err)
1311{
1312 const char *data = testdata;
1313 int datalen = testdatalen;
1314 int res;
1315 int fd;
1316
1317 start_test("open_acc(%s) mode: 0%03o message: '%s'", flags_str, mode,
1318 strerror(err));
1319 unlink(testfile);
1320 res = create_testfile(testfile, data, datalen);
1321 if (res == -1)
1322 return -1;
1323
1324 res = chmod(testfile, mode);
1325 if (res == -1) {
1326 PERROR("chmod");
1327 return -1;
1328 }
1329
1330 res = check_testfile_mode(testfile, mode);
1331 if (res == -1)
1332 return -1;
1333
1334 fd = open(testfile, flags);
1335 if (fd == -1) {
1336 if (err != errno) {
1337 PERROR("open");
1338 return -1;
1339 }
1340 } else {
1341 if (err) {
1342 ERROR("open should have failed");
1343 close(fd);
1344 return -1;
1345 }
1346 close(fd);
1347 }
1348
1349 res = unlink(testfile);
1350 if (res == -1) {
1351 PERROR("unlink");
1352 return -1;
1353 }
1354 res = check_nonexist(testfile);
1355 if (res == -1)
1356 return -1;
1357 res = check_nonexist(testfile_r);
1358 if (res == -1)
1359 return -1;
1360
1361 success();
1362 return 0;
1363}
1364
1365static int test_symlink(void)
1366{
1367 char buf[1024];
1368 const char *data = testdata;
1369 int datalen = testdatalen;
1370 int linklen = strlen(testfile);
1371 int err = 0;
1372 int res;
1373
1374 start_test("symlink");
1375 res = create_testfile(testfile, data, datalen);
1376 if (res == -1)
1377 return -1;
1378
1379 unlink(testfile2);
1380 res = symlink(testfile, testfile2);
1381 if (res == -1) {
1382 PERROR("symlink");
1383 return -1;
1384 }
1385 res = check_type(testfile2, S_IFLNK);
1386 if (res == -1)
1387 return -1;
1388 err += check_mode(testfile2, 0777);
1389 err += check_nlink(testfile2, 1);
1390 res = readlink(testfile2, buf, sizeof(buf));
1391 if (res == -1) {
1392 PERROR("readlink");
1393 err--;
1394 }
1395 if (res != linklen) {
1396 ERROR("short readlink: %u instead of %u", res, linklen);
1397 err--;
1398 }
1399 if (memcmp(buf, testfile, linklen) != 0) {
1400 ERROR("link mismatch");
1401 err--;
1402 }
1403 err += check_size(testfile2, datalen);
1404 err += check_data(testfile2, data, 0, datalen);
1405 res = unlink(testfile2);
1406 if (res == -1) {
1407 PERROR("unlink");
1408 return -1;
1409 }
1410 res = check_nonexist(testfile2);
1411 if (res == -1)
1412 return -1;
1413 if (err)
1414 return -1;
1415
1416 res = unlink(testfile);
1417 if (res == -1) {
1418 PERROR("unlink");
1419 return -1;
1420 }
1421 res = check_nonexist(testfile);
1422 if (res == -1)
1423 return -1;
1424
1425 success();
1426 return 0;
1427}
1428
1429static int test_link(void)
1430{
1431 const char *data = testdata;
1432 int datalen = testdatalen;
1433 int err = 0;
1434 int res;
1435
1436 start_test("link");
1437 res = create_testfile(testfile, data, datalen);
1438 if (res == -1)
1439 return -1;
1440
1441 unlink(testfile2);
1442 res = link(testfile, testfile2);
1443 if (res == -1) {
1444 PERROR("link");
1445 return -1;
1446 }
1447 res = check_type(testfile2, S_IFREG);
1448 if (res == -1)
1449 return -1;
1450 err += check_mode(testfile2, 0644);
1451 err += check_nlink(testfile2, 2);
1452 err += check_size(testfile2, datalen);
1453 err += check_data(testfile2, data, 0, datalen);
1454 res = unlink(testfile);
1455 if (res == -1) {
1456 PERROR("unlink");
1457 return -1;
1458 }
1459 res = check_nonexist(testfile);
1460 if (res == -1)
1461 return -1;
1462
1463 err += check_nlink(testfile2, 1);
1464 res = unlink(testfile2);
1465 if (res == -1) {
1466 PERROR("unlink");
1467 return -1;
1468 }
1469 res = check_nonexist(testfile2);
1470 if (res == -1)
1471 return -1;
1472 if (err)
1473 return -1;
1474
1475 success();
1476 return 0;
1477}
1478
1479static int test_link2(void)
1480{
1481 const char *data = testdata;
1482 int datalen = testdatalen;
1483 int err = 0;
1484 int res;
1485
1486 start_test("link-unlink-link");
1487 res = create_testfile(testfile, data, datalen);
1488 if (res == -1)
1489 return -1;
1490
1491 unlink(testfile2);
1492 res = link(testfile, testfile2);
1493 if (res == -1) {
1494 PERROR("link");
1495 return -1;
1496 }
1497 res = unlink(testfile);
1498 if (res == -1) {
1499 PERROR("unlink");
1500 return -1;
1501 }
1502 res = check_nonexist(testfile);
1503 if (res == -1)
1504 return -1;
1505 res = link(testfile2, testfile);
1506 if (res == -1) {
1507 PERROR("link");
1508 }
1509 res = check_type(testfile, S_IFREG);
1510 if (res == -1)
1511 return -1;
1512 err += check_mode(testfile, 0644);
1513 err += check_nlink(testfile, 2);
1514 err += check_size(testfile, datalen);
1515 err += check_data(testfile, data, 0, datalen);
1516
1517 res = unlink(testfile2);
1518 if (res == -1) {
1519 PERROR("unlink");
1520 return -1;
1521 }
1522 err += check_nlink(testfile, 1);
1523 res = unlink(testfile);
1524 if (res == -1) {
1525 PERROR("unlink");
1526 return -1;
1527 }
1528 res = check_nonexist(testfile);
1529 if (res == -1)
1530 return -1;
1531 if (err)
1532 return -1;
1533
1534 success();
1535 return 0;
1536}
1537
1538static int test_rename_file(void)
1539{
1540 const char *data = testdata;
1541 int datalen = testdatalen;
1542 int err = 0;
1543 int res;
1544
1545 start_test("rename file");
1546 res = create_testfile(testfile, data, datalen);
1547 if (res == -1)
1548 return -1;
1549
1550 unlink(testfile2);
1551 res = rename(testfile, testfile2);
1552 if (res == -1) {
1553 PERROR("rename");
1554 return -1;
1555 }
1556 res = check_nonexist(testfile);
1557 if (res == -1)
1558 return -1;
1559 res = check_type(testfile2, S_IFREG);
1560 if (res == -1)
1561 return -1;
1562 err += check_mode(testfile2, 0644);
1563 err += check_nlink(testfile2, 1);
1564 err += check_size(testfile2, datalen);
1565 err += check_data(testfile2, data, 0, datalen);
1566 res = unlink(testfile2);
1567 if (res == -1) {
1568 PERROR("unlink");
1569 return -1;
1570 }
1571 res = check_nonexist(testfile2);
1572 if (res == -1)
1573 return -1;
1574 if (err)
1575 return -1;
1576
1577 success();
1578 return 0;
1579}
1580
1581static int test_rename_dir(void)
1582{
1583 int err = 0;
1584 int res;
1585
1586 start_test("rename dir");
1587 res = create_dir(testdir, testdir_files);
1588 if (res == -1)
1589 return -1;
1590
1591 rmdir(testdir2);
1592 res = rename(testdir, testdir2);
1593 if (res == -1) {
1594 PERROR("rename");
1595 cleanup_dir(testdir, testdir_files, 1);
1596 return -1;
1597 }
1598 res = check_nonexist(testdir);
1599 if (res == -1) {
1600 cleanup_dir(testdir, testdir_files, 1);
1601 return -1;
1602 }
1603 res = check_type(testdir2, S_IFDIR);
1604 if (res == -1) {
1605 cleanup_dir(testdir2, testdir_files, 1);
1606 return -1;
1607 }
1608 err += check_mode(testdir2, 0755);
1609 err += check_dir_contents(testdir2, testdir_files);
1610 err += cleanup_dir(testdir2, testdir_files, 0);
1611 res = rmdir(testdir2);
1612 if (res == -1) {
1613 PERROR("rmdir");
1614 return -1;
1615 }
1616 res = check_nonexist(testdir2);
1617 if (res == -1)
1618 return -1;
1619 if (err)
1620 return -1;
1621
1622 success();
1623 return 0;
1624}
1625
1626static int test_rename_dir_loop(void)
1627{
1628#define PATH(p) (snprintf(path, sizeof path, "%s/%s", testdir, p), path)
1629#define PATH2(p) (snprintf(path2, sizeof path2, "%s/%s", testdir, p), path2)
1630
1631 char path[1280], path2[1280];
1632 int err = 0;
1633 int res;
1634
1635 start_test("rename dir loop");
1636
1637 res = create_dir(testdir, testdir_files);
1638 if (res == -1)
1639 return -1;
1640
1641 res = mkdir(PATH("a"), 0755);
1642 if (res == -1) {
1643 PERROR("mkdir");
1644 goto fail;
1645 }
1646
1647 res = rename(PATH("a"), PATH2("a"));
1648 if (res == -1) {
1649 PERROR("rename");
1650 goto fail;
1651 }
1652
1653 errno = 0;
1654 res = rename(PATH("a"), PATH2("a/b"));
1655 if (res == 0 || errno != EINVAL) {
1656 PERROR("rename");
1657 goto fail;
1658 }
1659
1660 res = mkdir(PATH("a/b"), 0755);
1661 if (res == -1) {
1662 PERROR("mkdir");
1663 goto fail;
1664 }
1665
1666 res = mkdir(PATH("a/b/c"), 0755);
1667 if (res == -1) {
1668 PERROR("mkdir");
1669 goto fail;
1670 }
1671
1672 errno = 0;
1673 res = rename(PATH("a"), PATH2("a/b/c"));
1674 if (res == 0 || errno != EINVAL) {
1675 PERROR("rename");
1676 goto fail;
1677 }
1678
1679 errno = 0;
1680 res = rename(PATH("a"), PATH2("a/b/c/a"));
1681 if (res == 0 || errno != EINVAL) {
1682 PERROR("rename");
1683 goto fail;
1684 }
1685
1686 errno = 0;
1687 res = rename(PATH("a/b/c"), PATH2("a"));
1688 if (res == 0 || errno != ENOTEMPTY) {
1689 PERROR("rename");
1690 goto fail;
1691 }
1692
1693 res = open(PATH("a/foo"), O_CREAT, 0644);
1694 if (res == -1) {
1695 PERROR("open");
1696 goto fail;
1697 }
1698 close(res);
1699
1700 res = rename(PATH("a/foo"), PATH2("a/bar"));
1701 if (res == -1) {
1702 PERROR("rename");
1703 goto fail;
1704 }
1705
1706 res = rename(PATH("a/bar"), PATH2("a/foo"));
1707 if (res == -1) {
1708 PERROR("rename");
1709 goto fail;
1710 }
1711
1712 res = rename(PATH("a/foo"), PATH2("a/b/bar"));
1713 if (res == -1) {
1714 PERROR("rename");
1715 goto fail;
1716 }
1717
1718 res = rename(PATH("a/b/bar"), PATH2("a/foo"));
1719 if (res == -1) {
1720 PERROR("rename");
1721 goto fail;
1722 }
1723
1724 res = rename(PATH("a/foo"), PATH2("a/b/c/bar"));
1725 if (res == -1) {
1726 PERROR("rename");
1727 goto fail;
1728 }
1729
1730 res = rename(PATH("a/b/c/bar"), PATH2("a/foo"));
1731 if (res == -1) {
1732 PERROR("rename");
1733 goto fail;
1734 }
1735
1736 res = open(PATH("a/bar"), O_CREAT, 0644);
1737 if (res == -1) {
1738 PERROR("open");
1739 goto fail;
1740 }
1741 close(res);
1742
1743 res = rename(PATH("a/foo"), PATH2("a/bar"));
1744 if (res == -1) {
1745 PERROR("rename");
1746 goto fail;
1747 }
1748
1749 unlink(PATH("a/bar"));
1750
1751 res = rename(PATH("a/b"), PATH2("a/d"));
1752 if (res == -1) {
1753 PERROR("rename");
1754 goto fail;
1755 }
1756
1757 res = rename(PATH("a/d"), PATH2("a/b"));
1758 if (res == -1) {
1759 PERROR("rename");
1760 goto fail;
1761 }
1762
1763 res = mkdir(PATH("a/d"), 0755);
1764 if (res == -1) {
1765 PERROR("mkdir");
1766 goto fail;
1767 }
1768
1769 res = rename(PATH("a/b"), PATH2("a/d"));
1770 if (res == -1) {
1771 PERROR("rename");
1772 goto fail;
1773 }
1774
1775 res = rename(PATH("a/d"), PATH2("a/b"));
1776 if (res == -1) {
1777 PERROR("rename");
1778 goto fail;
1779 }
1780
1781 res = mkdir(PATH("a/d"), 0755);
1782 if (res == -1) {
1783 PERROR("mkdir");
1784 goto fail;
1785 }
1786
1787 res = mkdir(PATH("a/d/e"), 0755);
1788 if (res == -1) {
1789 PERROR("mkdir");
1790 goto fail;
1791 }
1792
1793 errno = 0;
1794 res = rename(PATH("a/b"), PATH2("a/d"));
1795 if (res == 0 || (errno != ENOTEMPTY && errno != EEXIST)) {
1796 PERROR("rename");
1797 goto fail;
1798 }
1799
1800 rmdir(PATH("a/d/e"));
1801 rmdir(PATH("a/d"));
1802
1803 rmdir(PATH("a/b/c"));
1804 rmdir(PATH("a/b"));
1805 rmdir(PATH("a"));
1806
1807 err += cleanup_dir(testdir, testdir_files, 0);
1808 res = rmdir(testdir);
1809 if (res == -1) {
1810 PERROR("rmdir");
1811 goto fail;
1812 }
1813 res = check_nonexist(testdir);
1814 if (res == -1)
1815 return -1;
1816 if (err)
1817 return -1;
1818
1819 success();
1820 return 0;
1821
1822fail:
1823 unlink(PATH("a/bar"));
1824
1825 rmdir(PATH("a/d/e"));
1826 rmdir(PATH("a/d"));
1827
1828 rmdir(PATH("a/b/c"));
1829 rmdir(PATH("a/b"));
1830 rmdir(PATH("a"));
1831
1832 cleanup_dir(testdir, testdir_files, 1);
1833 rmdir(testdir);
1834
1835 return -1;
1836
1837#undef PATH2
1838#undef PATH
1839}
1840
1841static int test_mkfifo(void)
1842{
1843 int res;
1844 int err = 0;
1845
1846 start_test("mkfifo");
1847 unlink(testfile);
1848 res = mkfifo(testfile, 0644);
1849 if (res == -1) {
1850 PERROR("mkfifo");
1851 return -1;
1852 }
1853 res = check_type(testfile, S_IFIFO);
1854 if (res == -1)
1855 return -1;
1856 err += check_mode(testfile, 0644);
1857 err += check_nlink(testfile, 1);
1858 res = unlink(testfile);
1859 if (res == -1) {
1860 PERROR("unlink");
1861 return -1;
1862 }
1863 res = check_nonexist(testfile);
1864 if (res == -1)
1865 return -1;
1866 if (err)
1867 return -1;
1868
1869 success();
1870 return 0;
1871}
1872
1873static int test_mkdir(void)
1874{
1875 int res;
1876 int err = 0;
1877 const char *dir_contents[] = {NULL};
1878
1879 start_test("mkdir");
1880 rmdir(testdir);
1881 res = mkdir(testdir, 0755);
1882 if (res == -1) {
1883 PERROR("mkdir");
1884 return -1;
1885 }
1886 res = check_type(testdir, S_IFDIR);
1887 if (res == -1)
1888 return -1;
1889 err += check_mode(testdir, 0755);
1890 /* Some file systems (like btrfs) don't track link
1891 count for directories */
1892 //err += check_nlink(testdir, 2);
1893 err += check_dir_contents(testdir, dir_contents);
1894 res = rmdir(testdir);
1895 if (res == -1) {
1896 PERROR("rmdir");
1897 return -1;
1898 }
1899 res = check_nonexist(testdir);
1900 if (res == -1)
1901 return -1;
1902 if (err)
1903 return -1;
1904
1905 success();
1906 return 0;
1907}
1908
1909static int test_socket(void)
1910{
1911 struct sockaddr_un su;
1912 int fd;
1913 int res;
1914 int err = 0;
1915 const size_t test_sock_len = strlen(testsock) + 1;
1916
1917 start_test("socket");
1918 if (test_sock_len > sizeof(su.sun_path)) {
1919 fprintf(stderr, "Need to shorten mount point by %zu chars\n",
1920 strlen(testsock) + 1 - sizeof(su.sun_path));
1921 return -1;
1922 }
1923 unlink(testsock);
1924 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1925 if (fd < 0) {
1926 PERROR("socket");
1927 return -1;
1928 }
1929 su.sun_family = AF_UNIX;
1930
1931 strncpy(su.sun_path, testsock, test_sock_len);
1932 su.sun_path[sizeof(su.sun_path) - 1] = '\0';
1933 res = bind(fd, (struct sockaddr*)&su, sizeof(su));
1934 if (res == -1) {
1935 PERROR("bind");
1936 return -1;
1937 }
1938
1939 res = check_type(testsock, S_IFSOCK);
1940 if (res == -1) {
1941 close(fd);
1942 return -1;
1943 }
1944 err += check_nlink(testsock, 1);
1945 close(fd);
1946 res = unlink(testsock);
1947 if (res == -1) {
1948 PERROR("unlink");
1949 return -1;
1950 }
1951 res = check_nonexist(testsock);
1952 if (res == -1)
1953 return -1;
1954 if (err)
1955 return -1;
1956
1957 success();
1958 return 0;
1959}
1960
1961#define test_create_ro_dir(flags) \
1962 do_test_create_ro_dir(flags, #flags)
1963
1964static int do_test_create_ro_dir(int flags, const char *flags_str)
1965{
1966 int res;
1967 int err = 0;
1968 int fd;
1969
1970 start_test("open(%s) in read-only directory", flags_str);
1971 rmdir(testdir);
1972 res = mkdir(testdir, 0555);
1973 if (res == -1) {
1974 PERROR("mkdir");
1975 return -1;
1976 }
1977 fd = open(subfile, flags, 0644);
1978 if (fd != -1) {
1979 close(fd);
1980 unlink(subfile);
1981 ERROR("open should have failed");
1982 err--;
1983 } else {
1984 res = check_nonexist(subfile);
1985 if (res == -1)
1986 err--;
1987 }
1988 unlink(subfile);
1989 res = rmdir(testdir);
1990 if (res == -1) {
1991 PERROR("rmdir");
1992 return -1;
1993 }
1994 res = check_nonexist(testdir);
1995 if (res == -1)
1996 return -1;
1997 if (err)
1998 return -1;
1999
2000 success();
2001 return 0;
2002}
2003
2004#ifndef __FreeBSD__
2005/* this tests open with O_TMPFILE
2006 note that this will only work with the fuse low level api
2007 you will get ENOTSUP with the high level api */
2008static int test_create_tmpfile(void)
2009{
2010 rmdir(testdir);
2011 int res = mkdir(testdir, 0777);
2012 if (res)
2013 return -1;
2014
2015 start_test("create tmpfile");
2016
2017 int fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
2018 if(fd == -1) {
2019 if (errno == ENOTSUP) {
2020 /* don't bother if we're working on an old kernel
2021 or on the high level API */
2022 return 0;
2023 }
2024
2025 PERROR("open O_TMPFILE | O_RDWR");
2026 return -1;
2027 }
2028 close(fd);
2029
2030 fd = open(testdir, O_TMPFILE | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
2031 if(fd == -1){
2032 PERROR("open with O_TMPFILE | O_WRONLY | O_EXCL");
2033 return -1;
2034 };
2035 close(fd);
2036
2037 fd = open(testdir, O_TMPFILE | O_RDONLY, S_IRUSR);
2038 if (fd != -1) {
2039 ERROR("open with O_TMPFILE | O_RDONLY succeeded");
2040 return -1;
2041 }
2042
2043 success();
2044 return 0;
2045}
2046
2047static int test_create_and_link_tmpfile(void)
2048{
2049 /* skip this test for now since the github runner will fail in the linkat call below */
2050 return 0;
2051
2052 rmdir(testdir);
2053 unlink(testfile);
2054
2055 int res = mkdir(testdir, 0777);
2056 if (res)
2057 return -1;
2058
2059 start_test("create and link tmpfile");
2060
2061 int fd = open(testdir, O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
2062 if(fd == -1) {
2063 if (errno == ENOTSUP) {
2064 /* don't bother if we're working on an old kernel
2065 or on the high level API */
2066 return 0;
2067 }
2068 PERROR("open with O_TMPFILE | O_RDWR | O_EXCL");
2069 return -1;
2070 }
2071
2072 if (!linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
2073 ERROR("linkat succeeded on a tmpfile opened with O_EXCL");
2074 return -1;
2075 }
2076 close(fd);
2077
2078 fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
2079 if(fd == -1) {
2080 PERROR("open O_TMPFILE");
2081 return -1;
2082 }
2083
2084 if (check_nonexist(testfile)) {
2085 return -1;
2086 }
2087
2088 if (linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
2089 PERROR("linkat tempfile");
2090 return -1;
2091 }
2092 close(fd);
2093
2094 if (check_nlink(testfile, 1)) {
2095 return -1;
2096 }
2097 unlink(testfile);
2098
2099 success();
2100 return 0;
2101}
2102#endif
2103
2104int main(int argc, char *argv[])
2105{
2106 int err = 0;
2107 int a;
2108 int is_root;
2109
2110 umask(0);
2111 if (argc < 2 || argc > 4) {
2112 fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#] [-u]\n", argv[0]);
2113 return 1;
2114 }
2115 basepath = argv[1];
2116 basepath_r = basepath;
2117 for (a = 2; a < argc; a++) {
2118 char *endptr;
2119 char *arg = argv[a];
2120 if (arg[0] == ':') {
2121 basepath_r = arg + 1;
2122 } else {
2123 if (arg[0] == '-') {
2124 arg++;
2125 if (arg[0] == 'u') {
2126 unlinked_test = 1;
2127 endptr = arg + 1;
2128 } else {
2129 skip_test = strtoul(arg, &endptr, 10);
2130 }
2131 } else {
2132 select_test = strtoul(arg, &endptr, 10);
2133 }
2134 if (arg[0] == '\0' || *endptr != '\0') {
2135 fprintf(stderr, "invalid option: '%s'\n", argv[a]);
2136 return 1;
2137 }
2138 }
2139 }
2140 assert(strlen(basepath) < 512);
2141 assert(strlen(basepath_r) < 512);
2142 if (basepath[0] != '/') {
2143 fprintf(stderr, "testdir must be an absolute path\n");
2144 return 1;
2145 }
2146
2147 sprintf(testfile, "%s/testfile", basepath);
2148 sprintf(testfile2, "%s/testfile2", basepath);
2149 sprintf(testdir, "%s/testdir", basepath);
2150 sprintf(testdir2, "%s/testdir2", basepath);
2151 sprintf(subfile, "%s/subfile", testdir2);
2152 sprintf(testsock, "%s/testsock", basepath);
2153
2154 sprintf(testfile_r, "%s/testfile", basepath_r);
2155 sprintf(testfile2_r, "%s/testfile2", basepath_r);
2156 sprintf(testdir_r, "%s/testdir", basepath_r);
2157 sprintf(testdir2_r, "%s/testdir2", basepath_r);
2158 sprintf(subfile_r, "%s/subfile", testdir2_r);
2159
2160 is_root = (geteuid() == 0);
2161
2162 err += test_create();
2163 err += test_create_unlink();
2164 err += test_symlink();
2165 err += test_link();
2166 err += test_link2();
2167 err += test_mknod();
2168 err += test_mkfifo();
2169 err += test_mkdir();
2170 err += test_rename_file();
2171 err += test_rename_dir();
2172 err += test_rename_dir_loop();
2173 err += test_seekdir();
2174 err += test_socket();
2175 err += test_utime();
2176 err += test_truncate(0);
2177 err += test_truncate(testdatalen / 2);
2178 err += test_truncate(testdatalen);
2179 err += test_truncate(testdatalen + 100);
2180 err += test_ftruncate(0, 0600);
2181 err += test_ftruncate(testdatalen / 2, 0600);
2182 err += test_ftruncate(testdatalen, 0600);
2183 err += test_ftruncate(testdatalen + 100, 0600);
2184 err += test_ftruncate(0, 0400);
2185 err += test_ftruncate(0, 0200);
2186 err += test_ftruncate(0, 0000);
2187 err += test_open(0, O_RDONLY, 0);
2188 err += test_open(1, O_RDONLY, 0);
2189 err += test_open(1, O_RDWR, 0);
2190 err += test_open(1, O_WRONLY, 0);
2191 err += test_open(0, O_RDWR | O_CREAT, 0600);
2192 err += test_open(1, O_RDWR | O_CREAT, 0600);
2193 err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600);
2194 err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600);
2195 err += test_open(0, O_RDONLY | O_CREAT, 0600);
2196 err += test_open(0, O_RDONLY | O_CREAT, 0400);
2197 err += test_open(0, O_RDONLY | O_CREAT, 0200);
2198 err += test_open(0, O_RDONLY | O_CREAT, 0000);
2199 err += test_open(0, O_WRONLY | O_CREAT, 0600);
2200 err += test_open(0, O_WRONLY | O_CREAT, 0400);
2201 err += test_open(0, O_WRONLY | O_CREAT, 0200);
2202 err += test_open(0, O_WRONLY | O_CREAT, 0000);
2203 err += test_open(0, O_RDWR | O_CREAT, 0400);
2204 err += test_open(0, O_RDWR | O_CREAT, 0200);
2205 err += test_open(0, O_RDWR | O_CREAT, 0000);
2206 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600);
2207 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600);
2208 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000);
2209 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000);
2210 err += test_open_acc(O_RDONLY, 0600, 0);
2211 err += test_open_acc(O_WRONLY, 0600, 0);
2212 err += test_open_acc(O_RDWR, 0600, 0);
2213 err += test_open_acc(O_RDONLY, 0400, 0);
2214 err += test_open_acc(O_WRONLY, 0200, 0);
2215 if(!is_root) {
2216 err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES);
2217 err += test_open_acc(O_WRONLY, 0400, EACCES);
2218 err += test_open_acc(O_RDWR, 0400, EACCES);
2219 err += test_open_acc(O_RDONLY, 0200, EACCES);
2220 err += test_open_acc(O_RDWR, 0200, EACCES);
2221 err += test_open_acc(O_RDONLY, 0000, EACCES);
2222 err += test_open_acc(O_WRONLY, 0000, EACCES);
2223 err += test_open_acc(O_RDWR, 0000, EACCES);
2224 }
2225 err += test_create_ro_dir(O_CREAT);
2226 err += test_create_ro_dir(O_CREAT | O_EXCL);
2227 err += test_create_ro_dir(O_CREAT | O_WRONLY);
2228 err += test_create_ro_dir(O_CREAT | O_TRUNC);
2229 err += test_copy_file_range();
2230 err += test_statx();
2231#ifndef __FreeBSD__
2232 err += test_create_tmpfile();
2233 err += test_create_and_link_tmpfile();
2234#endif
2235
2236 unlink(testfile2);
2237 unlink(testsock);
2238 rmdir(testdir);
2239 rmdir(testdir2);
2240
2241 if (err) {
2242 fprintf(stderr, "%i tests failed\n", -err);
2243 return 1;
2244 }
2245
2246 return check_unlinked_testfiles();
2247}
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2test__want__conversion_8c_source.html0000644000175000017500000013400015156613430026141 0ustar berndbernd libfuse: fuse-3.18.2/test/test_want_conversion.c Source File
libfuse
test_want_conversion.c
1#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
2
3#include "util.h"
4#include "fuse_i.h"
5#include "fuse_lowlevel.h"
6#include <stdio.h>
7#include <assert.h>
8#include <inttypes.h>
9#include <stdbool.h>
10#include <err.h>
11
12static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
13{
14 struct fuse_session *se = container_of(conn, struct fuse_session, conn);
15
16 printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64
17 " want_default=0x%" PRIx32 " want_ext_default=0x%" PRIx64 "\n",
18 prefix, conn->want, conn->want_ext, se->conn_want,
19 se->conn_want_ext);
20}
21
22static void application_init_old_style(struct fuse_conn_info *conn)
23{
24 /* Simulate application init the old style */
26 conn->want &= ~FUSE_CAP_SPLICE_READ;
27
28 /*
29 * Also use new style API, as that might happen through
30 * fuse_apply_conn_info_opts()
31 */
33}
34
35static void application_init_new_style(struct fuse_conn_info *conn)
36{
37 /* Simulate application init the new style */
41}
42
43static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
44{
45 /* High-level init */
47
48 if (new_style)
49 application_init_new_style(conn);
50 else
51 application_init_old_style(conn);
52}
53
54static void test_do_init(struct fuse_conn_info *conn, bool new_style)
55{
56 /* Initial setup */
61 conn->capable = fuse_lower_32_bits(conn->capable_ext);
62
66
67 print_conn_info("Initial state", conn);
68
69 int rc;
70
71 test_fuse_fs_init(conn, new_style);
72 print_conn_info("After init", conn);
73
75 assert(rc == 0);
76
77 /* Verify all expected flags are set */
78 assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
79 assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
80 assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
81 assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
82 assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
83 assert(conn->want_ext & FUSE_CAP_IOCTL_DIR);
84
85 /* Verify no other flags are set */
86 assert(conn->want_ext ==
90
91 print_conn_info("After init", conn);
92}
93
94static void test_want_conversion_basic(void)
95{
96 const struct fuse_lowlevel_ops ops = { 0 };
97 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
98 struct fuse_session *se;
99 struct fuse_conn_info *conn;
100
101 /* Add the program name to arg[0] */
102 if (fuse_opt_add_arg(&args, "test_signals")) {
103 fprintf(stderr, "Failed to add argument\n");
104 errx(1, "Failed to add argument");
105 }
106
107
108 se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
109 assert(se);
110 conn = &se->conn;
111 printf("\nTesting basic want conversion, old style:\n");
112 test_do_init(conn, false);
114
115 se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
116 assert(se);
117 conn = &se->conn;
118 printf("\nTesting basic want conversion, new style:\n");
119 test_do_init(conn, true);
120 print_conn_info("After init", conn);
122
123 fuse_opt_free_args(&args);
124
125}
126
127static void test_want_conversion_conflict(void)
128{
129 struct fuse_conn_info conn = { 0 };
130 int rc;
131
132 printf("\nTesting want conversion conflict:\n");
133
134 /* Test conflicting values */
135 /* Initialize like fuse_lowlevel.c does */
139 conn.capable = fuse_lower_32_bits(conn.capable_ext);
140 conn.want_ext = conn.capable_ext;
141 conn.want = fuse_lower_32_bits(conn.want_ext);
142 print_conn_info("Test conflict initial", &conn);
143
144 /* Simulate application init modifying capabilities */
145 conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
146 conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */
147
149 assert(rc == -EINVAL);
150 print_conn_info("Test conflict after", &conn);
151
152 printf("Want conversion conflict test passed\n");
153}
154
155static void test_want_conversion_high_bits(void)
156{
157 struct fuse_conn_info conn = { 0 };
158 int rc;
159
160 printf("\nTesting want conversion high bits preservation:\n");
161
162 /* Test high bits preservation */
163 conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ;
164 conn.want = fuse_lower_32_bits(conn.want_ext);
165 print_conn_info("Test high bits initial", &conn);
166
168 assert(rc == 0);
169 assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
170 print_conn_info("Test high bits after", &conn);
171
172 printf("Want conversion high bits test passed\n");
173}
174
175int main(void)
176{
177 test_want_conversion_basic();
178 test_want_conversion_conflict();
179 test_want_conversion_high_bits();
180 return 0;
181}
#define FUSE_CAP_IOCTL_DIR
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
uint64_t capable_ext
uint32_t capable
uint64_t want_ext
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2test__write__cache_8c_source.html0000644000175000017500000017735115156613430025220 0ustar berndbernd libfuse: fuse-3.18.2/test/test_write_cache.c Source File
libfuse
test_write_cache.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
9#define FUSE_USE_VERSION 30
10
11/* Not really needed - just to test build with FUSE_USE_VERSION == 30 */
12#include <fuse.h>
13
14#include <fuse_config.h>
15#include <fuse_lowlevel.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <assert.h>
22#include <stddef.h>
23#include <unistd.h>
24#include <sys/stat.h>
25#include <pthread.h>
26#include <stdatomic.h>
27
28#ifndef __linux__
29#include <limits.h>
30#else
31#include <linux/limits.h>
32#endif
33
34#define FILE_INO 2
35#define FILE_NAME "write_me"
36
37/* Command line parsing */
38struct options {
39 int writeback;
40 int data_size;
41 int delay_ms;
42} options = {
43 .writeback = 0,
44 .data_size = 2048,
45 .delay_ms = 0,
46};
47
48#define WRITE_SYSCALLS 64
49
50#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
51static const struct fuse_opt option_spec[] = {
52 OPTION("writeback_cache", writeback),
53 OPTION("--data-size=%d", data_size), OPTION("--delay_ms=%d", delay_ms),
55};
56static int got_write;
57static atomic_int write_cnt;
58
59pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
60pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
61static int write_start, write_done;
62
63static void tfs_init(void *userdata, struct fuse_conn_info *conn)
64{
65 (void)userdata;
66
67 if (options.writeback) {
70 }
71}
72
73static int tfs_stat(fuse_ino_t ino, struct stat *stbuf)
74{
75 stbuf->st_ino = ino;
76 if (ino == FUSE_ROOT_ID) {
77 stbuf->st_mode = S_IFDIR | 0755;
78 stbuf->st_nlink = 1;
79 }
80
81 else if (ino == FILE_INO) {
82 stbuf->st_mode = S_IFREG | 0222;
83 stbuf->st_nlink = 1;
84 stbuf->st_size = 0;
85 }
86
87 else
88 return -1;
89
90 return 0;
91}
92
93static void tfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
94{
95 struct fuse_entry_param e;
96
97 memset(&e, 0, sizeof(e));
98
99 if (parent != FUSE_ROOT_ID)
100 goto err_out;
101 else if (strcmp(name, FILE_NAME) == 0)
102 e.ino = FILE_INO;
103 else
104 goto err_out;
105
106 if (tfs_stat(e.ino, &e.attr) != 0)
107 goto err_out;
108 fuse_reply_entry(req, &e);
109 return;
110
111err_out:
112 fuse_reply_err(req, ENOENT);
113}
114
115static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
116 struct fuse_file_info *fi)
117{
118 struct stat stbuf;
119
120 (void)fi;
121
122 memset(&stbuf, 0, sizeof(stbuf));
123 if (tfs_stat(ino, &stbuf) != 0)
124 fuse_reply_err(req, ENOENT);
125 else
126 fuse_reply_attr(req, &stbuf, 5);
127}
128
129static void tfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
130{
131 if (ino == FUSE_ROOT_ID)
132 fuse_reply_err(req, EISDIR);
133 else {
134 assert(ino == FILE_INO);
135 /* Test close(rofd) does not block waiting for pending writes */
136 fi->noflush = !options.writeback && options.delay_ms &&
137 (fi->flags & O_ACCMODE) == O_RDONLY;
138 fuse_reply_open(req, fi);
139 }
140}
141
142static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
143 size_t size, off_t off, struct fuse_file_info *fi)
144{
145 (void)fi;
146 (void)buf;
147 (void)off;
148 size_t expected;
149
150 assert(ino == FILE_INO);
151 expected = options.data_size;
152 if (options.writeback)
153 expected *= 2;
154
155 write_cnt++;
156
157 if (size != expected && !options.writeback)
158 fprintf(stderr, "ERROR: Expected %zd bytes, got %zd\n!",
159 expected, size);
160 else
161 got_write = 1;
162
163 /* Simulate waiting for pending writes */
164 if (options.delay_ms) {
165 pthread_mutex_lock(&lock);
166 write_start = 1;
167 pthread_cond_signal(&cond);
168 pthread_mutex_unlock(&lock);
169
170 usleep(options.delay_ms * 1000);
171
172 pthread_mutex_lock(&lock);
173 write_done = 1;
174 pthread_cond_signal(&cond);
175 pthread_mutex_unlock(&lock);
176 }
177
178 fuse_reply_write(req, size);
179}
180
181static struct fuse_lowlevel_ops tfs_oper = {
182 .init = tfs_init,
183 .lookup = tfs_lookup,
184 .getattr = tfs_getattr,
185 .open = tfs_open,
186 .write = tfs_write,
187};
188
189static void *close_rofd(void *data)
190{
191 int rofd = (int)(long)data;
192
193 /* Wait for first write to start */
194 pthread_mutex_lock(&lock);
195 while (!write_start && !write_done)
196 pthread_cond_wait(&cond, &lock);
197 pthread_mutex_unlock(&lock);
198
199 close(rofd);
200 printf("rofd closed. write_start: %d write_done: %d\n", write_start,
201 write_done);
202
203 /* First write should not have been completed */
204 if (write_done)
205 fprintf(stderr, "ERROR: close(rofd) blocked on write!\n");
206
207 return NULL;
208}
209
210static void *run_fs(void *data)
211{
212 struct fuse_session *se = (struct fuse_session *)data;
213
214 assert(fuse_session_loop(se) == 0);
215 return NULL;
216}
217
218static void test_fs(char *mountpoint)
219{
220 char fname[PATH_MAX];
221 char *buf;
222 const size_t iosize = options.data_size;
223 const size_t dsize = options.data_size * WRITE_SYSCALLS;
224 int fd, rofd;
225 pthread_t rofd_thread;
226 off_t off = 0;
227
228 buf = malloc(dsize);
229 assert(buf != NULL);
230 assert((fd = open("/dev/urandom", O_RDONLY)) != -1);
231 assert(read(fd, buf, dsize) == dsize);
232 close(fd);
233
234 assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME, mountpoint) > 0);
235 fd = open(fname, O_WRONLY);
236 if (fd == -1) {
237 perror(fname);
238 assert(0);
239 }
240
241 if (options.delay_ms) {
242 /* Verify that close(rofd) does not block waiting for pending writes */
243 rofd = open(fname, O_RDONLY);
244 assert(pthread_create(&rofd_thread, NULL, close_rofd,
245 (void *)(long)rofd) == 0);
246 /* Give close_rofd time to start */
247 usleep(options.delay_ms * 1000);
248 }
249
250 for (int cnt = 0; cnt < WRITE_SYSCALLS; cnt++) {
251 assert(pwrite(fd, buf + off, iosize, off) == iosize);
252 off += iosize;
253 assert(off <= dsize);
254 }
255 free(buf);
256 close(fd);
257
258 if (options.delay_ms) {
259 printf("rwfd closed. write_start: %d write_done: %d\n",
260 write_start, write_done);
261 assert(pthread_join(rofd_thread, NULL) == 0);
262 }
263}
264
265int main(int argc, char *argv[])
266{
267 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
268 struct fuse_session *se;
269 struct fuse_cmdline_opts fuse_opts;
270 pthread_t fs_thread;
271
272 assert(fuse_opt_parse(&args, &options, option_spec, NULL) == 0);
273 assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
274#ifndef __FreeBSD__
275 assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
276#endif
277 se = fuse_session_new(&args, &tfs_oper, sizeof(tfs_oper), NULL);
278 fuse_opt_free_args(&args);
279 assert(se != NULL);
280 assert(fuse_set_signal_handlers(se) == 0);
281 assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
282
283 /* Start file-system thread */
284 assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
285
286 /* Write test data */
287 test_fs(fuse_opts.mountpoint);
288 free(fuse_opts.mountpoint);
289
290 /* Stop file system */
293 assert(pthread_join(fs_thread, NULL) == 0);
294
295 assert(got_write == 1);
296
297 /*
298 * when writeback cache is enabled, kernel side can merge requests, but
299 * memory pressure, system 'sync' might trigger data flushes before - flush
300 * might happen in between write syscalls - merging subpage writes into
301 * a single page and pages into large fuse requests might or might not work.
302 * Though we can expect that that at least some (but maybe all) write
303 * system calls can be merged.
304 */
305 if (options.writeback)
306 assert(write_cnt < WRITE_SYSCALLS);
307 else
308 assert(write_cnt == WRITE_SYSCALLS);
309
312
313 printf("Test completed successfully.\n");
314 return 0;
315}
316
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_WRITEBACK_CACHE
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
uint64_t fuse_ino_t
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
fuse_ino_t ino
uint32_t noflush
Definition fuse_common.h:93
void(* init)(void *userdata, struct fuse_conn_info *conn)
fuse-3.18.2/doc/html/fuse-3_818_82_2test_2wrong__command_8c_source.html0000644000175000017500000001203215156613430024357 0ustar berndbernd libfuse: fuse-3.18.2/test/wrong_command.c Source File
libfuse
wrong_command.c
1#include <stdio.h>
2
3int main(void) {
4#ifdef MESON_IS_SUBPROJECT
5 fprintf(stderr, "libfuse tests were skipped because it's a meson subproject.\n"
6 "If you wish to run them try:\n"
7 "'cd <srcdir>/subprojects/libfuse && meson . build && cd build && python3 -m pytest test/' instead");
8 return 77; /* report as a skipped test */
9#else
10 fprintf(stderr, "\x1B[31m\e[1m"
11 "This is not the command you are looking for.\n"
12 "You probably want to run 'python3 -m pytest test/' instead"
13 "\e[0m\n");
14 return 1;
15#endif
16}
fuse-3.18.2/doc/html/fuse-3_818_82_2util_2fusermount_8c_source.html0000644000175000017500000101435015156613431023602 0ustar berndbernd libfuse: fuse-3.18.2/util/fusermount.c Source File
libfuse
fusermount.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8/* This program does the mounting and unmounting of FUSE filesystems */
9
10#define _GNU_SOURCE /* for clone,strchrnul and close_range */
11#include "fuse_config.h"
12#include "mount_util.h"
13#include "util.h"
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <ctype.h>
19#include <unistd.h>
20#include <getopt.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <pwd.h>
24#include <paths.h>
25#include <mntent.h>
26#include <sys/wait.h>
27#include <sys/stat.h>
28#include <sys/param.h>
29
30#include "fuse_mount_compat.h"
31
32#include <sys/fsuid.h>
33#include <sys/socket.h>
34#include <sys/utsname.h>
35#include <sched.h>
36#include <stdbool.h>
37#include <sys/vfs.h>
38
39#if defined HAVE_CLOSE_RANGE && defined linux
40#include <linux/close_range.h>
41#endif
42
43#if defined HAVE_LISTMOUNT
44#include <linux/mount.h>
45#include <syscall.h>
46#include <stdint.h>
47#endif
48
49#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
50#define FUSE_KERN_DEVICE_ENV "FUSE_KERN_DEVICE"
51
52#define FUSE_DEV "/dev/fuse"
53
54static const char *progname;
55
56static int user_allow_other = 0;
57static int mount_max = 1000;
58
59static int auto_unmount = 0;
60
61#ifdef GETMNTENT_NEEDS_UNESCAPING
62// Older versions of musl libc don't unescape entries in /etc/mtab
63
64// unescapes octal sequences like \040 in-place
65// That's ok, because unescaping can not extend the length of the string.
66static void unescape(char *buf) {
67 char *src = buf;
68 char *dest = buf;
69 while (1) {
70 char *next_src = strchrnul(src, '\\');
71 int offset = next_src - src;
72 memmove(dest, src, offset);
73 src = next_src;
74 dest += offset;
75
76 if(*src == '\0') {
77 *dest = *src;
78 return;
79 }
80 src++;
81
82 if('0' <= src[0] && src[0] < '2' &&
83 '0' <= src[1] && src[1] < '8' &&
84 '0' <= src[2] && src[2] < '8') {
85 *dest++ = (src[0] - '0') << 6
86 | (src[1] - '0') << 3
87 | (src[2] - '0') << 0;
88 src += 3;
89 } else if (src[0] == '\\') {
90 *dest++ = '\\';
91 src += 1;
92 } else {
93 *dest++ = '\\';
94 }
95 }
96}
97
98static struct mntent *GETMNTENT(FILE *stream)
99{
100 struct mntent *entp = getmntent(stream);
101 if(entp != NULL) {
102 unescape(entp->mnt_fsname);
103 unescape(entp->mnt_dir);
104 unescape(entp->mnt_type);
105 unescape(entp->mnt_opts);
106 }
107 return entp;
108}
109#else
110#define GETMNTENT getmntent
111#endif // GETMNTENT_NEEDS_UNESCAPING
112
113/*
114 * Take a ',' separated option string and extract "x-" options
115 */
116static int extract_x_options(const char *original, char **non_x_opts,
117 char **x_opts)
118{
119 size_t orig_len;
120 const char *opt, *opt_end;
121
122 orig_len = strlen(original) + 1;
123
124 *non_x_opts = calloc(1, orig_len);
125 *x_opts = calloc(1, orig_len);
126
127 size_t non_x_opts_len = orig_len;
128 size_t x_opts_len = orig_len;
129
130 if (*non_x_opts == NULL || *x_opts == NULL) {
131 fprintf(stderr, "%s: Failed to allocate %zuB.\n",
132 __func__, orig_len);
133 return -ENOMEM;
134 }
135
136 for (opt = original; opt < original + orig_len; opt = opt_end + 1) {
137 char *opt_buf;
138
139 opt_end = strchr(opt, ',');
140 if (opt_end == NULL)
141 opt_end = original + orig_len;
142
143 size_t opt_len = opt_end - opt;
144 size_t opt_len_left = orig_len - (opt - original);
145 size_t buf_len;
146 bool is_x_opts;
147
148 if (strncmp(opt, "x-", MIN(2, opt_len_left)) == 0) {
149 buf_len = x_opts_len;
150 is_x_opts = true;
151 opt_buf = *x_opts;
152 } else {
153 buf_len = non_x_opts_len;
154 is_x_opts = false;
155 opt_buf = *non_x_opts;
156 }
157
158 if (buf_len < orig_len) {
159 strncat(opt_buf, ",", 2);
160 buf_len -= 1;
161 }
162
163 /* omits ',' */
164 if ((ssize_t)(buf_len - opt_len) < 0) {
165 /* This would be a bug */
166 fprintf(stderr, "%s: no buf space left in copy, orig='%s'\n",
167 __func__, original);
168 return -EIO;
169 }
170
171 strncat(opt_buf, opt, opt_end - opt);
172 buf_len -= opt_len;
173
174 if (is_x_opts)
175 x_opts_len = buf_len;
176 else
177 non_x_opts_len = buf_len;
178 }
179
180 return 0;
181}
182
183static const char *get_user_name(void)
184{
185 struct passwd *pw = getpwuid(getuid());
186 if (pw != NULL && pw->pw_name != NULL)
187 return pw->pw_name;
188 else {
189 fprintf(stderr, "%s: could not determine username\n", progname);
190 return NULL;
191 }
192}
193
194static uid_t oldfsuid;
195static gid_t oldfsgid;
196
197static void drop_privs(void)
198{
199 if (getuid() != 0) {
200 oldfsuid = setfsuid(getuid());
201 oldfsgid = setfsgid(getgid());
202 }
203}
204
205static void restore_privs(void)
206{
207 if (getuid() != 0) {
208 setfsuid(oldfsuid);
209 setfsgid(oldfsgid);
210 }
211}
212
213#ifndef IGNORE_MTAB
214/*
215 * Make sure that /etc/mtab is checked and updated atomically
216 */
217static int lock_umount(void)
218{
219 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
220 int mtablock;
221 int res;
222 struct stat mtab_stat;
223
224 /* /etc/mtab could be a symlink to /proc/mounts */
225 if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
226 return -1;
227
228 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
229 if (mtablock == -1) {
230 fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
231 progname, strerror(errno));
232 return -1;
233 }
234 res = lockf(mtablock, F_LOCK, 0);
235 if (res < 0) {
236 fprintf(stderr, "%s: error getting lock: %s\n", progname,
237 strerror(errno));
238 close(mtablock);
239 return -1;
240 }
241
242 return mtablock;
243}
244
245static void unlock_umount(int mtablock)
246{
247 if (mtablock >= 0) {
248 int res;
249
250 res = lockf(mtablock, F_ULOCK, 0);
251 if (res < 0) {
252 fprintf(stderr, "%s: error releasing lock: %s\n",
253 progname, strerror(errno));
254 }
255 close(mtablock);
256 }
257}
258
259static int add_mount(const char *source, const char *mnt, const char *type,
260 const char *opts)
261{
262 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
263}
264
265static int may_unmount(const char *mnt, int quiet)
266{
267 struct mntent *entp;
268 FILE *fp;
269 const char *user = NULL;
270 char uidstr[32];
271 unsigned uidlen = 0;
272 int found;
273 const char *mtab = _PATH_MOUNTED;
274
275 user = get_user_name();
276 if (user == NULL)
277 return -1;
278
279 fp = setmntent(mtab, "r");
280 if (fp == NULL) {
281 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
282 strerror(errno));
283 return -1;
284 }
285
286 uidlen = sprintf(uidstr, "%u", getuid());
287
288 found = 0;
289 while ((entp = GETMNTENT(fp)) != NULL) {
290 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
291 (strcmp(entp->mnt_type, "fuse") == 0 ||
292 strcmp(entp->mnt_type, "fuseblk") == 0 ||
293 strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
294 strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
295 char *p = strstr(entp->mnt_opts, "user=");
296 if (p &&
297 (p == entp->mnt_opts || *(p-1) == ',') &&
298 strcmp(p + 5, user) == 0) {
299 found = 1;
300 break;
301 }
302 /* /etc/mtab is a link pointing to
303 /proc/mounts: */
304 else if ((p =
305 strstr(entp->mnt_opts, "user_id=")) &&
306 (p == entp->mnt_opts ||
307 *(p-1) == ',') &&
308 strncmp(p + 8, uidstr, uidlen) == 0 &&
309 (*(p+8+uidlen) == ',' ||
310 *(p+8+uidlen) == '\0')) {
311 found = 1;
312 break;
313 }
314 }
315 }
316 endmntent(fp);
317
318 if (!found) {
319 if (!quiet)
320 fprintf(stderr,
321 "%s: entry for %s not found in %s\n",
322 progname, mnt, mtab);
323 return -1;
324 }
325
326 return 0;
327}
328#endif
329
330/*
331 * Check whether the file specified in "fusermount3 -u" is really a
332 * mountpoint and not a symlink. This is necessary otherwise the user
333 * could move the mountpoint away and replace it with a symlink
334 * pointing to an arbitrary mount, thereby tricking fusermount3 into
335 * unmounting that (umount(2) will follow symlinks).
336 *
337 * This is the child process running in a separate mount namespace, so
338 * we don't mess with the global namespace and if the process is
339 * killed for any reason, mounts are automatically cleaned up.
340 *
341 * First make sure nothing is propagated back into the parent
342 * namespace by marking all mounts "private".
343 *
344 * Then bind mount parent onto a stable base where the user can't move
345 * it around.
346 *
347 * Finally check /proc/mounts for an entry matching the requested
348 * mountpoint. If it's found then we are OK, and the user can't move
349 * it around within the parent directory as rename() will return
350 * EBUSY. Be careful to ignore any mounts that existed before the
351 * bind.
352 */
353static int check_is_mount_child(void *p)
354{
355 const char **a = p;
356 const char *last = a[0];
357 const char *mnt = a[1];
358 const char *type = a[2];
359 int res;
360 const char *procmounts = "/proc/mounts";
361 int found;
362 FILE *fp;
363 struct mntent *entp;
364 int count;
365
366 res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
367 if (res == -1) {
368 fprintf(stderr, "%s: failed to mark mounts private: %s\n",
369 progname, strerror(errno));
370 return 1;
371 }
372
373 fp = setmntent(procmounts, "r");
374 if (fp == NULL) {
375 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
376 procmounts, strerror(errno));
377 return 1;
378 }
379
380 count = 0;
381 while (GETMNTENT(fp) != NULL)
382 count++;
383 endmntent(fp);
384
385 fp = setmntent(procmounts, "r");
386 if (fp == NULL) {
387 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
388 procmounts, strerror(errno));
389 return 1;
390 }
391
392 res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
393 if (res == -1) {
394 fprintf(stderr, "%s: failed to bind parent to /: %s\n",
395 progname, strerror(errno));
396 return 1;
397 }
398
399 found = 0;
400 while ((entp = GETMNTENT(fp)) != NULL) {
401 if (count > 0) {
402 count--;
403 continue;
404 }
405 if (entp->mnt_dir[0] == '/' &&
406 strcmp(entp->mnt_dir + 1, last) == 0 &&
407 (!type || strcmp(entp->mnt_type, type) == 0)) {
408 found = 1;
409 break;
410 }
411 }
412 endmntent(fp);
413
414 if (!found) {
415 fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
416 return 1;
417 }
418
419 return 0;
420}
421
422static pid_t clone_newns(void *a)
423{
424 char buf[131072];
425 char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
426
427#ifdef __ia64__
428 extern int __clone2(int (*fn)(void *),
429 void *child_stack_base, size_t stack_size,
430 int flags, void *arg, pid_t *ptid,
431 void *tls, pid_t *ctid);
432
433 return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
434 CLONE_NEWNS, a, NULL, NULL, NULL);
435#else
436 return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
437#endif
438}
439
440static int check_is_mount(const char *last, const char *mnt, const char *type)
441{
442 pid_t pid, p;
443 int status;
444 const char *a[3] = { last, mnt, type };
445
446 pid = clone_newns((void *) a);
447 if (pid == (pid_t) -1) {
448 fprintf(stderr, "%s: failed to clone namespace: %s\n",
449 progname, strerror(errno));
450 return -1;
451 }
452 p = waitpid(pid, &status, __WCLONE);
453 if (p == (pid_t) -1) {
454 fprintf(stderr, "%s: waitpid failed: %s\n",
455 progname, strerror(errno));
456 return -1;
457 }
458 if (!WIFEXITED(status)) {
459 fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
460 progname, status);
461 return -1;
462 }
463 if (WEXITSTATUS(status) != 0)
464 return -1;
465
466 return 0;
467}
468
469static int chdir_to_parent(char *copy, const char **lastp)
470{
471 char *tmp;
472 const char *parent;
473 char buf[65536];
474 int res;
475
476 tmp = strrchr(copy, '/');
477 if (tmp == NULL || tmp[1] == '\0') {
478 fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
479 progname, copy);
480 return -1;
481 }
482 if (tmp != copy) {
483 *tmp = '\0';
484 parent = copy;
485 *lastp = tmp + 1;
486 } else if (tmp[1] != '\0') {
487 *lastp = tmp + 1;
488 parent = "/";
489 } else {
490 *lastp = ".";
491 parent = "/";
492 }
493
494 res = chdir(parent);
495 if (res == -1) {
496 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
497 progname, parent, strerror(errno));
498 return -1;
499 }
500
501 if (getcwd(buf, sizeof(buf)) == NULL) {
502 fprintf(stderr, "%s: failed to obtain current directory: %s\n",
503 progname, strerror(errno));
504 return -1;
505 }
506 if (strcmp(buf, parent) != 0) {
507 fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
508 parent, buf);
509 return -1;
510
511 }
512
513 return 0;
514}
515
516#ifndef IGNORE_MTAB
517static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
518{
519 int res;
520 char *copy;
521 const char *last;
522 int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW;
523
524 if (getuid() != 0) {
525 res = may_unmount(mnt, quiet);
526 if (res == -1)
527 return -1;
528 }
529
530 copy = strdup(mnt);
531 if (copy == NULL) {
532 fprintf(stderr, "%s: failed to allocate memory\n", progname);
533 return -1;
534 }
535
536 drop_privs();
537 res = chdir_to_parent(copy, &last);
538 if (res == -1) {
539 restore_privs();
540 goto out;
541 }
542
543 res = umount2(last, umount_flags);
544 restore_privs();
545 if (res == -1 && !quiet) {
546 fprintf(stderr, "%s: failed to unmount %s: %s\n",
547 progname, mnt, strerror(errno));
548 }
549
550out:
551 free(copy);
552 if (res == -1)
553 return -1;
554
555 res = chdir("/");
556 if (res == -1) {
557 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
558 return -1;
559 }
560
561 return fuse_mnt_remove_mount(progname, mnt);
562}
563
564static int unmount_fuse(const char *mnt, int quiet, int lazy)
565{
566 int res;
567 int mtablock = lock_umount();
568
569 res = unmount_fuse_locked(mnt, quiet, lazy);
570 unlock_umount(mtablock);
571
572 return res;
573}
574
575static int count_fuse_fs_mtab(void)
576{
577 struct mntent *entp;
578 int count = 0;
579 const char *mtab = _PATH_MOUNTED;
580 FILE *fp = setmntent(mtab, "r");
581 if (fp == NULL) {
582 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
583 strerror(errno));
584 return -1;
585 }
586 while ((entp = GETMNTENT(fp)) != NULL) {
587 if (strcmp(entp->mnt_type, "fuse") == 0 ||
588 strncmp(entp->mnt_type, "fuse.", 5) == 0)
589 count ++;
590 }
591 endmntent(fp);
592 return count;
593}
594
595#ifdef HAVE_LISTMOUNT
596static int count_fuse_fs_ls_mnt(void)
597{
598 #define SMBUF_SIZE 1024
599 #define MNT_ID_LEN 128
600
601 int fuse_count = 0;
602 int n_mounts = 0;
603 int ret = 0;
604 uint64_t mnt_ids[MNT_ID_LEN];
605 unsigned char smbuf[SMBUF_SIZE];
606 struct mnt_id_req req = {
607 .size = sizeof(struct mnt_id_req),
608 };
609 struct statmount *sm;
610
611 for (;;) {
612 req.mnt_id = LSMT_ROOT;
613
614 n_mounts = syscall(SYS_listmount, &req, &mnt_ids, MNT_ID_LEN, 0);
615 if (n_mounts == -1) {
616 if (errno != ENOSYS) {
617 fprintf(stderr, "%s: failed to list mounts: %s\n", progname,
618 strerror(errno));
619 }
620 return -1;
621 }
622
623 for (int i = 0; i < n_mounts; i++) {
624 req.mnt_id = mnt_ids[i];
625 req.param = STATMOUNT_FS_TYPE;
626 ret = syscall(SYS_statmount, &req, &smbuf, SMBUF_SIZE, 0);
627 if (ret) {
628 if (errno == ENOENT)
629 continue;
630
631 fprintf(stderr, "%s: failed to stat mount %lld: %s\n", progname,
632 req.mnt_id, strerror(errno));
633 return -1;
634 }
635
636 sm = (struct statmount *)smbuf;
637 if (sm->mask & STATMOUNT_FS_TYPE &&
638 strcmp(&sm->str[sm->fs_type], "fuse") == 0)
639 fuse_count++;
640 }
641
642 if (n_mounts < MNT_ID_LEN)
643 break;
644 req.param = mnt_ids[MNT_ID_LEN - 1];
645 }
646 return fuse_count;
647}
648
649static int count_fuse_fs(void)
650{
651 int count = count_fuse_fs_ls_mnt();
652
653 return count >= 0 ? count : count_fuse_fs_mtab();
654}
655#else
656static int count_fuse_fs(void)
657{
658 return count_fuse_fs_mtab();
659}
660#endif
661
662#else /* IGNORE_MTAB */
663static int count_fuse_fs(void)
664{
665 return 0;
666}
667
668static int add_mount(const char *source, const char *mnt, const char *type,
669 const char *opts)
670{
671 (void) source;
672 (void) mnt;
673 (void) type;
674 (void) opts;
675 return 0;
676}
677
678static int unmount_fuse(const char *mnt, int quiet, int lazy)
679{
680 (void) quiet;
681 return fuse_mnt_umount(progname, mnt, mnt, lazy);
682}
683#endif /* IGNORE_MTAB */
684
685static void strip_line(char *line)
686{
687 char *s = strchr(line, '#');
688 if (s != NULL)
689 s[0] = '\0';
690 for (s = line + strlen(line) - 1;
691 s >= line && isspace((unsigned char) *s); s--);
692 s[1] = '\0';
693 for (s = line; isspace((unsigned char) *s); s++);
694 if (s != line)
695 memmove(line, s, strlen(s)+1);
696}
697
698static void parse_line(char *line, int linenum)
699{
700 int tmp;
701 if (strcmp(line, "user_allow_other") == 0)
702 user_allow_other = 1;
703 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
704 mount_max = tmp;
705 else if(line[0])
706 fprintf(stderr,
707 "%s: unknown parameter in %s at line %i: '%s'\n",
708 progname, FUSE_CONF, linenum, line);
709}
710
711static void read_conf(void)
712{
713 FILE *fp = fopen(FUSE_CONF, "r");
714 if (fp != NULL) {
715 int linenum = 1;
716 char line[256];
717 int isnewline = 1;
718 while (fgets(line, sizeof(line), fp) != NULL) {
719 if (isnewline) {
720 if (line[strlen(line)-1] == '\n') {
721 strip_line(line);
722 parse_line(line, linenum);
723 } else {
724 isnewline = 0;
725 }
726 } else if(line[strlen(line)-1] == '\n') {
727 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
728
729 isnewline = 1;
730 }
731 if (isnewline)
732 linenum ++;
733 }
734 if (!isnewline) {
735 fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
736
737 }
738 if (ferror(fp)) {
739 fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
740 exit(1);
741 }
742 fclose(fp);
743 } else if (errno != ENOENT) {
744 bool fatal = (errno != EACCES && errno != ELOOP &&
745 errno != ENAMETOOLONG && errno != ENOTDIR &&
746 errno != EOVERFLOW);
747 fprintf(stderr, "%s: failed to open %s: %s\n",
748 progname, FUSE_CONF, strerror(errno));
749 if (fatal)
750 exit(1);
751 }
752}
753
754static int begins_with(const char *s, const char *beg)
755{
756 if (strncmp(s, beg, strlen(beg)) == 0)
757 return 1;
758 else
759 return 0;
760}
761
762struct mount_flags {
763 const char *opt;
764 unsigned long flag;
765 int on;
766 int safe;
767};
768
769static struct mount_flags mount_flags[] = {
770 {"rw", MS_RDONLY, 0, 1},
771 {"ro", MS_RDONLY, 1, 1},
772 {"suid", MS_NOSUID, 0, 0},
773 {"nosuid", MS_NOSUID, 1, 1},
774 {"dev", MS_NODEV, 0, 0},
775 {"nodev", MS_NODEV, 1, 1},
776 {"exec", MS_NOEXEC, 0, 1},
777 {"noexec", MS_NOEXEC, 1, 1},
778 {"async", MS_SYNCHRONOUS, 0, 1},
779 {"sync", MS_SYNCHRONOUS, 1, 1},
780 {"atime", MS_NOATIME, 0, 1},
781 {"noatime", MS_NOATIME, 1, 1},
782 {"diratime", MS_NODIRATIME, 0, 1},
783 {"nodiratime", MS_NODIRATIME, 1, 1},
784 {"lazytime", MS_LAZYTIME, 1, 1},
785 {"nolazytime", MS_LAZYTIME, 0, 1},
786 {"relatime", MS_RELATIME, 1, 1},
787 {"norelatime", MS_RELATIME, 0, 1},
788 {"strictatime", MS_STRICTATIME, 1, 1},
789 {"nostrictatime", MS_STRICTATIME, 0, 1},
790 {"dirsync", MS_DIRSYNC, 1, 1},
791 {"symfollow", MS_NOSYMFOLLOW, 0, 1},
792 {"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
793 {NULL, 0, 0, 0}
794};
795
796static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
797{
798 int i;
799
800 for (i = 0; mount_flags[i].opt != NULL; i++) {
801 const char *opt = mount_flags[i].opt;
802 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
803 *on = mount_flags[i].on;
804 *flag = mount_flags[i].flag;
805 if (!mount_flags[i].safe && getuid() != 0) {
806 *flag = 0;
807 fprintf(stderr,
808 "%s: unsafe option %s ignored\n",
809 progname, opt);
810 }
811 return 1;
812 }
813 }
814 return 0;
815}
816
817static int add_option(char **optsp, const char *opt, unsigned expand)
818{
819 char *newopts;
820 if (*optsp == NULL)
821 newopts = strdup(opt);
822 else {
823 unsigned oldsize = strlen(*optsp);
824 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
825 newopts = (char *) realloc(*optsp, newsize);
826 if (newopts)
827 sprintf(newopts + oldsize, ",%s", opt);
828 }
829 if (newopts == NULL) {
830 fprintf(stderr, "%s: failed to allocate memory\n", progname);
831 return -1;
832 }
833 *optsp = newopts;
834 return 0;
835}
836
837static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
838{
839 int i;
840 int l;
841
842 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
843 return -1;
844
845 for (i = 0; mount_flags[i].opt != NULL; i++) {
846 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
847 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
848 return -1;
849 }
850
851 if (add_option(mnt_optsp, opts, 0) == -1)
852 return -1;
853 /* remove comma from end of opts*/
854 l = strlen(*mnt_optsp);
855 if ((*mnt_optsp)[l-1] == ',')
856 (*mnt_optsp)[l-1] = '\0';
857 if (getuid() != 0) {
858 const char *user = get_user_name();
859 if (user == NULL)
860 return -1;
861
862 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
863 return -1;
864 strcat(*mnt_optsp, user);
865 }
866 return 0;
867}
868
869static int opt_eq(const char *s, unsigned len, const char *opt)
870{
871 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
872 return 1;
873 else
874 return 0;
875}
876
877static int get_string_opt(const char *s, unsigned len, const char *opt,
878 char **val)
879{
880 int i;
881 unsigned opt_len = strlen(opt);
882 char *d;
883
884 if (*val)
885 free(*val);
886 *val = (char *) malloc(len - opt_len + 1);
887 if (!*val) {
888 fprintf(stderr, "%s: failed to allocate memory\n", progname);
889 return 0;
890 }
891
892 d = *val;
893 s += opt_len;
894 len -= opt_len;
895 for (i = 0; i < len; i++) {
896 if (s[i] == '\\' && i + 1 < len)
897 i++;
898 *d++ = s[i];
899 }
900 *d = '\0';
901 return 1;
902}
903
904/* The kernel silently truncates the "data" argument to PAGE_SIZE-1 characters.
905 * This can be dangerous if it e.g. truncates the option "group_id=1000" to
906 * "group_id=1".
907 * This wrapper detects this case and bails out with an error.
908 */
909static int mount_notrunc(const char *source, const char *target,
910 const char *filesystemtype, unsigned long mountflags,
911 const char *data) {
912 if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
913 fprintf(stderr, "%s: mount options too long\n", progname);
914 errno = EINVAL;
915 return -1;
916 }
917 return mount(source, target, filesystemtype, mountflags, data);
918}
919
920
921static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
922 int fd, const char *opts, const char *dev, char **sourcep,
923 char **mnt_optsp)
924{
925 int res;
926 int flags = MS_NOSUID | MS_NODEV;
927 char *optbuf;
928 char *mnt_opts = NULL;
929 const char *s;
930 char *d;
931 char *fsname = NULL;
932 char *subtype = NULL;
933 char *source = NULL;
934 char *type = NULL;
935 int blkdev = 0;
936
937 optbuf = (char *) malloc(strlen(opts) + 128);
938 if (!optbuf) {
939 fprintf(stderr, "%s: failed to allocate memory\n", progname);
940 return -1;
941 }
942
943 for (s = opts, d = optbuf; *s;) {
944 unsigned len;
945 const char *fsname_str = "fsname=";
946 const char *subtype_str = "subtype=";
947 bool escape_ok = begins_with(s, fsname_str) ||
948 begins_with(s, subtype_str);
949 for (len = 0; s[len]; len++) {
950 if (escape_ok && s[len] == '\\' && s[len + 1])
951 len++;
952 else if (s[len] == ',')
953 break;
954 }
955 if (begins_with(s, fsname_str)) {
956 if (!get_string_opt(s, len, fsname_str, &fsname))
957 goto err;
958 } else if (begins_with(s, subtype_str)) {
959 if (!get_string_opt(s, len, subtype_str, &subtype))
960 goto err;
961 } else if (opt_eq(s, len, "blkdev")) {
962 if (getuid() != 0) {
963 fprintf(stderr,
964 "%s: option blkdev is privileged\n",
965 progname);
966 goto err;
967 }
968 blkdev = 1;
969 } else if (opt_eq(s, len, "auto_unmount")) {
970 auto_unmount = 1;
971 } else if (!opt_eq(s, len, "nonempty") &&
972 !begins_with(s, "fd=") &&
973 !begins_with(s, "rootmode=") &&
974 !begins_with(s, "user_id=") &&
975 !begins_with(s, "group_id=")) {
976 int on;
977 int flag;
978 int skip_option = 0;
979 if (opt_eq(s, len, "large_read")) {
980 struct utsname utsname;
981 unsigned kmaj, kmin;
982 res = uname(&utsname);
983 if (res == 0 &&
984 sscanf(utsname.release, "%u.%u",
985 &kmaj, &kmin) == 2 &&
986 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
987 fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
988 skip_option = 1;
989 }
990 }
991 if (getuid() != 0 && !user_allow_other &&
992 (opt_eq(s, len, "allow_other") ||
993 opt_eq(s, len, "allow_root"))) {
994 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
995 goto err;
996 }
997 if (!skip_option) {
998 if (find_mount_flag(s, len, &on, &flag)) {
999 if (on)
1000 flags |= flag;
1001 else
1002 flags &= ~flag;
1003 } else if (opt_eq(s, len, "default_permissions") ||
1004 opt_eq(s, len, "allow_other") ||
1005 begins_with(s, "max_read=") ||
1006 begins_with(s, "blksize=")) {
1007 memcpy(d, s, len);
1008 d += len;
1009 *d++ = ',';
1010 } else {
1011 fprintf(stderr, "%s: unknown option '%.*s'\n", progname, len, s);
1012 exit(1);
1013 }
1014 }
1015 }
1016 s += len;
1017 if (*s)
1018 s++;
1019 }
1020 *d = '\0';
1021 res = get_mnt_opts(flags, optbuf, &mnt_opts);
1022 if (res == -1)
1023 goto err;
1024
1025 sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
1026 fd, rootmode, getuid(), getgid());
1027
1028 source = malloc((fsname ? strlen(fsname) : 0) +
1029 (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
1030
1031 type = malloc((subtype ? strlen(subtype) : 0) + 32);
1032 if (!type || !source) {
1033 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1034 goto err;
1035 }
1036
1037 if (subtype)
1038 sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
1039 else
1040 strcpy(type, blkdev ? "fuseblk" : "fuse");
1041
1042 if (fsname)
1043 strcpy(source, fsname);
1044 else
1045 strcpy(source, subtype ? subtype : dev);
1046
1047 res = mount_notrunc(source, mnt, type, flags, optbuf);
1048 if (res == -1 && errno == ENODEV && subtype) {
1049 /* Probably missing subtype support */
1050 strcpy(type, blkdev ? "fuseblk" : "fuse");
1051 if (fsname) {
1052 if (!blkdev)
1053 sprintf(source, "%s#%s", subtype, fsname);
1054 } else {
1055 strcpy(source, type);
1056 }
1057
1058 res = mount_notrunc(source, mnt, type, flags, optbuf);
1059 }
1060 if (res == -1 && errno == EINVAL) {
1061 /* It could be an old version not supporting group_id */
1062 sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
1063 fd, rootmode, getuid());
1064 res = mount_notrunc(source, mnt, type, flags, optbuf);
1065 }
1066 if (res == -1) {
1067 int errno_save = errno;
1068 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
1069 fprintf(stderr, "%s: 'fuseblk' support missing\n",
1070 progname);
1071 else
1072 fprintf(stderr, "%s: mount failed: %s\n", progname,
1073 strerror(errno_save));
1074 goto err;
1075 }
1076 *sourcep = source;
1077 *typep = type;
1078 *mnt_optsp = mnt_opts;
1079 free(fsname);
1080 free(optbuf);
1081
1082 return 0;
1083
1084err:
1085 free(fsname);
1086 free(subtype);
1087 free(source);
1088 free(type);
1089 free(mnt_opts);
1090 free(optbuf);
1091 return -1;
1092}
1093
1094static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
1095{
1096 int res;
1097 const char *mnt = *mntp;
1098 const char *origmnt = mnt;
1099 struct statfs fs_buf;
1100 size_t i;
1101
1102 res = lstat(mnt, stbuf);
1103 if (res == -1) {
1104 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1105 progname, mnt, strerror(errno));
1106 return -1;
1107 }
1108
1109 /* No permission checking is done for root */
1110 if (getuid() == 0)
1111 return 0;
1112
1113 if (S_ISDIR(stbuf->st_mode)) {
1114 res = chdir(mnt);
1115 if (res == -1) {
1116 fprintf(stderr,
1117 "%s: failed to chdir to mountpoint: %s\n",
1118 progname, strerror(errno));
1119 return -1;
1120 }
1121 mnt = *mntp = ".";
1122 res = lstat(mnt, stbuf);
1123 if (res == -1) {
1124 fprintf(stderr,
1125 "%s: failed to access mountpoint %s: %s\n",
1126 progname, origmnt, strerror(errno));
1127 return -1;
1128 }
1129
1130 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
1131 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
1132 progname, origmnt);
1133 return -1;
1134 }
1135
1136 res = access(mnt, W_OK);
1137 if (res == -1) {
1138 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
1139 progname, origmnt);
1140 return -1;
1141 }
1142 } else if (S_ISREG(stbuf->st_mode)) {
1143 static char procfile[256];
1144 *mountpoint_fd = open(mnt, O_WRONLY);
1145 if (*mountpoint_fd == -1) {
1146 fprintf(stderr, "%s: failed to open %s: %s\n",
1147 progname, mnt, strerror(errno));
1148 return -1;
1149 }
1150 res = fstat(*mountpoint_fd, stbuf);
1151 if (res == -1) {
1152 fprintf(stderr,
1153 "%s: failed to access mountpoint %s: %s\n",
1154 progname, mnt, strerror(errno));
1155 return -1;
1156 }
1157 if (!S_ISREG(stbuf->st_mode)) {
1158 fprintf(stderr,
1159 "%s: mountpoint %s is no longer a regular file\n",
1160 progname, mnt);
1161 return -1;
1162 }
1163
1164 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
1165 *mntp = procfile;
1166 } else {
1167 fprintf(stderr,
1168 "%s: mountpoint %s is not a directory or a regular file\n",
1169 progname, mnt);
1170 return -1;
1171 }
1172
1173 /* Do not permit mounting over anything in procfs - it has a couple
1174 * places to which we have "write access" without being supposed to be
1175 * able to just put anything we want there.
1176 * Luckily, without allow_other, we can't get other users to actually
1177 * use any fake information we try to put there anyway.
1178 * Use a whitelist to be safe. */
1179 if (statfs(*mntp, &fs_buf)) {
1180 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1181 progname, mnt, strerror(errno));
1182 return -1;
1183 }
1184
1185 /* Define permitted filesystems for the mount target. This was
1186 * originally the same list as used by the ecryptfs mount helper
1187 * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
1188 * but got expanded as we found more filesystems that needed to be
1189 * overlaid. */
1190 typeof(fs_buf.f_type) f_type_whitelist[] = {
1191 0x61756673 /* AUFS_SUPER_MAGIC */,
1192 0x00000187 /* AUTOFS_SUPER_MAGIC */,
1193 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
1194 0x9123683E /* BTRFS_SUPER_MAGIC */,
1195 0x00C36400 /* CEPH_SUPER_MAGIC */,
1196 0xFF534D42 /* CIFS_MAGIC_NUMBER */,
1197 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
1198 0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
1199 0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
1200 0xF2F52010 /* F2FS_SUPER_MAGIC */,
1201 0x65735546 /* FUSE_SUPER_MAGIC */,
1202 0x01161970 /* GFS2_MAGIC */,
1203 0x47504653 /* GPFS_SUPER_MAGIC */,
1204 0x0000482b /* HFSPLUS_SUPER_MAGIC */,
1205 0x000072B6 /* JFFS2_SUPER_MAGIC */,
1206 0x3153464A /* JFS_SUPER_MAGIC */,
1207 0x0BD00BD0 /* LL_SUPER_MAGIC */,
1208 0X00004D44 /* MSDOS_SUPER_MAGIC */,
1209 0x0000564C /* NCP_SUPER_MAGIC */,
1210 0x00006969 /* NFS_SUPER_MAGIC */,
1211 0x00003434 /* NILFS_SUPER_MAGIC */,
1212 0x5346544E /* NTFS_SB_MAGIC */,
1213 0x7366746E /* NTFS3_SUPER_MAGIC */,
1214 0x5346414f /* OPENAFS_SUPER_MAGIC */,
1215 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
1216 0xAAD7AAEA /* PANFS_SUPER_MAGIC */,
1217 0x52654973 /* REISERFS_SUPER_MAGIC */,
1218 0xFE534D42 /* SMB2_SUPER_MAGIC */,
1219 0x73717368 /* SQUASHFS_MAGIC */,
1220 0x01021994 /* TMPFS_MAGIC */,
1221 0x24051905 /* UBIFS_SUPER_MAGIC */,
1222 0x18031977 /* WEKAFS_SUPER_MAGIC */,
1223#if __SIZEOF_LONG__ > 4
1224 0x736675005346544e /* UFSD */,
1225#endif
1226 0x58465342 /* XFS_SB_MAGIC */,
1227 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
1228 0x858458f6 /* RAMFS_MAGIC */,
1229 };
1230 for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
1231 if (f_type_whitelist[i] == fs_buf.f_type)
1232 return 0;
1233 }
1234
1235 fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
1236 progname, (unsigned long)fs_buf.f_type);
1237 return -1;
1238}
1239
1240static int open_fuse_device(const char *dev)
1241{
1242 int fd;
1243
1244 drop_privs();
1245 fd = open(dev, O_RDWR);
1246 if (fd == -1) {
1247 if (errno == ENODEV || errno == ENOENT)/* check for ENOENT too, for the udev case */
1248 fprintf(stderr,
1249 "%s: fuse device %s not found. Kernel module not loaded?\n",
1250 progname, dev);
1251 else
1252 fprintf(stderr,
1253 "%s: failed to open %s: %s\n", progname, dev, strerror(errno));
1254 }
1255 restore_privs();
1256 return fd;
1257}
1258
1259static int mount_fuse(const char *mnt, const char *opts, const char **type)
1260{
1261 int res;
1262 int fd;
1263 const char *dev = getenv(FUSE_KERN_DEVICE_ENV) ?: FUSE_DEV;
1264 struct stat stbuf;
1265 char *source = NULL;
1266 char *mnt_opts = NULL;
1267 const char *real_mnt = mnt;
1268 int mountpoint_fd = -1;
1269 char *do_mount_opts = NULL;
1270 char *x_opts = NULL;
1271
1272 fd = open_fuse_device(dev);
1273 if (fd == -1)
1274 return -1;
1275
1276 drop_privs();
1277 read_conf();
1278
1279 if (getuid() != 0 && mount_max != -1) {
1280 int mount_count = count_fuse_fs();
1281 if (mount_count >= mount_max) {
1282 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
1283 goto fail_close_fd;
1284 }
1285 }
1286
1287 // Extract any options starting with "x-"
1288 res= extract_x_options(opts, &do_mount_opts, &x_opts);
1289 if (res)
1290 goto fail_close_fd;
1291
1292 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1293 restore_privs();
1294 if (res != -1)
1295 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
1296 fd, do_mount_opts, dev, &source, &mnt_opts);
1297
1298 if (mountpoint_fd != -1)
1299 close(mountpoint_fd);
1300
1301 if (res == -1)
1302 goto fail_close_fd;
1303
1304 res = chdir("/");
1305 if (res == -1) {
1306 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1307 goto fail_close_fd;
1308 }
1309
1310 if (geteuid() == 0) {
1311 if (x_opts && strlen(x_opts) > 0) {
1312 /*
1313 * Add back the options starting with "x-" to opts from
1314 * do_mount. +2 for ',' and '\0'
1315 */
1316 size_t mnt_opts_len = strlen(mnt_opts);
1317 size_t x_mnt_opts_len = mnt_opts_len+
1318 strlen(x_opts) + 2;
1319 char *x_mnt_opts = calloc(1, x_mnt_opts_len);
1320
1321 if (mnt_opts_len) {
1322 strcpy(x_mnt_opts, mnt_opts);
1323 strncat(x_mnt_opts, ",", 2);
1324 }
1325
1326 strncat(x_mnt_opts, x_opts,
1327 x_mnt_opts_len - mnt_opts_len - 2);
1328
1329 free(mnt_opts);
1330 mnt_opts = x_mnt_opts;
1331 }
1332
1333 res = add_mount(source, mnt, *type, mnt_opts);
1334 if (res == -1) {
1335 /* Can't clean up mount in a non-racy way */
1336 goto fail_close_fd;
1337 }
1338 }
1339
1340out_free:
1341 free(source);
1342 free(mnt_opts);
1343 free(x_opts);
1344 free(do_mount_opts);
1345
1346 return fd;
1347
1348fail_close_fd:
1349 close(fd);
1350 fd = -1;
1351 goto out_free;
1352}
1353
1354static int send_fd(int sock_fd, int fd)
1355{
1356 int retval;
1357 struct msghdr msg;
1358 struct cmsghdr *p_cmsg;
1359 struct iovec vec;
1360 size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
1361 int *p_fds;
1362 char sendchar = 0;
1363
1364 msg.msg_control = cmsgbuf;
1365 msg.msg_controllen = sizeof(cmsgbuf);
1366 p_cmsg = CMSG_FIRSTHDR(&msg);
1367 p_cmsg->cmsg_level = SOL_SOCKET;
1368 p_cmsg->cmsg_type = SCM_RIGHTS;
1369 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1370 p_fds = (int *) CMSG_DATA(p_cmsg);
1371 *p_fds = fd;
1372 msg.msg_controllen = p_cmsg->cmsg_len;
1373 msg.msg_name = NULL;
1374 msg.msg_namelen = 0;
1375 msg.msg_iov = &vec;
1376 msg.msg_iovlen = 1;
1377 msg.msg_flags = 0;
1378 /* "To pass file descriptors or credentials you need to send/read at
1379 * least one byte" (man 7 unix) */
1380 vec.iov_base = &sendchar;
1381 vec.iov_len = sizeof(sendchar);
1382 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1383 if (retval != 1) {
1384 perror("sending file descriptor");
1385 return -1;
1386 }
1387 return 0;
1388}
1389
1390/* Helper for should_auto_unmount
1391 *
1392 * fusermount typically has the s-bit set - initial open of `mnt` was as root
1393 * and got EACCESS as 'allow_other' was not specified.
1394 * Try opening `mnt` again with uid and guid of the calling process.
1395 */
1396static int recheck_ENOTCONN_as_owner(const char *mnt)
1397{
1398 int pid = fork();
1399 if(pid == -1) {
1400 perror("fuse: recheck_ENOTCONN_as_owner can't fork");
1401 _exit(EXIT_FAILURE);
1402 } else if(pid == 0) {
1403 uid_t uid = getuid();
1404 gid_t gid = getgid();
1405 if(setresgid(gid, gid, gid) == -1) {
1406 perror("fuse: can't set resgid");
1407 _exit(EXIT_FAILURE);
1408 }
1409 if(setresuid(uid, uid, uid) == -1) {
1410 perror("fuse: can't set resuid");
1411 _exit(EXIT_FAILURE);
1412 }
1413
1414 int fd = open(mnt, O_RDONLY);
1415 if(fd == -1 && errno == ENOTCONN)
1416 _exit(EXIT_SUCCESS);
1417 else
1418 _exit(EXIT_FAILURE);
1419 } else {
1420 int status;
1421 int res = waitpid(pid, &status, 0);
1422 if (res == -1) {
1423 perror("fuse: waiting for child failed");
1424 _exit(EXIT_FAILURE);
1425 }
1426 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
1427 }
1428}
1429
1430/* The parent fuse process has died: decide whether to auto_unmount.
1431 *
1432 * In the normal case (umount or fusermount -u), the filesystem
1433 * has already been unmounted. If we simply unmount again we can
1434 * cause problems with stacked mounts (e.g. autofs).
1435 *
1436 * So we unmount here only in abnormal case where fuse process has
1437 * died without unmount happening. To detect this, we first look in
1438 * the mount table to make sure the mountpoint is still mounted and
1439 * has proper type. If so, we then see if opening the mount dir is
1440 * returning 'Transport endpoint is not connected'.
1441 *
1442 * The order of these is important, because if autofs is in use,
1443 * opening the dir to check for ENOTCONN will cause a new mount
1444 * in the normal case where filesystem has been unmounted cleanly.
1445 */
1446static int should_auto_unmount(const char *mnt, const char *type)
1447{
1448 char *copy;
1449 const char *last;
1450 int result = 0;
1451 int fd;
1452
1453 copy = strdup(mnt);
1454 if (copy == NULL) {
1455 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1456 return 0;
1457 }
1458
1459 if (chdir_to_parent(copy, &last) == -1)
1460 goto out;
1461 if (check_is_mount(last, mnt, type) == -1)
1462 goto out;
1463
1464 fd = open(mnt, O_RDONLY);
1465
1466 if (fd != -1) {
1467 close(fd);
1468 } else {
1469 switch(errno) {
1470 case ENOTCONN:
1471 result = 1;
1472 break;
1473 case EACCES:
1474 result = recheck_ENOTCONN_as_owner(mnt);
1475 break;
1476 default:
1477 result = 0;
1478 break;
1479 }
1480 }
1481out:
1482 free(copy);
1483 return result;
1484}
1485
1486static void usage(void)
1487{
1488 printf("%s: [options] mountpoint\n"
1489 "Options:\n"
1490 " -h print help\n"
1491 " -V print version\n"
1492 " -o opt[,opt...] mount options\n"
1493 " -u unmount\n"
1494 " -q quiet\n"
1495 " -z lazy unmount\n",
1496 progname);
1497 exit(1);
1498}
1499
1500static void show_version(void)
1501{
1502 printf("fusermount3 version: %s\n", PACKAGE_VERSION);
1503 exit(0);
1504}
1505
1506static void close_range_loop(int min_fd, int max_fd, int cfd)
1507{
1508 for (int fd = min_fd; fd <= max_fd; fd++)
1509 if (fd != cfd)
1510 close(fd);
1511}
1512
1513/*
1514 * Close all inherited fds that are not needed
1515 * Ideally these wouldn't come up at all, applications should better
1516 * use FD_CLOEXEC / O_CLOEXEC
1517 */
1518static int close_inherited_fds(int cfd)
1519{
1520 int rc = -1;
1521 int nullfd;
1522
1523 /* We can't even report an error */
1524 if (cfd <= STDERR_FILENO)
1525 return -EINVAL;
1526
1527#ifdef HAVE_CLOSE_RANGE
1528 if (cfd < STDERR_FILENO + 2) {
1529 close_range_loop(STDERR_FILENO + 1, cfd - 1, cfd);
1530 } else {
1531 rc = close_range(STDERR_FILENO + 1, cfd - 1, 0);
1532 if (rc < 0)
1533 goto fallback;
1534 }
1535
1536 /* Close high range */
1537 rc = close_range(cfd + 1, ~0U, 0);
1538#else
1539 goto fallback; /* make use of fallback to avoid compiler warnings */
1540#endif
1541
1542fallback:
1543 if (rc < 0) {
1544 int max_fd = sysconf(_SC_OPEN_MAX) - 1;
1545
1546 close_range_loop(STDERR_FILENO + 1, max_fd, cfd);
1547 }
1548
1549 nullfd = open("/dev/null", O_RDWR);
1550 if (nullfd < 0) {
1551 perror("fusermount: cannot open /dev/null");
1552 return -errno;
1553 }
1554
1555 /* Redirect stdin, stdout, stderr to /dev/null */
1556 dup2(nullfd, STDIN_FILENO);
1557 dup2(nullfd, STDOUT_FILENO);
1558 dup2(nullfd, STDERR_FILENO);
1559 if (nullfd > STDERR_FILENO)
1560 close(nullfd);
1561
1562 return 0;
1563}
1564
1565int main(int argc, char *argv[])
1566{
1567 sigset_t sigset;
1568 int ch;
1569 int fd;
1570 int res;
1571 char *origmnt;
1572 char *mnt;
1573 static int unmount = 0;
1574 static int lazy = 0;
1575 static int quiet = 0;
1576 char *commfd = NULL;
1577 long cfd;
1578 const char *opts = "";
1579 const char *type = NULL;
1580 int setup_auto_unmount_only = 0;
1581
1582 static const struct option long_opts[] = {
1583 {"unmount", no_argument, NULL, 'u'},
1584 {"lazy", no_argument, NULL, 'z'},
1585 {"quiet", no_argument, NULL, 'q'},
1586 {"help", no_argument, NULL, 'h'},
1587 {"version", no_argument, NULL, 'V'},
1588 {"options", required_argument, NULL, 'o'},
1589 // Note: auto-unmount and comm-fd don't have short versions.
1590 // They'ne meant for internal use by mount.c
1591 {"auto-unmount", no_argument, NULL, 'U'},
1592 {"comm-fd", required_argument, NULL, 'c'},
1593 {0, 0, 0, 0}};
1594
1595 progname = strdup(argc > 0 ? argv[0] : "fusermount");
1596 if (progname == NULL) {
1597 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1598 exit(1);
1599 }
1600
1601 while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
1602 NULL)) != -1) {
1603 switch (ch) {
1604 case 'h':
1605 usage();
1606 break;
1607
1608 case 'V':
1609 show_version();
1610 break;
1611
1612 case 'o':
1613 opts = optarg;
1614 break;
1615
1616 case 'u':
1617 unmount = 1;
1618 break;
1619 case 'U':
1620 unmount = 1;
1621 auto_unmount = 1;
1622 setup_auto_unmount_only = 1;
1623 break;
1624 case 'c':
1625 commfd = optarg;
1626 break;
1627 case 'z':
1628 lazy = 1;
1629 break;
1630
1631 case 'q':
1632 quiet = 1;
1633 break;
1634
1635 default:
1636 exit(1);
1637 }
1638 }
1639
1640 if (lazy && !unmount) {
1641 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1642 exit(1);
1643 }
1644
1645 if (optind >= argc) {
1646 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1647 exit(1);
1648 } else if (argc > optind + 1) {
1649 fprintf(stderr, "%s: extra arguments after the mountpoint\n",
1650 progname);
1651 exit(1);
1652 }
1653
1654 origmnt = argv[optind];
1655
1656 drop_privs();
1657 mnt = fuse_mnt_resolve_path(progname, origmnt);
1658 if (mnt != NULL) {
1659 res = chdir("/");
1660 if (res == -1) {
1661 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1662 goto err_out;
1663 }
1664 }
1665 restore_privs();
1666 if (mnt == NULL)
1667 exit(1);
1668
1669 umask(033);
1670 if (!setup_auto_unmount_only && unmount)
1671 goto do_unmount;
1672
1673 if(commfd == NULL)
1674 commfd = getenv(FUSE_COMMFD_ENV);
1675 if (commfd == NULL) {
1676 fprintf(stderr, "%s: old style mounting not supported\n",
1677 progname);
1678 goto err_out;
1679 }
1680
1681 res = libfuse_strtol(commfd, &cfd);
1682 if (res) {
1683 fprintf(stderr,
1684 "%s: invalid _FUSE_COMMFD: %s\n",
1685 progname, commfd);
1686 goto err_out;
1687
1688 }
1689
1690 {
1691 struct stat statbuf;
1692 fstat(cfd, &statbuf);
1693 if(!S_ISSOCK(statbuf.st_mode)) {
1694 fprintf(stderr,
1695 "%s: file descriptor %li is not a socket, can't send fuse fd\n",
1696 progname, cfd);
1697 goto err_out;
1698 }
1699 }
1700
1701 if (setup_auto_unmount_only)
1702 goto wait_for_auto_unmount;
1703
1704 fd = mount_fuse(mnt, opts, &type);
1705 if (fd == -1)
1706 goto err_out;
1707
1708 res = send_fd(cfd, fd);
1709 if (res != 0) {
1710 umount2(mnt, MNT_DETACH); /* lazy umount */
1711 goto err_out;
1712 }
1713 close(fd);
1714
1715 if (!auto_unmount) {
1716 free(mnt);
1717 free((void*) type);
1718 return 0;
1719 }
1720
1721wait_for_auto_unmount:
1722 /* Become a daemon and wait for the parent to exit or die.
1723 ie For the control socket to get closed.
1724 Btw, we don't want to use daemon() function here because
1725 it forks and messes with the file descriptors. */
1726
1727 res = close_inherited_fds(cfd);
1728 if (res < 0)
1729 exit(EXIT_FAILURE);
1730
1731 setsid();
1732 res = chdir("/");
1733 if (res == -1) {
1734 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1735 goto err_out;
1736 }
1737
1738 sigfillset(&sigset);
1739 sigprocmask(SIG_BLOCK, &sigset, NULL);
1740
1741 lazy = 1;
1742 quiet = 1;
1743
1744 while (1) {
1745 unsigned char buf[16];
1746 int n = recv(cfd, buf, sizeof(buf), 0);
1747 if (!n)
1748 break;
1749
1750 if (n < 0) {
1751 if (errno == EINTR)
1752 continue;
1753 break;
1754 }
1755 }
1756
1757 if (!should_auto_unmount(mnt, type)) {
1758 goto success_out;
1759 }
1760
1761do_unmount:
1762 if (geteuid() == 0)
1763 res = unmount_fuse(mnt, quiet, lazy);
1764 else {
1765 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
1766 if (res == -1 && !quiet)
1767 fprintf(stderr,
1768 "%s: failed to unmount %s: %s\n",
1769 progname, mnt, strerror(errno));
1770 }
1771 if (res == -1)
1772 goto err_out;
1773
1774success_out:
1775 free((void*) type);
1776 free(mnt);
1777 return 0;
1778
1779err_out:
1780 free((void*) type);
1781 free(mnt);
1782 exit(1);
1783}
fuse-3.18.2/doc/html/fuse-3_818_82_2util_2mount_8fuse_8c_source.html0000644000175000017500000021165015156613431023650 0ustar berndbernd libfuse: fuse-3.18.2/util/mount.fuse.c Source File
libfuse
mount.fuse.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPLv2.
6 See the file GPL2.txt.
7*/
8
9#include "fuse_config.h"
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15#include <errno.h>
16#include <stdint.h>
17#include <fcntl.h>
18#include <pwd.h>
19#include <sys/wait.h>
20
21#ifdef linux
22#include <sys/prctl.h>
23#include <sys/syscall.h>
24#include <linux/capability.h>
25#include <linux/securebits.h>
26/* for 2.6 kernels */
27#if !defined(SECBIT_KEEP_CAPS) && defined(SECURE_KEEP_CAPS)
28#define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS))
29#endif
30#if !defined(SECBIT_KEEP_CAPS_LOCKED) && defined(SECURE_KEEP_CAPS_LOCKED)
31#define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED))
32#endif
33#if !defined(SECBIT_NO_SETUID_FIXUP) && defined(SECURE_NO_SETUID_FIXUP)
34#define SECBIT_NO_SETUID_FIXUP (issecure_mask(SECURE_NO_SETUID_FIXUP))
35#endif
36#if !defined(SECBIT_NO_SETUID_FIXUP_LOCKED) && defined(SECURE_NO_SETUID_FIXUP_LOCKED)
37#define SECBIT_NO_SETUID_FIXUP_LOCKED (issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED))
38#endif
39#if !defined(SECBIT_NOROOT) && defined(SECURE_NOROOT)
40#define SECBIT_NOROOT (issecure_mask(SECURE_NOROOT))
41#endif
42#if !defined(SECBIT_NOROOT_LOCKED) && defined(SECURE_NOROOT_LOCKED)
43#define SECBIT_NOROOT_LOCKED (issecure_mask(SECURE_NOROOT_LOCKED))
44#endif
45#endif
46/* linux < 3.5 */
47#ifndef PR_SET_NO_NEW_PRIVS
48#define PR_SET_NO_NEW_PRIVS 38
49#endif
50
51#include "fuse.h"
52
53static char *progname;
54
55static char *xstrdup(const char *s)
56{
57 char *t = strdup(s);
58 if (!t) {
59 fprintf(stderr, "%s: failed to allocate memory\n", progname);
60 exit(1);
61 }
62 return t;
63}
64
65static void *xrealloc(void *oldptr, size_t size)
66{
67 void *ptr = realloc(oldptr, size);
68 if (!ptr) {
69 fprintf(stderr, "%s: failed to allocate memory\n", progname);
70 exit(1);
71 }
72 return ptr;
73}
74
75static void add_arg(char **cmdp, const char *opt)
76{
77 size_t optlen = strlen(opt);
78 size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
79 if (optlen >= (SIZE_MAX - cmdlen - 4)/4) {
80 fprintf(stderr, "%s: argument too long\n", progname);
81 exit(1);
82 }
83 char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
84 char *s;
85 s = cmd + cmdlen;
86 if (*cmdp)
87 *s++ = ' ';
88
89 *s++ = '\'';
90 for (; *opt; opt++) {
91 if (*opt == '\'') {
92 *s++ = '\'';
93 *s++ = '\\';
94 *s++ = '\'';
95 *s++ = '\'';
96 } else
97 *s++ = *opt;
98 }
99 *s++ = '\'';
100 *s = '\0';
101 *cmdp = cmd;
102}
103
104static char *add_option(const char *opt, char *options)
105{
106 int oldlen = options ? strlen(options) : 0;
107
108 options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
109 if (!oldlen)
110 strcpy(options, opt);
111 else {
112 strcat(options, ",");
113 strcat(options, opt);
114 }
115 return options;
116}
117
118static int prepare_fuse_fd(const char *mountpoint, const char* subtype,
119 const char *options)
120{
121 int fuse_fd = -1;
122 int flags = -1;
123 int subtype_len = strlen(subtype) + 9;
124 char* options_copy = xrealloc(NULL, subtype_len);
125
126 snprintf(options_copy, subtype_len, "subtype=%s", subtype);
127 options_copy = add_option(options, options_copy);
128 fuse_fd = fuse_open_channel(mountpoint, options_copy);
129 if (fuse_fd == -1) {
130 exit(1);
131 }
132
133 flags = fcntl(fuse_fd, F_GETFD);
134 if (flags == -1 || fcntl(fuse_fd, F_SETFD, flags & ~FD_CLOEXEC) == 1) {
135 fprintf(stderr, "%s: Failed to clear CLOEXEC: %s\n",
136 progname, strerror(errno));
137 exit(1);
138 }
139
140 return fuse_fd;
141}
142
143#ifdef linux
144static uint64_t get_capabilities(void)
145{
146 /*
147 * This invokes the capset syscall directly to avoid the libcap
148 * dependency, which isn't really justified just for this.
149 */
150 struct __user_cap_header_struct header = {
151 .version = _LINUX_CAPABILITY_VERSION_3,
152 .pid = 0,
153 };
154 struct __user_cap_data_struct data[2];
155 memset(data, 0, sizeof(data));
156 if (syscall(SYS_capget, &header, data) == -1) {
157 fprintf(stderr, "%s: Failed to get capabilities: %s\n",
158 progname, strerror(errno));
159 exit(1);
160 }
161
162 return data[0].effective | ((uint64_t) data[1].effective << 32);
163}
164
165static void set_capabilities(uint64_t caps)
166{
167 /*
168 * This invokes the capset syscall directly to avoid the libcap
169 * dependency, which isn't really justified just for this.
170 */
171 struct __user_cap_header_struct header = {
172 .version = _LINUX_CAPABILITY_VERSION_3,
173 .pid = 0,
174 };
175 struct __user_cap_data_struct data[2];
176 memset(data, 0, sizeof(data));
177 data[0].effective = data[0].permitted = caps;
178 data[1].effective = data[1].permitted = caps >> 32;
179 if (syscall(SYS_capset, &header, data) == -1) {
180 fprintf(stderr, "%s: Failed to set capabilities: %s\n",
181 progname, strerror(errno));
182 exit(1);
183 }
184}
185
186static void drop_and_lock_capabilities(void)
187{
188 /* Set and lock securebits. */
189 if (prctl(PR_SET_SECUREBITS,
190 SECBIT_KEEP_CAPS_LOCKED |
191 SECBIT_NO_SETUID_FIXUP |
192 SECBIT_NO_SETUID_FIXUP_LOCKED |
193 SECBIT_NOROOT |
194 SECBIT_NOROOT_LOCKED) == -1) {
195 fprintf(stderr, "%s: Failed to set securebits %s\n",
196 progname, strerror(errno));
197 exit(1);
198 }
199
200 /* Clear the capability bounding set. */
201 int cap;
202 for (cap = 0; ; cap++) {
203 int cap_status = prctl(PR_CAPBSET_READ, cap);
204 if (cap_status == 0) {
205 continue;
206 }
207 if (cap_status == -1 && errno == EINVAL) {
208 break;
209 }
210
211 if (cap_status != 1) {
212 fprintf(stderr,
213 "%s: Failed to get capability %u: %s\n",
214 progname, cap, strerror(errno));
215 exit(1);
216 }
217 if (prctl(PR_CAPBSET_DROP, cap) == -1) {
218 fprintf(stderr,
219 "%s: Failed to drop capability %u: %s\n",
220 progname, cap, strerror(errno));
221 }
222 }
223
224 /* Drop capabilities. */
225 set_capabilities(0);
226
227 /* Prevent re-acquisition of privileges. */
228 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
229 fprintf(stderr, "%s: Failed to set no_new_privs: %s\n",
230 progname, strerror(errno));
231 exit(1);
232 }
233}
234#endif
235
236int main(int argc, char *argv[])
237{
238 char *type = NULL;
239 char *source;
240 char *dup_source = NULL;
241 const char *mountpoint;
242 char *basename;
243 char *options = NULL;
244 char *command = NULL;
245 char *setuid_name = NULL;
246 int i;
247 int dev = 1;
248 int suid = 1;
249 int pass_fuse_fd = 0;
250 int fuse_fd = 0;
251 int drop_privileges = 0;
252 char *dev_fd_mountpoint = NULL;
253
254 progname = argv[0];
255 basename = strrchr(argv[0], '/');
256 if (basename)
257 basename++;
258 else
259 basename = argv[0];
260
261 if (strncmp(basename, "mount.fuse.", 11) == 0)
262 type = basename + 11;
263 if (strncmp(basename, "mount.fuseblk.", 14) == 0)
264 type = basename + 14;
265
266 if (type && !type[0])
267 type = NULL;
268
269 if (argc < 3) {
270 fprintf(stderr,
271 "usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
272 progname, type ? "source" : "type#[source]");
273 exit(1);
274 }
275
276 source = argv[1];
277 if (!source[0])
278 source = NULL;
279
280 mountpoint = argv[2];
281
282 for (i = 3; i < argc; i++) {
283 if (strcmp(argv[i], "-v") == 0) {
284 continue;
285 } else if (strcmp(argv[i], "-t") == 0) {
286 i++;
287
288 if (i == argc) {
289 fprintf(stderr,
290 "%s: missing argument to option '-t'\n",
291 progname);
292 exit(1);
293 }
294 type = argv[i];
295 if (strncmp(type, "fuse.", 5) == 0)
296 type += 5;
297 else if (strncmp(type, "fuseblk.", 8) == 0)
298 type += 8;
299
300 if (!type[0]) {
301 fprintf(stderr,
302 "%s: empty type given as argument to option '-t'\n",
303 progname);
304 exit(1);
305 }
306 } else if (strcmp(argv[i], "-o") == 0) {
307 char *opts;
308 char *opt;
309 i++;
310 if (i == argc)
311 break;
312
313 opts = xstrdup(argv[i]);
314 opt = strtok(opts, ",");
315 while (opt) {
316 int j;
317 int ignore = 0;
318 const char *ignore_opts[] = { "",
319 "user",
320 "nofail",
321 "nouser",
322 "users",
323 "auto",
324 "noauto",
325 "_netdev",
326 NULL};
327 if (strncmp(opt, "setuid=", 7) == 0) {
328 setuid_name = xstrdup(opt + 7);
329 ignore = 1;
330 } else if (strcmp(opt,
331 "drop_privileges") == 0) {
332 pass_fuse_fd = 1;
333 drop_privileges = 1;
334 ignore = 1;
335 }
336 for (j = 0; ignore_opts[j]; j++)
337 if (strcmp(opt, ignore_opts[j]) == 0)
338 ignore = 1;
339
340 if (!ignore) {
341 if (strcmp(opt, "nodev") == 0)
342 dev = 0;
343 else if (strcmp(opt, "nosuid") == 0)
344 suid = 0;
345
346 options = add_option(opt, options);
347 }
348 opt = strtok(NULL, ",");
349 }
350 free(opts);
351 }
352 }
353
354 if (drop_privileges) {
355 uint64_t required_caps = CAP_TO_MASK(CAP_SETPCAP) |
356 CAP_TO_MASK(CAP_SYS_ADMIN);
357 if ((get_capabilities() & required_caps) != required_caps) {
358 fprintf(stderr, "%s: drop_privileges was requested, which launches the FUSE file system fully unprivileged. In order to do so %s must be run with privileges, please invoke with CAP_SYS_ADMIN and CAP_SETPCAP (e.g. as root).\n",
359 progname, progname);
360 exit(1);
361 }
362 }
363
364 if (dev)
365 options = add_option("dev", options);
366 if (suid)
367 options = add_option("suid", options);
368
369 if (!type) {
370 if (source) {
371 dup_source = xstrdup(source);
372 type = dup_source;
373 source = strchr(type, '#');
374 if (source)
375 *source++ = '\0';
376 if (!type[0]) {
377 fprintf(stderr, "%s: empty filesystem type\n",
378 progname);
379 exit(1);
380 }
381 } else {
382 fprintf(stderr, "%s: empty source\n", progname);
383 exit(1);
384 }
385 }
386
387 if (setuid_name && setuid_name[0]) {
388#ifdef linux
389 if (drop_privileges) {
390 /*
391 * Make securebits more permissive before calling
392 * setuid(). Specifically, if SECBIT_KEEP_CAPS and
393 * SECBIT_NO_SETUID_FIXUP weren't set, setuid() would
394 * have the side effect of dropping all capabilities,
395 * and we need to retain CAP_SETPCAP in order to drop
396 * all privileges before exec().
397 */
398 if (prctl(PR_SET_SECUREBITS,
399 SECBIT_KEEP_CAPS |
400 SECBIT_NO_SETUID_FIXUP) == -1) {
401 fprintf(stderr,
402 "%s: Failed to set securebits %s\n",
403 progname, strerror(errno));
404 exit(1);
405 }
406 }
407#endif
408
409 struct passwd *pwd = getpwnam(setuid_name);
410 if (!pwd || setgid(pwd->pw_gid) == -1 || setuid(pwd->pw_uid) == -1) {
411 fprintf(stderr, "%s: Failed to setuid to %s: %s\n",
412 progname, setuid_name, strerror(errno));
413 exit(1);
414 }
415 } else if (!getenv("HOME")) {
416 /* Hack to make filesystems work in the boot environment */
417 setenv("HOME", "/root", 0);
418 }
419
420 if (pass_fuse_fd) {
421 fuse_fd = prepare_fuse_fd(mountpoint, type, options);
422 dev_fd_mountpoint = xrealloc(NULL, 20);
423 snprintf(dev_fd_mountpoint, 20, "/dev/fd/%u", fuse_fd);
424 mountpoint = dev_fd_mountpoint;
425 }
426
427#ifdef linux
428 if (drop_privileges) {
429 drop_and_lock_capabilities();
430 }
431#endif
432 add_arg(&command, type);
433 if (source)
434 add_arg(&command, source);
435 add_arg(&command, mountpoint);
436 if (options) {
437 add_arg(&command, "-o");
438 add_arg(&command, options);
439 }
440
441 free(options);
442 free(dev_fd_mountpoint);
443 free(dup_source);
444 free(setuid_name);
445
446 execl("/bin/sh", "/bin/sh", "-c", command, NULL);
447 fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname,
448 strerror(errno));
449
450 if (pass_fuse_fd)
451 close(fuse_fd);
452 free(command);
453 return 1;
454}
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:482
fuse-3.18.2/doc/html/fuse-3_818_82_2example_2cuse_8c.html0000644000175000017500000011774215156613431021440 0ustar berndbernd libfuse: fuse-3.18.2/example/cuse.c File Reference
libfuse
cuse.c File Reference
#include <cuse_lowlevel.h>
#include <fuse_opt.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This example demonstrates how to implement a character device in userspace ("CUSE"). This is only allowed for root. The character device should appear in /dev under the specified name. It can be tested with the cuse_client.c program.

Mount the file system with:

cuse -f --name=mydevice

You should now have a new /dev/mydevice character device. To "unmount" it, kill the "cuse" process.

To compile this example, run

gcc -Wall cuse.c `pkg-config fuse3 --cflags --libs` -o cuse

Source code

/*
CUSE example: Character device in Userspace
Copyright (C) 2008-2009 SUSE Linux Products GmbH
Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <cuse_lowlevel.h>
#include <fuse_opt.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ioctl.h"
static void *cusexmp_buf;
static size_t cusexmp_size;
static const char *usage =
"usage: cusexmp [options]\n"
"\n"
"options:\n"
" --help|-h print this help message\n"
" --maj=MAJ|-M MAJ device major number\n"
" --min=MIN|-m MIN device minor number\n"
" --name=NAME|-n NAME device name (mandatory)\n"
" -d -o debug enable debug output (implies -f)\n"
" -f foreground operation\n"
" -s disable multi-threaded operation\n"
"\n";
static int cusexmp_resize(size_t new_size)
{
void *new_buf;
if (new_size == cusexmp_size)
return 0;
new_buf = realloc(cusexmp_buf, new_size);
if (!new_buf && new_size)
return -ENOMEM;
if (new_size > cusexmp_size)
memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
cusexmp_buf = new_buf;
cusexmp_size = new_size;
return 0;
}
static int cusexmp_expand(size_t new_size)
{
if (new_size > cusexmp_size)
return cusexmp_resize(new_size);
return 0;
}
static void cusexmp_init(void *userdata, struct fuse_conn_info *conn)
{
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
{
fuse_reply_open(req, fi);
}
static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
struct fuse_file_info *fi)
{
(void)fi;
if (off >= cusexmp_size)
off = cusexmp_size;
if (size > cusexmp_size - off)
size = cusexmp_size - off;
fuse_reply_buf(req, cusexmp_buf + off, size);
}
static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
off_t off, struct fuse_file_info *fi)
{
(void)fi;
if (cusexmp_expand(off + size)) {
fuse_reply_err(req, ENOMEM);
return;
}
memcpy(cusexmp_buf + off, buf, size);
fuse_reply_write(req, size);
}
static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
size_t in_bufsz, size_t out_bufsz, int is_read)
{
const struct fioc_rw_arg *arg;
struct iovec in_iov[2], out_iov[3], iov[3];
size_t cur_size;
/* read in arg */
in_iov[0].iov_base = addr;
in_iov[0].iov_len = sizeof(*arg);
if (!in_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
return;
}
arg = in_buf;
in_buf += sizeof(*arg);
in_bufsz -= sizeof(*arg);
/* prepare size outputs */
out_iov[0].iov_base =
addr + offsetof(struct fioc_rw_arg, prev_size);
out_iov[0].iov_len = sizeof(arg->prev_size);
out_iov[1].iov_base =
addr + offsetof(struct fioc_rw_arg, new_size);
out_iov[1].iov_len = sizeof(arg->new_size);
/* prepare client buf */
if (is_read) {
out_iov[2].iov_base = arg->buf;
out_iov[2].iov_len = arg->size;
if (!out_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
return;
}
} else {
in_iov[1].iov_base = arg->buf;
in_iov[1].iov_len = arg->size;
if (arg->size && !in_bufsz) {
fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
return;
}
}
/* we're all set */
cur_size = cusexmp_size;
iov[0].iov_base = &cur_size;
iov[0].iov_len = sizeof(cur_size);
iov[1].iov_base = &cusexmp_size;
iov[1].iov_len = sizeof(cusexmp_size);
if (is_read) {
size_t off = arg->offset;
size_t size = arg->size;
if (off >= cusexmp_size)
off = cusexmp_size;
if (size > cusexmp_size - off)
size = cusexmp_size - off;
iov[2].iov_base = cusexmp_buf + off;
iov[2].iov_len = size;
fuse_reply_ioctl_iov(req, size, iov, 3);
} else {
if (cusexmp_expand(arg->offset + in_bufsz)) {
fuse_reply_err(req, ENOMEM);
return;
}
memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
}
}
static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
struct fuse_file_info *fi, unsigned flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{
int is_read = 0;
(void)fi;
if (flags & FUSE_IOCTL_COMPAT) {
fuse_reply_err(req, ENOSYS);
return;
}
switch (cmd) {
case FIOC_GET_SIZE:
if (!out_bufsz) {
struct iovec iov = { arg, sizeof(size_t) };
fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
} else
fuse_reply_ioctl(req, 0, &cusexmp_size,
sizeof(cusexmp_size));
break;
case FIOC_SET_SIZE:
if (!in_bufsz) {
struct iovec iov = { arg, sizeof(size_t) };
fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
} else {
cusexmp_resize(*(size_t *)in_buf);
fuse_reply_ioctl(req, 0, NULL, 0);
}
break;
case FIOC_READ:
is_read = 1;
/* fall through */
case FIOC_WRITE:
fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
break;
default:
fuse_reply_err(req, EINVAL);
}
}
struct cusexmp_param {
unsigned major;
unsigned minor;
char *dev_name;
int is_help;
};
#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
static const struct fuse_opt cusexmp_opts[] = {
CUSEXMP_OPT("-M %u", major),
CUSEXMP_OPT("--maj=%u", major),
CUSEXMP_OPT("-m %u", minor),
CUSEXMP_OPT("--min=%u", minor),
CUSEXMP_OPT("-n %s", dev_name),
CUSEXMP_OPT("--name=%s", dev_name),
FUSE_OPT_KEY("-h", 0),
FUSE_OPT_KEY("--help", 0),
};
static int cusexmp_process_arg(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct cusexmp_param *param = data;
(void)outargs;
(void)arg;
switch (key) {
case 0:
param->is_help = 1;
fprintf(stderr, "%s", usage);
return fuse_opt_add_arg(outargs, "-ho");
default:
return 1;
}
}
static const struct cuse_lowlevel_ops cusexmp_clop = {
.init = cusexmp_init,
.open = cusexmp_open,
.read = cusexmp_read,
.write = cusexmp_write,
.ioctl = cusexmp_ioctl,
};
int main(int argc, char **argv)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct cusexmp_param param = { 0, 0, NULL, 0 };
char dev_name[128] = "DEVNAME=";
const char *dev_info_argv[] = { dev_name };
struct cuse_info ci;
int ret = 1;
if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
printf("failed to parse option\n");
free(param.dev_name);
goto out;
}
if (!param.is_help) {
if (!param.dev_name) {
fprintf(stderr, "Error: device name missing\n");
goto out;
}
strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME="));
free(param.dev_name);
}
memset(&ci, 0, sizeof(ci));
ci.dev_major = param.major;
ci.dev_minor = param.minor;
ci.dev_info_argc = 1;
ci.dev_info_argv = dev_info_argv;
ci.flags = CUSE_UNRESTRICTED_IOCTL;
ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL);
out:
return ret;
}
#define FUSE_IOCTL_COMPAT
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count)
int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_OPT_KEY(templ, key)
Definition fuse_opt.h:98
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt

Definition in file cuse.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2cuse__client_8c.html0000644000175000017500000003224615156613431023130 0ustar berndbernd libfuse: fuse-3.18.2/example/cuse_client.c File Reference
libfuse
cuse_client.c File Reference
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This program tests the cuse.c example file system.

Example usage (assuming that /dev/foobar is a CUSE device provided by the cuse.c example file system):

$ cuse_client /dev/foobar s
0

$ echo "hello" | cuse_client /dev/foobar w 6
Writing 6 bytes
transferred 6 bytes (0 -> 6)

$ cuse_client /dev/foobar s
6

$ cuse_client /dev/foobar r 10
hello
transferred 6 bytes (6 -> 6)

Compiling this example

gcc -Wall cuse_client.c -o cuse_client

Source Code

/*
FUSE fioclient: FUSE ioctl example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"
const char *usage =
"Usage: cuse_client FIOC_FILE COMMAND\n"
"\n"
"COMMANDS\n"
" s [SIZE] : get size if SIZE is omitted, set size otherwise\n"
" r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n"
" w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n"
"\n";
static int do_rw(int fd, int is_read, size_t size, off_t offset,
size_t *prev_size, size_t *new_size)
{
struct fioc_rw_arg arg = { .offset = offset };
ssize_t ret;
arg.buf = calloc(1, size);
if (!arg.buf) {
fprintf(stderr, "failed to allocated %zu bytes\n", size);
return -1;
}
if (is_read) {
arg.size = size;
ret = ioctl(fd, FIOC_READ, &arg);
if (ret >= 0)
fwrite(arg.buf, 1, ret, stdout);
} else {
arg.size = fread(arg.buf, 1, size, stdin);
fprintf(stderr, "Writing %zu bytes\n", arg.size);
ret = ioctl(fd, FIOC_WRITE, &arg);
}
if (ret >= 0) {
*prev_size = arg.prev_size;
*new_size = arg.new_size;
} else
perror("ioctl");
free(arg.buf);
return ret;
}
int main(int argc, char **argv)
{
size_t param[2] = { };
size_t size, prev_size = 0, new_size = 0;
char cmd;
int fd, i, rc;
if (argc < 3)
goto usage;
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
cmd = tolower(argv[2][0]);
argc -= 3;
argv += 3;
for (i = 0; i < argc; i++) {
char *endp;
param[i] = strtoul(argv[i], &endp, 0);
if (endp == argv[i] || *endp != '\0')
goto usage;
}
switch (cmd) {
case 's':
if (!argc) {
if (ioctl(fd, FIOC_GET_SIZE, &size)) {
perror("ioctl");
goto error;
}
printf("%zu\n", size);
} else {
size = param[0];
if (ioctl(fd, FIOC_SET_SIZE, &size)) {
perror("ioctl");
goto error;
}
}
close(fd);
return 0;
case 'r':
case 'w':
rc = do_rw(fd, cmd == 'r', param[0], param[1],
&prev_size, &new_size);
if (rc < 0)
goto error;
fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n",
rc, prev_size, new_size);
close(fd);
return 0;
}
usage:
fprintf(stderr, "%s", usage);
return 1;
error:
close(fd);
return 1;
}

Definition in file cuse_client.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2hello_8c.html0000644000175000017500000007134615156613431021603 0ustar berndbernd libfuse: fuse-3.18.2/example/hello.c File Reference
libfuse
hello.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using high-level API

Compile with:

gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>
/*
* Command line options
*
* We can't set default values for the char* fields here because
* fuse_opt_parse would attempt to free() them when the user specifies
* different values on the command line.
*/
static struct options {
const char *filename;
const char *contents;
int show_help;
} options;
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--name=%s", filename),
OPTION("--contents=%s", contents),
OPTION("-h", show_help),
OPTION("--help", show_help),
};
static void *hello_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->kernel_cache = 1;
/* Test setting flags the old way */
return NULL;
}
static int hello_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int res = 0;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else if (strcmp(path+1, options.filename) == 0) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(options.contents);
} else
res = -ENOENT;
return res;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
if (strcmp(path+1, options.filename) != 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
size_t len;
(void) fi;
if(strcmp(path+1, options.filename) != 0)
return -ENOENT;
len = strlen(options.contents);
if (offset < len) {
if (offset + size > len)
size = len - offset;
memcpy(buf, options.contents + offset, size);
} else
size = 0;
return size;
}
static const struct fuse_operations hello_oper = {
.init = hello_init,
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --name=<s> Name of the \"hello\" file\n"
" (default: \"hello\")\n"
" --contents=<s> Contents \"hello\" file\n"
" (default \"Hello, World!\\n\")\n"
"\n");
}
int main(int argc, char *argv[])
{
int ret;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
/* Set defaults -- we have to use strdup so that
fuse_opt_parse can free the defaults if other
values are specified */
options.filename = strdup("hello");
options.contents = strdup("Hello World!\n");
/* Parse options */
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
/* When --help is specified, first print our own file-system
specific help text, then signal fuse_main to show
additional help (by adding `--help` to the options again)
without usage: line (by setting argv[0] to the empty
string) */
if (options.show_help) {
show_help(argv[0]);
assert(fuse_opt_add_arg(&args, "--help") == 0);
args.argv[0][0] = '\0';
}
ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
return ret;
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file hello.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2test_2hello_8c.html0000644000175000017500000007133215156613431021122 0ustar berndbernd libfuse: fuse-3.18.2/test/hello.c File Reference
libfuse
hello.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using high-level API

Compile with:

gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>
/*
* Command line options
*
* We can't set default values for the char* fields here because
* fuse_opt_parse would attempt to free() them when the user specifies
* different values on the command line.
*/
static struct options {
const char *filename;
const char *contents;
int show_help;
} options;
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--name=%s", filename),
OPTION("--contents=%s", contents),
OPTION("-h", show_help),
OPTION("--help", show_help),
};
static void *hello_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->kernel_cache = 1;
/* Test setting flags the old way */
return NULL;
}
static int hello_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int res = 0;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else if (strcmp(path+1, options.filename) == 0) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(options.contents);
} else
res = -ENOENT;
return res;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
if (strcmp(path+1, options.filename) != 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
size_t len;
(void) fi;
if(strcmp(path+1, options.filename) != 0)
return -ENOENT;
len = strlen(options.contents);
if (offset < len) {
if (offset + size > len)
size = len - offset;
memcpy(buf, options.contents + offset, size);
} else
size = 0;
return size;
}
static const struct fuse_operations hello_oper = {
.init = hello_init,
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --name=<s> Name of the \"hello\" file\n"
" (default: \"hello\")\n"
" --contents=<s> Contents \"hello\" file\n"
" (default \"Hello, World!\\n\")\n"
"\n");
}
int main(int argc, char *argv[])
{
int ret;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
/* Set defaults -- we have to use strdup so that
fuse_opt_parse can free the defaults if other
values are specified */
options.filename = strdup("hello");
options.contents = strdup("Hello World!\n");
/* Parse options */
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
/* When --help is specified, first print our own file-system
specific help text, then signal fuse_main to show
additional help (by adding `--help` to the options again)
without usage: line (by setting argv[0] to the empty
string) */
if (options.show_help) {
show_help(argv[0]);
assert(fuse_opt_add_arg(&args, "--help") == 0);
args.argv[0][0] = '\0';
}
ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
return ret;
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_ASYNC_READ
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition fuse_opt.c:55
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
int32_t kernel_cache
Definition fuse.h:245
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file hello.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2hello__ll_8c.html0000644000175000017500000013124715156613431022426 0ustar berndbernd libfuse: fuse-3.18.2/example/hello_ll.c File Reference
libfuse
hello_ll.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem using low-level API

Compile with:

gcc -Wall hello_ll.c `pkg-config fuse3 --cflags --libs` -o hello_ll

Note: If the pkg-config command fails due to the absence of the fuse3.pc file, you should configure the path to the fuse3.pc file in the PKG_CONFIG_PATH variable.

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.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_init(void *userdata, struct fuse_conn_info *conn)
{
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
/* Test setting flags the old way */
conn->want &= ~FUSE_CAP_ASYNC_READ;
}
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,
{
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 void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
(void)size;
assert(ino == 1 || 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 == 1 || 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 == 1 || 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 = {
.init = hello_ll_init,
.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[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
se = fuse_session_new(&args, &hello_ll_oper,
sizeof(hello_ll_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file hello_ll.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2hello__ll__uds_8c.html0000644000175000017500000013141215156613431023432 0ustar berndbernd libfuse: fuse-3.18.2/example/hello_ll_uds.c File Reference
libfuse
hello_ll_uds.c File Reference
#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>

Go to the source code of this file.

Detailed Description

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

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.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_init(void *userdata, struct fuse_conn_info *conn)
{
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
/* Test setting flags the old way */
conn->want &= ~FUSE_CAP_ASYNC_READ;
}
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,
{
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 void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
(void)size;
assert(ino == 1 || 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 == 1 || 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 == 1 || 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 = {
.init = hello_ll_init,
.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[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
se = fuse_session_new(&args, &hello_ll_oper,
sizeof(hello_ll_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file hello_ll_uds.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2invalidate__path_8c.html0000644000175000017500000012405715156613431023771 0ustar berndbernd libfuse: fuse-3.18.2/example/invalidate_path.c File Reference
libfuse
invalidate_path.c File Reference
#include <fuse.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with two files:

  • 'current-time', whose contents change dynamically: it always contains the current time (same as in notify_inval_inode.c).
  • 'growing', whose size changes dynamically, growing by 1 byte after each update. This aims to check if cached file metadata is also invalidated.

Compilation

gcc -Wall invalidate_path.c `pkg-config fuse3 --cflags --libs` -o invalidate_path

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
(C) 2017 EditShare LLC <slawek.rudnicki@editshare.com>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 34
#include <fuse.h>
#include <fuse_lowlevel.h> /* for fuse_cmdline_opts */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define TIME_FILE_NAME "current_time"
#define TIME_FILE_INO 2
#define GROW_FILE_NAME "growing"
#define GROW_FILE_INO 3
static char time_file_contents[MAX_STR_LEN];
static size_t grow_file_size;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
{
(void) conn;
cfg->entry_timeout = NO_TIMEOUT;
cfg->attr_timeout = NO_TIMEOUT;
cfg->negative_timeout = 0;
return NULL;
}
static int xmp_getattr(const char *path,
struct stat *stbuf, struct fuse_file_info* fi) {
(void) fi;
if (strcmp(path, "/") == 0) {
stbuf->st_ino = 1;
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
} else if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
stbuf->st_ino = TIME_FILE_INO;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(time_file_contents);
} else if (strcmp(path, "/" GROW_FILE_NAME) == 0) {
stbuf->st_ino = GROW_FILE_INO;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = grow_file_size;
} else {
return -ENOENT;
}
return 0;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags) {
(void) fi;
(void) offset;
(void) flags;
if (strcmp(path, "/") != 0) {
return -ENOTDIR;
} else {
(void) filler;
(void) buf;
struct stat file_stat;
xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL);
filler(buf, TIME_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL);
filler(buf, GROW_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
}
static int xmp_open(const char *path, struct fuse_file_info *fi) {
(void) path;
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) {
(void) fi;
(void) offset;
if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
int file_length = strlen(time_file_contents);
int to_copy = offset + size <= file_length
? size
: file_length - offset;
memcpy(buf, time_file_contents, to_copy);
return to_copy;
} else {
assert(strcmp(path, "/" GROW_FILE_NAME) == 0);
int to_copy = offset + size <= grow_file_size
? size
: grow_file_size - offset;
memset(buf, 'x', to_copy);
return to_copy;
}
}
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.readdir = xmp_readdir,
.open = xmp_open,
.read = xmp_read,
};
static void update_fs(void) {
static int count = 0;
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
int time_file_size = strftime(time_file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(time_file_size != 0);
grow_file_size = count++;
}
static int invalidate(struct fuse *fuse, const char *path) {
int status = fuse_invalidate_path(fuse, path);
if (status == -ENOENT) {
return 0;
} else {
return status;
}
}
static void* update_fs_loop(void *data) {
struct fuse *fuse = (struct fuse*) data;
while (1) {
update_fs();
if (!options.no_notify) {
assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0);
assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0);
}
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\n");
}
int main(int argc, char *argv[]) {
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse *fuse;
struct fuse_cmdline_opts opts;
struct fuse_loop_config config;
int res;
/* Initialize the files */
update_fs();
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
res = 0;
goto out1;
} else if (opts.show_help) {
show_help(argv[0]);
fuse_lib_help(&args);
res = 0;
goto out1;
} else if (!opts.mountpoint) {
fprintf(stderr, "error: no mountpoint specified\n");
res = 1;
goto out1;
}
fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL);
if (fuse == NULL) {
res = 1;
goto out1;
}
if (fuse_mount(fuse,opts.mountpoint) != 0) {
res = 1;
goto out2;
}
if (fuse_daemonize(opts.foreground) != 0) {
res = 1;
goto out3;
}
pthread_t updater; /* Start thread to update file contents */
int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n", strerror(ret));
return 1;
};
struct fuse_session *se = fuse_get_session(fuse);
if (fuse_set_signal_handlers(se) != 0) {
res = 1;
goto out3;
}
if (opts.singlethread)
res = fuse_loop(fuse);
else {
config.clone_fd = opts.clone_fd;
config.max_idle_threads = opts.max_idle_threads;
res = fuse_loop_mt(fuse, &config);
}
if (res)
res = 1;
out3:
fuse_unmount(fuse);
out2:
fuse_destroy(fuse);
out1:
free(opts.mountpoint);
return res;
}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5197
void fuse_destroy(struct fuse *f)
Definition fuse.c:5146
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4673
int fuse_loop(struct fuse *f)
Definition fuse.c:4577
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4744
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4520
void fuse_unmount(struct fuse *f)
Definition fuse.c:5202
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
char ** argv
Definition fuse_opt.h:114
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t keep_cache
Definition fuse_common.h:69
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641
unsigned long offset
Definition fuse_opt.h:85

Definition in file invalidate_path.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2ioctl_8c.html0000644000175000017500000005504315156613431021606 0ustar berndbernd libfuse: fuse-3.18.2/example/ioctl.c File Reference
libfuse
ioctl.c File Reference
#include <fuse.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This example illustrates how to write a FUSE file system that can process (a restricted set of) ioctls. It can be tested with the ioctl_client.c program.

Compile with:

gcc -Wall ioctl.c `pkg-config fuse3 --cflags --libs` -o ioctl

Source code

/*
FUSE fioc: FUSE ioctl example
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 35
#include <fuse.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "ioctl.h"
#define FIOC_NAME "fioc"
enum {
FIOC_NONE,
FIOC_ROOT,
FIOC_FILE,
};
static void *fioc_buf;
static size_t fioc_size;
static int fioc_resize(size_t new_size)
{
void *new_buf;
if (new_size == fioc_size)
return 0;
new_buf = realloc(fioc_buf, new_size);
if (!new_buf && new_size)
return -ENOMEM;
if (new_size > fioc_size)
memset(new_buf + fioc_size, 0, new_size - fioc_size);
fioc_buf = new_buf;
fioc_size = new_size;
return 0;
}
static int fioc_expand(size_t new_size)
{
if (new_size > fioc_size)
return fioc_resize(new_size);
return 0;
}
static int fioc_file_type(const char *path)
{
if (strcmp(path, "/") == 0)
return FIOC_ROOT;
if (strcmp(path, "/" FIOC_NAME) == 0)
return FIOC_FILE;
return FIOC_NONE;
}
static int fioc_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
stbuf->st_uid = getuid();
stbuf->st_gid = getgid();
stbuf->st_atime = stbuf->st_mtime = time(NULL);
switch (fioc_file_type(path)) {
case FIOC_ROOT:
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
break;
case FIOC_FILE:
stbuf->st_mode = S_IFREG | 0644;
stbuf->st_nlink = 1;
stbuf->st_size = fioc_size;
break;
case FIOC_NONE:
return -ENOENT;
}
return 0;
}
static int fioc_open(const char *path, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_NONE)
return 0;
return -ENOENT;
}
static int fioc_do_read(char *buf, size_t size, off_t offset)
{
if (offset >= fioc_size)
return 0;
if (size > fioc_size - offset)
size = fioc_size - offset;
memcpy(buf, fioc_buf + offset, size);
return size;
}
static int fioc_read(const char *path, char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_do_read(buf, size, offset);
}
static int fioc_do_write(const char *buf, size_t size, off_t offset)
{
if (fioc_expand(offset + size))
return -ENOMEM;
memcpy(fioc_buf + offset, buf, size);
return size;
}
static int fioc_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_do_write(buf, size, offset);
}
static int fioc_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
(void) fi;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
return fioc_resize(size);
}
static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
(void) fi;
(void) offset;
(void) flags;
if (fioc_file_type(path) != FIOC_ROOT)
return -ENOENT;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, FIOC_NAME, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
return 0;
}
static int fioc_ioctl(const char *path, unsigned int cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags, void *data)
{
(void) arg;
(void) fi;
(void) flags;
if (fioc_file_type(path) != FIOC_FILE)
return -EINVAL;
if (flags & FUSE_IOCTL_COMPAT)
return -ENOSYS;
switch (cmd) {
case FIOC_GET_SIZE:
*(size_t *)data = fioc_size;
return 0;
case FIOC_SET_SIZE:
fioc_resize(*(size_t *)data);
return 0;
}
return -EINVAL;
}
static const struct fuse_operations fioc_oper = {
.getattr = fioc_getattr,
.readdir = fioc_readdir,
.truncate = fioc_truncate,
.open = fioc_open,
.read = fioc_read,
.write = fioc_write,
.ioctl = fioc_ioctl,
};
int main(int argc, char *argv[])
{
return fuse_main(argc, argv, &fioc_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
#define FUSE_IOCTL_COMPAT
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361

Definition in file ioctl.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2ioctl_8h.html0000644000175000017500000001254415156613431021612 0ustar berndbernd libfuse: fuse-3.18.2/example/ioctl.h File Reference
libfuse
ioctl.h File Reference
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/ioctl.h>

Go to the source code of this file.

Detailed Description

Header file to share definitions between the ioctl.c example file system and the ioctl_client.c test program.

/*
FUSE-ioctl: ioctl support for FUSE
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
enum {
FIOC_GET_SIZE = _IOR('E', 0, size_t),
FIOC_SET_SIZE = _IOW('E', 1, size_t),
/*
* The following two ioctls don't follow usual encoding rules
* and transfer variable amount of data.
*/
FIOC_READ = _IO('E', 2),
FIOC_WRITE = _IO('E', 3),
};
struct fioc_rw_arg {
off_t offset;
void *buf;
size_t size;
size_t prev_size; /* out param for previous total size */
size_t new_size; /* out param for new total size */
};

Definition in file ioctl.h.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2ioctl__client_8c.html0000644000175000017500000001747215156613431023307 0ustar berndbernd libfuse: fuse-3.18.2/example/ioctl_client.c File Reference
libfuse
ioctl_client.c File Reference
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"

Go to the source code of this file.

Detailed Description

This program tests the ioctl.c example file systsem.

Compile with:

gcc -Wall ioctl_client.c -o ioctl_client

Source code

/*
FUSE fioclient: FUSE ioctl example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include "ioctl.h"
const char *usage =
"Usage: fioclient FIOC_FILE [size]\n"
"\n"
"Get size if <size> is omitted, set size otherwise\n"
"\n";
int main(int argc, char **argv)
{
size_t size;
int fd;
int ret = 0;
if (argc < 2) {
fprintf(stderr, "%s", usage);
return 1;
}
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
if (argc == 2) {
if (ioctl(fd, FIOC_GET_SIZE, &size)) {
perror("ioctl");
ret = 1;
goto out;
}
printf("%zu\n", size);
} else {
size = strtoul(argv[2], NULL, 0);
if (ioctl(fd, FIOC_SET_SIZE, &size)) {
perror("ioctl");
ret = 1;
goto out;
}
}
out:
close(fd);
return ret;
}

Definition in file ioctl_client.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2notify__inval__entry_8c.html0000644000175000017500000015442115156613431024714 0ustar berndbernd libfuse: fuse-3.18.2/example/notify_inval_entry.c File Reference
libfuse
notify_inval_entry.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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>

Go to the source code of this file.

Detailed Description

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() and fuse_lowlevel_notify_expire_entry() functions.

To see the effect, first start the file system with the --no-notify

$ notify_inval_entry --update-interval=1 --timeout=30 --no-notify mnt/

Observe that ls always prints the correct directory contents (since readdir output is not cached)::

$ ls mnt; sleep 1; ls mnt; sleep 1; ls mnt
Time_is_15h_48m_33s  current_time
Time_is_15h_48m_34s  current_time
Time_is_15h_48m_35s  current_time

However, if you try to access a file by name the kernel will report that it still exists:

$ file=$(ls mnt/); echo $file
Time_is_15h_50m_09s
$ sleep 5; stat mnt/$file
  File: ‘mnt/Time_is_15h_50m_09s’
  Size: 32                Blocks: 0          IO Block: 4096   regular file
Device: 2ah/42d     Inode: 3           Links: 1
Access: (0444/-r--r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 1969-12-31 16:00:00.000000000 -0800
Modify: 1969-12-31 16:00:00.000000000 -0800
Change: 1969-12-31 16:00:00.000000000 -0800
 Birth: -

Only once the kernel cache timeout has been reached will the stat call fail:

$ sleep 30; stat mnt/$file
stat: cannot stat ‘mnt/Time_is_15h_50m_09s’: No such file or directory

In contrast, if you enable notifications you will be unable to stat the file as soon as the file system updates its name:

$ notify_inval_entry --update-interval=1 --timeout=30 mnt/
$ file=$(ls mnt/); stat mnt/$file
  File: ‘mnt/Time_is_20h_42m_11s’
  Size: 0                 Blocks: 0          IO Block: 4096   regular empty file
Device: 2ah/42d     Inode: 2           Links: 1
Access: (0000/----------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 1969-12-31 16:00:00.000000000 -0800
Modify: 1969-12-31 16:00:00.000000000 -0800
Change: 1969-12-31 16:00:00.000000000 -0800
 Birth: -
$ 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

Another possible command-line option is –inc-epoch, which will use the FUSE low-level function fuse_lowlevel_notify_increment_epoch() instead. This will function will force the invalidation of all dentries next time they are revalidated. Note that –inc-epoch and –only-expire options are mutually exclusive.

Compilation

gcc -Wall notify_inval_entry.c `pkg-config fuse3 --cflags --libs` -o notify_inval_entry

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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>
#define MAX_STR_LEN 128
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;
int inc_epoch;
};
static struct options options = {
.timeout = 5,
.no_notify = 0,
.update_interval = 1,
.only_expire = 0,
.inc_epoch = 0,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
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),
OPTION("--inc-epoch", inc_epoch),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == file_ino) {
stbuf->st_mode = S_IFREG | 0000;
stbuf->st_nlink = 1;
stbuf->st_size = 0;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, file_name) == 0) {
e.ino = file_ino;
lookup_cnt++;
} else
goto err_out;
e.attr_timeout = options.timeout;
e.entry_timeout = options.timeout;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == file_ino)
lookup_cnt -= nlookup;
else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, options.timeout);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, file_name, file_ino);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.forget = tfs_forget,
};
static void update_fs(void) {
time_t t;
struct tm *now;
ssize_t ret;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
ret = strftime(file_name, MAX_STR_LEN,
"Time_is_%Hh_%Mm_%Ss", now);
assert(ret != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
char *old_name;
int ret = 0;
while(!fuse_session_exited(se)) {
old_name = strdup(file_name);
update_fs();
if (!options.no_notify && lookup_cnt) {
if(options.only_expire) { // 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");
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 if (options.inc_epoch) { // increment epoch
if (ret == -ENOSYS) {
printf("fuse_lowlevel_notify_increment_epoch not supported by kernel\n");
break;
}
assert(ret == 0);
} else { // invalidate entry
(se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
}
}
free(old_name);
sleep(options.update_interval);
}
if (ret == -ENOSYS) {
printf("Exiting...\n");
// Make sure to exit now, rather than on next request from userspace
pthread_kill(main_thread, SIGPIPE);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --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"
" --inc-epoch Increment epoch, invalidating all dentries\n"
"\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;
struct fuse_loop_config *config;
pthread_t updater;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if (options.only_expire && options.inc_epoch) {
printf("'only-expire' and 'inc-epoch' options are exclusive\n");
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), &se);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
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) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread) {
ret = fuse_session_loop(se);
} else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_exited(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se)
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_inval_entry.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2notify__inval__inode_8c.html0000644000175000017500000015037115156613431024651 0ustar berndbernd libfuse: fuse-3.18.2/example/notify_inval_inode.c File Reference
libfuse
notify_inval_inode.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdatomic.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with a single file whose contents change dynamically: it always contains the current time.

While notify_store_retrieve.c uses fuse_lowlevel_notify_store() to actively push the updated data into the kernel cache, this example uses fuse_lowlevel_notify_inval_inode() to notify the kernel that the cache has to be invalidated - but the kernel still has to explicitly request the updated data on the next read.

To see the effect, first start the file system with the --no-notify option:

$ notify_inval_inode –update-interval=1 –no-notify mnt/

Observe that the output never changes, even though the file system updates it once per second. This is because the contents are cached in the kernel:

$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18

If you instead enable the notification functions, the changes become visible:

 $ notify_inval_inode --update-interval=1 mnt/
 $ for i in 1 2 3 4 5; do
 >     cat mnt/current_time
 >     sleep 1
 > done
 The current time is 15:58:40
 The current time is 15:58:41
 The current time is 15:58:42
 The current time is 15:58:43
 The current time is 15:58:44

Compilation

gcc -Wall notify_inval_inode.c `pkg-config fuse3 --cflags --libs` -o notify_inval_inode

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdatomic.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define FILE_INO 2
#define FILE_NAME "current_time"
static char file_contents[MAX_STR_LEN];
static int lookup_cnt = 0;
static size_t file_size;
static _Atomic bool is_stop = false;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == FILE_INO) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = file_size;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_destroy(void *userarg)
{
(void)userarg;
is_stop = true;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, FILE_NAME) == 0) {
e.ino = FILE_INO;
lookup_cnt++;
} else
goto err_out;
e.attr_timeout = NO_TIMEOUT;
e.entry_timeout = NO_TIMEOUT;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == FILE_INO)
lookup_cnt -= nlookup;
else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, FILE_NAME, FILE_INO);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static void tfs_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
if (ino == FUSE_ROOT_ID)
fuse_reply_err(req, EISDIR);
else if ((fi->flags & O_ACCMODE) != O_RDONLY)
fuse_reply_err(req, EACCES);
else if (ino == FILE_INO)
fuse_reply_open(req, fi);
else {
// This should not happen
fprintf(stderr, "Got open for non-existing inode!\n");
fuse_reply_err(req, ENOENT);
}
}
static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
assert(ino == FILE_INO);
reply_buf_limited(req, file_contents, file_size, off, size);
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.destroy = tfs_destroy,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.open = tfs_open,
.read = tfs_read,
.forget = tfs_forget,
};
static void update_fs(void) {
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
file_size = strftime(file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(file_size != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
while(!is_stop) {
update_fs();
if (!options.no_notify && lookup_cnt) {
/* Only send notification if the kernel is aware of the inode */
/* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
* might come up during umount, when kernel side already releases
* all inodes, but does not send FUSE_DESTROY yet.
*/
int ret =
fuse_lowlevel_notify_inval_inode(se, FILE_INO, 0, 0);
if ((ret != 0 && !is_stop) &&
ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
fprintf(stderr,
"ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
strerror(-ret), -ret);
abort();
}
}
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\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;
struct fuse_loop_config *config;
pthread_t updater;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0) {
ret = 1;
goto err_out1;
}
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Start thread to update file contents */
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_inval_inode.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2notify__store__retrieve_8c.html0000644000175000017500000017024015156613431025420 0ustar berndbernd libfuse: fuse-3.18.2/example/notify_store_retrieve.c File Reference
libfuse
notify_store_retrieve.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>

Go to the source code of this file.

Detailed Description

This example implements a file system with a single file whose contents change dynamically: it always contains the current time.

While notify_inval_inode.c uses fuse_lowlevel_notify_inval_inode() to let the kernel know that it has to invalidate the cache, this example actively pushes the updated data into the kernel cache using fuse_lowlevel_notify_store().

To see the effect, first start the file system with the --no-notify option:

$ notify_store_retrieve --update-interval=1 --no-notify mnt/

Observe that the output never changes, even though the file system updates it once per second. This is because the contents are cached in the kernel:

$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18
The current time is 15:58:18

If you instead enable the notification functions, the changes become visible:

$ notify_store_retrieve --update-interval=1 mnt/
$ for i in 1 2 3 4 5; do
>     cat mnt/current_time
>     sleep 1
> done
The current time is 15:58:40
The current time is 15:58:41
The current time is 15:58:42
The current time is 15:58:43
The current time is 15:58:44

Compilation

gcc -Wall notify_store_retrieve.c `pkg-config fuse3 --cflags --libs` -o notify_store_retrieve

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
/* We can't actually tell the kernel that there is no
timeout, so we just send a big value */
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define FILE_INO 2
#define FILE_NAME "current_time"
static char file_contents[MAX_STR_LEN];
static int lookup_cnt = 0;
static int open_cnt = 0;
static size_t file_size;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
/* Keep track if we ever stored data (==1), and
received it back correctly (==2) */
static int retrieve_status = 0;
static bool is_umount = false;
/* updater thread tid */
static pthread_t updater;
/* Command line parsing */
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
stbuf->st_ino = ino;
if (ino == FUSE_ROOT_ID) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == FILE_INO) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = file_size;
}
else
return -1;
return 0;
}
static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
(void)userdata;
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
if (parent != FUSE_ROOT_ID)
goto err_out;
else if (strcmp(name, FILE_NAME) == 0) {
e.ino = FILE_INO;
} else
goto err_out;
e.attr_timeout = NO_TIMEOUT;
e.entry_timeout = NO_TIMEOUT;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
fuse_reply_entry(req, &e);
/*
* must only be set when the kernel knows about the entry,
* otherwise update_fs_loop() might see a positive count, but kernel
* would not have the entry yet
*/
if (e.ino == FILE_INO) {
pthread_mutex_lock(&lock);
lookup_cnt++;
pthread_mutex_unlock(&lock);
}
return;
err_out:
fuse_reply_err(req, ENOENT);
}
static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
uint64_t nlookup) {
(void) req;
if(ino == FILE_INO) {
pthread_mutex_lock(&lock);
lookup_cnt -= nlookup;
pthread_mutex_unlock(&lock);
} else
assert(ino == FUSE_ROOT_ID);
}
static void tfs_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 (tfs_stat(ino, &stbuf) != 0)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
}
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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
if (ino != FUSE_ROOT_ID)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, FILE_NAME, FILE_INO);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static void tfs_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
/* Make cache persistent even if file is closed,
this makes it easier to see the effects */
fi->keep_cache = 1;
if (ino == FUSE_ROOT_ID)
fuse_reply_err(req, EISDIR);
else if ((fi->flags & O_ACCMODE) != O_RDONLY)
fuse_reply_err(req, EACCES);
else if (ino == FILE_INO) {
fuse_reply_open(req, fi);
pthread_mutex_lock(&lock);
open_cnt++;
pthread_mutex_unlock(&lock);
} else {
// This should not happen
fprintf(stderr, "Got open for non-existing inode!\n");
fuse_reply_err(req, ENOENT);
}
}
static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi) {
(void) fi;
assert(ino == FILE_INO);
reply_buf_limited(req, file_contents, file_size, off, size);
}
static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
off_t offset, struct fuse_bufvec *data) {
struct fuse_bufvec bufv;
char buf[MAX_STR_LEN];
char *expected;
ssize_t ret;
assert(ino == FILE_INO);
assert(offset == 0);
expected = (char*) cookie;
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = MAX_STR_LEN;
bufv.buf[0].mem = buf;
bufv.buf[0].flags = 0;
ret = fuse_buf_copy(&bufv, data, 0);
assert(ret > 0);
assert(strncmp(buf, expected, ret) == 0);
free(expected);
retrieve_status = 2;
}
static void tfs_destroy(void *userdata)
{
(void)userdata;
is_umount = true;
pthread_join(updater, NULL);
}
static const struct fuse_lowlevel_ops tfs_oper = {
.init = tfs_init,
.lookup = tfs_lookup,
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.open = tfs_open,
.read = tfs_read,
.forget = tfs_forget,
.retrieve_reply = tfs_retrieve_reply,
.destroy = tfs_destroy,
};
static void update_fs(void) {
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
file_size = strftime(file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(file_size != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
struct fuse_bufvec bufv;
int ret;
while(!is_umount) {
update_fs();
pthread_mutex_lock(&lock);
if (!options.no_notify && open_cnt && lookup_cnt) {
/* Only send notification if the kernel
is aware of the inode */
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = file_size;
bufv.buf[0].mem = file_contents;
bufv.buf[0].flags = 0;
/*
* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they
* might come up during umount, when kernel side already releases
* all inodes, but does not send FUSE_DESTROY yet.
*/
ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
if ((ret != 0 && !is_umount) &&
ret != -ENOENT && ret != -EBADF && ret != -ENODEV) {
fprintf(stderr,
"ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
strerror(-ret), -ret);
abort();
}
/* To make sure that everything worked correctly, ask the
kernel to send us back the stored data */
ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
0, (void*) strdup(file_contents));
assert((ret == 0 || is_umount) || ret == -ENOENT || ret == -EBADF ||
ret != -ENODEV);
if(retrieve_status == 0)
retrieve_status = 1;
}
pthread_mutex_unlock(&lock);
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\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;
struct fuse_loop_config *config;
int ret = -1;
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
/* Initial contents */
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Start thread to update file contents */
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
assert(retrieve_status != 1);
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
Definition helper.c:253
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
#define FUSE_ROOT_ID
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
struct fuse_buf buf[1]
uint32_t no_interrupt
fuse_ino_t ino
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file notify_store_retrieve.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2null_8c.html0000644000175000017500000022054115156613431021443 0ustar berndbernd libfuse: fuse-3.18.2/example/null.c File Reference
libfuse
null.c File Reference
#include <fuse.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>

Go to the source code of this file.

Detailed Description

This "filesystem" provides only a single file. The mountpoint needs to be a file rather than a directory. All writes to the file will be discarded, and reading the file always returns \0.

Compile with:

gcc -Wall null.c `pkg-config fuse3 --cflags --libs` -o null

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#include <fuse.h>
#ifdef HAVE_LIBULOCKMGR
#include <ulockmgr.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <sys/file.h> /* flock(2) */
#include "passthrough_helpers.h"
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = 1;
cfg->nullpath_ok = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
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_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
struct xmp_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static int xmp_opendir(const char *path, struct fuse_file_info *fi)
{
int res;
struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
if (d == NULL)
return -ENOMEM;
d->dp = opendir(path);
if (d->dp == NULL) {
res = -errno;
free(d);
return res;
}
d->offset = 0;
d->entry = NULL;
fi->fh = (unsigned long) d;
return 0;
}
static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
{
return (struct xmp_dirp *) (uintptr_t) fi->fh;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
if (offset != d->offset) {
#ifndef __FreeBSD__
seekdir(d->dp, offset);
#else
/* Subtract the one that we add when calling
telldir() below */
seekdir(d->dp, offset-1);
#endif
d->entry = NULL;
d->offset = offset;
}
while (1) {
struct stat st;
off_t nextoff;
if (!d->entry) {
d->entry = readdir(d->dp);
if (!d->entry)
break;
}
#ifdef HAVE_FSTATAT
if (flags & FUSE_READDIR_PLUS) {
int res;
res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
AT_SYMLINK_NOFOLLOW);
if (res != -1)
fill_flags |= FUSE_FILL_DIR_PLUS;
}
#endif
if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
memset(&st, 0, sizeof(st));
st.st_ino = d->entry->d_ino;
st.st_mode = d->entry->d_type << 12;
}
nextoff = telldir(d->dp);
#ifdef __FreeBSD__
/* Under FreeBSD, telldir() may return 0 the first time
it is called. But for libfuse, an offset of zero
means that offsets are not supported, so we shift
everything by one. */
nextoff++;
#endif
if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
break;
d->entry = NULL;
d->offset = nextoff;
}
return 0;
}
static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
closedir(d->dp);
free(d);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
if (S_ISFIFO(mode))
res = mkfifo(path, mode);
else
res = mknod(path, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to, unsigned int flags)
{
int res;
/* When we have renameat2() in libc, then we can implement flags */
if (flags)
return -EINVAL;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = fchmod(fi->fh, mode);
else
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
int res;
if (fi)
res = fchown(fi->fh, uid, gid);
else
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
int res;
/* don't use utime/utimes since they follow symlinks */
if (fi)
res = futimens(fi->fh, ts);
else
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
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_open(const char *path, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags);
if (fd == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = fd;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void) path;
res = pread(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
size_t size, off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec *src;
(void) path;
src = malloc(sizeof(struct fuse_bufvec));
if (src == NULL)
return -ENOMEM;
*src = FUSE_BUFVEC_INIT(size);
src->buf[0].fd = fi->fh;
src->buf[0].pos = offset;
*bufp = src;
return 0;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res;
(void) path;
res = pwrite(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
(void) path;
dst.buf[0].fd = fi->fh;
dst.buf[0].pos = offset;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_flush(const char *path, struct fuse_file_info *fi)
{
int res;
(void) path;
/* This is called from every close on an open file, so call the
close on the underlying filesystem. But since flush may be
called multiple times for an open file, this must not really
close the file. This is important if used on a network
filesystem like NFS which flush the data/metadata on close() */
res = close(dup(fi->fh));
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
int res;
(void) path;
#ifndef HAVE_FDATASYNC
(void) isdatasync;
#else
if (isdatasync)
res = fdatasync(fi->fh);
else
#endif
res = fsync(fi->fh);
if (res == -1)
return -errno;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
(void) path;
return do_fallocate(fi->fh, mode, offset, length);
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_LIBULOCKMGR
static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
(void) path;
return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
sizeof(fi->lock_owner));
}
#endif
static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
{
int res;
(void) path;
res = flock(fi->fh, op);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t off_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t off_out, size_t len, int flags)
{
ssize_t res;
(void) path_in;
(void) path_out;
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res == -1)
return -errno;
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
off_t res;
(void) path;
res = lseek(fi->fh, off, whence);
if (res == -1)
return -errno;
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.opendir = xmp_opendir,
.readdir = xmp_readdir,
.releasedir = xmp_releasedir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.create = xmp_create,
.open = xmp_open,
.read = xmp_read,
.read_buf = xmp_read_buf,
.write = xmp_write,
.write_buf = xmp_write_buf,
.statfs = xmp_statfs,
.flush = xmp_flush,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_LIBULOCKMGR
.lock = xmp_lock,
#endif
.flock = xmp_flock,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file null.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2passthrough_8c.html0000644000175000017500000015527315156613431023051 0ustar berndbernd libfuse: fuse-3.18.2/example/passthrough.c File Reference
libfuse
passthrough.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. Its performance is terrible.

Compile with

gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#ifdef linux
/* For pread()/pwrite()/utimensat() */
#define _XOPEN_SOURCE 700
#endif
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include "passthrough_helpers.h"
static int fill_dir_plus = 0;
static int readdir_zero_ino;
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = !readdir_zero_ino;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
if (!cfg->auto_cache) {
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)
{
(void) fi;
int res;
res = lstat(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
DIR *dp;
struct dirent *de;
(void) offset;
(void) fi;
(void) flags;
dp = opendir(path);
if (dp == NULL)
return -errno;
while ((de = readdir(dp)) != NULL) {
struct stat st;
if (fill_dir_plus) {
fstatat(dirfd(dp), de->d_name, &st,
AT_SYMLINK_NOFOLLOW);
} else {
memset(&st, 0, sizeof(st));
st.st_ino = de->d_ino;
st.st_mode = de->d_type << 12;
}
if (readdir_zero_ino)
st.st_ino = 0;
if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
break;
}
closedir(dp);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
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;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
(void) fi;
int res;
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
(void) fi;
int res;
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if (fi != NULL)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
(void) fi;
int res;
/* don't use utime/utimes since they follow symlinks */
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
static int xmp_create(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
res = open(path, fi->flags, mode);
if (res == -1)
return -errno;
fi->fh = res;
return 0;
}
static int xmp_open(const char *path, struct fuse_file_info *fi)
{
int res;
res = open(path, fi->flags);
if (res == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = res;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int fd;
int res;
if(fi == NULL)
fd = open(path, O_RDONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = pread(fd, buf, size, offset);
if (res == -1)
res = -errno;
if(fi == NULL)
close(fd);
return res;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
if(fi == NULL)
fd = open(path, O_WRONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = pwrite(fd, buf, size, offset);
if (res == -1)
res = -errno;
if(fi == NULL)
close(fd);
return res;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
/* Just a stub. This method is optional and can safely be left
unimplemented */
(void) path;
(void) isdatasync;
(void) fi;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
int fd;
int res;
(void) fi;
if(fi == NULL)
fd = open(path, O_WRONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = do_fallocate(fd, mode, offset, length);
if(fi == NULL)
close(fd);
return res;
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t offset_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t offset_out, size_t len, int flags)
{
int fd_in, fd_out;
ssize_t res;
if(fi_in == NULL)
fd_in = open(path_in, O_RDONLY);
else
fd_in = fi_in->fh;
if (fd_in == -1)
return -errno;
if(fi_out == NULL)
fd_out = open(path_out, O_WRONLY);
else
fd_out = fi_out->fh;
if (fd_out == -1) {
close(fd_in);
return -errno;
}
res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
flags);
if (res == -1)
res = -errno;
if (fi_out == NULL)
close(fd_out);
if (fi_in == NULL)
close(fd_in);
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
int fd;
off_t res;
if (fi == NULL)
fd = open(path, O_RDONLY);
else
fd = fi->fh;
if (fd == -1)
return -errno;
res = lseek(fd, off, whence);
if (res == -1)
res = -errno;
if (fi == NULL)
close(fd);
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.readdir = xmp_readdir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.open = xmp_open,
.create = xmp_create,
.read = xmp_read,
.write = xmp_write,
.statfs = xmp_statfs,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
enum { MAX_ARGS = 10 };
int i,new_argc;
char *new_argv[MAX_ARGS];
umask(0);
/* Process the "--plus" option apart */
for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
if (!strcmp(argv[i], "--plus")) {
fill_dir_plus = FUSE_FILL_DIR_PLUS;
} else if (!strcmp(argv[i], "--readdir-zero-inodes")) {
// Return zero inodes from readdir
readdir_zero_ino = 1;
} else {
new_argv[new_argc++] = argv[i];
}
}
return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_readdir_flags
Definition fuse.h:42
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t auto_cache
Definition fuse.h:253
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file passthrough.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2passthrough__fh_8c.html0000644000175000017500000022141315156613431023653 0ustar berndbernd libfuse: fuse-3.18.2/example/passthrough_fh.c File Reference
libfuse
passthrough_fh.c File Reference
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/file.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. This implementation is a little more sophisticated than the one in passthrough.c, so performance is not quite as bad.

Compile with:

gcc -Wall passthrough_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o passthrough_fh

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#define _GNU_SOURCE
#include <fuse.h>
#ifdef HAVE_LIBULOCKMGR
#include <ulockmgr.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <sys/file.h> /* flock(2) */
#include "passthrough_helpers.h"
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = 1;
cfg->nullpath_ok = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need either set cfg->direct_io
in current function (recommended in high level API) or set fi->direct_io
in xmp_create() or xmp_open(). */
// cfg->direct_io = 1;
/* Pick up changes from lower filesystem right away. This is
also necessary for better hardlink support. When the kernel
calls the unlink() handler, it does not know the inode of
the to-be-removed entry and can therefore not invalidate
the cache of the associated inode - resulting in an
incorrect st_nlink value being reported for any remaining
hardlinks to this inode. */
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_access(const char *path, int mask)
{
int res;
res = access(path, mask);
if (res == -1)
return -errno;
return 0;
}
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1)
return -errno;
buf[res] = '\0';
return 0;
}
struct xmp_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static int xmp_opendir(const char *path, struct fuse_file_info *fi)
{
int res;
struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
if (d == NULL)
return -ENOMEM;
d->dp = opendir(path);
if (d->dp == NULL) {
res = -errno;
free(d);
return res;
}
d->offset = 0;
d->entry = NULL;
fi->fh = (unsigned long) d;
return 0;
}
static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
{
return (struct xmp_dirp *) (uintptr_t) fi->fh;
}
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
if (offset != d->offset) {
#ifndef __FreeBSD__
seekdir(d->dp, offset);
#else
/* Subtract the one that we add when calling
telldir() below */
seekdir(d->dp, offset-1);
#endif
d->entry = NULL;
d->offset = offset;
}
while (1) {
struct stat st;
off_t nextoff;
if (!d->entry) {
d->entry = readdir(d->dp);
if (!d->entry)
break;
}
#ifdef HAVE_FSTATAT
if (flags & FUSE_READDIR_PLUS) {
int res;
res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
AT_SYMLINK_NOFOLLOW);
if (res != -1)
fill_flags |= FUSE_FILL_DIR_PLUS;
}
#endif
if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
memset(&st, 0, sizeof(st));
st.st_ino = d->entry->d_ino;
st.st_mode = d->entry->d_type << 12;
}
nextoff = telldir(d->dp);
#ifdef __FreeBSD__
/* Under FreeBSD, telldir() may return 0 the first time
it is called. But for libfuse, an offset of zero
means that offsets are not supported, so we shift
everything by one. */
nextoff++;
#endif
if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
break;
d->entry = NULL;
d->offset = nextoff;
}
return 0;
}
static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
{
struct xmp_dirp *d = get_dirp(fi);
(void) path;
closedir(d->dp);
free(d);
return 0;
}
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
if (S_ISFIFO(mode))
res = mkfifo(path, mode);
else
res = mknod(path, mode, rdev);
if (res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
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_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to, unsigned int flags)
{
int res;
/* When we have renameat2() in libc, then we can implement flags */
if (flags)
return -EINVAL;
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = fchmod(fi->fh, mode);
else
res = chmod(path, mode);
if (res == -1)
return -errno;
return 0;
}
static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{
int res;
if (fi)
res = fchown(fi->fh, uid, gid);
else
res = lchown(path, uid, gid);
if (res == -1)
return -errno;
return 0;
}
static int xmp_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int res;
if(fi)
res = ftruncate(fi->fh, size);
else
res = truncate(path, size);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_UTIMENSAT
static int xmp_utimens(const char *path, const struct timespec ts[2],
struct fuse_file_info *fi)
{
int res;
/* don't use utime/utimes since they follow symlinks */
if (fi)
res = futimens(fi->fh, ts);
else
res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
if (res == -1)
return -errno;
return 0;
}
#endif
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_open(const char *path, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags);
if (fd == -1)
return -errno;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file). */
if (fi->flags & O_DIRECT) {
fi->direct_io = 1;
}
fi->fh = fd;
return 0;
}
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void) path;
res = pread(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
size_t size, off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec *src;
(void) path;
src = malloc(sizeof(struct fuse_bufvec));
if (src == NULL)
return -ENOMEM;
*src = FUSE_BUFVEC_INIT(size);
src->buf[0].fd = fi->fh;
src->buf[0].pos = offset;
*bufp = src;
return 0;
}
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res;
(void) path;
res = pwrite(fi->fh, buf, size, offset);
if (res == -1)
res = -errno;
return res;
}
static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
(void) path;
dst.buf[0].fd = fi->fh;
dst.buf[0].pos = offset;
}
static int xmp_statfs(const char *path, struct statvfs *stbuf)
{
int res;
res = statvfs(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_flush(const char *path, struct fuse_file_info *fi)
{
int res;
(void) path;
/* This is called from every close on an open file, so call the
close on the underlying filesystem. But since flush may be
called multiple times for an open file, this must not really
close the file. This is important if used on a network
filesystem like NFS which flush the data/metadata on close() */
res = close(dup(fi->fh));
if (res == -1)
return -errno;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
close(fi->fh);
return 0;
}
static int xmp_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
int res;
(void) path;
#ifndef HAVE_FDATASYNC
(void) isdatasync;
#else
if (isdatasync)
res = fdatasync(fi->fh);
else
#endif
res = fsync(fi->fh);
if (res == -1)
return -errno;
return 0;
}
static int xmp_fallocate(const char *path, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
(void) path;
return do_fallocate(fi->fh, mode, offset, length);
}
#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int xmp_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags)
{
int res = lsetxattr(path, name, value, size, flags);
if (res == -1)
return -errno;
return 0;
}
static int xmp_getxattr(const char *path, const char *name, char *value,
size_t size)
{
int res = lgetxattr(path, name, value, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_listxattr(const char *path, char *list, size_t size)
{
int res = llistxattr(path, list, size);
if (res == -1)
return -errno;
return res;
}
static int xmp_removexattr(const char *path, const char *name)
{
int res = lremovexattr(path, name);
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_SETXATTR */
#ifdef HAVE_LIBULOCKMGR
static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
(void) path;
return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
sizeof(fi->lock_owner));
}
#endif
static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
{
int res;
(void) path;
res = flock(fi->fh, op);
if (res == -1)
return -errno;
return 0;
}
#ifdef HAVE_COPY_FILE_RANGE
static ssize_t xmp_copy_file_range(const char *path_in,
struct fuse_file_info *fi_in,
off_t off_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t off_out, size_t len, int flags)
{
ssize_t res;
(void) path_in;
(void) path_out;
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res == -1)
return -errno;
return res;
}
#endif
static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
{
off_t res;
(void) path;
res = lseek(fi->fh, off, whence);
if (res == -1)
return -errno;
return res;
}
#ifdef HAVE_STATX
static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
struct fuse_file_info *fi)
{
int fd = -1;
int res;
if (fi)
fd = fi->fh;
res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
if (res == -1)
return -errno;
return 0;
}
#endif
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.access = xmp_access,
.readlink = xmp_readlink,
.opendir = xmp_opendir,
.readdir = xmp_readdir,
.releasedir = xmp_releasedir,
.mknod = xmp_mknod,
.mkdir = xmp_mkdir,
.symlink = xmp_symlink,
.unlink = xmp_unlink,
.rmdir = xmp_rmdir,
.rename = xmp_rename,
.link = xmp_link,
.chmod = xmp_chmod,
.chown = xmp_chown,
.truncate = xmp_truncate,
#ifdef HAVE_UTIMENSAT
.utimens = xmp_utimens,
#endif
.create = xmp_create,
.open = xmp_open,
.read = xmp_read,
.read_buf = xmp_read_buf,
.write = xmp_write,
.write_buf = xmp_write_buf,
.statfs = xmp_statfs,
.flush = xmp_flush,
.release = xmp_release,
.fsync = xmp_fsync,
.fallocate = xmp_fallocate,
#ifdef HAVE_SETXATTR
.setxattr = xmp_setxattr,
.getxattr = xmp_getxattr,
.listxattr = xmp_listxattr,
.removexattr = xmp_removexattr,
#endif
#ifdef HAVE_LIBULOCKMGR
.lock = xmp_lock,
#endif
.flock = xmp_flock,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
.lseek = xmp_lseek,
#ifdef HAVE_STATX
.statx = xmp_statx,
#endif
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
fuse_fill_dir_flags
Definition fuse.h:58
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
@ FUSE_BUF_SPLICE_NONBLOCK
enum fuse_buf_flags flags
off_t pos
struct fuse_buf buf[1]
int32_t nullpath_ok
Definition fuse.h:273
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
uint64_t lock_owner
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition fuse.h:641

Definition in file passthrough_fh.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2passthrough__ll_8c.html0000644000175000017500000052633215156613431023675 0ustar berndbernd libfuse: fuse-3.18.2/example/passthrough_ll.c File Reference
libfuse
passthrough_ll.c File Reference
#include <fuse_lowlevel.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/file.h>
#include <sys/xattr.h>
#include "passthrough_helpers.h"

Go to the source code of this file.

Detailed Description

This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. In contrast to passthrough.c and passthrough_fh.c, this implementation uses the low-level API. Its performance should be the least bad among the three.

When writeback caching is enabled (-o writeback mount option), it is only possible to write to files for which the mounting user has read permissions. This is because the writeback cache requires the kernel to be able to issue read requests for all files (which the passthrough filesystem cannot satisfy if it can't read the file in the underlying filesystem).

Compile with:

gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define _GNU_SOURCE
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18)
#include <fuse_lowlevel.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/file.h>
#include <sys/xattr.h>
#include "passthrough_helpers.h"
/* We are re-using pointers to our `struct lo_inode` and `struct
lo_dirp` elements as inodes. This means that we must be able to
store uintptr_t values in a fuse_ino_t variable. The following
incantation checks this condition at compile time. */
#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
"fuse_ino_t too small to hold uintptr_t values!");
#else
struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
{ unsigned _uintptr_to_must_hold_fuse_ino_t:
((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
#endif
struct lo_inode {
struct lo_inode *next; /* protected by lo->mutex */
struct lo_inode *prev; /* protected by lo->mutex */
int fd;
ino_t ino;
dev_t dev;
uint64_t refcount; /* protected by lo->mutex */
};
enum {
CACHE_NEVER,
CACHE_NORMAL,
CACHE_ALWAYS,
};
struct lo_data {
pthread_mutex_t mutex;
int debug;
int writeback;
int flock;
int xattr;
char *source;
double timeout;
int cache;
int timeout_set;
struct lo_inode root; /* protected by lo->mutex */
};
static const struct fuse_opt lo_opts[] = {
{ "writeback",
offsetof(struct lo_data, writeback), 1 },
{ "no_writeback",
offsetof(struct lo_data, writeback), 0 },
{ "source=%s",
offsetof(struct lo_data, source), 0 },
{ "flock",
offsetof(struct lo_data, flock), 1 },
{ "no_flock",
offsetof(struct lo_data, flock), 0 },
{ "xattr",
offsetof(struct lo_data, xattr), 1 },
{ "no_xattr",
offsetof(struct lo_data, xattr), 0 },
{ "timeout=%lf",
offsetof(struct lo_data, timeout), 0 },
{ "timeout=",
offsetof(struct lo_data, timeout_set), 1 },
{ "cache=never",
offsetof(struct lo_data, cache), CACHE_NEVER },
{ "cache=auto",
offsetof(struct lo_data, cache), CACHE_NORMAL },
{ "cache=always",
offsetof(struct lo_data, cache), CACHE_ALWAYS },
};
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);
}
static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
{
if (ino == FUSE_ROOT_ID)
return &lo_data(req)->root;
else
return (struct lo_inode *) (uintptr_t) ino;
}
static int lo_fd(fuse_req_t req, fuse_ino_t ino)
{
return lo_inode(req, ino)->fd;
}
static bool lo_debug(fuse_req_t req)
{
return lo_data(req)->debug != 0;
}
static void lo_init(void *userdata,
struct fuse_conn_info *conn)
{
struct lo_data *lo = (struct lo_data *)userdata;
bool has_flag;
if (lo->writeback) {
if (lo->debug && has_flag)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating writeback\n");
}
if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
if (lo->debug && has_flag)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating flock locks\n");
}
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
conn->no_interrupt = 1;
}
static void lo_destroy(void *userdata)
{
struct lo_data *lo = (struct lo_data*) userdata;
while (lo->root.next != &lo->root) {
struct lo_inode* next = lo->root.next;
lo->root.next = next->next;
close(next->fd);
free(next);
}
}
static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
int res;
struct stat buf;
struct lo_data *lo = lo_data(req);
int fd = fi ? fi->fh : lo_fd(req, ino);
(void) fi;
res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
return (void) fuse_reply_err(req, errno);
fuse_reply_attr(req, &buf, lo->timeout);
}
static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
int valid, struct fuse_file_info *fi)
{
int saverr;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
int ifd = inode->fd;
int res;
if (valid & FUSE_SET_ATTR_MODE) {
if (fi) {
res = fchmod(fi->fh, attr->st_mode);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = chmod(procname, attr->st_mode);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
attr->st_uid : (uid_t) -1;
gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
attr->st_gid : (gid_t) -1;
res = fchownat(ifd, "", uid, gid,
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
}
if (valid & FUSE_SET_ATTR_SIZE) {
if (fi) {
res = ftruncate(fi->fh, attr->st_size);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = truncate(procname, attr->st_size);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
struct timespec tv[2];
tv[0].tv_sec = 0;
tv[1].tv_sec = 0;
tv[0].tv_nsec = UTIME_OMIT;
tv[1].tv_nsec = UTIME_OMIT;
if (valid & FUSE_SET_ATTR_ATIME_NOW)
tv[0].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_ATIME)
tv[0] = attr->st_atim;
if (valid & FUSE_SET_ATTR_MTIME_NOW)
tv[1].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_MTIME)
tv[1] = attr->st_mtim;
if (fi)
res = futimens(fi->fh, tv);
else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = utimensat(AT_FDCWD, procname, tv, 0);
}
if (res == -1)
goto out_err;
}
return lo_getattr(req, ino, fi);
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
{
struct lo_inode *p;
struct lo_inode *ret = NULL;
pthread_mutex_lock(&lo->mutex);
for (p = lo->root.next; p != &lo->root; p = p->next) {
if (p->ino == st->st_ino && p->dev == st->st_dev) {
assert(p->refcount > 0);
ret = p;
ret->refcount++;
break;
}
}
pthread_mutex_unlock(&lo->mutex);
return ret;
}
static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo)
{
struct lo_inode *inode = NULL;
struct lo_inode *prev, *next;
inode = calloc(1, sizeof(struct lo_inode));
if (!inode)
return NULL;
inode->refcount = 1;
inode->fd = fd;
inode->ino = e->attr.st_ino;
inode->dev = e->attr.st_dev;
pthread_mutex_lock(&lo->mutex);
prev = &lo->root;
next = prev->next;
next->prev = inode;
inode->next = next;
inode->prev = prev;
prev->next = inode;
pthread_mutex_unlock(&lo->mutex);
return inode;
}
static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e)
{
int res;
struct lo_data *lo = lo_data(req);
memset(e, 0, sizeof(*e));
e->attr_timeout = lo->timeout;
e->entry_timeout = lo->timeout;
res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
return errno;
e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%d -> %lli\n",
(unsigned long long) parent, fd, (unsigned long long) e->ino);
return 0;
}
static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
struct fuse_entry_param *e)
{
int newfd;
int res;
int saverr;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode;
memset(e, 0, sizeof(*e));
e->attr_timeout = lo->timeout;
e->entry_timeout = lo->timeout;
newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
if (newfd == -1)
goto out_err;
res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
inode = lo_find(lo_data(req), &e->attr);
if (inode) {
close(newfd);
newfd = -1;
} else {
inode = create_new_inode(newfd, e, lo);
if (!inode)
goto out_err;
}
e->ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name, (unsigned long long) e->ino);
return 0;
out_err:
saverr = errno;
if (newfd != -1)
close(newfd);
return saverr;
}
static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
parent, name);
err = lo_do_lookup(req, parent, name, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_entry(req, &e);
}
static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev,
const char *link)
{
int res;
int saverr;
struct lo_inode *dir = lo_inode(req, parent);
struct fuse_entry_param e;
res = mknod_wrapper(dir->fd, name, link, mode, rdev);
saverr = errno;
if (res == -1)
goto out;
saverr = lo_do_lookup(req, parent, name, &e);
if (saverr)
goto out;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name, (unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out:
fuse_reply_err(req, saverr);
}
static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev)
{
lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
}
static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode)
{
lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
}
static void lo_symlink(fuse_req_t req, const char *link,
fuse_ino_t parent, const char *name)
{
lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
}
static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
const char *name)
{
int res;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
struct fuse_entry_param e;
char procname[64];
int saverr;
memset(&e, 0, sizeof(struct fuse_entry_param));
e.attr_timeout = lo->timeout;
e.entry_timeout = lo->timeout;
sprintf(procname, "/proc/self/fd/%i", inode->fd);
res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
AT_SYMLINK_FOLLOW);
if (res == -1)
goto out_err;
res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
pthread_mutex_lock(&lo->mutex);
inode->refcount++;
pthread_mutex_unlock(&lo->mutex);
e.ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
(unsigned long long) parent, name,
(unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char *newname,
unsigned int flags)
{
int res;
if (flags) {
fuse_reply_err(req, EINVAL);
return;
}
res = renameat(lo_fd(req, parent), name,
lo_fd(req, newparent), newname);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, 0);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
{
if (!inode)
return;
pthread_mutex_lock(&lo->mutex);
assert(inode->refcount >= n);
inode->refcount -= n;
if (!inode->refcount) {
struct lo_inode *prev, *next;
prev = inode->prev;
next = inode->next;
next->prev = prev;
prev->next = next;
pthread_mutex_unlock(&lo->mutex);
close(inode->fd);
free(inode);
} else {
pthread_mutex_unlock(&lo->mutex);
}
}
static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
(unsigned long long) ino,
(unsigned long long) inode->refcount,
(unsigned long long) nlookup);
}
unref_inode(lo, inode, nlookup);
}
static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{
lo_forget_one(req, ino, nlookup);
}
static void lo_forget_multi(fuse_req_t req, size_t count,
struct fuse_forget_data *forgets)
{
int i;
for (i = 0; i < count; i++)
lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
}
static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
{
char buf[PATH_MAX + 1];
int res;
res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
if (res == -1)
return (void) fuse_reply_err(req, errno);
if (res == sizeof(buf))
return (void) fuse_reply_err(req, ENAMETOOLONG);
buf[res] = '\0';
}
struct lo_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
{
return (struct lo_dirp *) (uintptr_t) fi->fh;
}
static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int error = ENOMEM;
struct lo_data *lo = lo_data(req);
struct lo_dirp *d;
int fd = -1;
d = calloc(1, sizeof(struct lo_dirp));
if (d == NULL)
goto out_err;
fd = openat(lo_fd(req, ino), ".", O_RDONLY);
if (fd == -1)
goto out_errno;
d->dp = fdopendir(fd);
if (d->dp == NULL)
goto out_errno;
d->offset = 0;
d->entry = NULL;
fi->fh = (uintptr_t) d;
if (lo->cache != CACHE_NEVER)
fi->cache_readdir = 1;
if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
fuse_reply_open(req, fi);
return;
out_errno:
error = errno;
out_err:
if (d) {
if (fd != -1)
close(fd);
free(d);
}
fuse_reply_err(req, error);
}
static int is_dot_or_dotdot(const char *name)
{
return name[0] == '.' && (name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0'));
}
static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi, int plus)
{
struct lo_dirp *d = lo_dirp(fi);
char *buf;
char *p;
size_t rem = size;
int err;
(void) ino;
buf = calloc(1, size);
if (!buf) {
err = ENOMEM;
goto error;
}
p = buf;
if (offset != d->offset) {
seekdir(d->dp, offset);
d->entry = NULL;
d->offset = offset;
}
while (1) {
size_t entsize;
off_t nextoff;
const char *name;
if (!d->entry) {
errno = 0;
d->entry = readdir(d->dp);
if (!d->entry) {
if (errno) { // Error
err = errno;
goto error;
} else { // End of stream
break;
}
}
}
nextoff = d->entry->d_off;
name = d->entry->d_name;
fuse_ino_t entry_ino = 0;
if (plus) {
struct fuse_entry_param e;
if (is_dot_or_dotdot(name)) {
e = (struct fuse_entry_param) {
.attr.st_ino = d->entry->d_ino,
.attr.st_mode = d->entry->d_type << 12,
};
} else {
err = lo_do_lookup(req, ino, name, &e);
if (err)
goto error;
entry_ino = e.ino;
}
entsize = fuse_add_direntry_plus(req, p, rem, name,
&e, nextoff);
} else {
struct stat st = {
.st_ino = d->entry->d_ino,
.st_mode = d->entry->d_type << 12,
};
entsize = fuse_add_direntry(req, p, rem, name,
&st, nextoff);
}
if (entsize > rem) {
if (entry_ino != 0)
lo_forget_one(req, entry_ino, 1);
break;
}
p += entsize;
rem -= entsize;
d->entry = NULL;
d->offset = nextoff;
}
err = 0;
error:
// If there's an error, we can only signal it if we haven't stored
// any entries yet - otherwise we'd end up with wrong lookup
// counts for the entries that are already in the buffer. So we
// return what we've collected until that point.
if (err && rem == size)
fuse_reply_err(req, err);
else
fuse_reply_buf(req, buf, size - rem);
free(buf);
}
static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
lo_do_readdir(req, ino, size, offset, fi, 0);
}
static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
lo_do_readdir(req, ino, size, offset, fi, 1);
}
static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
struct lo_dirp *d = lo_dirp(fi);
(void) ino;
closedir(d->dp);
free(d);
fuse_reply_err(req, 0);
}
static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent,
mode_t mode, struct fuse_file_info *fi)
{
int fd;
struct lo_data *lo = lo_data(req);
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n",
parent);
fd = openat(lo_fd(req, parent), ".",
(fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
err = fill_entry_param_new_inode(req, parent, fd, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_create(req, &e, fi);
}
static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, struct fuse_file_info *fi)
{
int fd;
struct lo_data *lo = lo_data(req);
struct fuse_entry_param e;
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
parent, name);
fd = openat(lo_fd(req, parent), name,
(fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
err = lo_do_lookup(req, parent, name, &e);
if (err)
fuse_reply_err(req, err);
else
fuse_reply_create(req, &e, fi);
}
static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
int fd = dirfd(lo_dirp(fi)->dp);
(void) ino;
if (datasync)
res = fdatasync(fd);
else
res = fsync(fd);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int fd;
char buf[64];
struct lo_data *lo = lo_data(req);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
ino, fi->flags);
/* With writeback cache, kernel may send read requests even
when userspace opened write-only */
if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
fi->flags &= ~O_ACCMODE;
fi->flags |= O_RDWR;
}
/* With writeback cache, O_APPEND is handled by the kernel.
This breaks atomicity (since the file may change in the
underlying filesystem, so that the kernel's idea of the
end of the file isn't accurate anymore). In this example,
we just accept that. A more rigorous filesystem may want
to return an error here */
if (lo->writeback && (fi->flags & O_APPEND))
fi->flags &= ~O_APPEND;
sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
fd = open(buf, fi->flags & ~O_NOFOLLOW);
if (fd == -1)
return (void) fuse_reply_err(req, errno);
fi->fh = fd;
if (lo->cache == CACHE_NEVER)
fi->direct_io = 1;
else if (lo->cache == CACHE_ALWAYS)
fi->keep_cache = 1;
/* Enable direct_io when open has flags O_DIRECT to enjoy the feature
parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
for writes to the same file in the kernel). */
if (fi->flags & O_DIRECT)
fi->direct_io = 1;
/* parallel_direct_writes feature depends on direct_io features.
To make parallel_direct_writes valid, need set fi->direct_io
in current function. */
fuse_reply_open(req, fi);
}
static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
(void) ino;
close(fi->fh);
fuse_reply_err(req, 0);
}
static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int res;
(void) ino;
res = close(dup(fi->fh));
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
(void) ino;
if (datasync)
res = fdatasync(fi->fh);
else
res = fsync(fi->fh);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
"off=%lu)\n", ino, size, (unsigned long) offset);
buf.buf[0].fd = fi->fh;
buf.buf[0].pos = offset;
}
static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
struct fuse_bufvec *in_buf, off_t off,
struct fuse_file_info *fi)
{
(void) ino;
ssize_t res;
struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
out_buf.buf[0].fd = fi->fh;
out_buf.buf[0].pos = off;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%jd)\n",
ino, out_buf.buf[0].size, (intmax_t) off);
res = fuse_buf_copy(&out_buf, in_buf, 0);
if(res < 0)
fuse_reply_err(req, -res);
else
fuse_reply_write(req, (size_t) res);
}
static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
{
int res;
struct statvfs stbuf;
res = fstatvfs(lo_fd(req, ino), &stbuf);
if (res == -1)
fuse_reply_err(req, errno);
else
fuse_reply_statfs(req, &stbuf);
}
static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
int err;
(void) ino;
err = -do_fallocate(fi->fh, mode, offset, length);
fuse_reply_err(req, err);
}
static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
int op)
{
int res;
(void) ino;
res = flock(fi->fh, op);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
ino, name, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = getxattr(procname, name, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
fuse_reply_buf(req, value, ret);
} else {
ret = getxattr(procname, name, NULL, 0);
if (ret == -1)
goto out_err;
fuse_reply_xattr(req, ret);
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
fuse_reply_err(req, saverr);
goto out_free;
}
static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
ino, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = listxattr(procname, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
fuse_reply_buf(req, value, ret);
} else {
ret = listxattr(procname, NULL, 0);
if (ret == -1)
goto out_err;
fuse_reply_xattr(req, ret);
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
fuse_reply_err(req, saverr);
goto out_free;
}
static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags)
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
ino, name, value, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = setxattr(procname, name, value, size, flags);
saverr = ret == -1 ? errno : 0;
out:
fuse_reply_err(req, saverr);
}
static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
ino, name);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = removexattr(procname, name);
saverr = ret == -1 ? errno : 0;
out:
fuse_reply_err(req, saverr);
}
#ifdef HAVE_COPY_FILE_RANGE
static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
struct fuse_file_info *fi_in,
fuse_ino_t ino_out, off_t off_out,
struct fuse_file_info *fi_out, size_t len,
int flags)
{
ssize_t res;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"%s(ino=%lld fd=%lld off=%jd ino=%lld fd=%lld off=%jd, size=%zd, flags=0x%x)\n",
__func__, (unsigned long long)ino_in,
(unsigned long long)fi_in->fh,
(intmax_t) off_in, (unsigned long long)ino_out,
(unsigned long long)fi_out->fh, (intmax_t) off_out,
len, flags);
res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
flags);
if (res < 0)
fuse_reply_err(req, errno);
else
fuse_reply_write(req, res);
}
#endif
static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
struct fuse_file_info *fi)
{
off_t res;
(void)ino;
res = lseek(fi->fh, off, whence);
if (res != -1)
fuse_reply_lseek(req, res);
else
fuse_reply_err(req, errno);
}
#ifdef HAVE_STATX
static void lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
struct fuse_file_info *fi)
{
struct lo_data *lo = lo_data(req);
struct statx buf;
int res;
int fd;
if (fi)
fd = fi->fh;
else
fd = lo_fd(req, ino);
res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, &buf);
if (res == -1)
fuse_reply_err(req, errno);
else
fuse_reply_statx(req, 0, &buf, lo->timeout);
}
#endif
static const struct fuse_lowlevel_ops lo_oper = {
.init = lo_init,
.destroy = lo_destroy,
.lookup = lo_lookup,
.mkdir = lo_mkdir,
.mknod = lo_mknod,
.symlink = lo_symlink,
.link = lo_link,
.unlink = lo_unlink,
.rmdir = lo_rmdir,
.rename = lo_rename,
.forget = lo_forget,
.forget_multi = lo_forget_multi,
.getattr = lo_getattr,
.setattr = lo_setattr,
.readlink = lo_readlink,
.opendir = lo_opendir,
.readdir = lo_readdir,
.readdirplus = lo_readdirplus,
.releasedir = lo_releasedir,
.fsyncdir = lo_fsyncdir,
.create = lo_create,
.tmpfile = lo_tmpfile,
.open = lo_open,
.release = lo_release,
.flush = lo_flush,
.fsync = lo_fsync,
.read = lo_read,
.write_buf = lo_write_buf,
.statfs = lo_statfs,
.fallocate = lo_fallocate,
.flock = lo_flock,
.getxattr = lo_getxattr,
.listxattr = lo_listxattr,
.setxattr = lo_setxattr,
.removexattr = lo_removexattr,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = lo_copy_file_range,
#endif
.lseek = lo_lseek,
#ifdef HAVE_STATX
.statx = lo_statx,
#endif
};
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct fuse_loop_config *config;
struct lo_data lo = { .debug = 0,
.writeback = 0 };
int ret = -1;
/* Don't mask creation mode, kernel already did that */
umask(0);
pthread_mutex_init(&lo.mutex, NULL);
lo.root.next = lo.root.prev = &lo.root;
lo.root.fd = -1;
lo.cache = CACHE_NORMAL;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
passthrough_ll_help();
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
return 1;
lo.debug = opts.debug;
lo.root.refcount = 2;
if (lo.source) {
struct stat stat;
int res;
res = lstat(lo.source, &stat);
if (res == -1) {
fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
lo.source);
exit(1);
}
if (!S_ISDIR(stat.st_mode)) {
fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
exit(1);
}
} else {
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) {
case CACHE_NEVER:
lo.timeout = 0.0;
break;
case CACHE_NORMAL:
lo.timeout = 1.0;
break;
case CACHE_ALWAYS:
lo.timeout = 86400.0;
break;
}
} else if (lo.timeout < 0) {
fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
lo.timeout);
exit(1);
}
lo.root.fd = open(lo.source, O_PATH);
if (lo.root.fd == -1) {
fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
lo.source);
exit(1);
}
se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, opts.mountpoint) != 0)
goto err_out3;
fuse_daemonize(opts.foreground);
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
ret = fuse_session_loop(se);
else {
config = fuse_loop_cfg_create();
fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
fuse_loop_cfg_set_max_threads(config, opts.max_threads);
ret = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
config = NULL;
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
if (lo.root.fd >= 0)
close(lo.root.fd);
free(lo.source);
return ret ? 1 : 0;
}
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition buffer.c:22
#define FUSE_CAP_WRITEBACK_CACHE
@ FUSE_BUF_FD_SEEK
@ FUSE_BUF_IS_FD
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition buffer.c:284
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
@ FUSE_BUF_SPLICE_MOVE
int fuse_daemonize(int foreground)
Definition helper.c:253
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
Definition helper.c:130
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
uint64_t fuse_ino_t
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition fuse_opt.c:398
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
#define FUSE_OPT_END
Definition fuse_opt.h:104
int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
char ** argv
Definition fuse_opt.h:114
enum fuse_buf_flags flags
off_t pos
size_t size
struct fuse_buf buf[1]
uint32_t no_interrupt
uint32_t capable
double entry_timeout
fuse_ino_t ino
double attr_timeout
struct stat attr
uint32_t cache_readdir
Definition fuse_common.h:89
uint32_t parallel_direct_writes
Definition fuse_common.h:97
uint32_t direct_io
Definition fuse_common.h:63
uint32_t keep_cache
Definition fuse_common.h:69
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file passthrough_ll.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2poll_8c.html0000644000175000017500000007777515156613431021462 0ustar berndbernd libfuse: fuse-3.18.2/example/poll.c File Reference
libfuse
poll.c File Reference
#include <fuse.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <poll.h>
#include <stdbool.h>

Go to the source code of this file.

Detailed Description

This example illustrates how to write a FUSE file system that supports polling for changes that don't come through the kernel. It can be tested with the poll_client.c program.

Compile with:

gcc -Wall poll.c `pkg-config fuse3 --cflags --libs` -o poll

Source code

/*
FUSE fsel: FUSE select example
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <poll.h>
#include <stdbool.h>
/*
* fsel_open_mask is used to limit the number of opens to 1 per file.
* This is to use file index (0-F) as fh as poll support requires
* unique fh per open file. Lifting this would require proper open
* file management.
*/
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 up to 10 chars */
#define FSEL_FILES 16
static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
static _Atomic bool fsel_stop = false;
static pthread_t fsel_producer_thread;
static int fsel_path_index(const char *path)
{
char ch = path[1];
if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
return -1;
return ch <= '9' ? ch - '0' : ch - 'A' + 10;
}
static void fsel_destroy(void *private_data)
{
(void)private_data;
fsel_stop = true;
pthread_join(fsel_producer_thread, NULL);
}
static int fsel_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
(void) fi;
int idx;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0555;
stbuf->st_nlink = 2;
return 0;
}
idx = fsel_path_index(path);
if (idx < 0)
return -ENOENT;
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = fsel_cnt[idx];
return 0;
}
static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{
char name[2] = { };
int i;
(void) offset;
(void) fi;
(void) flags;
if (strcmp(path, "/") != 0)
return -ENOENT;
for (i = 0; i < FSEL_FILES; i++) {
name[0] = fsel_hex_map[i];
filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
}
return 0;
}
static int fsel_open(const char *path, struct fuse_file_info *fi)
{
int idx = fsel_path_index(path);
if (idx < 0)
return -ENOENT;
if ((fi->flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
if (fsel_open_mask & (1 << idx))
return -EBUSY;
fsel_open_mask |= (1 << idx);
/*
* fsel files are nonseekable somewhat pipe-like files which
* gets filled up periodically by producer thread and consumed
* on read. Tell FUSE as such.
*/
fi->fh = idx;
fi->direct_io = 1;
fi->nonseekable = 1;
return 0;
}
static int fsel_release(const char *path, struct fuse_file_info *fi)
{
int idx = fi->fh;
(void) path;
fsel_open_mask &= ~(1 << idx);
return 0;
}
static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int idx = fi->fh;
(void) path;
(void) offset;
pthread_mutex_lock(&fsel_mutex);
if (fsel_cnt[idx] < size)
size = fsel_cnt[idx];
printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
fsel_cnt[idx] -= size;
pthread_mutex_unlock(&fsel_mutex);
memset(buf, fsel_hex_map[idx], size);
return size;
}
static int fsel_poll(const char *path, struct fuse_file_info *fi,
struct fuse_pollhandle *ph, unsigned *reventsp)
{
static unsigned polled_zero;
int idx = fi->fh;
(void) path;
/*
* Poll notification requires pointer to struct fuse which
* can't be obtained when using fuse_main(). As notification
* happens only after poll is called, fill it here from
* fuse_context.
*/
if (!fsel_fuse) {
struct fuse_context *cxt = fuse_get_context();
if (cxt)
fsel_fuse = cxt->fuse;
}
pthread_mutex_lock(&fsel_mutex);
if (ph != NULL) {
struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
if (oldph)
fsel_poll_notify_mask |= (1 << idx);
fsel_poll_handle[idx] = ph;
}
if (fsel_cnt[idx]) {
*reventsp |= POLLIN;
printf("POLL %X cnt=%u polled_zero=%u\n",
idx, fsel_cnt[idx], polled_zero);
polled_zero = 0;
} else
polled_zero++;
pthread_mutex_unlock(&fsel_mutex);
return 0;
}
static const struct fuse_operations fsel_oper = {
.destroy = fsel_destroy,
.getattr = fsel_getattr,
.readdir = fsel_readdir,
.open = fsel_open,
.release = fsel_release,
.read = fsel_read,
.poll = fsel_poll,
};
static void *fsel_producer(void *data)
{
const struct timespec interval = { 0, 250000000 };
unsigned idx = 0, nr = 1;
(void) data;
while (!fsel_stop) {
int i, t;
pthread_mutex_lock(&fsel_mutex);
/*
* This is the main producer loop which is executed
* ever 500ms. On each iteration, it fills one byte
* to 1, 2 or 4 files and sends poll notification if
* requested.
*/
for (i = 0, t = idx; i < nr;
i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
if (fsel_cnt[t] == FSEL_CNT_MAX)
continue;
fsel_cnt[t]++;
if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
struct fuse_pollhandle *ph;
printf("NOTIFY %X\n", t);
ph = fsel_poll_handle[t];
fuse_notify_poll(ph);
fsel_poll_notify_mask &= ~(1 << t);
fsel_poll_handle[t] = NULL;
}
}
idx = (idx + 1) % FSEL_FILES;
if (idx == 0)
nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
pthread_mutex_unlock(&fsel_mutex);
nanosleep(&interval, NULL);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_attr_t attr;
int ret;
errno = pthread_mutex_init(&fsel_mutex, NULL);
if (errno) {
perror("pthread_mutex_init");
return 1;
}
errno = pthread_attr_init(&attr);
if (errno) {
perror("pthread_attr_init");
return 1;
}
errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL);
if (errno) {
perror("pthread_create");
return 1;
}
ret = fuse_main(argc, argv, &fsel_oper, NULL);
return ret;
}
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
Definition fuse.h:42
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
struct fuse * fuse
Definition fuse.h:862
uint32_t nonseekable
Definition fuse_common.h:78
uint32_t direct_io
Definition fuse_common.h:63
void(* destroy)(void *private_data)
Definition fuse.h:649

Definition in file poll.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2poll__client_8c.html0000644000175000017500000002132515156613431023133 0ustar berndbernd libfuse: fuse-3.18.2/example/poll_client.c File Reference
libfuse
poll_client.c File Reference
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

Go to the source code of this file.

Detailed Description

This program tests the poll.c example file systsem.

Compile with:

 gcc -Wall poll_client.c -o poll_client

Source code

/*
FUSE fselclient: FUSE select example client
Copyright (C) 2008 SUSE Linux Products GmbH
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define FSEL_FILES 16
int main(void)
{
static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
int fds[FSEL_FILES];
int i, nfds, tries;
for (i = 0; i < FSEL_FILES; i++) {
char name[] = { hex_map[i], '\0' };
fds[i] = open(name, O_RDONLY);
if (fds[i] < 0) {
perror("open");
return 1;
}
}
nfds = fds[FSEL_FILES - 1] + 1;
for(tries=0; tries < 16; tries++) {
static char buf[4096];
fd_set rfds;
int rc;
FD_ZERO(&rfds);
for (i = 0; i < FSEL_FILES; i++)
FD_SET(fds[i], &rfds);
rc = select(nfds, &rfds, NULL, NULL, NULL);
if (rc < 0) {
perror("select");
return 1;
}
for (i = 0; i < FSEL_FILES; i++) {
if (!FD_ISSET(fds[i], &rfds)) {
printf("_: ");
continue;
}
printf("%X:", i);
rc = read(fds[i], buf, sizeof(buf));
if (rc < 0) {
perror("read");
return 1;
}
printf("%02d ", rc);
}
printf("\n");
}
return 0;
}

Definition in file poll_client.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2example_2printcap_8c.html0000644000175000017500000011473715156613431022322 0ustar berndbernd libfuse: fuse-3.18.2/example/printcap.c File Reference
libfuse
printcap.c File Reference
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

Go to the source code of this file.

Detailed Description

minimal example filesystem that prints out all capabilities supported by the kernel and then exits.

Compile with:

gcc -Wall printcap.c `pkg-config fuse3 --cflags --libs` -o printcap

Source code

/*
FUSE: Filesystem in Userspace
Copyright (C) 2017 Nikolaus Rath <Nikolaus@rath.org>
This program can be distributed under the terms of the GNU GPLv2.
See the file GPL2.txt.
*/
#define FUSE_USE_VERSION 31
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
struct fuse_session *se;
// Define a structure to hold capability information
struct cap_info {
uint64_t flag;
const char *name;
};
// Define an array of all capabilities
static const struct cap_info capabilities[] = {
{FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"},
{FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"},
{FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"},
{FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"},
{FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"},
{FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"},
{FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"},
{FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"},
{FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"},
{FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"},
{FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"},
{FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"},
{FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"},
{FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"},
{FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"},
{FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"},
{FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"},
{FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"},
{FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"},
{FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"},
{FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"},
{FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"},
{FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"},
{FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"},
{FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"},
{FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"},
{FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"},
{FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"},
// Add any new capabilities here
{0, NULL} // Sentinel to mark the end of the array
};
static void print_capabilities(struct fuse_conn_info *conn)
{
printf("Capabilities:\n");
for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) {
if (fuse_get_feature_flag(conn, cap->flag)) {
printf("\t%s\n", cap->name);
}
}
}
static void pc_init(void *userdata, struct fuse_conn_info *conn)
{
(void) userdata;
printf("Protocol version: %d.%d\n", conn->proto_major,
conn->proto_minor);
print_capabilities(conn);
}
static const struct fuse_lowlevel_ops pc_oper = {
.init = pc_init,
};
int main(int argc, char **argv)
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
char *mountpoint;
int ret = -1;
mountpoint = strdup("/tmp/fuse_printcap_XXXXXX");
if(mkdtemp(mountpoint) == NULL) {
perror("mkdtemp");
return 1;
}
printf("FUSE library version %s\n", fuse_pkgversion());
se = fuse_session_new(&args, &pc_oper,
sizeof(pc_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
if (fuse_session_mount(se, mountpoint) != 0)
goto err_out3;
ret = fuse_session_loop(se);
err_out3:
err_out2:
err_out1:
rmdir(mountpoint);
free(mountpoint);
return ret ? 1 : 0;
}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
#define FUSE_CAP_HANDLE_KILLPRIV
#define FUSE_CAP_AUTO_INVAL_DATA
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_HANDLE_KILLPRIV_V2
#define FUSE_CAP_SPLICE_READ
#define FUSE_CAP_PARALLEL_DIROPS
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPIRE_ONLY
#define FUSE_CAP_ATOMIC_O_TRUNC
#define FUSE_CAP_ASYNC_READ
#define FUSE_CAP_SPLICE_WRITE
#define FUSE_CAP_CACHE_SYMLINKS
#define FUSE_CAP_POSIX_ACL
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_POSIX_LOCKS
#define FUSE_CAP_EXPLICIT_INVAL_DATA
#define FUSE_CAP_READDIRPLUS_AUTO
#define FUSE_CAP_NO_OPENDIR_SUPPORT
#define FUSE_CAP_ASYNC_DIO
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
#define FUSE_CAP_PASSTHROUGH
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
#define FUSE_CAP_NO_OPEN_SUPPORT
#define FUSE_CAP_READDIRPLUS
const char * fuse_pkgversion(void)
Definition fuse.c:5211
void fuse_remove_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_SETXATTR_EXT
#define FUSE_CAP_SPLICE_MOVE
#define FUSE_CAP_NO_EXPORT_SUPPORT
#define FUSE_CAP_FLOCK_LOCKS
void fuse_session_destroy(struct fuse_session *se)
void fuse_session_exit(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition fuse_loop.c:19
void fuse_session_unmount(struct fuse_session *se)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_lowlevel_version(void)
void fuse_opt_free_args(struct fuse_args *args)
Definition fuse_opt.c:34
#define FUSE_ARGS_INIT(argc, argv)
Definition fuse_opt.h:123
char ** argv
Definition fuse_opt.h:114
uint32_t proto_major
uint32_t proto_minor
void(* init)(void *userdata, struct fuse_conn_info *conn)

Definition in file printcap.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse_8h.html0000644000175000017500000013554515156613431021441 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse.h File Reference
libfuse
fuse.h File Reference
#include "fuse_common.h"
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>

Go to the source code of this file.

Data Structures

struct  fuse_config
 
struct  fuse_operations
 
struct  fuse_context
 

Macros

#define FUSE_REGISTER_MODULE(name_, factory_)    fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
 

Typedefs

typedef int(* fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
 
typedef struct fuse_fs *(* fuse_module_factory_t) (struct fuse_args *args, struct fuse_fs *fs[])
 

Enumerations

enum  fuse_readdir_flags { FUSE_READDIR_DEFAULTS = 0 , FUSE_READDIR_PLUS = (1 << 0) }
 
enum  fuse_fill_dir_flags { FUSE_FILL_DIR_DEFAULTS = 0 , FUSE_FILL_DIR_PLUS = (1 << 1) }
 

Functions

int fuse_main_real_versioned (int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data)
 
void fuse_lib_help (struct fuse_args *args)
 
int fuse_mount (struct fuse *f, const char *mountpoint)
 
void fuse_unmount (struct fuse *f)
 
void fuse_destroy (struct fuse *f)
 
int fuse_loop (struct fuse *f)
 
void fuse_exit (struct fuse *f)
 
struct fuse_contextfuse_get_context (void)
 
int fuse_getgroups (int size, gid_t list[])
 
int fuse_interrupted (void)
 
int fuse_invalidate_path (struct fuse *f, const char *path)
 
int fuse_start_cleanup_thread (struct fuse *fuse)
 
void fuse_stop_cleanup_thread (struct fuse *fuse)
 
int fuse_clean_cache (struct fuse *fuse)
 
struct fuse_fs * fuse_fs_new (const struct fuse_operations *op, size_t op_size, void *private_data)
 
struct fuse_session * fuse_get_session (struct fuse *f)
 
int fuse_open_channel (const char *mountpoint, const char *options)
 

Detailed Description

This file defines the library interface of FUSE

IMPORTANT: you should define FUSE_USE_VERSION before including this header.

Definition in file fuse.h.

Macro Definition Documentation

◆ FUSE_REGISTER_MODULE

#define FUSE_REGISTER_MODULE (   name_,
  factory_ 
)     fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_

Register filesystem module

If the "-omodules=*name*_:..." option is present, filesystem objects are created and pushed onto the stack with the factory_ function.

Parameters
name_the name of this filesystem module
factory_the factory function for this filesystem module

Definition at line 1414 of file fuse.h.

Typedef Documentation

◆ fuse_fill_dir_t

typedef int(* fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)

Function to add an entry in a readdir() operation

The off parameter can be any non-zero value that enables the filesystem to identify the current point in the directory stream. It does not need to be the actual physical position. A value of zero is reserved to indicate that seeking in directories is not supported.

Parameters
bufthe buffer passed to the readdir() operation
namethe file name of the directory entry
stbuffile attributes, can be NULL
offoffset of the next entry or zero
flagsfill flags
Returns
1 if buffer is full, zero otherwise

Definition at line 93 of file fuse.h.

◆ fuse_module_factory_t

typedef struct fuse_fs *(* fuse_module_factory_t) (struct fuse_args *args, struct fuse_fs *fs[])

Factory for creating filesystem objects

The function may use and remove options from 'args' that belong to this module.

For now the 'fs' vector always contains exactly one filesystem. This is the filesystem which will be below the newly created filesystem in the stack.

Parameters
argsthe command line arguments
fsNULL terminated filesystem object vector
Returns
the new filesystem object

Definition at line 1385 of file fuse.h.

Enumeration Type Documentation

◆ fuse_fill_dir_flags

Readdir flags, passed to fuse_fill_dir_t callback.

Enumerator
FUSE_FILL_DIR_DEFAULTS 

"Plus" mode: file attributes are valid

The attributes are used by the kernel to prefill the inode cache during a readdir.

It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set and vice versa.

This does not make libfuse honor the 'st_ino' field. That is controlled by the 'use_ino' option instead.

Definition at line 61 of file fuse.h.

◆ fuse_readdir_flags

Readdir flags, passed to ->readdir()

Enumerator
FUSE_READDIR_DEFAULTS 

"Plus" mode.

The kernel wants to prefill the inode cache during readdir. The filesystem may honour this by filling in the attributes and setting FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also just ignore this flag completely.

Definition at line 45 of file fuse.h.

Function Documentation

◆ fuse_clean_cache()

int fuse_clean_cache ( struct fuse *  fuse)

Iterate over cache removing stale entries use in conjunction with "-oremember"

NOTE: This is already done for the standard sessions

Parameters
fusestruct fuse pointer for fuse instance
Returns
the number of seconds until the next cleanup

Definition at line 4433 of file fuse.c.

◆ fuse_destroy()

void fuse_destroy ( struct fuse *  f)

Destroy the FUSE handle.

NOTE: This function does not unmount the filesystem. If this is needed, call fuse_unmount() before calling this function.

Parameters
fthe FUSE handle

Definition at line 5146 of file fuse.c.

◆ fuse_exit()

void fuse_exit ( struct fuse *  f)

Flag session as terminated

This function will cause any running event loops to exit on the next opportunity.

Parameters
fthe FUSE handle

Definition at line 4639 of file fuse.c.

◆ fuse_fs_new()

struct fuse_fs * fuse_fs_new ( const struct fuse_operations op,
size_t  op_size,
void *  private_data 
)

Create a new fuse filesystem object

This is usually called from the factory of a fuse module to create a new instance of a filesystem.

Parameters
opthe filesystem operations
op_sizethe size of the fuse_operations structure
private_dataInitial value for the private_data field of struct fuse_context. May be overridden by the struct fuse_operations.init handler.
Returns
a new filesystem object

Definition at line 4854 of file fuse.c.

◆ fuse_get_context()

struct fuse_context * fuse_get_context ( void  )

Get the current context

The context is only valid for the duration of a filesystem operation, and thus must not be stored and used later.

Returns
the context

Definition at line 4644 of file fuse.c.

◆ fuse_get_session()

struct fuse_session * fuse_get_session ( struct fuse *  f)

Get session from fuse object

Definition at line 4520 of file fuse.c.

◆ fuse_getgroups()

int fuse_getgroups ( int  size,
gid_t  list[] 
)

Get the current supplementary group IDs for the current request

Similar to the getgroups(2) system call, except the return value is always the total number of group IDs, even if it is larger than the specified size.

The current fuse kernel module in linux (as of 2.6.30) doesn't pass the group list to userspace, hence this function needs to parse "/proc/$TID/task/$TID/status" to get the group IDs.

This feature may not be supported on all operating systems. In such a case this function will return -ENOSYS.

Parameters
sizesize of given array
listarray of group IDs to be filled in
Returns
the total number of supplementary group IDs or -errno on failure

Definition at line 4654 of file fuse.c.

◆ fuse_interrupted()

int fuse_interrupted ( void  )

Check if the current request has already been interrupted

Returns
1 if the request has been interrupted, 0 otherwise

Definition at line 4663 of file fuse.c.

◆ fuse_invalidate_path()

int fuse_invalidate_path ( struct fuse *  f,
const char *  path 
)

Invalidates cache for the given path.

This calls fuse_lowlevel_notify_inval_inode internally.

Returns
0 on successful invalidation, negative error value otherwise. This routine may return -ENOENT to indicate that there was no entry to be invalidated, e.g., because the path has not been seen before or has been forgotten; this should not be considered to be an error.

Definition at line 4673 of file fuse.c.

◆ fuse_lib_help()

void fuse_lib_help ( struct fuse_args args)

Print available options (high- and low-level) to stdout. This is not an exhaustive list, but includes only those options that may be of interest to an end-user of a file system.

The function looks at the argument vector only to determine if there are additional modules to be loaded (module=foo option), and attempts to call their help functions as well.

Parameters
argsthe argument vector.

Definition at line 4744 of file fuse.c.

◆ fuse_loop()

int fuse_loop ( struct fuse *  f)

FUSE event loop.

Requests from the kernel are processed, and the appropriate operations are called.

For a description of the return value and the conditions when the event loop exits, refer to the documentation of fuse_session_loop().

Parameters
fthe FUSE handle
Returns
see fuse_session_loop()

See also: fuse_loop_mt()

Definition at line 4577 of file fuse.c.

◆ fuse_main_real_versioned()

int fuse_main_real_versioned ( int  argc,
char *  argv[],
const struct fuse_operations op,
size_t  op_size,
struct libfuse_version version,
void *  user_data 
)

The real main function

Do not call this directly, use fuse_main()

Definition at line 307 of file helper.c.

◆ fuse_mount()

int fuse_mount ( struct fuse *  f,
const char *  mountpoint 
)

Mount a FUSE file system.

Parameters
mountpointthe mount point path
fthe FUSE handle
Returns
0 on success, -1 on failure.

Definition at line 5197 of file fuse.c.

◆ fuse_open_channel()

int fuse_open_channel ( const char *  mountpoint,
const char *  options 
)

Open a FUSE file descriptor and set up the mount for the given mountpoint and flags.

Parameters
mountpointreference to the mount in the file system
optionsmount options
Returns
the FUSE file descriptor or -1 upon error

Definition at line 482 of file helper.c.

◆ fuse_start_cleanup_thread()

int fuse_start_cleanup_thread ( struct fuse *  fuse)

Start the cleanup thread when using option "remember".

This is done automatically by fuse_loop_mt()

Parameters
fusestruct fuse pointer for fuse instance
Returns
0 on success and -1 on error

Definition at line 4906 of file fuse.c.

◆ fuse_stop_cleanup_thread()

void fuse_stop_cleanup_thread ( struct fuse *  fuse)

Stop the cleanup thread when using option "remember".

This is done automatically by fuse_loop_mt()

Parameters
fusestruct fuse pointer for fuse instance

Definition at line 4914 of file fuse.c.

◆ fuse_unmount()

void fuse_unmount ( struct fuse *  f)

Unmount a FUSE file system.

See fuse_session_unmount() for additional information.

Parameters
fthe FUSE handle

Definition at line 5202 of file fuse.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__common_8h.html0000644000175000017500000027007715156613431023150 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_common.h File Reference
libfuse
fuse_common.h File Reference
#include "libfuse_config.h"
#include "fuse_opt.h"
#include "fuse_log.h"
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <assert.h>

Go to the source code of this file.

Data Structures

struct  fuse_file_info
 
struct  fuse_loop_config
 
struct  fuse_conn_info
 
struct  fuse_buf
 
struct  fuse_bufvec
 
struct  libfuse_version
 

Macros

#define FUSE_CAP_ASYNC_READ   (1UL << 0)
 
#define FUSE_CAP_POSIX_LOCKS   (1UL << 1)
 
#define FUSE_CAP_ATOMIC_O_TRUNC   (1UL << 3)
 
#define FUSE_CAP_EXPORT_SUPPORT   (1UL << 4)
 
#define FUSE_CAP_DONT_MASK   (1UL << 6)
 
#define FUSE_CAP_SPLICE_WRITE   (1UL << 7)
 
#define FUSE_CAP_SPLICE_MOVE   (1UL << 8)
 
#define FUSE_CAP_SPLICE_READ   (1UL << 9)
 
#define FUSE_CAP_FLOCK_LOCKS   (1UL << 10)
 
#define FUSE_CAP_IOCTL_DIR   (1UL << 11)
 
#define FUSE_CAP_AUTO_INVAL_DATA   (1UL << 12)
 
#define FUSE_CAP_READDIRPLUS   (1UL << 13)
 
#define FUSE_CAP_READDIRPLUS_AUTO   (1UL << 14)
 
#define FUSE_CAP_ASYNC_DIO   (1UL << 15)
 
#define FUSE_CAP_WRITEBACK_CACHE   (1UL << 16)
 
#define FUSE_CAP_NO_OPEN_SUPPORT   (1UL << 17)
 
#define FUSE_CAP_PARALLEL_DIROPS   (1UL << 18)
 
#define FUSE_CAP_POSIX_ACL   (1UL << 19)
 
#define FUSE_CAP_HANDLE_KILLPRIV   (1UL << 20)
 
#define FUSE_CAP_HANDLE_KILLPRIV_V2   (1UL << 21)
 
#define FUSE_CAP_CACHE_SYMLINKS   (1UL << 23)
 
#define FUSE_CAP_NO_OPENDIR_SUPPORT   (1UL << 24)
 
#define FUSE_CAP_EXPLICIT_INVAL_DATA   (1UL << 25)
 
#define FUSE_CAP_EXPIRE_ONLY   (1UL << 26)
 
#define FUSE_CAP_SETXATTR_EXT   (1UL << 27)
 
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP   (1UL << 28)
 
#define FUSE_CAP_PASSTHROUGH   (1UL << 29)
 
#define FUSE_CAP_NO_EXPORT_SUPPORT   (1UL << 30)
 
#define FUSE_CAP_OVER_IO_URING   (1UL << 31)
 
#define FUSE_IOCTL_COMPAT   (1 << 0)
 
#define FUSE_BACKING_STACKED_UNDER   (0)
 

Enumerations

enum  fuse_buf_flags { FUSE_BUF_IS_FD = (1 << 1) , FUSE_BUF_FD_SEEK = (1 << 2) , FUSE_BUF_FD_RETRY = (1 << 3) }
 
enum  fuse_buf_copy_flags { FUSE_BUF_NO_SPLICE = (1 << 1) , FUSE_BUF_FORCE_SPLICE = (1 << 2) , FUSE_BUF_SPLICE_MOVE = (1 << 3) , FUSE_BUF_SPLICE_NONBLOCK = (1 << 4) }
 

Functions

struct fuse_conn_info_opts * fuse_parse_conn_info_opts (struct fuse_args *args)
 
void fuse_apply_conn_info_opts (struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
 
int fuse_daemonize (int foreground)
 
int fuse_version (void)
 
const char * fuse_pkgversion (void)
 
void fuse_pollhandle_destroy (struct fuse_pollhandle *ph)
 
size_t fuse_buf_size (const struct fuse_bufvec *bufv)
 
ssize_t fuse_buf_copy (struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
 
int fuse_set_signal_handlers (struct fuse_session *se)
 
int fuse_set_fail_signal_handlers (struct fuse_session *se)
 
void fuse_remove_signal_handlers (struct fuse_session *se)
 
bool fuse_set_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
void fuse_unset_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
bool fuse_get_feature_flag (struct fuse_conn_info *conn, uint64_t flag)
 
int fuse_convert_to_conn_want_ext (struct fuse_conn_info *conn)
 

Macro Definition Documentation

◆ FUSE_BACKING_STACKED_UNDER

#define FUSE_BACKING_STACKED_UNDER   (0)

When FUSE_CAP_PASSTHROUGH is enabled, this is the maximum allowed stacking depth of the backing files. In current kernel, the maximum allowed stack depth if FILESYSTEM_MAX_STACK_DEPTH (2), which includes the FUSE passthrough layer, so the maximum stacking depth for backing files is 1.

The default is FUSE_BACKING_STACKED_UNDER (0), meaning that the backing files cannot be on a stacked filesystem, but another stacked filesystem can be stacked over this FUSE passthrough filesystem.

Set this to FUSE_BACKING_STACKED_OVER (1) if backing files may be on a stacked filesystem, such as overlayfs or another FUSE passthrough. In this configuration, another stacked filesystem cannot be stacked over this FUSE passthrough filesystem.

Definition at line 670 of file fuse_common.h.

◆ FUSE_CAP_ASYNC_DIO

#define FUSE_CAP_ASYNC_DIO   (1UL << 15)

Indicates that the filesystem supports asynchronous direct I/O submission.

If this capability is not requested/available, the kernel will ensure that there is at most one pending read and one pending write request per direct I/O file-handle at any time.

This feature is enabled by default when supported by the kernel.

Definition at line 328 of file fuse_common.h.

◆ FUSE_CAP_ASYNC_READ

#define FUSE_CAP_ASYNC_READ   (1UL << 0)

Indicates that the filesystem supports asynchronous read requests.

If this capability is not requested/available, the kernel will ensure that there is at most one pending read request per file-handle at any time, and will attempt to order read requests by increasing offset.

This feature is enabled by default when supported by the kernel.

Definition at line 177 of file fuse_common.h.

◆ FUSE_CAP_ATOMIC_O_TRUNC

#define FUSE_CAP_ATOMIC_O_TRUNC   (1UL << 3)

Indicates that the filesystem supports the O_TRUNC open flag. If disabled, and an application specifies O_TRUNC, fuse first calls truncate() and then open() with O_TRUNC filtered out.

This feature is enabled by default when supported by the kernel.

Definition at line 194 of file fuse_common.h.

◆ FUSE_CAP_AUTO_INVAL_DATA

#define FUSE_CAP_AUTO_INVAL_DATA   (1UL << 12)

Traditionally, while a file is open the FUSE kernel module only asks the filesystem for an update of the file's attributes when a client attempts to read beyond EOF. This is unsuitable for e.g. network filesystems, where the file contents may change without the kernel knowing about it.

If this flag is set, FUSE will check the validity of the attributes on every read. If the attributes are no longer valid (i.e., if the attr_timeout passed to fuse_reply_attr() or set in struct fuse_entry_param has passed), it will first issue a getattr request. If the new mtime differs from the previous value, any cached file contents will be invalidated as well.

This flag should always be set when available. If all file changes go through the kernel, attr_timeout should be set to a very large number to avoid unnecessary getattr() calls.

This feature is enabled by default when supported by the kernel.

Definition at line 281 of file fuse_common.h.

◆ FUSE_CAP_CACHE_SYMLINKS

#define FUSE_CAP_CACHE_SYMLINKS   (1UL << 23)

Indicates that the kernel supports caching symlinks in its page cache.

When this feature is enabled, symlink targets are saved in the page cache. You can invalidate a cached link by calling: fuse_lowlevel_notify_inval_inode(se, ino, 0, 0);

This feature is disabled by default. If the kernel supports it (>= 4.20), you can enable this feature by setting this flag in the want field of the fuse_conn_info structure.

Definition at line 418 of file fuse_common.h.

◆ FUSE_CAP_DIRECT_IO_ALLOW_MMAP

#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP   (1UL << 28)

Files opened with FUSE_DIRECT_IO do not support MAP_SHARED mmap. This restriction is relaxed through FUSE_CAP_DIRECT_IO_RELAX (kernel flag: FUSE_DIRECT_IO_RELAX). MAP_SHARED is disabled by default for FUSE_DIRECT_IO, as this flag can be used to ensure coherency between mount points (or network clients) and with kernel page cache as enforced by mmap that cannot be guaranteed anymore.

Definition at line 488 of file fuse_common.h.

◆ FUSE_CAP_DONT_MASK

#define FUSE_CAP_DONT_MASK   (1UL << 6)

Indicates that the kernel should not apply the umask to the file mode on create operations.

This feature is disabled by default.

Definition at line 214 of file fuse_common.h.

◆ FUSE_CAP_EXPIRE_ONLY

#define FUSE_CAP_EXPIRE_ONLY   (1UL << 26)

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.

Definition at line 472 of file fuse_common.h.

◆ FUSE_CAP_EXPLICIT_INVAL_DATA

#define FUSE_CAP_EXPLICIT_INVAL_DATA   (1UL << 25)

Indicates support for invalidating cached pages only on explicit request.

If this flag is set in the capable field of the fuse_conn_info structure, then the FUSE kernel module supports invalidating cached pages only on explicit request by the filesystem through fuse_lowlevel_notify_inval_inode() or fuse_invalidate_path().

By setting this flag in the want field of the fuse_conn_info structure, the filesystem is responsible for invalidating cached pages through explicit requests to the kernel.

Note that setting this flag does not prevent the cached pages from being flushed by OS itself and/or through user actions.

Note that if both FUSE_CAP_EXPLICIT_INVAL_DATA and FUSE_CAP_AUTO_INVAL_DATA are set in the capable field of the fuse_conn_info structure then FUSE_CAP_AUTO_INVAL_DATA takes precedence.

This feature is disabled by default.

Definition at line 456 of file fuse_common.h.

◆ FUSE_CAP_EXPORT_SUPPORT

#define FUSE_CAP_EXPORT_SUPPORT   (1UL << 4)

Indicates that the filesystem supports lookups of "." and "..".

When this flag is set, the filesystem must be prepared to receive requests for invalid inodes (i.e., for which a FORGET request was received or which have been used in a previous instance of the filesystem daemon) and must not reuse node-ids (even when setting generation numbers).

This feature is disabled by default.

Definition at line 206 of file fuse_common.h.

◆ FUSE_CAP_FLOCK_LOCKS

#define FUSE_CAP_FLOCK_LOCKS   (1UL << 10)

If set, the calls to flock(2) will be emulated using POSIX locks and must then be handled by the filesystem's setlock() handler.

If not set, flock(2) calls will be handled by the FUSE kernel module internally (so any access that does not go through the kernel cannot be taken into account).

This feature is enabled by default when supported by the kernel and if the filesystem implements a flock() handler.

Definition at line 252 of file fuse_common.h.

◆ FUSE_CAP_HANDLE_KILLPRIV

#define FUSE_CAP_HANDLE_KILLPRIV   (1UL << 20)

Indicates that the filesystem is responsible for unsetting setuid and setgid bits when a file is written, truncated, or its owner is changed.

This feature is disabled by default.

Definition at line 388 of file fuse_common.h.

◆ FUSE_CAP_HANDLE_KILLPRIV_V2

#define FUSE_CAP_HANDLE_KILLPRIV_V2   (1UL << 21)

Indicates that the filesystem is responsible for unsetting setuid and setgid bit and additionally cap (stored as xattr) when a file is written, truncated, or its owner is changed. 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). KILLPRIV_V2 requires handling of

  • FUSE_OPEN_KILL_SUIDGID (set in struct fuse_create_in::open_flags)
  • FATTR_KILL_SUIDGID (set in struct fuse_setattr_in::valid)
  • FUSE_WRITE_KILL_SUIDGID (set in struct fuse_write_in::write_flags)

This feature is disabled by default.

Definition at line 405 of file fuse_common.h.

◆ FUSE_CAP_IOCTL_DIR

#define FUSE_CAP_IOCTL_DIR   (1UL << 11)

Indicates that the filesystem supports ioctl's on directories.

This feature is enabled by default when supported by the kernel.

Definition at line 259 of file fuse_common.h.

◆ FUSE_CAP_NO_EXPORT_SUPPORT

#define FUSE_CAP_NO_EXPORT_SUPPORT   (1UL << 30)

Indicates that the file system cannot handle NFS export

If this flag is set NFS export and name_to_handle_at is not going to work at all and will fail with EOPNOTSUPP.

Definition at line 508 of file fuse_common.h.

◆ FUSE_CAP_NO_OPEN_SUPPORT

#define FUSE_CAP_NO_OPEN_SUPPORT   (1UL << 17)

Indicates support for zero-message opens. If this flag is set in the capable field of the fuse_conn_info structure, then the filesystem may return ENOSYS from the open() handler to indicate success. Further attempts to open files will be handled in the kernel. (If this flag is not set, returning ENOSYS will be treated as an error and signaled to the caller).

Setting this flag in the want field enables this behavior automatically within libfuse for low level API users. If non-low level users wish to have this behavior you must return ENOSYS from the open() handler on supporting kernels.

Definition at line 352 of file fuse_common.h.

◆ FUSE_CAP_NO_OPENDIR_SUPPORT

#define FUSE_CAP_NO_OPENDIR_SUPPORT   (1UL << 24)

Indicates support for zero-message opendirs. If this flag is set in the capable field of the fuse_conn_info structure, then the filesystem may return ENOSYS from the opendir() handler to indicate success. Further opendir and releasedir messages will be handled in the kernel. (If this flag is not set, returning ENOSYS will be treated as an error and signalled to the caller.)

Setting this flag in the want field enables this behavior automatically within libfuse for low level API users. If non-low level users with to have this behavior you must return ENOSYS from the opendir() handler on supporting kernels.

Definition at line 433 of file fuse_common.h.

◆ FUSE_CAP_OVER_IO_URING

#define FUSE_CAP_OVER_IO_URING   (1UL << 31)

Indicates support for io-uring between fuse-server and fuse-client

Definition at line 513 of file fuse_common.h.

◆ FUSE_CAP_PARALLEL_DIROPS

#define FUSE_CAP_PARALLEL_DIROPS   (1UL << 18)

Indicates support for parallel directory operations. If this flag is unset, the FUSE kernel module will ensure that lookup() and readdir() requests are never issued concurrently for the same directory.

Definition at line 360 of file fuse_common.h.

◆ FUSE_CAP_PASSTHROUGH

#define FUSE_CAP_PASSTHROUGH   (1UL << 29)

Indicates support for passthrough mode access for read/write operations.

If this flag is set in the capable field of the fuse_conn_info structure, then the FUSE kernel module supports redirecting read/write operations to the backing file instead of letting them to be handled by the FUSE daemon.

This feature is disabled by default.

Definition at line 500 of file fuse_common.h.

◆ FUSE_CAP_POSIX_ACL

#define FUSE_CAP_POSIX_ACL   (1UL << 19)

Indicates support for POSIX ACLs.

If this feature is enabled, the kernel will cache and have responsibility for enforcing ACLs. ACL will be stored as xattrs and passed to userspace, which is responsible for updating the ACLs in the filesystem, keeping the file mode in sync with the ACL, and ensuring inheritance of default ACLs when new filesystem nodes are created. Note that this requires that the file system is able to parse and interpret the xattr representation of ACLs.

Enabling this feature implicitly turns on the default_permissions mount option (even if it was not passed to mount(2)).

This feature is disabled by default.

Definition at line 379 of file fuse_common.h.

◆ FUSE_CAP_POSIX_LOCKS

#define FUSE_CAP_POSIX_LOCKS   (1UL << 1)

Indicates that the filesystem supports "remote" locking.

This feature is enabled by default when supported by the kernel, and if getlk() and setlk() handlers are implemented.

Definition at line 185 of file fuse_common.h.

◆ FUSE_CAP_READDIRPLUS

#define FUSE_CAP_READDIRPLUS   (1UL << 13)

Indicates that the filesystem supports readdirplus.

This feature is enabled by default when supported by the kernel and if the filesystem implements a readdirplus() handler.

Definition at line 289 of file fuse_common.h.

◆ FUSE_CAP_READDIRPLUS_AUTO

#define FUSE_CAP_READDIRPLUS_AUTO   (1UL << 14)

Indicates that the filesystem supports adaptive readdirplus.

If FUSE_CAP_READDIRPLUS is not set, this flag has no effect.

If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel will always issue readdirplus() requests to retrieve directory contents.

If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel will issue both readdir() and readdirplus() requests, depending on how much information is expected to be required.

As of Linux 4.20, the algorithm is as follows: when userspace starts to read directory entries, issue a READDIRPLUS request to the filesystem. If any entry attributes have been looked up by the time userspace requests the next batch of entries continue with READDIRPLUS, otherwise switch to plain READDIR. This will reasult in eg plain "ls" triggering READDIRPLUS first then READDIR after that because it doesn't do lookups. "ls -l" should result in all READDIRPLUS, except if dentries are already cached.

This feature is enabled by default when supported by the kernel and if the filesystem implements both a readdirplus() and a readdir() handler.

Definition at line 317 of file fuse_common.h.

◆ FUSE_CAP_SETXATTR_EXT

#define FUSE_CAP_SETXATTR_EXT   (1UL << 27)

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.

Definition at line 479 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_MOVE

#define FUSE_CAP_SPLICE_MOVE   (1UL << 8)

Indicates that libfuse should try to move pages instead of copying when writing to / reading from the fuse device. This may improve performance.

This feature is disabled by default.

Definition at line 230 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_READ

#define FUSE_CAP_SPLICE_READ   (1UL << 9)

Indicates that libfuse should try to use splice() when reading from the fuse device. This may improve performance.

This feature is enabled by default when supported by the kernel and if the filesystem implements a write_buf() handler.

Definition at line 239 of file fuse_common.h.

◆ FUSE_CAP_SPLICE_WRITE

#define FUSE_CAP_SPLICE_WRITE   (1UL << 7)

Indicates that libfuse should try to use splice() when writing to the fuse device. This may improve performance.

This feature is disabled by default.

Definition at line 222 of file fuse_common.h.

◆ FUSE_CAP_WRITEBACK_CACHE

#define FUSE_CAP_WRITEBACK_CACHE   (1UL << 16)

Indicates that writeback caching should be enabled. This means that individual write request may be buffered and merged in the kernel before they are send to the filesystem.

This feature is disabled by default.

Definition at line 337 of file fuse_common.h.

◆ FUSE_IOCTL_COMPAT

#define FUSE_IOCTL_COMPAT   (1 << 0)

Ioctl flags

FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed FUSE_IOCTL_RETRY: retry with new iovecs FUSE_IOCTL_DIR: is a directory

FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs

Definition at line 525 of file fuse_common.h.

Enumeration Type Documentation

◆ fuse_buf_copy_flags

Buffer copy flags

Enumerator
FUSE_BUF_NO_SPLICE 

Don't use splice(2)

Always fall back to using read and write instead of splice(2) to copy data from one file descriptor to another.

If this flag is not set, then only fall back if splice is unavailable.

FUSE_BUF_FORCE_SPLICE 

Force splice

Always use splice(2) to copy data from one file descriptor to another. If splice is not available, return -EINVAL.

FUSE_BUF_SPLICE_MOVE 

Try to move data with splice.

If splice is used, try to move pages from the source to the destination instead of copying. See documentation of SPLICE_F_MOVE in splice(2) man page.

FUSE_BUF_SPLICE_NONBLOCK 

Don't block on the pipe when copying data with splice

Makes the operations on the pipe non-blocking (if the pipe is full or empty). See SPLICE_F_NONBLOCK in the splice(2) man page.

Definition at line 840 of file fuse_common.h.

◆ fuse_buf_flags

Buffer flags

Enumerator
FUSE_BUF_IS_FD 

Buffer contains a file descriptor

If this flag is set, the .fd field is valid, otherwise the .mem fields is valid.

FUSE_BUF_FD_SEEK 

Seek on the file descriptor

If this flag is set then the .pos field is valid and is used to seek to the given offset before performing operation on file descriptor.

FUSE_BUF_FD_RETRY 

Retry operation on file descriptor

If this flag is set then retry operation on file descriptor until .size bytes have been copied or an error or EOF is detected.

Definition at line 809 of file fuse_common.h.

Function Documentation

◆ fuse_apply_conn_info_opts()

void fuse_apply_conn_info_opts ( struct fuse_conn_info_opts *  opts,
struct fuse_conn_info conn 
)

This function applies the (parsed) parameters in opts to the conn pointer. It may modify the following fields: wants, max_write, max_readahead, congestion_threshold, max_background, time_gran. A field is only set (or unset) if the corresponding option has been explicitly set.

Definition at line 412 of file helper.c.

◆ fuse_buf_copy()

ssize_t fuse_buf_copy ( struct fuse_bufvec dst,
struct fuse_bufvec src,
enum fuse_buf_copy_flags  flags 
)

Copy data from one buffer vector to another

Parameters
dstdestination buffer vector
srcsource buffer vector
flagsflags controlling the copy
Returns
actual number of bytes copied or -errno on error

Definition at line 284 of file buffer.c.

◆ fuse_buf_size()

size_t fuse_buf_size ( const struct fuse_bufvec bufv)

Get total size of data in a fuse buffer vector

Parameters
bufvbuffer vector
Returns
size of data

Definition at line 22 of file buffer.c.

◆ fuse_convert_to_conn_want_ext()

int fuse_convert_to_conn_want_ext ( struct fuse_conn_info conn)

Get the wanted capability flags, converting from old format if necessary

Definition at line 2000 of file fuse_lowlevel.c.

◆ fuse_daemonize()

int fuse_daemonize ( int  foreground)

Go into the background

Parameters
foregroundif true, stay in the foreground
Returns
0 on success, -1 on failure

Definition at line 253 of file helper.c.

◆ fuse_get_feature_flag()

bool fuse_get_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Get the value of a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be checked
Returns
true if the flag is set, false otherwise

Definition at line 2061 of file fuse_lowlevel.c.

◆ fuse_parse_conn_info_opts()

struct fuse_conn_info_opts * fuse_parse_conn_info_opts ( struct fuse_args args)

This function parses several command-line options that can be used to override elements of struct fuse_conn_info. The pointer returned by this function should be passed to the fuse_apply_conn_info_opts() method by the file system's init() handler.

Before using this function, think twice if you really want these parameters to be adjustable from the command line. In most cases, they should be determined by the file system internally.

The following options are recognized:

-o max_write=N sets conn->max_write -o max_readahead=N sets conn->max_readahead -o max_background=N sets conn->max_background -o congestion_threshold=N sets conn->congestion_threshold -o async_read sets FUSE_CAP_ASYNC_READ in conn->want -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets FUSE_CAP_READDIRPLUS_AUTO in conn->want -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and FUSE_CAP_READDIRPLUS_AUTO in conn->want -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want -o time_gran=N sets conn->time_gran

Known options will be removed from args, unknown options will be passed through unchanged.

Parameters
argsargument vector (input+output)
Returns
parsed options

Definition at line 466 of file helper.c.

◆ fuse_pkgversion()

const char * fuse_pkgversion ( void  )

Get the full package version string of the library

Returns
the package version

Definition at line 5211 of file fuse.c.

◆ fuse_pollhandle_destroy()

void fuse_pollhandle_destroy ( struct fuse_pollhandle *  ph)

Destroy poll handle

Parameters
phthe poll handle

Definition at line 1903 of file fuse_lowlevel.c.

◆ fuse_remove_signal_handlers()

void fuse_remove_signal_handlers ( struct fuse_session *  se)

Restore default signal handlers

Resets global session. After this fuse_set_signal_handlers() may be called again.

Parameters
sethe same session as given in fuse_set_signal_handlers()

See also: fuse_set_signal_handlers()

Definition at line 187 of file fuse_signals.c.

◆ fuse_set_fail_signal_handlers()

int fuse_set_fail_signal_handlers ( struct fuse_session *  se)

Print a stack backtrace diagnostic on critical signals ()

Stores session in a global variable. May only be called once per process until fuse_remove_signal_handlers() is called.

Once either of the POSIX signals arrives, the signal handler calls fuse_session_exit().

Parameters
sethe session to exit
Returns
0 on success, -1 on failure

See also: fuse_remove_signal_handlers()

Definition at line 165 of file fuse_signals.c.

◆ fuse_set_feature_flag()

bool fuse_set_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Config operations. These APIs are introduced in version 312 (FUSE_MAKE_VERSION(3, 12)). Using them in earlier versions will result in errors. Set a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be set
Returns
true if the flag was set, false if the flag is not supported

Definition at line 2035 of file fuse_lowlevel.c.

◆ fuse_set_signal_handlers()

int fuse_set_signal_handlers ( struct fuse_session *  se)

Exit session on HUP, TERM and INT signals and ignore PIPE signal

Stores session in a global variable. May only be called once per process until fuse_remove_signal_handlers() is called.

Once either of the POSIX signals arrives, the signal handler calls fuse_session_exit().

Parameters
sethe session to exit
Returns
0 on success, -1 on failure

See also: fuse_remove_signal_handlers()

Definition at line 140 of file fuse_signals.c.

◆ fuse_unset_feature_flag()

void fuse_unset_feature_flag ( struct fuse_conn_info conn,
uint64_t  flag 
)

Unset a feature flag in the want_ext field of fuse_conn_info.

Parameters
connconnection information
flagfeature flag to be unset

Definition at line 2050 of file fuse_lowlevel.c.

◆ fuse_version()

int fuse_version ( void  )

Get the version of the library

Returns
the version

Definition at line 5206 of file fuse.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__log_8h.html0000644000175000017500000003433215156613431022431 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_log.h File Reference
libfuse
fuse_log.h File Reference
#include <stdarg.h>

Go to the source code of this file.

Typedefs

typedef void(* fuse_log_func_t) (enum fuse_log_level level, const char *fmt, va_list ap)
 

Enumerations

enum  fuse_log_level
 

Functions

void fuse_set_log_func (fuse_log_func_t func)
 
void fuse_log (enum fuse_log_level level, const char *fmt,...) __attribute__((format(printf
 
void void fuse_log_enable_syslog (const char *ident, int option, int facility)
 
void fuse_log_close_syslog (void)
 

Detailed Description

This file defines the logging interface of FUSE

Definition in file fuse_log.h.

Typedef Documentation

◆ fuse_log_func_t

typedef void(* fuse_log_func_t) (enum fuse_log_level level, const char *fmt, va_list ap)

Log message handler function.

This function must be thread-safe. It may be called from any libfuse function, including fuse_parse_cmdline() and other functions invoked before a FUSE filesystem is created.

Install a custom log message handler function using fuse_set_log_func().

Parameters
levellog severity level
fmtsprintf-style format string including newline
apformat string arguments

Definition at line 52 of file fuse_log.h.

Enumeration Type Documentation

◆ fuse_log_level

Log severity level

These levels correspond to syslog(2) log levels since they are widely used.

Definition at line 28 of file fuse_log.h.

Function Documentation

◆ fuse_log()

void fuse_log ( enum fuse_log_level  level,
const char *  fmt,
  ... 
)

Emit a log message

Parameters
levelseverity level (FUSE_LOG_ERR, FUSE_LOG_DEBUG, etc)
fmtsprintf-style format string including newline

Definition at line 76 of file fuse_log.c.

◆ fuse_log_close_syslog()

void fuse_log_close_syslog ( void  )

To be called at teardown to close syslog.

Definition at line 93 of file fuse_log.c.

◆ fuse_log_enable_syslog()

void void fuse_log_enable_syslog ( const char *  ident,
int  option,
int  facility 
)

Switch default log handler from stderr to syslog

Passed options are according to 'man 3 openlog'

Definition at line 86 of file fuse_log.c.

◆ fuse_set_log_func()

void fuse_set_log_func ( fuse_log_func_t  func)

Install a custom log handler function.

Log messages are emitted by libfuse functions to report errors and debug information. Messages are printed to stderr by default but this can be overridden by installing a custom log message handler function.

The log message handler function is global and affects all FUSE filesystems created within this process.

Parameters
funca custom log message handler function or NULL to revert to the default

Definition at line 69 of file fuse_log.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__lowlevel_8h.html0000644000175000017500000043450115156613431023503 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_lowlevel.h File Reference
libfuse
fuse_lowlevel.h File Reference
#include "fuse_common.h"
#include <stddef.h>
#include <utime.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>

Go to the source code of this file.

Data Structures

struct  fuse_entry_param
 
struct  fuse_ctx
 
struct  fuse_lowlevel_ops
 
struct  fuse_cmdline_opts
 

Macros

#define FUSE_ROOT_ID   1
 

Typedefs

typedef uint64_t fuse_ino_t
 
typedef struct fuse_req * fuse_req_t
 
typedef void(* fuse_interrupt_func_t) (fuse_req_t req, void *data)
 

Enumerations

enum  fuse_notify_entry_flags
 

Functions

int fuse_reply_err (fuse_req_t req, int err)
 
void fuse_reply_none (fuse_req_t req)
 
int fuse_reply_entry (fuse_req_t req, const struct fuse_entry_param *e)
 
int fuse_reply_create (fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
 
int fuse_reply_attr (fuse_req_t req, const struct stat *attr, double attr_timeout)
 
int fuse_reply_readlink (fuse_req_t req, const char *link)
 
int fuse_passthrough_open (fuse_req_t req, int fd)
 
int fuse_reply_open (fuse_req_t req, const struct fuse_file_info *fi)
 
int fuse_reply_write (fuse_req_t req, size_t count)
 
int fuse_reply_buf (fuse_req_t req, const char *buf, size_t size)
 
int fuse_reply_data (fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
 
int fuse_reply_iov (fuse_req_t req, const struct iovec *iov, int count)
 
int fuse_reply_statfs (fuse_req_t req, const struct statvfs *stbuf)
 
int fuse_reply_xattr (fuse_req_t req, size_t count)
 
int fuse_reply_lock (fuse_req_t req, const struct flock *lock)
 
int fuse_reply_bmap (fuse_req_t req, uint64_t idx)
 
size_t fuse_add_direntry (fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
 
size_t fuse_add_direntry_plus (fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
 
int fuse_reply_ioctl_retry (fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count)
 
int fuse_reply_ioctl (fuse_req_t req, int result, const void *buf, size_t size)
 
int fuse_reply_ioctl_iov (fuse_req_t req, int result, const struct iovec *iov, int count)
 
int fuse_reply_poll (fuse_req_t req, unsigned revents)
 
int fuse_reply_lseek (fuse_req_t req, off_t off)
 
int fuse_reply_statx (fuse_req_t req, int flags, struct statx *statx, double attr_timeout)
 
int fuse_lowlevel_notify_poll (struct fuse_pollhandle *ph)
 
int fuse_lowlevel_notify_inval_inode (struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len)
 
int fuse_lowlevel_notify_increment_epoch (struct fuse_session *se)
 
int fuse_lowlevel_notify_inval_entry (struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_expire_entry (struct fuse_session *se, fuse_ino_t parent, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_delete (struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen)
 
int fuse_lowlevel_notify_store (struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
 
int fuse_lowlevel_notify_retrieve (struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
 
void * fuse_req_userdata (fuse_req_t req)
 
const struct fuse_ctxfuse_req_ctx (fuse_req_t req)
 
int fuse_req_getgroups (fuse_req_t req, int size, gid_t list[])
 
void fuse_req_interrupt_func (fuse_req_t req, fuse_interrupt_func_t func, void *data)
 
int fuse_req_interrupted (fuse_req_t req)
 
void fuse_lowlevel_version (void)
 
void fuse_lowlevel_help (void)
 
void fuse_cmdline_help (void)
 
int fuse_parse_cmdline_30 (struct fuse_args *args, struct fuse_cmdline_opts *opts)
 
int fuse_session_mount (struct fuse_session *se, const char *mountpoint)
 
int fuse_session_loop (struct fuse_session *se)
 
void fuse_session_exit (struct fuse_session *se)
 
void fuse_session_reset (struct fuse_session *se)
 
int fuse_session_exited (struct fuse_session *se)
 
void fuse_session_unmount (struct fuse_session *se)
 
void fuse_session_destroy (struct fuse_session *se)
 
int fuse_session_fd (struct fuse_session *se)
 
void fuse_session_process_buf (struct fuse_session *se, const struct fuse_buf *buf)
 
int fuse_session_receive_buf (struct fuse_session *se, struct fuse_buf *buf)
 
bool fuse_req_is_uring (fuse_req_t req)
 
int fuse_req_get_payload (fuse_req_t req, char **payload, size_t *payload_sz, void **mr)
 

Detailed Description

Low level API

IMPORTANT: you should define FUSE_USE_VERSION before including this header. To use the newest API define it to 35 (recommended for any new application).

Definition in file fuse_lowlevel.h.

Macro Definition Documentation

◆ FUSE_ROOT_ID

#define FUSE_ROOT_ID   1

The node ID of the root inode

Definition at line 44 of file fuse_lowlevel.h.

Typedef Documentation

◆ fuse_ino_t

typedef uint64_t fuse_ino_t

Inode number type

Definition at line 47 of file fuse_lowlevel.h.

◆ fuse_interrupt_func_t

typedef void(* fuse_interrupt_func_t) (fuse_req_t req, void *data)

Callback function for an interrupt

Parameters
reqinterrupted request
datauser data

Definition at line 1992 of file fuse_lowlevel.h.

◆ fuse_req_t

typedef struct fuse_req* fuse_req_t

Request pointer type

Definition at line 50 of file fuse_lowlevel.h.

Enumeration Type Documentation

◆ fuse_notify_entry_flags

Flags for fuse_lowlevel_notify_entry() 0 = invalidate entry FUSE_LL_EXPIRE_ONLY = expire entry

Definition at line 151 of file fuse_lowlevel.h.

Function Documentation

◆ fuse_add_direntry()

size_t fuse_add_direntry ( fuse_req_t  req,
char *  buf,
size_t  bufsize,
const char *  name,
const struct stat *  stbuf,
off_t  off 
)

Add a directory entry to the buffer

Buffer needs to be large enough to hold the entry. If it's not, then the entry is not filled in but the size of the entry is still returned. The caller can check this by comparing the bufsize parameter with the returned entry size. If the entry size is larger than the buffer size, the operation failed.

From the 'stbuf' argument the st_ino field and bits 12-15 of the st_mode field are used. The other fields are ignored.

off should be any non-zero value that the filesystem can use to identify the current point in the directory stream. It does not need to be the actual physical position. A value of zero is reserved to mean "from the beginning", and should therefore never be used (the first call to fuse_add_direntry should be passed the offset of the second directory entry).

Parameters
reqrequest handle
bufthe point where the new entry will be added to the buffer
bufsizeremaining size of the buffer
namethe name of the entry
stbufthe file attributes
offthe offset of the next entry
Returns
the space needed for the entry

Definition at line 286 of file fuse_lowlevel.c.

◆ fuse_add_direntry_plus()

size_t fuse_add_direntry_plus ( fuse_req_t  req,
char *  buf,
size_t  bufsize,
const char *  name,
const struct fuse_entry_param e,
off_t  off 
)

Add a directory entry to the buffer with the attributes

See documentation of fuse_add_direntry() for more details.

Parameters
reqrequest handle
bufthe point where the new entry will be added to the buffer
bufsizeremaining size of the buffer
namethe name of the entry
ethe directory entry
offthe offset of the next entry
Returns
the space needed for the entry

Definition at line 376 of file fuse_lowlevel.c.

◆ fuse_cmdline_help()

void fuse_cmdline_help ( void  )

Print available options for fuse_parse_cmdline().

Definition at line 130 of file helper.c.

◆ fuse_lowlevel_help()

void fuse_lowlevel_help ( void  )

Print available low-level options to stdout. This is not an exhaustive list, but includes only those options that may be of interest to an end-user of a file system.

Definition at line 3000 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_delete()

int fuse_lowlevel_notify_delete ( struct fuse_session *  se,
fuse_ino_t  parent,
fuse_ino_t  child,
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):

If the provided child inode matches the inode that is currently associated with the cached dentry, and if there are any inotify watches registered for the dentry, then the watchers are informed that the dentry has been deleted.

To avoid a deadlock this function must not be called while 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).

When called correctly, this function will never block.

Added in FUSE protocol version 7.18. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
parentinode number
childinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure

Definition at line 2562 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_expire_entry()

int fuse_lowlevel_notify_expire_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.

Parameters
sethe session object
parentinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure, -enosys if no kernel support

Definition at line 2549 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_increment_epoch()

int fuse_lowlevel_notify_increment_epoch ( struct fuse_session *  se)

Notify to increment the epoch for the current

Each fuse connection has an 'epoch', which is initialized during INIT. Caching will then be validated against the epoch value: if the current epoch is higher than an object being revalidated, the object is invalid.

This function simply increment the current epoch value.

Parameters
sethe session object
Returns
zero for success, -errno for failure

Definition at line 3116 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_inval_entry()

int fuse_lowlevel_notify_inval_entry ( struct fuse_session *  se,
fuse_ino_t  parent,
const char *  name,
size_t  namelen 
)

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 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() request for the parent, and a setattr(), unlink(), rmdir(), rename(), setxattr(), removexattr(), readdir() or readdirplus() request for the inode itself.

When called correctly, this function will never block.

Added in FUSE protocol version 7.12. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
parentinode number
namefile name
namelenstrlen() of file name
Returns
zero for success, -errno for failure

Definition at line 2543 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_inval_inode()

int fuse_lowlevel_notify_inval_inode ( struct fuse_session *  se,
fuse_ino_t  ino,
off_t  off,
off_t  len 
)

Notify to invalidate cache for an inode.

Added in FUSE protocol version 7.12. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

If the filesystem has writeback caching enabled, invalidating an inode will first trigger a writeback of all dirty pages. The call will block until all writeback requests have completed and the inode has been invalidated. It will, however, not wait for completion of pending writeback requests that have been issued before.

If there are no dirty pages, this function will never block.

Parameters
sethe session object
inothe inode number
offthe offset in the inode where to start invalidating or negative to invalidate attributes only
lenthe amount of cache to invalidate or 0 for all
Returns
zero for success, -errno for failure

Definition at line 2475 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_poll()

int fuse_lowlevel_notify_poll ( struct fuse_pollhandle *  ph)

Notify IO readiness event

For more information, please read comment for poll operation.

Parameters
phpoll handle to notify IO readiness event for

Definition at line 2458 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_retrieve()

int fuse_lowlevel_notify_retrieve ( struct fuse_session *  se,
fuse_ino_t  ino,
size_t  size,
off_t  offset,
void *  cookie 
)

Retrieve data from the kernel buffers

Retrieve data in the kernel buffers belonging to the given inode. If successful then the retrieve_reply() method will be called with the returned data.

Only present pages are returned in the retrieve reply. Retrieving stops when it finds a non-present page and only data prior to that is returned.

If this function returns an error, then the retrieve will not be completed and no reply will be sent.

This function doesn't change the dirty state of pages in the kernel buffer. For dirty pages the write() method will be called regardless of having been retrieved previously.

Added in FUSE protocol version 7.15. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
inothe inode number
sizethe number of bytes to retrieve
offsetthe starting offset into the file to retrieve from
cookieuser data to supply to the reply callback
Returns
zero for success, -errno for failure

Definition at line 2668 of file fuse_lowlevel.c.

◆ fuse_lowlevel_notify_store()

int fuse_lowlevel_notify_store ( struct fuse_session *  se,
fuse_ino_t  ino,
off_t  offset,
struct fuse_bufvec bufv,
enum fuse_buf_copy_flags  flags 
)

Store data to the kernel buffers

Synchronously store data in the kernel buffers belonging to the given inode. The stored data is marked up-to-date (no read will be performed against it, unless it's invalidated or evicted from the cache).

If the stored data overflows the current file size, then the size is extended, similarly to a write(2) on the filesystem.

If this function returns an error, then the store wasn't fully completed, but it may have been partially completed.

Added in FUSE protocol version 7.15. If the kernel does not support this (or a newer) version, the function will return -ENOSYS and do nothing.

Parameters
sethe session object
inothe inode number
offsetthe starting offset into the file to store to
bufvbuffer vector
flagsflags controlling the copy
Returns
zero for success, -errno for failure

Definition at line 2588 of file fuse_lowlevel.c.

◆ fuse_lowlevel_version()

void fuse_lowlevel_version ( void  )

Print low-level version information to stdout.

Definition at line 2993 of file fuse_lowlevel.c.

◆ fuse_parse_cmdline_30()

int fuse_parse_cmdline_30 ( struct fuse_args args,
struct fuse_cmdline_opts opts 
)

Utility function to parse common options for simple file systems using the low-level API. A help text that describes the available options can be printed with fuse_cmdline_help. A single non-option argument is treated as the mountpoint. Multiple non-option arguments will result in an error.

If neither -o subtype= or -o fsname= options are given, a new subtype option will be added and set to the basename of the program (the fsname will remain unset, and then defaults to "fuse").

Known options will be removed from args, unknown options will remain.

Parameters
argsargument vector (input+output)
optsoutput argument for parsed options
Returns
0 on success, -1 on failure

struct fuse_cmdline_opts got extended in libfuse-3.12

Definition at line 237 of file helper.c.

◆ fuse_passthrough_open()

int fuse_passthrough_open ( fuse_req_t  req,
int  fd 
)

Setup passthrough backing file for open reply

Currently there should be only one backing id per node / backing file.

Possible requests: open, opendir, create

Parameters
reqrequest handle
fdbacking file descriptor
Returns
positive backing id for success, 0 for failure

Definition at line 480 of file fuse_lowlevel.c.

◆ fuse_reply_attr()

int fuse_reply_attr ( fuse_req_t  req,
const struct stat *  attr,
double  attr_timeout 
)

Reply with attributes

Possible requests: getattr, setattr

Parameters
reqrequest handle
attrthe attributes
attr_timeoutvalidity timeout (in seconds) for the attributes
Returns
zero for success, -errno for failure to send reply

Definition at line 460 of file fuse_lowlevel.c.

◆ fuse_reply_bmap()

int fuse_reply_bmap ( fuse_req_t  req,
uint64_t  idx 
)

Reply with block index

Possible requests: bmap

Parameters
reqrequest handle
idxblock index within device
Returns
zero for success, -errno for failure to send reply

Definition at line 973 of file fuse_lowlevel.c.

◆ fuse_reply_buf()

int fuse_reply_buf ( fuse_req_t  req,
const char *  buf,
size_t  size 
)

Reply with data

Possible requests: read, readdir, getxattr, listxattr

Parameters
reqrequest handle
bufbuffer containing data
sizethe size of data in bytes
Returns
zero for success, -errno for failure to send reply

Definition at line 524 of file fuse_lowlevel.c.

◆ fuse_reply_create()

int fuse_reply_create ( fuse_req_t  req,
const struct fuse_entry_param e,
const struct fuse_file_info fi 
)

Reply with a directory entry and open parameters

currently the following members of 'fi' are used: fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, parallel_direct_writes

Possible requests: create

Side effects: increments the lookup count on success

Parameters
reqrequest handle
ethe entry parameters
fifile information
Returns
zero for success, -errno for failure to send reply

Definition at line 444 of file fuse_lowlevel.c.

◆ fuse_reply_data()

int fuse_reply_data ( fuse_req_t  req,
struct fuse_bufvec bufv,
enum fuse_buf_copy_flags  flags 
)

Reply with data copied/moved from buffer(s)

Zero copy data transfer ("splicing") will be used under the following circumstances:

  1. FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and
  2. the kernel supports splicing from the fuse device (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and
  3. flags does not contain FUSE_BUF_NO_SPLICE
  4. The amount of data that is provided in file-descriptor backed buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) is at least twice the page size.

In order for SPLICE_F_MOVE to be used, the following additional conditions have to be fulfilled:

  1. FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and
  2. the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.capable), and
  3. flags contains FUSE_BUF_SPLICE_MOVE

Note that, if splice is used, the data is actually spliced twice: once into a temporary pipe (to prepend header data), and then again into the kernel. If some of the provided buffers are memory-backed, the data in them is copied in step one and spliced in step two.

The FUSE_BUF_SPLICE_FORCE_SPLICE and FUSE_BUF_SPLICE_NONBLOCK flags are silently ignored.

Possible requests: read, readdir, getxattr, listxattr

Side effects: when used to return data from a readdirplus() (but not readdir()) call, increments the lookup count of each returned entry by one on success.

Parameters
reqrequest handle
bufvbuffer vector
flagsflags controlling the copy
Returns
zero for success, -errno for failure to send reply

Definition at line 912 of file fuse_lowlevel.c.

◆ fuse_reply_entry()

int fuse_reply_entry ( fuse_req_t  req,
const struct fuse_entry_param e 
)

Reply with a directory entry

Possible requests: lookup, mknod, mkdir, symlink, link

Side effects: increments the lookup count on success

Parameters
reqrequest handle
ethe entry parameters
Returns
zero for success, -errno for failure to send reply

Definition at line 428 of file fuse_lowlevel.c.

◆ fuse_reply_err()

int fuse_reply_err ( fuse_req_t  req,
int  err 
)

Reply with an error code or success.

Possible requests: all except forget, forget_multi, retrieve_reply

Wherever possible, error codes should be chosen from the list of documented error conditions in the corresponding system calls manpage.

An error code of ENOSYS is sometimes treated specially. This is indicated in the documentation of the affected handler functions.

The following requests may be answered with a zero error code: unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, removexattr, setlk.

Parameters
reqrequest handle
errthe positive error value, or zero for success
Returns
zero for success, -errno for failure to send reply

Definition at line 331 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl()

int fuse_reply_ioctl ( fuse_req_t  req,
int  result,
const void *  buf,
size_t  size 
)

Reply to finish ioctl

Possible requests: ioctl

Parameters
reqrequest handle
resultresult to be passed to the caller
bufbuffer containing output data
sizelength of output data

Definition at line 1071 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl_iov()

int fuse_reply_ioctl_iov ( fuse_req_t  req,
int  result,
const struct iovec *  iov,
int  count 
)

Reply to finish ioctl with iov buffer

Possible requests: ioctl

Parameters
reqrequest handle
resultresult to be passed to the caller
iovthe vector containing the data
countthe size of vector

Definition at line 1092 of file fuse_lowlevel.c.

◆ fuse_reply_ioctl_retry()

int fuse_reply_ioctl_retry ( fuse_req_t  req,
const struct iovec *  in_iov,
size_t  in_count,
const struct iovec *  out_iov,
size_t  out_count 
)

Reply to ask for data fetch and output buffer preparation. ioctl will be retried with the specified input data fetched and output buffer prepared.

Possible requests: ioctl

Parameters
reqrequest handle
in_ioviovec specifying data to fetch from the caller
in_countnumber of entries in in_iov
out_ioviovec specifying addresses to write output to
out_countnumber of entries in out_iov
Returns
zero for success, -errno for failure to send reply

Definition at line 1001 of file fuse_lowlevel.c.

◆ fuse_reply_iov()

int fuse_reply_iov ( fuse_req_t  req,
const struct iovec *  iov,
int  count 
)

Reply with data vector

Possible requests: read, readdir, getxattr, listxattr

Parameters
reqrequest handle
iovthe vector containing the data
countthe size of vector
Returns
zero for success, -errno for failure to send reply

Definition at line 265 of file fuse_lowlevel.c.

◆ fuse_reply_lock()

int fuse_reply_lock ( fuse_req_t  req,
const struct flock *  lock 
)

Reply with file lock information

Possible requests: getlk

Parameters
reqrequest handle
lockthe lock information
Returns
zero for success, -errno for failure to send reply

Definition at line 956 of file fuse_lowlevel.c.

◆ fuse_reply_lseek()

int fuse_reply_lseek ( fuse_req_t  req,
off_t  off 
)

Reply with offset

Possible requests: lseek

Parameters
reqrequest handle
offoffset of next data or hole
Returns
zero for success, -errno for failure to send reply

Definition at line 1126 of file fuse_lowlevel.c.

◆ fuse_reply_none()

void fuse_reply_none ( fuse_req_t  req)

Don't send reply

Possible requests: forget forget_multi retrieve_reply

Parameters
reqrequest handle

Definition at line 336 of file fuse_lowlevel.c.

◆ fuse_reply_open()

int fuse_reply_open ( fuse_req_t  req,
const struct fuse_file_info fi 
)

Reply with open parameters

currently the following members of 'fi' are used: fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, parallel_direct_writes,

Possible requests: open, opendir

Parameters
reqrequest handle
fifile information
Returns
zero for success, -errno for failure to send reply

Definition at line 505 of file fuse_lowlevel.c.

◆ fuse_reply_poll()

int fuse_reply_poll ( fuse_req_t  req,
unsigned  revents 
)

Reply with poll result event mask

Parameters
reqrequest handle
reventspoll result event mask

Definition at line 1116 of file fuse_lowlevel.c.

◆ fuse_reply_readlink()

int fuse_reply_readlink ( fuse_req_t  req,
const char *  link 
)

Reply with the contents of a symbolic link

Possible requests: readlink

Parameters
reqrequest handle
linksymbolic link contents
Returns
zero for success, -errno for failure to send reply

Definition at line 475 of file fuse_lowlevel.c.

◆ fuse_reply_statfs()

int fuse_reply_statfs ( fuse_req_t  req,
const struct statvfs *  stbuf 
)

Reply with filesystem statistics

Possible requests: statfs

Parameters
reqrequest handle
stbuffilesystem statistics
Returns
zero for success, -errno for failure to send reply

Definition at line 934 of file fuse_lowlevel.c.

◆ fuse_reply_statx()

int fuse_reply_statx ( fuse_req_t  req,
int  flags,
struct statx *  statx,
double  attr_timeout 
)

Reply with extended file attributes.

Possible requests: statx

Parameters
reqrequest handle
flagsstatx flags
statxthe attributes
attr_timeoutvalidity timeout (in seconds) for the attributes
Returns
zero for success, -errno for failure to send reply

Definition at line 1260 of file fuse_lowlevel.c.

◆ fuse_reply_write()

int fuse_reply_write ( fuse_req_t  req,
size_t  count 
)

Reply with number of bytes written

Possible requests: write

Parameters
reqrequest handle
countthe number of bytes written
Returns
zero for success, -errno for failure to send reply

Definition at line 514 of file fuse_lowlevel.c.

◆ fuse_reply_xattr()

int fuse_reply_xattr ( fuse_req_t  req,
size_t  count 
)

Reply with needed buffer size

Possible requests: getxattr, listxattr

Parameters
reqrequest handle
countthe buffer size needed in bytes
Returns
zero for success, -errno for failure to send reply

Definition at line 946 of file fuse_lowlevel.c.

◆ fuse_req_ctx()

const struct fuse_ctx * fuse_req_ctx ( fuse_req_t  req)

Get the context from the request

The pointer returned by this function will only be valid for the request's lifetime

Parameters
reqrequest handle
Returns
the context structure

Definition at line 2718 of file fuse_lowlevel.c.

◆ fuse_req_get_payload()

int fuse_req_get_payload ( fuse_req_t  req,
char **  payload,
size_t *  payload_sz,
void **  mr 
)

Get the payload of a request (for requests submitted through fuse-io-uring only)

This is useful for a file system that wants to write data directly to the request buffer. With io-uring the req is the buffer owner and the file system can write directly to the buffer and avoid extra copying. For example useful for network file systems.

Parameters
reqthe request
payloadpointer to the payload
payload_szsize of the payload
mrmemory registration handle, currently unused
Returns
0 on success, -errno on failure

Definition at line 3386 of file fuse_lowlevel.c.

◆ fuse_req_getgroups()

int fuse_req_getgroups ( fuse_req_t  req,
int  size,
gid_t  list[] 
)

Get the current supplementary group IDs for the specified request

Similar to the getgroups(2) system call, except the return value is always the total number of group IDs, even if it is larger than the specified size.

The current fuse kernel module in linux (as of 2.6.30) doesn't pass the group list to userspace, hence this function needs to parse "/proc/$TID/task/$TID/status" to get the group IDs.

This feature may not be supported on all operating systems. In such a case this function will return -ENOSYS.

Parameters
reqrequest handle
sizesize of given array
listarray of group IDs to be filled in
Returns
the total number of supplementary group IDs or -errno on failure

Definition at line 3614 of file fuse_lowlevel.c.

◆ fuse_req_interrupt_func()

void fuse_req_interrupt_func ( fuse_req_t  req,
fuse_interrupt_func_t  func,
void *  data 
)

Register/unregister callback for an interrupt

If an interrupt has already happened, then the callback function is called from within this function, hence it's not possible for interrupts to be lost.

Parameters
reqrequest handle
functhe callback function or NULL for unregister
datauser data passed to the callback function

Definition at line 2723 of file fuse_lowlevel.c.

◆ fuse_req_interrupted()

int fuse_req_interrupted ( fuse_req_t  req)

Check if a request has already been interrupted

Parameters
reqrequest handle
Returns
1 if the request has been interrupted, 0 otherwise

Definition at line 2736 of file fuse_lowlevel.c.

◆ fuse_req_is_uring()

bool fuse_req_is_uring ( fuse_req_t  req)

Check if the request is submitted through fuse-io-uring

Definition at line 3380 of file fuse_lowlevel.c.

◆ fuse_req_userdata()

void * fuse_req_userdata ( fuse_req_t  req)

Get the userdata from the request

Parameters
reqrequest handle
Returns
the user data passed to fuse_session_new()

Definition at line 2713 of file fuse_lowlevel.c.

◆ fuse_session_destroy()

void fuse_session_destroy ( struct fuse_session *  se)

Destroy a session

Parameters
sethe session

Definition at line 3010 of file fuse_lowlevel.c.

◆ fuse_session_exit()

void fuse_session_exit ( struct fuse_session *  se)

Flag a session as terminated.

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).

Parameters
sethe session

◆ fuse_session_exited()

int fuse_session_exited ( struct fuse_session *  se)

Query the terminated flag of a session

Parameters
sethe session
Returns
1 if exited, 0 if not exited

◆ fuse_session_fd()

int fuse_session_fd ( struct fuse_session *  se)

Return file descriptor for communication with kernel.

The file selector can be used to integrate FUSE with a custom event loop. Whenever data is available for reading on the provided fd, the event loop should call fuse_session_receive_buf followed by fuse_session_process_buf to process the request.

The returned file descriptor is valid until fuse_session_unmount is called.

Parameters
sethe session
Returns
a file descriptor

Definition at line 3534 of file fuse_lowlevel.c.

◆ fuse_session_loop()

int fuse_session_loop ( struct fuse_session *  se)

Enter a single threaded, blocking event loop.

When the event loop terminates because the connection to the FUSE kernel module has been closed, this function returns zero. This happens when the filesystem is unmounted regularly (by the filesystem owner or root running the umount(8) or fusermount(1) command), or if connection is explicitly severed by writing 1 to theabort file in /sys/fs/fuse/connections/NNN. The only way to distinguish between these two conditions is to check if the filesystem is still mounted after the session loop returns.

When some error occurs during request processing, the function returns a negated errno(3) value.

If the loop has been terminated because of a signal handler installed by fuse_set_signal_handlers(), this function returns the (positive) signal value that triggered the exit.

Parameters
sethe session
Returns
0, -errno, or a signal value

Definition at line 19 of file fuse_loop.c.

◆ fuse_session_mount()

int fuse_session_mount ( struct fuse_session *  se,
const char *  mountpoint 
)

Mount a FUSE file system.

Parameters
mountpointthe mount point path
sesession object
Returns
0 on success, -1 on failure.

Definition at line 3473 of file fuse_lowlevel.c.

◆ fuse_session_process_buf()

void fuse_session_process_buf ( struct fuse_session *  se,
const struct fuse_buf buf 
)

Process a raw request supplied in a generic buffer

The fuse_buf may contain a memory buffer or a pipe file descriptor.

Parameters
sethe session
bufthe fuse_buf containing the request

Definition at line 2830 of file fuse_lowlevel.c.

◆ fuse_session_receive_buf()

int fuse_session_receive_buf ( struct fuse_session *  se,
struct fuse_buf buf 
)

Read a raw request from the kernel into the supplied buffer.

Depending on file system options, system capabilities, and request size the request is either read into a memory buffer or spliced into a temporary pipe.

Parameters
sethe session
bufthe fuse_buf to store the request in
Returns
the actual size of the raw request, or -errno on error

Definition at line 3285 of file fuse_lowlevel.c.

◆ fuse_session_reset()

void fuse_session_reset ( struct fuse_session *  se)

Reset the terminated flag of a session

Parameters
sethe session

◆ fuse_session_unmount()

void fuse_session_unmount ( struct fuse_session *  se)

Ensure that file system is unmounted.

In regular operation, the file system is typically unmounted by the user calling umount(8) or fusermount(1), which then terminates the FUSE session loop. However, the session loop may also terminate as a result of an explicit call to fuse_session_exit() (e.g. by a signal handler installed by fuse_set_signal_handler()). In this case the filesystem remains mounted, but any attempt to access it will block (while the filesystem process is still running) or give an ESHUTDOWN error (after the filesystem process has terminated).

If the communication channel with the FUSE kernel module is still open (i.e., if the session loop was terminated by an explicit call to fuse_session_exit()), this function will close it and unmount the filesystem. If the communication channel has been closed by the kernel, this method will do (almost) nothing.

NOTE: The above semantics mean that if the connection to the kernel is terminated via the /sys/fs/fuse/connections/NNN/abort file, this method will not unmount the filesystem.

Parameters
sethe session

Definition at line 3539 of file fuse_lowlevel.c.

fuse-3.18.2/doc/html/fuse-3_818_82_2include_2fuse__opt_8h.html0000644000175000017500000007717015156613431022461 0ustar berndbernd libfuse: fuse-3.18.2/include/fuse_opt.h File Reference
libfuse
fuse_opt.h File Reference

Go to the source code of this file.

Data Structures

struct  fuse_opt
 
struct  fuse_args
 

Macros

#define FUSE_OPT_KEY(templ, key)   { templ, -1U, key }
 
#define FUSE_OPT_END   { NULL, 0, 0 }
 
#define FUSE_ARGS_INIT(argc, argv)   { argc, argv, 0 }
 
#define FUSE_OPT_KEY_OPT   -1
 
#define FUSE_OPT_KEY_NONOPT   -2
 
#define FUSE_OPT_KEY_KEEP   -3
 
#define FUSE_OPT_KEY_DISCARD   -4
 

Typedefs

typedef int(* fuse_opt_proc_t) (void *data, const char *arg, int key, struct fuse_args *outargs)
 

Functions

int fuse_opt_parse (struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
 
int fuse_opt_add_opt (char **opts, const char *opt)
 
int fuse_opt_add_opt_escaped (char **opts, const char *opt)
 
int fuse_opt_add_arg (struct fuse_args *args, const char *arg)
 
int fuse_opt_insert_arg (struct fuse_args *args, int pos, const char *arg)
 
void fuse_opt_free_args (struct fuse_args *args)
 
int fuse_opt_match (const struct fuse_opt opts[], const char *opt)
 

Detailed Description

This file defines the option parsing interface of FUSE

Definition in file fuse_opt.h.

Macro Definition Documentation

◆ FUSE_ARGS_INIT

#define FUSE_ARGS_INIT (   argc,
  argv 
)    { argc, argv, 0 }

Initializer for 'struct fuse_args'

Definition at line 123 of file fuse_opt.h.

◆ FUSE_OPT_END

#define FUSE_OPT_END   { NULL, 0, 0 }

Last option. An array of 'struct fuse_opt' must end with a NULL template value

Definition at line 104 of file fuse_opt.h.

◆ FUSE_OPT_KEY

#define FUSE_OPT_KEY (   templ,
  key 
)    { templ, -1U, key }

Key option. In case of a match, the processing function will be called with the specified key.

Definition at line 98 of file fuse_opt.h.

◆ FUSE_OPT_KEY_DISCARD

#define FUSE_OPT_KEY_DISCARD   -4

Special key value for options to discard

Argument is not passed to processing function, but behave as if the processing function returned zero

Definition at line 153 of file fuse_opt.h.

◆ FUSE_OPT_KEY_KEEP

#define FUSE_OPT_KEY_KEEP   -3

Special key value for options to keep

Argument is not passed to processing function, but behave as if the processing function returned 1

Definition at line 145 of file fuse_opt.h.

◆ FUSE_OPT_KEY_NONOPT

#define FUSE_OPT_KEY_NONOPT   -2

Key value passed to the processing function for all non-options

Non-options are the arguments beginning with a character other than '-' or all arguments after the special '–' option

Definition at line 137 of file fuse_opt.h.

◆ FUSE_OPT_KEY_OPT

#define FUSE_OPT_KEY_OPT   -1

Key value passed to the processing function if an option did not match any template

Definition at line 129 of file fuse_opt.h.

Typedef Documentation

◆ fuse_opt_proc_t

typedef int(* fuse_opt_proc_t) (void *data, const char *arg, int key, struct fuse_args *outargs)

Processing function

This function is called if

  • option did not match any 'struct fuse_opt'
  • argument is a non-option
  • option did match and offset was set to -1

The 'arg' parameter will always contain the whole argument or option including the parameter if exists. A two-argument option ("-x foo") is always converted to single argument option of the form "-xfoo" before this function is called.

Options of the form '-ofoo' are passed to this function without the '-o' prefix.

The return value of this function determines whether this argument is to be inserted into the output argument vector, or discarded.

Parameters
datais the user data passed to the fuse_opt_parse() function
argis the whole argument or option
keydetermines why the processing function was called
outargsthe current output argument list
Returns
-1 on error, 0 if arg is to be discarded, 1 if arg should be kept

Definition at line 180 of file fuse_opt.h.

Function Documentation

◆ fuse_opt_add_arg()

int fuse_opt_add_arg ( struct fuse_args args,
const char *  arg 
)

Add an argument to a NULL terminated argument vector

Parameters
argsis the structure containing the current argument list
argis the new argument to add
Returns
-1 on allocation error, 0 on success

Definition at line 55 of file fuse_opt.c.

◆ fuse_opt_add_opt()

int fuse_opt_add_opt ( char **  opts,
const char *  opt 
)

Add an option to a comma separated option list

Parameters
optsis a pointer to an option list, may point to a NULL value
optis the option to add
Returns
-1 on allocation error, 0 on success

Definition at line 139 of file fuse_opt.c.

◆ fuse_opt_add_opt_escaped()

int fuse_opt_add_opt_escaped ( char **  opts,
const char *  opt 
)

Add an option, escaping commas, to a comma separated option list

Parameters
optsis a pointer to an option list, may point to a NULL value
optis the option to add
Returns
-1 on allocation error, 0 on success

Definition at line 144 of file fuse_opt.c.

◆ fuse_opt_free_args()

void fuse_opt_free_args ( struct fuse_args args)

Free the contents of argument list

The structure itself is not freed

Parameters
argsis the structure containing the argument list

Definition at line 34 of file fuse_opt.c.

◆ fuse_opt_insert_arg()

int fuse_opt_insert_arg ( struct fuse_args args,
int  pos,
const char *  arg 
)

Add an argument at the specified position in a NULL terminated argument vector

Adds the argument to the N-th position. This is useful for adding options at the beginning of the array which must not come after the special '–' option.

Parameters
argsis the structure containing the current argument list
posis the position at which to add the argument
argis the new argument to add
Returns
-1 on allocation error, 0 on success

Definition at line 95 of file fuse_opt.c.

◆ fuse_opt_match()

int fuse_opt_match ( const struct fuse_opt  opts[],
const char *  opt 
)

Check if an option matches

Parameters
optsis the option description array
optis the option to match
Returns
1 if a match is found, 0 if not

◆ fuse_opt_parse()

int fuse_opt_parse ( struct fuse_args args,
void *  data,
const struct fuse_opt  opts[],
fuse_opt_proc_t  proc 
)

Option parsing function

If 'args' was returned from a previous call to fuse_opt_parse() or it was constructed from

A NULL 'args' is equivalent to an empty argument vector

A NULL 'opts' is equivalent to an 'opts' array containing a single end marker

A NULL 'proc' is equivalent to a processing function always returning '1'

Parameters
argsis the input and output argument list
datais the user data
optsis the option description array
procis the processing function
Returns
-1 on error, 0 on success

Definition at line 398 of file fuse_opt.c.

fuse-3.18.2/doc/html/dir_042d006301a0f6339943f03c22d70dab.html0000644000175000017500000000414715156613431021066 0ustar berndbernd libfuse: fuse-3.18.2/doc Directory Reference
libfuse
doc Directory Reference
fuse-3.18.2/doc/html/dir_5dc95f8183277f5c6f5c244c43efe208.html0000644000175000017500000002345715156613431021315 0ustar berndbernd libfuse: fuse-3.18.2/example Directory Reference
libfuse
example Directory Reference
fuse-3.18.2/doc/html/dir_2379d157e40387655d5d09cb33050082.html0000644000175000017500000000733515156613431020707 0ustar berndbernd libfuse: fuse-3.18.2 Directory Reference
libfuse
fuse-3.18.2 Directory Reference

Directories

 example
 
 include
 
 lib
 
 test
 
 util
 
fuse-3.18.2/doc/html/dir_5cbcf3cc0c552e5807bf52f5541bdba8.html0000644000175000017500000001215215156613431021550 0ustar berndbernd libfuse: fuse-3.18.2/include Directory Reference
libfuse
include Directory Reference

Files

 cuse_lowlevel.h
 
 fuse.h
 
 fuse_common.h
 
 fuse_kernel.h
 
 fuse_log.h
 
 fuse_lowlevel.h
 
 fuse_mount_compat.h
 
 fuse_opt.h
 
fuse-3.18.2/doc/html/dir_5fb9f92dc673d37c57217e3feec2be83.html0000644000175000017500000002303315156613431021523 0ustar berndbernd libfuse: fuse-3.18.2/lib Directory Reference
libfuse
lib Directory Reference

Directories

 modules
 

Files

 buffer.c
 
 compat.c
 
 cuse_lowlevel.c
 
 fuse.c
 
 fuse_i.h
 
 fuse_log.c
 
 fuse_loop.c
 
 fuse_loop_mt.c
 
 fuse_lowlevel.c
 
 fuse_misc.h
 
 fuse_opt.c
 
 fuse_signals.c
 
 fuse_uring.c
 
 fuse_uring_i.h
 
 helper.c
 
 mount.c
 
 mount_bsd.c
 
 mount_util.c
 
 mount_util.h
 
 usdt.h
 
 util.c
 
 util.h
 
fuse-3.18.2/doc/html/dir_f353b27f473de27f9d371ee54fafa676.html0000644000175000017500000000575415156613431021455 0ustar berndbernd libfuse: fuse-3.18.2/lib/modules Directory Reference
libfuse
modules Directory Reference

Files

 iconv.c
 
 subdir.c
 
fuse-3.18.2/doc/html/dir_7015ddbf9bcaeb01169723f1ad3dc4f8.html0000644000175000017500000001351615156613431021554 0ustar berndbernd libfuse: fuse-3.18.2/test Directory Reference
libfuse
test Directory Reference

Files

 hello.c
 
 readdir_inode.c
 
 release_unlink_race.c
 
 stracedecode.c
 
 test_abi.c
 
 test_setattr.c
 
 test_signals.c
 
 test_syscalls.c
 
 test_want_conversion.c
 
 test_write_cache.c
 
 wrong_command.c
 
fuse-3.18.2/doc/html/dir_de6a3d84c7bdc16a0560151553ce19f4.html0000644000175000017500000000560315156613431021333 0ustar berndbernd libfuse: fuse-3.18.2/util Directory Reference
libfuse
util Directory Reference

Files

 fusermount.c
 
 mount.fuse.c
 
fuse-3.18.2/example/0000755000175000017500000000000015156613444013161 5ustar berndberndfuse-3.18.2/example/README.compile0000644000175000017500000000025215156613252015464 0ustar berndberndNote: * If the pkg-config command fails due to the absence of the fuse3.pc file, you should configure the path to the fuse3.pc file in the PKG_CONFIG_PATH variable.fuse-3.18.2/example/cuse.c0000644000175000017500000001665315156613252014274 0ustar berndbernd/* CUSE example: Character device in Userspace Copyright (C) 2008-2009 SUSE Linux Products GmbH Copyright (C) 2008-2009 Tejun Heo This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This example demonstrates how to implement a character device in * userspace ("CUSE"). This is only allowed for root. The character * device should appear in /dev under the specified name. It can be * tested with the cuse_client.c program. * * Mount the file system with: * * cuse -f --name=mydevice * * You should now have a new /dev/mydevice character device. To "unmount" it, * kill the "cuse" process. * * To compile this example, run * * gcc -Wall cuse.c `pkg-config fuse3 --cflags --libs` -o cuse * * ## Source code ## * \include cuse.c */ #define FUSE_USE_VERSION 31 #include #include #include #include #include #include #include #include #include "ioctl.h" static void *cusexmp_buf; static size_t cusexmp_size; static const char *usage = "usage: cusexmp [options]\n" "\n" "options:\n" " --help|-h print this help message\n" " --maj=MAJ|-M MAJ device major number\n" " --min=MIN|-m MIN device minor number\n" " --name=NAME|-n NAME device name (mandatory)\n" " -d -o debug enable debug output (implies -f)\n" " -f foreground operation\n" " -s disable multi-threaded operation\n" "\n"; static int cusexmp_resize(size_t new_size) { void *new_buf; if (new_size == cusexmp_size) return 0; new_buf = realloc(cusexmp_buf, new_size); if (!new_buf && new_size) return -ENOMEM; if (new_size > cusexmp_size) memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size); cusexmp_buf = new_buf; cusexmp_size = new_size; return 0; } static int cusexmp_expand(size_t new_size) { if (new_size > cusexmp_size) return cusexmp_resize(new_size); return 0; } static void cusexmp_init(void *userdata, struct fuse_conn_info *conn) { (void)userdata; /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; } static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi) { fuse_reply_open(req, fi); } static void cusexmp_read(fuse_req_t req, size_t size, off_t off, struct fuse_file_info *fi) { (void)fi; if (off >= cusexmp_size) off = cusexmp_size; if (size > cusexmp_size - off) size = cusexmp_size - off; fuse_reply_buf(req, cusexmp_buf + off, size); } static void cusexmp_write(fuse_req_t req, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) { (void)fi; if (cusexmp_expand(off + size)) { fuse_reply_err(req, ENOMEM); return; } memcpy(cusexmp_buf + off, buf, size); fuse_reply_write(req, size); } static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf, size_t in_bufsz, size_t out_bufsz, int is_read) { const struct fioc_rw_arg *arg; struct iovec in_iov[2], out_iov[3], iov[3]; size_t cur_size; /* read in arg */ in_iov[0].iov_base = addr; in_iov[0].iov_len = sizeof(*arg); if (!in_bufsz) { fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0); return; } arg = in_buf; in_buf += sizeof(*arg); in_bufsz -= sizeof(*arg); /* prepare size outputs */ out_iov[0].iov_base = addr + offsetof(struct fioc_rw_arg, prev_size); out_iov[0].iov_len = sizeof(arg->prev_size); out_iov[1].iov_base = addr + offsetof(struct fioc_rw_arg, new_size); out_iov[1].iov_len = sizeof(arg->new_size); /* prepare client buf */ if (is_read) { out_iov[2].iov_base = arg->buf; out_iov[2].iov_len = arg->size; if (!out_bufsz) { fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3); return; } } else { in_iov[1].iov_base = arg->buf; in_iov[1].iov_len = arg->size; if (arg->size && !in_bufsz) { fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2); return; } } /* we're all set */ cur_size = cusexmp_size; iov[0].iov_base = &cur_size; iov[0].iov_len = sizeof(cur_size); iov[1].iov_base = &cusexmp_size; iov[1].iov_len = sizeof(cusexmp_size); if (is_read) { size_t off = arg->offset; size_t size = arg->size; if (off >= cusexmp_size) off = cusexmp_size; if (size > cusexmp_size - off) size = cusexmp_size - off; iov[2].iov_base = cusexmp_buf + off; iov[2].iov_len = size; fuse_reply_ioctl_iov(req, size, iov, 3); } else { if (cusexmp_expand(arg->offset + in_bufsz)) { fuse_reply_err(req, ENOMEM); return; } memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz); fuse_reply_ioctl_iov(req, in_bufsz, iov, 2); } } static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz) { int is_read = 0; (void)fi; if (flags & FUSE_IOCTL_COMPAT) { fuse_reply_err(req, ENOSYS); return; } switch (cmd) { case FIOC_GET_SIZE: if (!out_bufsz) { struct iovec iov = { arg, sizeof(size_t) }; fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1); } else fuse_reply_ioctl(req, 0, &cusexmp_size, sizeof(cusexmp_size)); break; case FIOC_SET_SIZE: if (!in_bufsz) { struct iovec iov = { arg, sizeof(size_t) }; fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0); } else { cusexmp_resize(*(size_t *)in_buf); fuse_reply_ioctl(req, 0, NULL, 0); } break; case FIOC_READ: is_read = 1; /* fall through */ case FIOC_WRITE: fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read); break; default: fuse_reply_err(req, EINVAL); } } struct cusexmp_param { unsigned major; unsigned minor; char *dev_name; int is_help; }; #define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 } static const struct fuse_opt cusexmp_opts[] = { CUSEXMP_OPT("-M %u", major), CUSEXMP_OPT("--maj=%u", major), CUSEXMP_OPT("-m %u", minor), CUSEXMP_OPT("--min=%u", minor), CUSEXMP_OPT("-n %s", dev_name), CUSEXMP_OPT("--name=%s", dev_name), FUSE_OPT_KEY("-h", 0), FUSE_OPT_KEY("--help", 0), FUSE_OPT_END }; static int cusexmp_process_arg(void *data, const char *arg, int key, struct fuse_args *outargs) { struct cusexmp_param *param = data; (void)outargs; (void)arg; switch (key) { case 0: param->is_help = 1; fprintf(stderr, "%s", usage); return fuse_opt_add_arg(outargs, "-ho"); default: return 1; } } static const struct cuse_lowlevel_ops cusexmp_clop = { .init = cusexmp_init, .open = cusexmp_open, .read = cusexmp_read, .write = cusexmp_write, .ioctl = cusexmp_ioctl, }; int main(int argc, char **argv) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct cusexmp_param param = { 0, 0, NULL, 0 }; char dev_name[128] = "DEVNAME="; const char *dev_info_argv[] = { dev_name }; struct cuse_info ci; int ret = 1; if (fuse_opt_parse(&args, ¶m, cusexmp_opts, cusexmp_process_arg)) { printf("failed to parse option\n"); free(param.dev_name); goto out; } if (!param.is_help) { if (!param.dev_name) { fprintf(stderr, "Error: device name missing\n"); goto out; } strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME=")); free(param.dev_name); } memset(&ci, 0, sizeof(ci)); ci.dev_major = param.major; ci.dev_minor = param.minor; ci.dev_info_argc = 1; ci.dev_info_argv = dev_info_argv; ci.flags = CUSE_UNRESTRICTED_IOCTL; ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL); out: fuse_opt_free_args(&args); return ret; } fuse-3.18.2/example/cuse_client.c0000644000175000017500000000604715156613252015626 0ustar berndbernd/* FUSE fioclient: FUSE ioctl example client Copyright (C) 2008 SUSE Linux Products GmbH Copyright (C) 2008 Tejun Heo This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This program tests the cuse.c example file system. * * Example usage (assuming that /dev/foobar is a CUSE device provided * by the cuse.c example file system): * * $ cuse_client /dev/foobar s * 0 * * $ echo "hello" | cuse_client /dev/foobar w 6 * Writing 6 bytes * transferred 6 bytes (0 -> 6) * * $ cuse_client /dev/foobar s * 6 * * $ cuse_client /dev/foobar r 10 * hello * transferred 6 bytes (6 -> 6) * * Compiling this example * * gcc -Wall cuse_client.c -o cuse_client * * ## Source Code ## * \include cuse_client.c */ #include #include #include #include #include #include #include #include #include #include "ioctl.h" const char *usage = "Usage: cuse_client FIOC_FILE COMMAND\n" "\n" "COMMANDS\n" " s [SIZE] : get size if SIZE is omitted, set size otherwise\n" " r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n" " w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n" "\n"; static int do_rw(int fd, int is_read, size_t size, off_t offset, size_t *prev_size, size_t *new_size) { struct fioc_rw_arg arg = { .offset = offset }; ssize_t ret; arg.buf = calloc(1, size); if (!arg.buf) { fprintf(stderr, "failed to allocated %zu bytes\n", size); return -1; } if (is_read) { arg.size = size; ret = ioctl(fd, FIOC_READ, &arg); if (ret >= 0) fwrite(arg.buf, 1, ret, stdout); } else { arg.size = fread(arg.buf, 1, size, stdin); fprintf(stderr, "Writing %zu bytes\n", arg.size); ret = ioctl(fd, FIOC_WRITE, &arg); } if (ret >= 0) { *prev_size = arg.prev_size; *new_size = arg.new_size; } else perror("ioctl"); free(arg.buf); return ret; } int main(int argc, char **argv) { size_t param[2] = { }; size_t size, prev_size = 0, new_size = 0; char cmd; int fd, i, rc; if (argc < 3) goto usage; fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); return 1; } cmd = tolower(argv[2][0]); argc -= 3; argv += 3; for (i = 0; i < argc; i++) { char *endp; param[i] = strtoul(argv[i], &endp, 0); if (endp == argv[i] || *endp != '\0') goto usage; } switch (cmd) { case 's': if (!argc) { if (ioctl(fd, FIOC_GET_SIZE, &size)) { perror("ioctl"); goto error; } printf("%zu\n", size); } else { size = param[0]; if (ioctl(fd, FIOC_SET_SIZE, &size)) { perror("ioctl"); goto error; } } close(fd); return 0; case 'r': case 'w': rc = do_rw(fd, cmd == 'r', param[0], param[1], &prev_size, &new_size); if (rc < 0) goto error; fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n", rc, prev_size, new_size); close(fd); return 0; } usage: fprintf(stderr, "%s", usage); return 1; error: close(fd); return 1; } fuse-3.18.2/example/cxxopts.hpp0000644000175000017500000012322315156613252015402 0ustar berndbernd/* Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CXXOPTS_HPP_INCLUDED #define CXXOPTS_HPP_INCLUDED #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cpp_lib_optional #include #define CXXOPTS_HAS_OPTIONAL #endif #ifndef CXXOPTS_VECTOR_DELIMITER #define CXXOPTS_VECTOR_DELIMITER ',' #endif #define CXXOPTS__VERSION_MAJOR 2 #define CXXOPTS__VERSION_MINOR 2 #define CXXOPTS__VERSION_PATCH 1 namespace cxxopts { static constexpr struct { uint8_t major, minor, patch; } version = { CXXOPTS__VERSION_MAJOR, CXXOPTS__VERSION_MINOR, CXXOPTS__VERSION_PATCH }; } //when we ask cxxopts to use Unicode, help strings are processed using ICU, //which results in the correct lengths being computed for strings when they //are formatted for the help output //it is necessary to make sure that can be found by the //compiler, and that icu-uc is linked in to the binary. #ifdef CXXOPTS_USE_UNICODE #include namespace cxxopts { typedef icu::UnicodeString String; inline String toLocalString(std::string s) { return icu::UnicodeString::fromUTF8(std::move(s)); } class UnicodeStringIterator : public std::iterator { public: UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) : s(string) , i(pos) { } value_type operator*() const { return s->char32At(i); } bool operator==(const UnicodeStringIterator& rhs) const { return s == rhs.s && i == rhs.i; } bool operator!=(const UnicodeStringIterator& rhs) const { return !(*this == rhs); } UnicodeStringIterator& operator++() { ++i; return *this; } UnicodeStringIterator operator+(int32_t v) { return UnicodeStringIterator(s, i + v); } private: const icu::UnicodeString* s; int32_t i; }; inline String& stringAppend(String&s, String a) { return s.append(std::move(a)); } inline String& stringAppend(String& s, int n, UChar32 c) { for (int i = 0; i != n; ++i) { s.append(c); } return s; } template String& stringAppend(String& s, Iterator begin, Iterator end) { while (begin != end) { s.append(*begin); ++begin; } return s; } inline size_t stringLength(const String& s) { return s.length(); } inline std::string toUTF8String(const String& s) { std::string result; s.toUTF8String(result); return result; } inline bool empty(const String& s) { return s.isEmpty(); } } namespace std { inline cxxopts::UnicodeStringIterator begin(const icu::UnicodeString& s) { return cxxopts::UnicodeStringIterator(&s, 0); } inline cxxopts::UnicodeStringIterator end(const icu::UnicodeString& s) { return cxxopts::UnicodeStringIterator(&s, s.length()); } } //ifdef CXXOPTS_USE_UNICODE #else namespace cxxopts { typedef std::string String; template T toLocalString(T&& t) { return std::forward(t); } inline size_t stringLength(const String& s) { return s.length(); } inline String& stringAppend(String&s, String a) { return s.append(std::move(a)); } inline String& stringAppend(String& s, size_t n, char c) { return s.append(n, c); } template String& stringAppend(String& s, Iterator begin, Iterator end) { return s.append(begin, end); } template std::string toUTF8String(T&& t) { return std::forward(t); } inline bool empty(const std::string& s) { return s.empty(); } } //ifdef CXXOPTS_USE_UNICODE #endif namespace cxxopts { namespace { #ifdef _WIN32 const std::string LQUOTE("\'"); const std::string RQUOTE("\'"); #else const std::string LQUOTE("‘"); const std::string RQUOTE("’"); #endif } class Value : public std::enable_shared_from_this { public: virtual ~Value() = default; virtual std::shared_ptr clone() const = 0; virtual void parse(const std::string& text) const = 0; virtual void parse() const = 0; virtual bool has_default() const = 0; virtual bool is_container() const = 0; virtual bool has_implicit() const = 0; virtual std::string get_default_value() const = 0; virtual std::string get_implicit_value() const = 0; virtual std::shared_ptr default_value(const std::string& value) = 0; virtual std::shared_ptr implicit_value(const std::string& value) = 0; virtual std::shared_ptr no_implicit_value() = 0; virtual bool is_boolean() const = 0; }; class OptionException : public std::exception { public: OptionException(const std::string& message) : m_message(message) { } virtual const char* what() const noexcept { return m_message.c_str(); } private: std::string m_message; }; class OptionSpecException : public OptionException { public: OptionSpecException(const std::string& message) : OptionException(message) { } }; class OptionParseException : public OptionException { public: OptionParseException(const std::string& message) : OptionException(message) { } }; class option_exists_error : public OptionSpecException { public: option_exists_error(const std::string& option) : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") { } }; class invalid_option_format_error : public OptionSpecException { public: invalid_option_format_error(const std::string& format) : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) { } }; class option_syntax_exception : public OptionParseException { public: option_syntax_exception(const std::string& text) : OptionParseException("Argument " + LQUOTE + text + RQUOTE + " starts with a - but has incorrect syntax") { } }; class option_not_exists_exception : public OptionParseException { public: option_not_exists_exception(const std::string& option) : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") { } }; class missing_argument_exception : public OptionParseException { public: missing_argument_exception(const std::string& option) : OptionParseException( "Option " + LQUOTE + option + RQUOTE + " is missing an argument" ) { } }; class option_requires_argument_exception : public OptionParseException { public: option_requires_argument_exception(const std::string& option) : OptionParseException( "Option " + LQUOTE + option + RQUOTE + " requires an argument" ) { } }; class option_not_has_argument_exception : public OptionParseException { public: option_not_has_argument_exception ( const std::string& option, const std::string& arg ) : OptionParseException( "Option " + LQUOTE + option + RQUOTE + " does not take an argument, but argument " + LQUOTE + arg + RQUOTE + " given" ) { } }; class option_not_present_exception : public OptionParseException { public: option_not_present_exception(const std::string& option) : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") { } }; class argument_incorrect_type : public OptionParseException { public: argument_incorrect_type ( const std::string& arg ) : OptionParseException( "Argument " + LQUOTE + arg + RQUOTE + " failed to parse" ) { } }; class option_required_exception : public OptionParseException { public: option_required_exception(const std::string& option) : OptionParseException( "Option " + LQUOTE + option + RQUOTE + " is required but not present" ) { } }; namespace values { namespace { std::basic_regex integer_pattern ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); std::basic_regex truthy_pattern ("(t|T)(rue)?|1"); std::basic_regex falsy_pattern ("(f|F)(alse)?|0"); } namespace detail { template struct SignedCheck; template struct SignedCheck { template void operator()(bool negative, U u, const std::string& text) { if (negative) { if (u > static_cast((std::numeric_limits::min)())) { throw argument_incorrect_type(text); } } else { if (u > static_cast((std::numeric_limits::max)())) { throw argument_incorrect_type(text); } } } }; template struct SignedCheck { template void operator()(bool, U, const std::string&) {} }; template void check_signed_range(bool negative, U value, const std::string& text) { SignedCheck::is_signed>()(negative, value, text); } } template R checked_negate(T&& t, const std::string&, std::true_type) { // if we got to here, then `t` is a positive number that fits into // `R`. So to avoid MSVC C4146, we first cast it to `R`. // See https://github.com/jarro2783/cxxopts/issues/62 for more details. return -static_cast(t-1)-1; } template T checked_negate(T&&, const std::string& text, std::false_type) { throw argument_incorrect_type(text); } template void integer_parser(const std::string& text, T& value) { std::smatch match; std::regex_match(text, match, integer_pattern); if (match.length() == 0) { throw argument_incorrect_type(text); } if (match.length(4) > 0) { value = 0; return; } using US = typename std::make_unsigned::type; constexpr bool is_signed = std::numeric_limits::is_signed; const bool negative = match.length(1) > 0; const uint8_t base = match.length(2) > 0 ? 16 : 10; auto value_match = match[3]; US result = 0; for (auto iter = value_match.first; iter != value_match.second; ++iter) { US digit = 0; if (*iter >= '0' && *iter <= '9') { digit = static_cast(*iter - '0'); } else if (base == 16 && *iter >= 'a' && *iter <= 'f') { digit = static_cast(*iter - 'a' + 10); } else if (base == 16 && *iter >= 'A' && *iter <= 'F') { digit = static_cast(*iter - 'A' + 10); } else { throw argument_incorrect_type(text); } US next = result * base + digit; if (result > next) { throw argument_incorrect_type(text); } result = next; } detail::check_signed_range(negative, result, text); if (negative) { value = checked_negate(result, text, std::integral_constant()); } else { value = static_cast(result); } } template void stringstream_parser(const std::string& text, T& value) { std::stringstream in(text); in >> value; if (!in) { throw argument_incorrect_type(text); } } inline void parse_value(const std::string& text, uint8_t& value) { integer_parser(text, value); } inline void parse_value(const std::string& text, int8_t& value) { integer_parser(text, value); } inline void parse_value(const std::string& text, uint16_t& value) { integer_parser(text, value); } inline void parse_value(const std::string& text, int16_t& value) { integer_parser(text, value); } inline void parse_value(const std::string& text, uint32_t& value) { integer_parser(text, value); } inline void parse_value(const std::string& text, int32_t& value) { integer_parser(text, value); } inline void parse_value(const std::string& text, uint64_t& value) { integer_parser(text, value); } inline void parse_value(const std::string& text, int64_t& value) { integer_parser(text, value); } inline void parse_value(const std::string& text, bool& value) { std::smatch result; std::regex_match(text, result, truthy_pattern); if (!result.empty()) { value = true; return; } std::regex_match(text, result, falsy_pattern); if (!result.empty()) { value = false; return; } throw argument_incorrect_type(text); } inline void parse_value(const std::string& text, std::string& value) { value = text; } // The fallback parser. It uses the stringstream parser to parse all types // that have not been overloaded explicitly. It has to be placed in the // source code before all other more specialized templates. template void parse_value(const std::string& text, T& value) { stringstream_parser(text, value); } template void parse_value(const std::string& text, std::vector& value) { std::stringstream in(text); std::string token; while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { T v; parse_value(token, v); value.emplace_back(std::move(v)); } } #ifdef CXXOPTS_HAS_OPTIONAL template void parse_value(const std::string& text, std::optional& value) { T result; parse_value(text, result); value = std::move(result); } #endif template struct type_is_container { static constexpr bool value = false; }; template struct type_is_container> { static constexpr bool value = true; }; template class abstract_value : public Value { using Self = abstract_value; public: abstract_value() : m_result(std::make_shared()) , m_store(m_result.get()) { } abstract_value(T* t) : m_store(t) { } virtual ~abstract_value() = default; abstract_value(const abstract_value& rhs) { if (rhs.m_result) { m_result = std::make_shared(); m_store = m_result.get(); } else { m_store = rhs.m_store; } m_default = rhs.m_default; m_implicit = rhs.m_implicit; m_default_value = rhs.m_default_value; m_implicit_value = rhs.m_implicit_value; } void parse(const std::string& text) const { parse_value(text, *m_store); } bool is_container() const { return type_is_container::value; } void parse() const { parse_value(m_default_value, *m_store); } bool has_default() const { return m_default; } bool has_implicit() const { return m_implicit; } std::shared_ptr default_value(const std::string& value) { m_default = true; m_default_value = value; return shared_from_this(); } std::shared_ptr implicit_value(const std::string& value) { m_implicit = true; m_implicit_value = value; return shared_from_this(); } std::shared_ptr no_implicit_value() { m_implicit = false; return shared_from_this(); } std::string get_default_value() const { return m_default_value; } std::string get_implicit_value() const { return m_implicit_value; } bool is_boolean() const { return std::is_same::value; } const T& get() const { if (m_store == nullptr) { return *m_result; } else { return *m_store; } } protected: std::shared_ptr m_result; T* m_store; bool m_default = false; bool m_implicit = false; std::string m_default_value; std::string m_implicit_value; }; template class standard_value : public abstract_value { public: using abstract_value::abstract_value; std::shared_ptr clone() const { return std::make_shared>(*this); } }; template <> class standard_value : public abstract_value { public: ~standard_value() = default; standard_value() { set_default_and_implicit(); } standard_value(bool* b) : abstract_value(b) { set_default_and_implicit(); } std::shared_ptr clone() const { return std::make_shared>(*this); } private: void set_default_and_implicit() { m_default = true; m_default_value = "false"; m_implicit = true; m_implicit_value = "true"; } }; } template std::shared_ptr value() { return std::make_shared>(); } template std::shared_ptr value(T& t) { return std::make_shared>(&t); } class OptionAdder; class OptionDetails { public: OptionDetails ( const std::string& short_, const std::string& long_, const String& desc, std::shared_ptr val ) : m_short(short_) , m_long(long_) , m_desc(desc) , m_value(val) , m_count(0) { } OptionDetails(const OptionDetails& rhs) : m_desc(rhs.m_desc) , m_count(rhs.m_count) { m_value = rhs.m_value->clone(); } OptionDetails(OptionDetails&& rhs) = default; const String& description() const { return m_desc; } const Value& value() const { return *m_value; } std::shared_ptr make_storage() const { return m_value->clone(); } const std::string& short_name() const { return m_short; } const std::string& long_name() const { return m_long; } private: std::string m_short; std::string m_long; String m_desc; std::shared_ptr m_value; int m_count; }; struct HelpOptionDetails { std::string s; std::string l; String desc; bool has_default; std::string default_value; bool has_implicit; std::string implicit_value; std::string arg_help; bool is_container; bool is_boolean; }; struct HelpGroupDetails { std::string name; std::string description; std::vector options; }; class OptionValue { public: void parse ( std::shared_ptr details, const std::string& text ) { ensure_value(details); ++m_count; m_value->parse(text); } void parse_default(std::shared_ptr details) { ensure_value(details); m_default = true; m_value->parse(); } size_t count() const noexcept { return m_count; } // TODO: maybe default options should count towards the number of arguments bool has_default() const noexcept { return m_default; } template const T& as() const { if (m_value == nullptr) { throw std::domain_error("No value"); } #ifdef CXXOPTS_NO_RTTI return static_cast&>(*m_value).get(); #else return dynamic_cast&>(*m_value).get(); #endif } private: void ensure_value(std::shared_ptr details) { if (m_value == nullptr) { m_value = details->make_storage(); } } std::shared_ptr m_value; size_t m_count = 0; bool m_default = false; }; class KeyValue { public: KeyValue(std::string key_, std::string value_) : m_key(std::move(key_)) , m_value(std::move(value_)) { } const std::string& key() const { return m_key; } const std::string& value() const { return m_value; } template T as() const { T result; values::parse_value(m_value, result); return result; } private: std::string m_key; std::string m_value; }; class ParseResult { public: ParseResult( const std::shared_ptr< std::unordered_map> >, std::vector, bool allow_unrecognised, int&, char**&); size_t count(const std::string& o) const { auto iter = m_options->find(o); if (iter == m_options->end()) { return 0; } auto riter = m_results.find(iter->second); return riter->second.count(); } const OptionValue& operator[](const std::string& option) const { auto iter = m_options->find(option); if (iter == m_options->end()) { throw option_not_present_exception(option); } auto riter = m_results.find(iter->second); return riter->second; } const std::vector& arguments() const { return m_sequential; } private: void parse(int& argc, char**& argv); void add_to_option(const std::string& option, const std::string& arg); bool consume_positional(std::string a); void parse_option ( std::shared_ptr value, const std::string& name, const std::string& arg = "" ); void parse_default(std::shared_ptr details); void checked_parse_arg ( int argc, char* argv[], int& current, std::shared_ptr value, const std::string& name ); const std::shared_ptr< std::unordered_map> > m_options; std::vector m_positional; std::vector::iterator m_next_positional; std::unordered_set m_positional_set; std::unordered_map, OptionValue> m_results; bool m_allow_unrecognised; std::vector m_sequential; }; class Options { typedef std::unordered_map> OptionMap; public: Options(std::string program, std::string help_string = "") : m_program(std::move(program)) , m_help_string(toLocalString(std::move(help_string))) , m_custom_help("[OPTION...]") , m_positional_help("positional parameters") , m_show_positional(false) , m_allow_unrecognised(false) , m_options(std::make_shared()) , m_next_positional(m_positional.end()) { } Options& positional_help(std::string help_text) { m_positional_help = std::move(help_text); return *this; } Options& custom_help(std::string help_text) { m_custom_help = std::move(help_text); return *this; } Options& show_positional_help() { m_show_positional = true; return *this; } Options& allow_unrecognised_options() { m_allow_unrecognised = true; return *this; } ParseResult parse(int& argc, char**& argv); OptionAdder add_options(std::string group = ""); void add_option ( const std::string& group, const std::string& s, const std::string& l, std::string desc, std::shared_ptr value, std::string arg_help ); //parse positional arguments into the given option void parse_positional(std::string option); void parse_positional(std::vector options); void parse_positional(std::initializer_list options); template void parse_positional(Iterator begin, Iterator end) { parse_positional(std::vector{begin, end}); } std::string help(const std::vector& groups = {}) const; const std::vector groups() const; const HelpGroupDetails& group_help(const std::string& group) const; private: void add_one_option ( const std::string& option, std::shared_ptr details ); String help_one_group(const std::string& group) const; void generate_group_help ( String& result, const std::vector& groups ) const; void generate_all_groups_help(String& result) const; std::string m_program; String m_help_string; std::string m_custom_help; std::string m_positional_help; bool m_show_positional; bool m_allow_unrecognised; std::shared_ptr m_options; std::vector m_positional; std::vector::iterator m_next_positional; std::unordered_set m_positional_set; //mapping from groups to help options std::map m_help; }; class OptionAdder { public: OptionAdder(Options& options, std::string group) : m_options(options), m_group(std::move(group)) { } OptionAdder& operator() ( const std::string& opts, const std::string& desc, std::shared_ptr value = ::cxxopts::value(), std::string arg_help = "" ); private: Options& m_options; std::string m_group; }; namespace { constexpr int OPTION_LONGEST = 30; constexpr int OPTION_DESC_GAP = 2; std::basic_regex option_matcher ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); std::basic_regex option_specifier ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); String format_option ( const HelpOptionDetails& o ) { auto& s = o.s; auto& l = o.l; String result = " "; if (s.size() > 0) { result += "-" + toLocalString(s) + ","; } else { result += " "; } if (l.size() > 0) { result += " --" + toLocalString(l); } auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; if (!o.is_boolean) { if (o.has_implicit) { result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; } else { result += " " + arg; } } return result; } String format_description ( const HelpOptionDetails& o, size_t start, size_t width ) { auto desc = o.desc; if (o.has_default && (!o.is_boolean || o.default_value != "false")) { desc += toLocalString(" (default: " + o.default_value + ")"); } String result; auto current = std::begin(desc); auto startLine = current; auto lastSpace = current; auto size = size_t{}; while (current != std::end(desc)) { if (*current == ' ') { lastSpace = current; } if (*current == '\n') { startLine = current + 1; lastSpace = startLine; } else if (size > width) { if (lastSpace == startLine) { stringAppend(result, startLine, current + 1); stringAppend(result, "\n"); stringAppend(result, start, ' '); startLine = current + 1; lastSpace = startLine; } else { stringAppend(result, startLine, lastSpace); stringAppend(result, "\n"); stringAppend(result, start, ' '); startLine = lastSpace + 1; lastSpace = startLine; } size = 0; } else { ++size; } ++current; } //append whatever is left stringAppend(result, startLine, current); return result; } } inline ParseResult::ParseResult ( const std::shared_ptr< std::unordered_map> > options, std::vector positional, bool allow_unrecognised, int& argc, char**& argv ) : m_options(options) , m_positional(std::move(positional)) , m_next_positional(m_positional.begin()) , m_allow_unrecognised(allow_unrecognised) { parse(argc, argv); } inline OptionAdder Options::add_options(std::string group) { return OptionAdder(*this, std::move(group)); } inline OptionAdder& OptionAdder::operator() ( const std::string& opts, const std::string& desc, std::shared_ptr value, std::string arg_help ) { std::match_results result; std::regex_match(opts.c_str(), result, option_specifier); if (result.empty()) { throw invalid_option_format_error(opts); } const auto& short_match = result[2]; const auto& long_match = result[3]; if (!short_match.length() && !long_match.length()) { throw invalid_option_format_error(opts); } else if (long_match.length() == 1 && short_match.length()) { throw invalid_option_format_error(opts); } auto option_names = [] ( const std::sub_match& short_, const std::sub_match& long_ ) { if (long_.length() == 1) { return std::make_tuple(long_.str(), short_.str()); } else { return std::make_tuple(short_.str(), long_.str()); } }(short_match, long_match); m_options.add_option ( m_group, std::get<0>(option_names), std::get<1>(option_names), desc, value, std::move(arg_help) ); return *this; } inline void ParseResult::parse_default(std::shared_ptr details) { m_results[details].parse_default(details); } inline void ParseResult::parse_option ( std::shared_ptr value, const std::string& /*name*/, const std::string& arg ) { auto& result = m_results[value]; result.parse(value, arg); m_sequential.emplace_back(value->long_name(), arg); } inline void ParseResult::checked_parse_arg ( int argc, char* argv[], int& current, std::shared_ptr value, const std::string& name ) { if (current + 1 >= argc) { if (value->value().has_implicit()) { parse_option(value, name, value->value().get_implicit_value()); } else { throw missing_argument_exception(name); } } else { if (value->value().has_implicit()) { parse_option(value, name, value->value().get_implicit_value()); } else { parse_option(value, name, argv[current + 1]); ++current; } } } inline void ParseResult::add_to_option(const std::string& option, const std::string& arg) { auto iter = m_options->find(option); if (iter == m_options->end()) { throw option_not_exists_exception(option); } parse_option(iter->second, option, arg); } inline bool ParseResult::consume_positional(std::string a) { while (m_next_positional != m_positional.end()) { auto iter = m_options->find(*m_next_positional); if (iter != m_options->end()) { auto& result = m_results[iter->second]; if (!iter->second->value().is_container()) { if (result.count() == 0) { add_to_option(*m_next_positional, a); ++m_next_positional; return true; } else { ++m_next_positional; continue; } } else { add_to_option(*m_next_positional, a); return true; } } else { throw option_not_exists_exception(*m_next_positional); } } return false; } inline void Options::parse_positional(std::string option) { parse_positional(std::vector{std::move(option)}); } inline void Options::parse_positional(std::vector options) { m_positional = std::move(options); m_next_positional = m_positional.begin(); m_positional_set.insert(m_positional.begin(), m_positional.end()); } inline void Options::parse_positional(std::initializer_list options) { parse_positional(std::vector(std::move(options))); } inline ParseResult Options::parse(int& argc, char**& argv) { ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv); return result; } inline void ParseResult::parse(int& argc, char**& argv) { int current = 1; int nextKeep = 1; bool consume_remaining = false; while (current != argc) { if (strcmp(argv[current], "--") == 0) { consume_remaining = true; ++current; break; } std::match_results result; std::regex_match(argv[current], result, option_matcher); if (result.empty()) { //not a flag // but if it starts with a `-`, then it's an error if (argv[current][0] == '-' && argv[current][1] != '\0') { if (!m_allow_unrecognised) { throw option_syntax_exception(argv[current]); } } //if true is returned here then it was consumed, otherwise it is //ignored if (consume_positional(argv[current])) { } else { argv[nextKeep] = argv[current]; ++nextKeep; } //if we return from here then it was parsed successfully, so continue } else { //short or long option? if (result[4].length() != 0) { const std::string& s = result[4]; for (std::size_t i = 0; i != s.size(); ++i) { std::string name(1, s[i]); auto iter = m_options->find(name); if (iter == m_options->end()) { if (m_allow_unrecognised) { continue; } else { //error throw option_not_exists_exception(name); } } auto value = iter->second; if (i + 1 == s.size()) { //it must be the last argument checked_parse_arg(argc, argv, current, value, name); } else if (value->value().has_implicit()) { parse_option(value, name, value->value().get_implicit_value()); } else { //error throw option_requires_argument_exception(name); } } } else if (result[1].length() != 0) { const std::string& name = result[1]; auto iter = m_options->find(name); if (iter == m_options->end()) { if (m_allow_unrecognised) { // keep unrecognised options in argument list, skip to next argument argv[nextKeep] = argv[current]; ++nextKeep; ++current; continue; } else { //error throw option_not_exists_exception(name); } } auto opt = iter->second; //equals provided for long option? if (result[2].length() != 0) { //parse the option given parse_option(opt, name, result[3]); } else { //parse the next argument checked_parse_arg(argc, argv, current, opt, name); } } } ++current; } for (auto& opt : *m_options) { auto& detail = opt.second; auto& value = detail->value(); auto& store = m_results[detail]; if(value.has_default() && !store.count() && !store.has_default()){ parse_default(detail); } } if (consume_remaining) { while (current < argc) { if (!consume_positional(argv[current])) { break; } ++current; } //adjust argv for any that couldn't be swallowed while (current != argc) { argv[nextKeep] = argv[current]; ++nextKeep; ++current; } } argc = nextKeep; } inline void Options::add_option ( const std::string& group, const std::string& s, const std::string& l, std::string desc, std::shared_ptr value, std::string arg_help ) { auto stringDesc = toLocalString(std::move(desc)); auto option = std::make_shared(s, l, stringDesc, value); if (s.size() > 0) { add_one_option(s, option); } if (l.size() > 0) { add_one_option(l, option); } //add the help details auto& options = m_help[group]; options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, value->has_default(), value->get_default_value(), value->has_implicit(), value->get_implicit_value(), std::move(arg_help), value->is_container(), value->is_boolean()}); } inline void Options::add_one_option ( const std::string& option, std::shared_ptr details ) { auto in = m_options->emplace(option, details); if (!in.second) { throw option_exists_error(option); } } inline String Options::help_one_group(const std::string& g) const { typedef std::vector> OptionHelp; auto group = m_help.find(g); if (group == m_help.end()) { return ""; } OptionHelp format; size_t longest = 0; String result; if (!g.empty()) { result += toLocalString(" " + g + " options:\n"); } for (const auto& o : group->second.options) { if (m_positional_set.find(o.l) != m_positional_set.end() && !m_show_positional) { continue; } auto s = format_option(o); longest = (std::max)(longest, stringLength(s)); format.push_back(std::make_pair(s, String())); } longest = (std::min)(longest, static_cast(OPTION_LONGEST)); //widest allowed description auto allowed = size_t{76} - longest - OPTION_DESC_GAP; auto fiter = format.begin(); for (const auto& o : group->second.options) { if (m_positional_set.find(o.l) != m_positional_set.end() && !m_show_positional) { continue; } auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); result += fiter->first; if (stringLength(fiter->first) > longest) { result += '\n'; result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); } else { result += toLocalString(std::string(longest + OPTION_DESC_GAP - stringLength(fiter->first), ' ')); } result += d; result += '\n'; ++fiter; } return result; } inline void Options::generate_group_help ( String& result, const std::vector& print_groups ) const { for (size_t i = 0; i != print_groups.size(); ++i) { const String& group_help_text = help_one_group(print_groups[i]); if (empty(group_help_text)) { continue; } result += group_help_text; if (i < print_groups.size() - 1) { result += '\n'; } } } inline void Options::generate_all_groups_help(String& result) const { std::vector all_groups; all_groups.reserve(m_help.size()); for (auto& group : m_help) { all_groups.push_back(group.first); } generate_group_help(result, all_groups); } inline std::string Options::help(const std::vector& help_groups) const { String result = m_help_string + "\nUsage:\n " + toLocalString(m_program) + " " + toLocalString(m_custom_help); if (m_positional.size() > 0 && m_positional_help.size() > 0) { result += " " + toLocalString(m_positional_help); } result += "\n\n"; if (help_groups.size() == 0) { generate_all_groups_help(result); } else { generate_group_help(result, help_groups); } return toUTF8String(result); } inline const std::vector Options::groups() const { std::vector g; std::transform( m_help.begin(), m_help.end(), std::back_inserter(g), [] (const std::map::value_type& pair) { return pair.first; } ); return g; } inline const HelpGroupDetails& Options::group_help(const std::string& group) const { return m_help.at(group); } } #endif //CXXOPTS_HPP_INCLUDED fuse-3.18.2/example/hello.c0000644000175000017500000001047015156613252014427 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * minimal example filesystem using high-level API * * Compile with: * * gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello * * ## Source code ## * \include hello.c */ #define FUSE_USE_VERSION 31 #include #include #include #include #include #include #include /* * Command line options * * We can't set default values for the char* fields here because * fuse_opt_parse would attempt to free() them when the user specifies * different values on the command line. */ static struct options { const char *filename; const char *contents; int show_help; } options; #define OPTION(t, p) \ { t, offsetof(struct options, p), 1 } static const struct fuse_opt option_spec[] = { OPTION("--name=%s", filename), OPTION("--contents=%s", contents), OPTION("-h", show_help), OPTION("--help", show_help), FUSE_OPT_END }; static void *hello_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { (void) conn; cfg->kernel_cache = 1; /* Test setting flags the old way */ fuse_set_feature_flag(conn, FUSE_CAP_ASYNC_READ); fuse_unset_feature_flag(conn, FUSE_CAP_ASYNC_READ); return NULL; } static int hello_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { (void) fi; int res = 0; memset(stbuf, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; } else if (strcmp(path+1, options.filename) == 0) { stbuf->st_mode = S_IFREG | 0444; stbuf->st_nlink = 1; stbuf->st_size = strlen(options.contents); } else res = -ENOENT; return res; } static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { (void) offset; (void) fi; (void) flags; if (strcmp(path, "/") != 0) return -ENOENT; filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS); filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS); filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS); return 0; } static int hello_open(const char *path, struct fuse_file_info *fi) { if (strcmp(path+1, options.filename) != 0) return -ENOENT; if ((fi->flags & O_ACCMODE) != O_RDONLY) return -EACCES; return 0; } static int hello_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { size_t len; (void) fi; if(strcmp(path+1, options.filename) != 0) return -ENOENT; len = strlen(options.contents); if (offset < len) { if (offset + size > len) size = len - offset; memcpy(buf, options.contents + offset, size); } else size = 0; return size; } static const struct fuse_operations hello_oper = { .init = hello_init, .getattr = hello_getattr, .readdir = hello_readdir, .open = hello_open, .read = hello_read, }; static void show_help(const char *progname) { printf("usage: %s [options] \n\n", progname); printf("File-system specific options:\n" " --name= Name of the \"hello\" file\n" " (default: \"hello\")\n" " --contents= Contents \"hello\" file\n" " (default \"Hello, World!\\n\")\n" "\n"); } int main(int argc, char *argv[]) { int ret; struct fuse_args args = FUSE_ARGS_INIT(argc, argv); /* Set defaults -- we have to use strdup so that fuse_opt_parse can free the defaults if other values are specified */ options.filename = strdup("hello"); options.contents = strdup("Hello World!\n"); /* Parse options */ if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) return 1; /* When --help is specified, first print our own file-system specific help text, then signal fuse_main to show additional help (by adding `--help` to the options again) without usage: line (by setting argv[0] to the empty string) */ if (options.show_help) { show_help(argv[0]); assert(fuse_opt_add_arg(&args, "--help") == 0); args.argv[0][0] = '\0'; } ret = fuse_main(args.argc, args.argv, &hello_oper, NULL); fuse_opt_free_args(&args); return ret; } fuse-3.18.2/example/hello_ll.c0000644000175000017500000001505515156613252015122 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * minimal example filesystem using low-level API * * Compile with: * * gcc -Wall hello_ll.c `pkg-config fuse3 --cflags --libs` -o hello_ll * * Note: If the pkg-config command fails due to the absence of the fuse3.pc * file, you should configure the path to the fuse3.pc file in the * PKG_CONFIG_PATH variable. * * ## Source code ## * \include hello_ll.c */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include #include #include #include #include #include #include #include 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_init(void *userdata, struct fuse_conn_info *conn) { (void)userdata; /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; /* Test setting flags the old way */ conn->want = FUSE_CAP_ASYNC_READ; conn->want &= ~FUSE_CAP_ASYNC_READ; } 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 void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) { (void)size; assert(ino == 1 || 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 == 1 || 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 == 1 || 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 = { .init = hello_ll_init, .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[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_session *se; struct fuse_cmdline_opts opts; struct fuse_loop_config *config; 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(); 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; } if(opts.mountpoint == NULL) { printf("usage: %s [options] \n", argv[0]); printf(" %s --help\n", argv[0]); ret = 1; 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; if (fuse_session_mount(se, opts.mountpoint) != 0) goto err_out3; fuse_daemonize(opts.foreground); /* Block until ctrl+c or fusermount -u */ if (opts.singlethread) ret = fuse_session_loop(se); else { config = fuse_loop_cfg_create(); fuse_loop_cfg_set_clone_fd(config, opts.clone_fd); fuse_loop_cfg_set_max_threads(config, opts.max_threads); ret = fuse_session_loop_mt(se, config); fuse_loop_cfg_destroy(config); config = NULL; } fuse_session_unmount(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; } fuse-3.18.2/example/hello_ll_uds.c0000644000175000017500000002011315156613252015764 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Copyright (C) 2022 Tofik Sonono This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @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 #include #include #include #include #include #include #include #include #include #include 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_init(void *userdata, struct fuse_conn_info *conn) { (void)userdata; /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; } 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 = { .init = hello_ll_init, .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 %zu 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; } fuse-3.18.2/example/invalidate_path.c0000644000175000017500000001554715156613252016472 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2016 Nikolaus Rath (C) 2017 EditShare LLC This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This example implements a file system with two files: * * 'current-time', whose contents change dynamically: * it always contains the current time (same as in * notify_inval_inode.c). * * 'growing', whose size changes dynamically, growing * by 1 byte after each update. This aims to check * if cached file metadata is also invalidated. * * ## Compilation ## * * gcc -Wall invalidate_path.c `pkg-config fuse3 --cflags --libs` -o invalidate_path * * ## Source code ## * \include invalidate_path.c */ #define FUSE_USE_VERSION 34 #include #include /* for fuse_cmdline_opts */ #include #include #include #include #include #include #include #include #include /* We can't actually tell the kernel that there is no timeout, so we just send a big value */ #define NO_TIMEOUT 500000 #define MAX_STR_LEN 128 #define TIME_FILE_NAME "current_time" #define TIME_FILE_INO 2 #define GROW_FILE_NAME "growing" #define GROW_FILE_INO 3 static char time_file_contents[MAX_STR_LEN]; static size_t grow_file_size; /* Command line parsing */ struct options { int no_notify; int update_interval; }; static struct options options = { .no_notify = 0, .update_interval = 1, }; #define OPTION(t, p) { t, offsetof(struct options, p), 1 } static const struct fuse_opt option_spec[] = { OPTION("--no-notify", no_notify), OPTION("--update-interval=%d", update_interval), FUSE_OPT_END }; static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { (void) conn; cfg->entry_timeout = NO_TIMEOUT; cfg->attr_timeout = NO_TIMEOUT; cfg->negative_timeout = 0; return NULL; } static int xmp_getattr(const char *path, struct stat *stbuf, struct fuse_file_info* fi) { (void) fi; if (strcmp(path, "/") == 0) { stbuf->st_ino = 1; stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 1; } else if (strcmp(path, "/" TIME_FILE_NAME) == 0) { stbuf->st_ino = TIME_FILE_INO; stbuf->st_mode = S_IFREG | 0444; stbuf->st_nlink = 1; stbuf->st_size = strlen(time_file_contents); } else if (strcmp(path, "/" GROW_FILE_NAME) == 0) { stbuf->st_ino = GROW_FILE_INO; stbuf->st_mode = S_IFREG | 0444; stbuf->st_nlink = 1; stbuf->st_size = grow_file_size; } else { return -ENOENT; } return 0; } static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { (void) fi; (void) offset; (void) flags; if (strcmp(path, "/") != 0) { return -ENOTDIR; } else { (void) filler; (void) buf; struct stat file_stat; xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL); filler(buf, TIME_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS); xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL); filler(buf, GROW_FILE_NAME, &file_stat, 0, FUSE_FILL_DIR_DEFAULTS); return 0; } } static int xmp_open(const char *path, struct fuse_file_info *fi) { (void) path; /* Make cache persistent even if file is closed, this makes it easier to see the effects */ fi->keep_cache = 1; return 0; } static int xmp_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { (void) fi; (void) offset; if (strcmp(path, "/" TIME_FILE_NAME) == 0) { int file_length = strlen(time_file_contents); int to_copy = offset + size <= file_length ? size : file_length - offset; memcpy(buf, time_file_contents, to_copy); return to_copy; } else { assert(strcmp(path, "/" GROW_FILE_NAME) == 0); int to_copy = offset + size <= grow_file_size ? size : grow_file_size - offset; memset(buf, 'x', to_copy); return to_copy; } } static const struct fuse_operations xmp_oper = { .init = xmp_init, .getattr = xmp_getattr, .readdir = xmp_readdir, .open = xmp_open, .read = xmp_read, }; static void update_fs(void) { static int count = 0; struct tm *now; time_t t; t = time(NULL); now = localtime(&t); assert(now != NULL); int time_file_size = strftime(time_file_contents, MAX_STR_LEN, "The current time is %H:%M:%S\n", now); assert(time_file_size != 0); grow_file_size = count++; } static int invalidate(struct fuse *fuse, const char *path) { int status = fuse_invalidate_path(fuse, path); if (status == -ENOENT) { return 0; } else { return status; } } static void* update_fs_loop(void *data) { struct fuse *fuse = (struct fuse*) data; while (1) { update_fs(); if (!options.no_notify) { assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0); assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0); } sleep(options.update_interval); } return NULL; } static void show_help(const char *progname) { printf("usage: %s [options] \n\n", progname); printf("File-system specific options:\n" " --update-interval= Update-rate of file system contents\n" " --no-notify Disable kernel notifications\n" "\n"); } int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse *fuse; struct fuse_cmdline_opts opts; struct fuse_loop_config config; int res; /* Initialize the files */ update_fs(); if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) return 1; if (fuse_parse_cmdline(&args, &opts) != 0) return 1; if (opts.show_version) { printf("FUSE library version %s\n", fuse_pkgversion()); fuse_lowlevel_version(); res = 0; goto out1; } else if (opts.show_help) { show_help(argv[0]); fuse_cmdline_help(); fuse_lib_help(&args); res = 0; goto out1; } else if (!opts.mountpoint) { fprintf(stderr, "error: no mountpoint specified\n"); res = 1; goto out1; } fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL); if (fuse == NULL) { res = 1; goto out1; } if (fuse_mount(fuse,opts.mountpoint) != 0) { res = 1; goto out2; } if (fuse_daemonize(opts.foreground) != 0) { res = 1; goto out3; } pthread_t updater; /* Start thread to update file contents */ int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse); if (ret != 0) { fprintf(stderr, "pthread_create failed with %s\n", strerror(ret)); return 1; }; struct fuse_session *se = fuse_get_session(fuse); if (fuse_set_signal_handlers(se) != 0) { res = 1; goto out3; } if (opts.singlethread) res = fuse_loop(fuse); else { config.clone_fd = opts.clone_fd; config.max_idle_threads = opts.max_idle_threads; res = fuse_loop_mt(fuse, &config); } if (res) res = 1; fuse_remove_signal_handlers(se); out3: fuse_unmount(fuse); out2: fuse_destroy(fuse); out1: free(opts.mountpoint); fuse_opt_free_args(&args); return res; } fuse-3.18.2/example/ioctl.c0000644000175000017500000001047315156613252014441 0ustar berndbernd/* FUSE fioc: FUSE ioctl example Copyright (C) 2008 SUSE Linux Products GmbH Copyright (C) 2008 Tejun Heo This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * @tableofcontents * * This example illustrates how to write a FUSE file system that can * process (a restricted set of) ioctls. It can be tested with the * ioctl_client.c program. * * Compile with: * * gcc -Wall ioctl.c `pkg-config fuse3 --cflags --libs` -o ioctl * * ## Source code ## * \include ioctl.c */ #define FUSE_USE_VERSION 35 #include #include #include #include #include #include #include #include "ioctl.h" #define FIOC_NAME "fioc" enum { FIOC_NONE, FIOC_ROOT, FIOC_FILE, }; static void *fioc_buf; static size_t fioc_size; static int fioc_resize(size_t new_size) { void *new_buf; if (new_size == fioc_size) return 0; new_buf = realloc(fioc_buf, new_size); if (!new_buf && new_size) return -ENOMEM; if (new_size > fioc_size) memset(new_buf + fioc_size, 0, new_size - fioc_size); fioc_buf = new_buf; fioc_size = new_size; return 0; } static int fioc_expand(size_t new_size) { if (new_size > fioc_size) return fioc_resize(new_size); return 0; } static int fioc_file_type(const char *path) { if (strcmp(path, "/") == 0) return FIOC_ROOT; if (strcmp(path, "/" FIOC_NAME) == 0) return FIOC_FILE; return FIOC_NONE; } static int fioc_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { (void) fi; stbuf->st_uid = getuid(); stbuf->st_gid = getgid(); stbuf->st_atime = stbuf->st_mtime = time(NULL); switch (fioc_file_type(path)) { case FIOC_ROOT: stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; break; case FIOC_FILE: stbuf->st_mode = S_IFREG | 0644; stbuf->st_nlink = 1; stbuf->st_size = fioc_size; break; case FIOC_NONE: return -ENOENT; } return 0; } static int fioc_open(const char *path, struct fuse_file_info *fi) { (void) fi; if (fioc_file_type(path) != FIOC_NONE) return 0; return -ENOENT; } static int fioc_do_read(char *buf, size_t size, off_t offset) { if (offset >= fioc_size) return 0; if (size > fioc_size - offset) size = fioc_size - offset; memcpy(buf, fioc_buf + offset, size); return size; } static int fioc_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { (void) fi; if (fioc_file_type(path) != FIOC_FILE) return -EINVAL; return fioc_do_read(buf, size, offset); } static int fioc_do_write(const char *buf, size_t size, off_t offset) { if (fioc_expand(offset + size)) return -ENOMEM; memcpy(fioc_buf + offset, buf, size); return size; } static int fioc_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { (void) fi; if (fioc_file_type(path) != FIOC_FILE) return -EINVAL; return fioc_do_write(buf, size, offset); } static int fioc_truncate(const char *path, off_t size, struct fuse_file_info *fi) { (void) fi; if (fioc_file_type(path) != FIOC_FILE) return -EINVAL; return fioc_resize(size); } static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { (void) fi; (void) offset; (void) flags; if (fioc_file_type(path) != FIOC_ROOT) return -ENOENT; filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS); filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS); filler(buf, FIOC_NAME, NULL, 0, FUSE_FILL_DIR_DEFAULTS); return 0; } static int fioc_ioctl(const char *path, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned int flags, void *data) { (void) arg; (void) fi; (void) flags; if (fioc_file_type(path) != FIOC_FILE) return -EINVAL; if (flags & FUSE_IOCTL_COMPAT) return -ENOSYS; switch (cmd) { case FIOC_GET_SIZE: *(size_t *)data = fioc_size; return 0; case FIOC_SET_SIZE: fioc_resize(*(size_t *)data); return 0; } return -EINVAL; } static const struct fuse_operations fioc_oper = { .getattr = fioc_getattr, .readdir = fioc_readdir, .truncate = fioc_truncate, .open = fioc_open, .read = fioc_read, .write = fioc_write, .ioctl = fioc_ioctl, }; int main(int argc, char *argv[]) { return fuse_main(argc, argv, &fioc_oper, NULL); } fuse-3.18.2/example/ioctl.h0000644000175000017500000000164015156613252014442 0ustar berndbernd/* FUSE-ioctl: ioctl support for FUSE Copyright (C) 2008 SUSE Linux Products GmbH Copyright (C) 2008 Tejun Heo This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * @tableofcontents * * Header file to share definitions between the ioctl.c example file * system and the ioctl_client.c test program. * * \include ioctl.h */ #include #include #include enum { FIOC_GET_SIZE = _IOR('E', 0, size_t), FIOC_SET_SIZE = _IOW('E', 1, size_t), /* * The following two ioctls don't follow usual encoding rules * and transfer variable amount of data. */ FIOC_READ = _IO('E', 2), FIOC_WRITE = _IO('E', 3), }; struct fioc_rw_arg { off_t offset; void *buf; size_t size; size_t prev_size; /* out param for previous total size */ size_t new_size; /* out param for new total size */ }; fuse-3.18.2/example/ioctl_client.c0000644000175000017500000000241515156613252015774 0ustar berndbernd/* FUSE fioclient: FUSE ioctl example client Copyright (C) 2008 SUSE Linux Products GmbH Copyright (C) 2008 Tejun Heo This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This program tests the ioctl.c example file systsem. * * Compile with: * * gcc -Wall ioctl_client.c -o ioctl_client * * ## Source code ## * \include ioctl_client.c */ #include #include #include #include #include #include #include #include #include #include "ioctl.h" const char *usage = "Usage: fioclient FIOC_FILE [size]\n" "\n" "Get size if is omitted, set size otherwise\n" "\n"; int main(int argc, char **argv) { size_t size; int fd; int ret = 0; if (argc < 2) { fprintf(stderr, "%s", usage); return 1; } fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); return 1; } if (argc == 2) { if (ioctl(fd, FIOC_GET_SIZE, &size)) { perror("ioctl"); ret = 1; goto out; } printf("%zu\n", size); } else { size = strtoul(argv[2], NULL, 0); if (ioctl(fd, FIOC_SET_SIZE, &size)) { perror("ioctl"); ret = 1; goto out; } } out: close(fd); return ret; } fuse-3.18.2/example/memfs_ll.cc0000644000175000017500000005773715156613252015306 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2024 DataDirect Networks. This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LINUX_LIMITS_H #include #endif #define MEMFS_ATTR_TIMEOUT 0.0 #define MEMFS_ENTRY_TIMEOUT 0.0 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) class Inodes; class Inode; class Dentry; static void memfs_panic(std::string_view message); struct DirHandle { std::vector > entries; size_t offset; DirHandle(const std::vector > &entries) : entries(entries) , offset(0) { } }; class Inode { private: uint64_t ino; // Unique inode number std::string name; bool is_dir_; time_t ctime; time_t mtime; time_t atime; mode_t mode; std::vector content; std::vector dentries; mutable std::mutex mutex; uint64_t nlookup; mutable std::mutex attr_mutex; std::atomic nlink; uid_t uid; gid_t gid; friend class Inodes; public: Inode(uint64_t ino, const std::string &n, bool dir) : ino(ino) , name(n) , is_dir_(dir) , ctime(time(NULL)) , mtime(ctime) , atime(ctime) , mode(dir ? S_IFDIR | 0755 : S_IFREG | 0644) , nlookup(1) , nlink(dir ? 2 : 1) , uid(0) , gid(0) { } uint64_t get_ino() const { return ino; } // Method to lock the mutex void lock() const { mutex.lock(); } // Method to unlock the mutex void unlock() const { mutex.unlock(); } void inc_lookup() { std::lock_guard lock(mutex); nlookup++; } uint64_t dec_lookup(uint64_t count) { std::unique_lock lock(mutex); if (nlookup < count) { lock.unlock(); memfs_panic("Lookup count mismatch detected"); } nlookup -= count; return nlookup; } const std::string &get_name() const { return name; } bool is_dir() const { return is_dir_; } time_t get_ctime() const { return ctime; } time_t get_mtime() const { return mtime; } mode_t get_mode() const { return mode; } size_t content_size() const { return content.size(); } void read_content(char *buf, size_t size, off_t offset) const { size_t bytes_to_read = std::min(size, content.size() - (size_t)offset); std::copy(content.begin() + offset, content.begin() + offset + bytes_to_read, buf); } void write_content(const char *buf, size_t size, off_t offset) { std::lock_guard lock(mutex); if (offset + size > content.size()) { content.resize(offset + size); } std::copy(buf, buf + size, content.begin() + offset); mtime = time(NULL); } void set_uid(uid_t _uid) { std::lock_guard lock(attr_mutex); uid = _uid; } void set_gid(gid_t _gid) { std::lock_guard lock(attr_mutex); gid = _gid; } void set_mode(mode_t new_mode) { std::lock_guard lock(attr_mutex); mode = new_mode; } void set_atime(const struct timespec &_atime) { std::lock_guard lock(attr_mutex); atime = _atime.tv_sec; } void set_mtime(const struct timespec &_mtime) { std::lock_guard lock(attr_mutex); mtime = _mtime.tv_sec; } void truncate(off_t size) { std::lock_guard attr_lock(attr_mutex); if (size < content.size()) { content.resize(size); } else if (size > content.size()) { content.resize(size, 0); } mtime = time(NULL); } void get_attr(struct stat *stbuf) const { std::lock_guard lock(attr_mutex); stbuf->st_ino = ino; stbuf->st_mode = mode; stbuf->st_nlink = nlink; stbuf->st_uid = uid; stbuf->st_gid = gid; stbuf->st_size = content.size(); stbuf->st_blocks = DIV_ROUND_UP(content.size(), 512); stbuf->st_atime = atime; stbuf->st_mtime = mtime; stbuf->st_ctime = ctime; } bool is_empty() const { return dentries.empty(); } void inc_nlink() { nlink++; } nlink_t dec_nlink() { nlink_t old_value = nlink.fetch_sub(1, std::memory_order_relaxed); if (old_value == 0) { memfs_panic("Attempting to decrement nlink below zero"); } return old_value - 1; } /** * Methods that need Dentry knowledge */ int add_child_locked(const std::string &name, Dentry *child_dentry); int add_child(const std::string &name, Dentry *child_dentry); int remove_child(const std::string &name); std::vector > get_children() const; Dentry *find_child_locked(const std::string &name) const; Dentry *find_child(const std::string &name) const; }; class Dentry { public: std::string name; Inode *inode; Dentry(const std::string &n, Inode *i) : name(n) , inode(i) { } uint64_t get_ino() const { return inode->get_ino(); } bool is_dir() const { return inode->is_dir(); } const std::string &get_name() const { return name; } time_t get_ctime() const { return inode->get_ctime(); } time_t get_mtime() const { return inode->get_mtime(); } mode_t get_mode() const { return inode->get_mode(); } size_t content_size() const { return inode->content_size(); } Inode *get_inode() const { return inode; } void inc_lookup() { inode->inc_lookup(); } }; class Inodes { private: std::unordered_map > inodes; mutable std::shared_mutex inodes_mutex; std::atomic next_ino{ FUSE_ROOT_ID + 1 }; std::mutex mutex; public: Inodes() { auto root = std::make_unique(FUSE_ROOT_ID, "/", true); root->mode = S_IFDIR | 0755; root->nlink = 2; // . and .. inodes[FUSE_ROOT_ID] = std::move(root); } // New lock method void lock() { inodes_mutex.lock(); } // New unlock method void unlock() { inodes_mutex.unlock(); } void erase_locked(Inode *inode) { if (inode) { inodes.erase(inode->get_ino()); } } void erase(Inode *inode) { std::unique_lock lock(inodes_mutex); erase_locked(inode); } Inode *find_locked(fuse_ino_t ino) { auto it = inodes.find(ino); if (it == inodes.end()) { return nullptr; } return it->second.get(); } Inode *find(fuse_ino_t ino) { std::shared_lock lock(inodes_mutex); return find_locked(ino); } Inode *create(const std::string &name, bool is_dir, mode_t mode) { std::unique_lock lock(inodes_mutex); uint64_t ino = next_ino.fetch_add(1, std::memory_order_relaxed); auto new_inode = std::make_unique(ino, name, is_dir); new_inode->set_mode(mode); auto [it, inserted] = inodes.emplace(ino, std::move(new_inode)); if (!inserted) { // This should never happen, but let's handle it just in case return nullptr; } return it->second.get(); } size_t size() { std::lock_guard lock(mutex); return inodes.size(); } }; int Inode::add_child_locked(const std::string &name, Dentry *child_dentry) { if (!is_dir_) { return ENOTDIR; } // Check if a child with this name already exists auto it = std::find_if(dentries.begin(), dentries.end(), [&name](const Dentry *dentry) { return dentry->get_name() == name; }); if (it != dentries.end()) { return EEXIST; } dentries.push_back(child_dentry); if (child_dentry->is_dir()) { nlink++; } return 0; } int Inode::add_child(const std::string &name, Dentry *child_dentry) { std::lock_guard lock(mutex); return add_child_locked(name, child_dentry); } int Inode::remove_child(const std::string &name) { if (!is_dir_) { return ENOTDIR; } auto it = std::find_if(dentries.begin(), dentries.end(), [&name](const Dentry *dentry) { return dentry->get_name() == name; }); if (it == dentries.end()) { return ENOENT; } Dentry *child_dentry = *it; dentries.erase(it); if (child_dentry->is_dir()) { nlink--; } delete child_dentry; return 0; } Dentry *Inode::find_child_locked(const std::string &name) const { if (!is_dir_) { return nullptr; } auto it = std::find_if(dentries.begin(), dentries.end(), [&name](const Dentry *dentry) { return dentry->get_name() == name; }); return (it != dentries.end()) ? *it : nullptr; } Dentry *Inode::find_child(const std::string &name) const { std::lock_guard lock(mutex); return find_child_locked(name); } std::vector > Inode::get_children() const { if (!is_dir_) { return {}; // Return an empty vector if this is not a directory } std::vector > children; children.reserve(dentries.size()); for (size_t i = 0; i < dentries.size(); ++i) { const Dentry *dentry = dentries[i]; std::string name = dentry->get_name(); children.emplace_back(name, dentry); } return children; } static Inodes Inodes; static void memfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { auto *parentInode = Inodes.find(parent); if (!parentInode) { fuse_reply_err(req, ENOENT); return; } if (!parentInode->is_dir()) { fuse_reply_err(req, ENOTDIR); return; } Dentry *child = parentInode->find_child(name); if (!child) { fuse_reply_err(req, ENOENT); return; } struct fuse_entry_param e; memset(&e, 0, sizeof(e)); e.ino = child->get_ino(); e.attr_timeout = MEMFS_ATTR_TIMEOUT; e.entry_timeout = MEMFS_ENTRY_TIMEOUT; e.attr.st_ino = child->get_ino(); e.attr.st_mode = child->get_mode(); e.attr.st_nlink = child->is_dir() ? 2 : 1; child->inc_lookup(); fuse_reply_entry(req, &e); } static void memfs_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { fuse_ino_t actual_ino = fi ? fi->fh : ino; if (actual_ino == 0) { fuse_reply_err(req, EBADF); return; } auto *inode_data = Inodes.find(actual_ino); if (!inode_data) { fuse_reply_err(req, ENOENT); return; } struct stat stbuf; inode_data->get_attr(&stbuf); stbuf.st_ino = actual_ino; // Ensure the correct inode number is set fuse_reply_attr(req, &stbuf, MEMFS_ATTR_TIMEOUT); } static void memfs_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) { auto *parentInode = Inodes.find(parent); if (!parentInode || !parentInode->is_dir()) { fuse_reply_err(req, ENOENT); return; } if (parentInode->find_child(name)) { fuse_reply_err(req, EEXIST); return; } Inode *new_inode = Inodes.create(name, false, mode); if (!new_inode) { fuse_reply_err(req, EIO); return; } // Create a new Dentry and add it to the parent Dentry *new_dentry = new Dentry(name, new_inode); //std::cout << "Debug: Created new Dentry at address " // << (void *)new_dentry << ", name: '" << name // << "', inode address: " << (void *)new_inode << std::endl; parentInode->add_child(name, new_dentry); struct fuse_entry_param e; memset(&e, 0, sizeof(e)); e.ino = new_inode->get_ino(); e.attr_timeout = MEMFS_ATTR_TIMEOUT; e.entry_timeout = MEMFS_ENTRY_TIMEOUT; new_inode->get_attr(&e.attr); fi->fh = e.ino; fuse_reply_create(req, &e, fi); } static void memfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t offset, [[maybe_unused]] struct fuse_file_info *fi) { Inode *inode = Inodes.find(ino); if (!inode) { fuse_reply_err(req, ENOENT); return; } if (inode->is_dir()) { fuse_reply_err(req, EISDIR); return; } inode->write_content(buf, size, offset); fuse_reply_write(req, size); } static void memfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, [[maybe_unused]] struct fuse_file_info *fi) { Inode *inode = Inodes.find(ino); if (!inode || inode->is_dir()) { fuse_reply_err(req, ENOENT); return; } inode->lock(); if (offset >= inode->content_size()) { fuse_reply_buf(req, nullptr, 0); inode->unlock(); return; } std::vector content( std::min(size, inode->content_size() - (size_t)offset)); inode->read_content(content.data(), content.size(), offset); inode->unlock(); fuse_reply_buf(req, content.data(), content.size()); } static void memfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { auto *inode_data = Inodes.find(ino); if (!inode_data || inode_data->is_dir()) { fuse_reply_err(req, ENOENT); return; } // Use the inode number as the file handle fi->fh = ino; fuse_reply_open(req, fi); } static void memfs_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { auto *inode = Inodes.find(ino); if (!inode || !inode->is_dir()) { fuse_reply_err(req, ENOTDIR); return; } // Create a new DirHandle auto dir_handle = new DirHandle(inode->get_children()); // Store the pointer to the DirHandle in fi->fh fi->fh = reinterpret_cast(dir_handle); fuse_reply_open(req, fi); } static void memfs_readdir(fuse_req_t req, [[maybe_unused]] fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi) { auto *dir_handle = reinterpret_cast(fi->fh); if (!dir_handle) { fuse_reply_err(req, EBADF); return; } char *buffer = new char[size]; size_t buf_size = 0; for (off_t i = offset; i < static_cast(dir_handle->entries.size()); ++i) { const auto &entry = dir_handle->entries[i]; const std::string &name = entry.first; const Dentry *dentry = entry.second; struct stat stbuf; memset(&stbuf, 0, sizeof(stbuf)); stbuf.st_ino = dentry->get_inode()->get_ino(); dentry->get_inode()->get_attr(&stbuf); size_t entry_size = fuse_add_direntry(req, nullptr, 0, name.c_str(), nullptr, 0); if (buf_size + entry_size > size) { break; } fuse_add_direntry(req, buffer + buf_size, size - buf_size, name.c_str(), &stbuf, i + 1); buf_size += entry_size; } fuse_reply_buf(req, buffer, buf_size); delete[] buffer; } static void memfs_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { // No need to remove file handle (void)fi; (void)ino; fuse_reply_err(req, 0); } static void memfs_releasedir(fuse_req_t req, [[maybe_unused]] fuse_ino_t ino, struct fuse_file_info *fi) { auto *dir_handle = reinterpret_cast(fi->fh); delete dir_handle; fuse_reply_err(req, 0); } static void memfs_panic(std::string_view message) { std::cerr << "MEMFS PANIC: " << message << std::endl; std::abort(); } static void memfs_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { Inodes.lock(); Inode *inode = Inodes.find_locked(ino); uint64_t res; if (inode) { res = inode->dec_lookup(nlookup); if (res == 0) Inodes.erase_locked(inode); } Inodes.unlock(); fuse_reply_none(req); } static void memfs_forget_multi(fuse_req_t req, size_t count, struct fuse_forget_data *forgets) { for (size_t i = 0; i < count; i++) { fuse_ino_t ino = forgets[i].ino; uint64_t nlookup = forgets[i].nlookup; auto *inode_data = Inodes.find(ino); if (inode_data) { inode_data->dec_lookup(nlookup); } } fuse_reply_none(req); } static void memfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi) { fuse_ino_t actual_ino = fi ? fi->fh : ino; if (actual_ino == 0) { fuse_reply_err(req, EBADF); return; } auto *inode_data = Inodes.find(actual_ino); if (!inode_data) { fuse_reply_err(req, ENOENT); return; } inode_data->lock(); if (to_set & FUSE_SET_ATTR_MODE) inode_data->set_mode(attr->st_mode); if (to_set & FUSE_SET_ATTR_UID) inode_data->set_uid(attr->st_uid); if (to_set & FUSE_SET_ATTR_GID) inode_data->set_gid(attr->st_gid); if (to_set & FUSE_SET_ATTR_SIZE) inode_data->truncate(attr->st_size); if (to_set & FUSE_SET_ATTR_ATIME) inode_data->set_atime(attr->st_atim); if (to_set & FUSE_SET_ATTR_MTIME) inode_data->set_mtime(attr->st_mtim); struct stat st; inode_data->get_attr(&st); inode_data->unlock(); fuse_reply_attr(req, &st, MEMFS_ATTR_TIMEOUT); } static void memfs_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) { int error = 0; Inode *parentInode = nullptr; Inode *new_inode = nullptr; Dentry *new_dentry = nullptr; struct fuse_entry_param e; parentInode = Inodes.find(parent); if (!parentInode || !parentInode->is_dir()) { error = ENOENT; goto out; } new_inode = Inodes.create(name, true, mode | S_IFDIR); if (!new_inode) { error = EIO; goto out; } new_dentry = new Dentry(name, new_inode); error = parentInode->add_child(name, new_dentry); if (error != 0) { goto out_cleanup; } memset(&e, 0, sizeof(e)); e.ino = new_inode->get_ino(); e.attr_timeout = MEMFS_ATTR_TIMEOUT; e.entry_timeout = MEMFS_ENTRY_TIMEOUT; new_inode->get_attr(&e.attr); out: if (error == 0) { fuse_reply_entry(req, &e); } else { fuse_reply_err(req, error); } return; out_cleanup: delete new_dentry; Inodes.erase_locked(new_inode); goto out; } static void memfs_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) { auto *parentInode = Inodes.find(parent); if (!parentInode || !parentInode->is_dir()) { fuse_reply_err(req, ENOENT); return; } parentInode->lock(); auto child_dentry = parentInode->find_child_locked(name); if (child_dentry == nullptr) { parentInode->unlock(); fuse_reply_err(req, ENOENT); return; } Inode *child = child_dentry->get_inode(); if (!child || !child->is_dir() || !child->is_empty()) { parentInode->unlock(); fuse_reply_err(req, child ? (child->is_empty() ? ENOTDIR : ENOTEMPTY) : ENOENT); return; } parentInode->remove_child(name); child->dec_nlink(); // This should handle removal if nlink reaches 0 parentInode->unlock(); fuse_reply_err(req, 0); } static void memfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) { auto *parentInode = Inodes.find(parent); if (!parentInode || !parentInode->is_dir()) { fuse_reply_err(req, ENOENT); return; } parentInode->lock(); auto child_dentry = parentInode->find_child_locked(name); if (child_dentry == nullptr) { parentInode->unlock(); fuse_reply_err(req, ENOENT); return; } Inode *child = child_dentry->get_inode(); if (!child || child->is_dir()) { parentInode->unlock(); fuse_reply_err(req, child ? EISDIR : ENOENT); return; } parentInode->remove_child(name); child->dec_nlink(); parentInode->unlock(); fuse_reply_err(req, 0); } static void memfs_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags) { int error = 0; Inode *parentInode = nullptr; Inode *newparentInode = nullptr; Dentry *child_dentry = nullptr; Dentry *child_dentry_copy = nullptr; Dentry *existing_dentry = nullptr; #if defined(RENAME_EXCHANGE) && defined(RENAME_NOREPLACE) if (flags & (RENAME_EXCHANGE | RENAME_NOREPLACE)) { fuse_reply_err(req, EINVAL); return; } #else (void)flags; #endif Inodes.lock(); parentInode = Inodes.find_locked(parent); newparentInode = Inodes.find_locked(newparent); if (!parentInode || !parentInode->is_dir() || !newparentInode || !newparentInode->is_dir()) { error = ENOENT; goto out_unlock_global; } parentInode->lock(); if (parent != newparent) { newparentInode->lock(); } child_dentry = parentInode->find_child_locked(name); if (child_dentry == nullptr) { error = ENOENT; goto out_unlock; } existing_dentry = newparentInode->find_child_locked(newname); if (existing_dentry) { if (existing_dentry->is_dir()) { if (!existing_dentry->get_inode()->is_empty()) { error = ENOTEMPTY; goto out_unlock; } newparentInode->dec_nlink(); } newparentInode->remove_child(newname); existing_dentry->get_inode()->dec_nlink(); } child_dentry_copy = new Dentry(newname, child_dentry->get_inode()); parentInode->remove_child(name); newparentInode->add_child_locked(newname, child_dentry_copy); out_unlock: parentInode->unlock(); if (parent != newparent) { newparentInode->unlock(); } out_unlock_global: Inodes.unlock(); fuse_reply_err(req, error); } static void memfs_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname) { int error = 0; Inode *src_inode = nullptr; Inode *parent_inode = nullptr; struct fuse_entry_param e; std::unique_ptr new_dentry; Inodes.lock(); src_inode = Inodes.find(ino); if (!src_inode) { error = ENOENT; goto out_unlock_global; } parent_inode = Inodes.find(newparent); if (!parent_inode || !parent_inode->is_dir()) { error = ENOENT; goto out_unlock_global; } parent_inode->lock(); // Check if the new name already exists in the parent directory if (parent_inode->find_child_locked(newname) != nullptr) { error = EEXIST; goto out_unlock_parent; } src_inode->inc_nlink(); new_dentry = std::make_unique(newname, src_inode); parent_inode->add_child(newname, new_dentry.get()); memset(&e, 0, sizeof(e)); e.ino = ino; e.attr_timeout = MEMFS_ATTR_TIMEOUT; e.entry_timeout = MEMFS_ENTRY_TIMEOUT; src_inode->get_attr(&e.attr); out_unlock_parent: parent_inode->unlock(); out_unlock_global: Inodes.unlock(); if (error == 0) { fuse_reply_entry(req, &e); } else { fuse_reply_err(req, error); } } static void memfs_statfs(fuse_req_t req, [[maybe_unused]] fuse_ino_t ino) { struct statvfs stbuf; memset(&stbuf, 0, sizeof(stbuf)); stbuf.f_bsize = 4096; stbuf.f_frsize = 4096; stbuf.f_namemax = PATH_MAX; // Maximum filename length stbuf.f_files = Inodes.size(); // Total inodes (files + directories) stbuf.f_ffree = std::numeric_limits::max() - stbuf.f_files; // Free inodes // Set total and free blocks // For simplicity, we'll set a fixed total number of blocks and calculate free blocks based on used inodes stbuf.f_blocks = 1000000; // arbitrary number, needs to be a parameter stbuf.f_bfree = stbuf.f_blocks - (stbuf.f_files * 10); // Assume each file uses 10 blocks on average stbuf.f_bavail = stbuf.f_bfree; stbuf.f_fsid = 0; // Set flags stbuf.f_flag = ST_NOSUID; fuse_reply_statfs(req, &stbuf); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" static const struct fuse_lowlevel_ops memfs_oper = { .lookup = memfs_lookup, .forget = memfs_forget, .getattr = memfs_getattr, .setattr = memfs_setattr, .mkdir = memfs_mkdir, .unlink = memfs_unlink, .rmdir = memfs_rmdir, .rename = memfs_rename, .link = memfs_link, .open = memfs_open, .read = memfs_read, .write = memfs_write, .release = memfs_release, .opendir = memfs_opendir, .readdir = memfs_readdir, .releasedir = memfs_releasedir, .statfs = memfs_statfs, .create = memfs_create, .forget_multi = memfs_forget_multi, }; #pragma GCC diagnostic pop int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_session *se; struct fuse_cmdline_opts opts; int ret = -1; struct fuse_loop_config *config = fuse_loop_cfg_create(); if (config == NULL) { std::cerr << "fuse_loop_cfg_create failed" << std::endl; exit(EXIT_FAILURE); } if (fuse_parse_cmdline(&args, &opts) != 0) return 1; if (opts.show_help) { printf("usage: %s [options] \n\n", argv[0]); printf("File-system specific options:\n" " -o opt,[opt...] mount options\n" " -h --help print help\n" "\n"); fuse_cmdline_help(); 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; } if (opts.mountpoint == NULL) { printf("usage: %s [options] \n", argv[0]); printf(" %s --help\n", argv[0]); ret = 1; goto err_out1; } se = fuse_session_new(&args, &memfs_oper, sizeof(memfs_oper), NULL); if (se == NULL) goto err_out1; if (fuse_set_signal_handlers(se) != 0) goto err_out2; if (fuse_session_mount(se, opts.mountpoint) != 0) goto err_out3; fuse_daemonize(opts.foreground); ret = fuse_session_loop_mt(se, config); fuse_session_unmount(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; } fuse-3.18.2/example/meson.build0000644000175000017500000000266715156613252015333 0ustar berndberndexamples = [ 'passthrough', 'passthrough_fh', 'hello', 'hello_ll', 'printcap', 'ioctl_client', 'poll_client', 'ioctl', 'cuse', 'cuse_client' ] if not platform.endswith('bsd') and platform != 'dragonfly' examples += [ 'passthrough_ll', 'hello_ll_uds' ] # According to Conrad Meyer , FreeBSD doesn't # support mounting files, This is enforced in vfs_domount_first() # with the v_type != VDIR check. examples += [ 'null' ] endif threaded_examples = [ 'notify_inval_inode', 'invalidate_path', 'notify_store_retrieve', 'notify_inval_entry', 'poll' ] foreach ex : examples executable(ex, ex + '.c', dependencies: [ libfuse_dep ], install: false) endforeach foreach ex : threaded_examples executable(ex, ex + '.c', dependencies: [ thread_dep, libfuse_dep ], install: false) endforeach if platform != 'dragonfly' and add_languages('cpp', required : false) executable('passthrough_hp', 'passthrough_hp.cc', dependencies: [ thread_dep, libfuse_dep ], install: false) executable('memfs_ll', 'memfs_ll.cc', dependencies: [ thread_dep, libfuse_dep ], cpp_args : '-std=c++20', install: false) endif # TODO: Link passthrough_fh with ulockmgr if available fuse-3.18.2/example/notify_inval_entry.c0000644000175000017500000003046315156613252017252 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2016 Nikolaus Rath This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * 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() and * fuse_lowlevel_notify_expire_entry() functions. * * To see the effect, first start the file system with the * ``--no-notify`` * * $ notify_inval_entry --update-interval=1 --timeout=30 --no-notify mnt/ * * Observe that `ls` always prints the correct directory contents * (since `readdir` output is not cached):: * * $ ls mnt; sleep 1; ls mnt; sleep 1; ls mnt * Time_is_15h_48m_33s current_time * Time_is_15h_48m_34s current_time * Time_is_15h_48m_35s current_time * * However, if you try to access a file by name the kernel will * report that it still exists: * * $ file=$(ls mnt/); echo $file * Time_is_15h_50m_09s * $ sleep 5; stat mnt/$file * File: ‘mnt/Time_is_15h_50m_09s’ * Size: 32 Blocks: 0 IO Block: 4096 regular file * Device: 2ah/42d Inode: 3 Links: 1 * Access: (0444/-r--r--r--) Uid: ( 0/ root) Gid: ( 0/ root) * Access: 1969-12-31 16:00:00.000000000 -0800 * Modify: 1969-12-31 16:00:00.000000000 -0800 * Change: 1969-12-31 16:00:00.000000000 -0800 * Birth: - * * Only once the kernel cache timeout has been reached will the stat * call fail: * * $ sleep 30; stat mnt/$file * stat: cannot stat ‘mnt/Time_is_15h_50m_09s’: No such file or directory * * In contrast, if you enable notifications you will be unable to stat * the file as soon as the file system updates its name: * * $ notify_inval_entry --update-interval=1 --timeout=30 mnt/ * $ file=$(ls mnt/); stat mnt/$file * File: ‘mnt/Time_is_20h_42m_11s’ * Size: 0 Blocks: 0 IO Block: 4096 regular empty file * Device: 2ah/42d Inode: 2 Links: 1 * Access: (0000/----------) Uid: ( 0/ root) Gid: ( 0/ root) * Access: 1969-12-31 16:00:00.000000000 -0800 * Modify: 1969-12-31 16:00:00.000000000 -0800 * Change: 1969-12-31 16:00:00.000000000 -0800 * Birth: - * $ 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 * * Another possible command-line option is --inc-epoch, which will use the FUSE * low-level function fuse_lowlevel_notify_increment_epoch() instead. This will * function will force the invalidation of all dentries next time they are * revalidated. Note that --inc-epoch and --only-expire options are mutually * exclusive. * * ## Compilation ## * * gcc -Wall notify_inval_entry.c `pkg-config fuse3 --cflags --libs` -o notify_inval_entry * * ## Source code ## * \include notify_inval_entry.c */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_STR_LEN 128 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; int inc_epoch; }; static struct options options = { .timeout = 5, .no_notify = 0, .update_interval = 1, .only_expire = 0, .inc_epoch = 0, }; #define OPTION(t, p) \ { t, offsetof(struct options, p), 1 } 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), OPTION("--inc-epoch", inc_epoch), FUSE_OPT_END }; static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) { stbuf->st_ino = ino; if (ino == FUSE_ROOT_ID) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 1; } else if (ino == file_ino) { stbuf->st_mode = S_IFREG | 0000; stbuf->st_nlink = 1; stbuf->st_size = 0; } else return -1; return 0; } static void tfs_init(void *userdata, struct fuse_conn_info *conn) { (void)userdata; /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; } static void tfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse_entry_param e; memset(&e, 0, sizeof(e)); if (parent != FUSE_ROOT_ID) goto err_out; else if (strcmp(name, file_name) == 0) { e.ino = file_ino; lookup_cnt++; } else goto err_out; e.attr_timeout = options.timeout; e.entry_timeout = options.timeout; if (tfs_stat(e.ino, &e.attr) != 0) goto err_out; fuse_reply_entry(req, &e); return; err_out: fuse_reply_err(req, ENOENT); } static void tfs_forget (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { (void) req; if(ino == file_ino) lookup_cnt -= nlookup; else assert(ino == FUSE_ROOT_ID); fuse_reply_none(req); } static void tfs_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 (tfs_stat(ino, &stbuf) != 0) fuse_reply_err(req, ENOENT); else fuse_reply_attr(req, &stbuf, options.timeout); } 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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { (void) fi; if (ino != FUSE_ROOT_ID) fuse_reply_err(req, ENOTDIR); else { struct dirbuf b; memset(&b, 0, sizeof(b)); dirbuf_add(req, &b, file_name, file_ino); reply_buf_limited(req, b.p, b.size, off, size); free(b.p); } } static const struct fuse_lowlevel_ops tfs_oper = { .init = tfs_init, .lookup = tfs_lookup, .getattr = tfs_getattr, .readdir = tfs_readdir, .forget = tfs_forget, }; static void update_fs(void) { time_t t; struct tm *now; ssize_t ret; t = time(NULL); now = localtime(&t); assert(now != NULL); ret = strftime(file_name, MAX_STR_LEN, "Time_is_%Hh_%Mm_%Ss", now); assert(ret != 0); } static void* update_fs_loop(void *data) { struct fuse_session *se = (struct fuse_session*) data; char *old_name; int ret = 0; while(!fuse_session_exited(se)) { old_name = strdup(file_name); update_fs(); if (!options.no_notify && lookup_cnt) { if(options.only_expire) { // expire entry 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"); 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 if (options.inc_epoch) { // increment epoch ret = fuse_lowlevel_notify_increment_epoch(se); if (ret == -ENOSYS) { printf("fuse_lowlevel_notify_increment_epoch not supported by kernel\n"); break; } assert(ret == 0); } 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); } if (ret == -ENOSYS) { printf("Exiting...\n"); fuse_session_exit(se); // Make sure to exit now, rather than on next request from userspace pthread_kill(main_thread, SIGPIPE); } return NULL; } static void show_help(const char *progname) { printf("usage: %s [options] \n\n", progname); printf("File-system specific options:\n" " --timeout= Timeout for kernel caches\n" " --update-interval= Update-rate of file system contents\n" " --no-notify Disable kernel notifications\n" " --only-expire Expire entries instead of invalidating them\n" " --inc-epoch Increment epoch, invalidating all dentries\n" "\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; struct fuse_loop_config *config; pthread_t updater; int ret = -1; if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) return 1; if (fuse_parse_cmdline(&args, &opts) != 0) return 1; if (opts.show_help) { show_help(argv[0]); fuse_cmdline_help(); 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; } if (options.only_expire && options.inc_epoch) { printf("'only-expire' and 'inc-epoch' options are exclusive\n"); ret = 0; goto err_out1; } /* Initial contents */ update_fs(); se = fuse_session_new(&args, &tfs_oper, sizeof(tfs_oper), &se); if (se == NULL) goto err_out1; if (fuse_set_signal_handlers(se) != 0) goto err_out2; if (fuse_session_mount(se, opts.mountpoint) != 0) goto err_out3; 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) { fprintf(stderr, "pthread_create failed with %s\n", strerror(ret)); goto err_out3; } /* Block until ctrl+c or fusermount -u */ if (opts.singlethread) { ret = fuse_session_loop(se); } else { config = fuse_loop_cfg_create(); fuse_loop_cfg_set_clone_fd(config, opts.clone_fd); fuse_loop_cfg_set_max_threads(config, opts.max_threads); ret = fuse_session_loop_mt(se, config); fuse_loop_cfg_destroy(config); config = NULL; } fuse_session_unmount(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; } /** * Local Variables: * mode: c * indent-tabs-mode: nil * c-basic-offset: 4 * End: */ fuse-3.18.2/example/notify_inval_inode.c0000644000175000017500000002503415156613252017205 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2016 Nikolaus Rath This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This example implements a file system with a single file whose * contents change dynamically: it always contains the current time. * * While notify_store_retrieve.c uses fuse_lowlevel_notify_store() to * actively push the updated data into the kernel cache, this example * uses fuse_lowlevel_notify_inval_inode() to notify the kernel that * the cache has to be invalidated - but the kernel still has to * explicitly request the updated data on the next read. * * To see the effect, first start the file system with the * ``--no-notify`` option: * * $ notify_inval_inode --update-interval=1 --no-notify mnt/ * * Observe that the output never changes, even though the file system * updates it once per second. This is because the contents are cached * in the kernel: * * $ for i in 1 2 3 4 5; do * > cat mnt/current_time * > sleep 1 * > done * The current time is 15:58:18 * The current time is 15:58:18 * The current time is 15:58:18 * The current time is 15:58:18 * The current time is 15:58:18 * * If you instead enable the notification functions, the changes become * visible: * * $ notify_inval_inode --update-interval=1 mnt/ * $ for i in 1 2 3 4 5; do * > cat mnt/current_time * > sleep 1 * > done * The current time is 15:58:40 * The current time is 15:58:41 * The current time is 15:58:42 * The current time is 15:58:43 * The current time is 15:58:44 * * ## Compilation ## * * gcc -Wall notify_inval_inode.c `pkg-config fuse3 --cflags --libs` -o notify_inval_inode * * ## Source code ## * \include notify_inval_inode.c */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include #include #include #include #include #include #include #include #include #include #include #include /* We can't actually tell the kernel that there is no timeout, so we just send a big value */ #define NO_TIMEOUT 500000 #define MAX_STR_LEN 128 #define FILE_INO 2 #define FILE_NAME "current_time" static char file_contents[MAX_STR_LEN]; static int lookup_cnt = 0; static size_t file_size; static _Atomic bool is_stop = false; /* Command line parsing */ struct options { int no_notify; int update_interval; }; static struct options options = { .no_notify = 0, .update_interval = 1, }; #define OPTION(t, p) \ { t, offsetof(struct options, p), 1 } static const struct fuse_opt option_spec[] = { OPTION("--no-notify", no_notify), OPTION("--update-interval=%d", update_interval), FUSE_OPT_END }; static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) { stbuf->st_ino = ino; if (ino == FUSE_ROOT_ID) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 1; } else if (ino == FILE_INO) { stbuf->st_mode = S_IFREG | 0444; stbuf->st_nlink = 1; stbuf->st_size = file_size; } else return -1; return 0; } static void tfs_init(void *userdata, struct fuse_conn_info *conn) { (void)userdata; /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; } static void tfs_destroy(void *userarg) { (void)userarg; is_stop = true; } static void tfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse_entry_param e; memset(&e, 0, sizeof(e)); if (parent != FUSE_ROOT_ID) goto err_out; else if (strcmp(name, FILE_NAME) == 0) { e.ino = FILE_INO; lookup_cnt++; } else goto err_out; e.attr_timeout = NO_TIMEOUT; e.entry_timeout = NO_TIMEOUT; if (tfs_stat(e.ino, &e.attr) != 0) goto err_out; fuse_reply_entry(req, &e); return; err_out: fuse_reply_err(req, ENOENT); } static void tfs_forget (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { (void) req; if(ino == FILE_INO) lookup_cnt -= nlookup; else assert(ino == FUSE_ROOT_ID); fuse_reply_none(req); } static void tfs_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 (tfs_stat(ino, &stbuf) != 0) fuse_reply_err(req, ENOENT); else fuse_reply_attr(req, &stbuf, NO_TIMEOUT); } 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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { (void) fi; if (ino != FUSE_ROOT_ID) fuse_reply_err(req, ENOTDIR); else { struct dirbuf b; memset(&b, 0, sizeof(b)); dirbuf_add(req, &b, FILE_NAME, FILE_INO); reply_buf_limited(req, b.p, b.size, off, size); free(b.p); } } static void tfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { /* Make cache persistent even if file is closed, this makes it easier to see the effects */ fi->keep_cache = 1; if (ino == FUSE_ROOT_ID) fuse_reply_err(req, EISDIR); else if ((fi->flags & O_ACCMODE) != O_RDONLY) fuse_reply_err(req, EACCES); else if (ino == FILE_INO) fuse_reply_open(req, fi); else { // This should not happen fprintf(stderr, "Got open for non-existing inode!\n"); fuse_reply_err(req, ENOENT); } } static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { (void) fi; assert(ino == FILE_INO); reply_buf_limited(req, file_contents, file_size, off, size); } static const struct fuse_lowlevel_ops tfs_oper = { .init = tfs_init, .destroy = tfs_destroy, .lookup = tfs_lookup, .getattr = tfs_getattr, .readdir = tfs_readdir, .open = tfs_open, .read = tfs_read, .forget = tfs_forget, }; static void update_fs(void) { struct tm *now; time_t t; t = time(NULL); now = localtime(&t); assert(now != NULL); file_size = strftime(file_contents, MAX_STR_LEN, "The current time is %H:%M:%S\n", now); assert(file_size != 0); } static void* update_fs_loop(void *data) { struct fuse_session *se = (struct fuse_session*) data; while(!is_stop) { update_fs(); if (!options.no_notify && lookup_cnt) { /* Only send notification if the kernel is aware of the inode */ /* Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they * might come up during umount, when kernel side already releases * all inodes, but does not send FUSE_DESTROY yet. */ int ret = fuse_lowlevel_notify_inval_inode(se, FILE_INO, 0, 0); if ((ret != 0 && !is_stop) && ret != -ENOENT && ret != -EBADF && ret != -ENODEV) { fprintf(stderr, "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n", strerror(-ret), -ret); abort(); } } sleep(options.update_interval); } return NULL; } static void show_help(const char *progname) { printf("usage: %s [options] \n\n", progname); printf("File-system specific options:\n" " --update-interval= Update-rate of file system contents\n" " --no-notify Disable kernel notifications\n" "\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; struct fuse_loop_config *config; pthread_t updater; int ret = -1; if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) return 1; if (fuse_parse_cmdline(&args, &opts) != 0) { ret = 1; goto err_out1; } if (opts.show_help) { show_help(argv[0]); fuse_cmdline_help(); 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; } /* Initial contents */ update_fs(); se = fuse_session_new(&args, &tfs_oper, sizeof(tfs_oper), NULL); if (se == NULL) goto err_out1; if (fuse_set_signal_handlers(se) != 0) goto err_out2; if (fuse_session_mount(se, opts.mountpoint) != 0) goto err_out3; fuse_daemonize(opts.foreground); /* Start thread to update file contents */ ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se); if (ret != 0) { fprintf(stderr, "pthread_create failed with %s\n", strerror(ret)); goto err_out3; } /* Block until ctrl+c or fusermount -u */ if (opts.singlethread) ret = fuse_session_loop(se); else { config = fuse_loop_cfg_create(); fuse_loop_cfg_set_clone_fd(config, opts.clone_fd); fuse_loop_cfg_set_max_threads(config, opts.max_threads); ret = fuse_session_loop_mt(se, config); fuse_loop_cfg_destroy(config); config = NULL; } fuse_session_unmount(se); err_out3: fuse_remove_signal_handlers(se); err_out2: fuse_session_destroy(se); err_out1: fuse_opt_free_args(&args); free(opts.mountpoint); return ret ? 1 : 0; } /** * Local Variables: * mode: c * indent-tabs-mode: nil * c-basic-offset: 4 * End: */ fuse-3.18.2/example/notify_store_retrieve.c0000644000175000017500000003123315156613252017755 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2016 Nikolaus Rath This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This example implements a file system with a single file whose * contents change dynamically: it always contains the current time. * * While notify_inval_inode.c uses fuse_lowlevel_notify_inval_inode() * to let the kernel know that it has to invalidate the cache, this * example actively pushes the updated data into the kernel cache * using fuse_lowlevel_notify_store(). * * To see the effect, first start the file system with the * ``--no-notify`` option: * * $ notify_store_retrieve --update-interval=1 --no-notify mnt/ * * Observe that the output never changes, even though the file system * updates it once per second. This is because the contents are cached * in the kernel: * * $ for i in 1 2 3 4 5; do * > cat mnt/current_time * > sleep 1 * > done * The current time is 15:58:18 * The current time is 15:58:18 * The current time is 15:58:18 * The current time is 15:58:18 * The current time is 15:58:18 * * If you instead enable the notification functions, the changes become * visible: * * $ notify_store_retrieve --update-interval=1 mnt/ * $ for i in 1 2 3 4 5; do * > cat mnt/current_time * > sleep 1 * > done * The current time is 15:58:40 * The current time is 15:58:41 * The current time is 15:58:42 * The current time is 15:58:43 * The current time is 15:58:44 * * ## Compilation ## * * gcc -Wall notify_store_retrieve.c `pkg-config fuse3 --cflags --libs` -o notify_store_retrieve * * ## Source code ## * \include notify_store_retrieve.c */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include #include #include #include #include #include #include #include #include #include #include /* We can't actually tell the kernel that there is no timeout, so we just send a big value */ #define NO_TIMEOUT 500000 #define MAX_STR_LEN 128 #define FILE_INO 2 #define FILE_NAME "current_time" static char file_contents[MAX_STR_LEN]; static int lookup_cnt = 0; static int open_cnt = 0; static size_t file_size; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; /* Keep track if we ever stored data (==1), and received it back correctly (==2) */ static int retrieve_status = 0; static bool is_umount = false; /* updater thread tid */ static pthread_t updater; /* Command line parsing */ struct options { int no_notify; int update_interval; }; static struct options options = { .no_notify = 0, .update_interval = 1, }; #define OPTION(t, p) \ { t, offsetof(struct options, p), 1 } static const struct fuse_opt option_spec[] = { OPTION("--no-notify", no_notify), OPTION("--update-interval=%d", update_interval), FUSE_OPT_END }; static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) { stbuf->st_ino = ino; if (ino == FUSE_ROOT_ID) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 1; } else if (ino == FILE_INO) { stbuf->st_mode = S_IFREG | 0444; stbuf->st_nlink = 1; stbuf->st_size = file_size; } else return -1; return 0; } static void tfs_init(void *userdata, struct fuse_conn_info *conn) { (void)userdata; /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; } static void tfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse_entry_param e; memset(&e, 0, sizeof(e)); if (parent != FUSE_ROOT_ID) goto err_out; else if (strcmp(name, FILE_NAME) == 0) { e.ino = FILE_INO; } else goto err_out; e.attr_timeout = NO_TIMEOUT; e.entry_timeout = NO_TIMEOUT; if (tfs_stat(e.ino, &e.attr) != 0) goto err_out; fuse_reply_entry(req, &e); /* * must only be set when the kernel knows about the entry, * otherwise update_fs_loop() might see a positive count, but kernel * would not have the entry yet */ if (e.ino == FILE_INO) { pthread_mutex_lock(&lock); lookup_cnt++; pthread_mutex_unlock(&lock); } return; err_out: fuse_reply_err(req, ENOENT); } static void tfs_forget (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { (void) req; if(ino == FILE_INO) { pthread_mutex_lock(&lock); lookup_cnt -= nlookup; pthread_mutex_unlock(&lock); } else assert(ino == FUSE_ROOT_ID); fuse_reply_none(req); } static void tfs_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 (tfs_stat(ino, &stbuf) != 0) fuse_reply_err(req, ENOENT); else fuse_reply_attr(req, &stbuf, NO_TIMEOUT); } 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 tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { (void) fi; if (ino != FUSE_ROOT_ID) fuse_reply_err(req, ENOTDIR); else { struct dirbuf b; memset(&b, 0, sizeof(b)); dirbuf_add(req, &b, FILE_NAME, FILE_INO); reply_buf_limited(req, b.p, b.size, off, size); free(b.p); } } static void tfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { /* Make cache persistent even if file is closed, this makes it easier to see the effects */ fi->keep_cache = 1; if (ino == FUSE_ROOT_ID) fuse_reply_err(req, EISDIR); else if ((fi->flags & O_ACCMODE) != O_RDONLY) fuse_reply_err(req, EACCES); else if (ino == FILE_INO) { fuse_reply_open(req, fi); pthread_mutex_lock(&lock); open_cnt++; pthread_mutex_unlock(&lock); } else { // This should not happen fprintf(stderr, "Got open for non-existing inode!\n"); fuse_reply_err(req, ENOENT); } } static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { (void) fi; assert(ino == FILE_INO); reply_buf_limited(req, file_contents, file_size, off, size); } static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino, off_t offset, struct fuse_bufvec *data) { struct fuse_bufvec bufv; char buf[MAX_STR_LEN]; char *expected; ssize_t ret; assert(ino == FILE_INO); assert(offset == 0); expected = (char*) cookie; bufv.count = 1; bufv.idx = 0; bufv.off = 0; bufv.buf[0].size = MAX_STR_LEN; bufv.buf[0].mem = buf; bufv.buf[0].flags = 0; ret = fuse_buf_copy(&bufv, data, 0); assert(ret > 0); assert(strncmp(buf, expected, ret) == 0); free(expected); retrieve_status = 2; fuse_reply_none(req); } static void tfs_destroy(void *userdata) { (void)userdata; is_umount = true; pthread_join(updater, NULL); } static const struct fuse_lowlevel_ops tfs_oper = { .init = tfs_init, .lookup = tfs_lookup, .getattr = tfs_getattr, .readdir = tfs_readdir, .open = tfs_open, .read = tfs_read, .forget = tfs_forget, .retrieve_reply = tfs_retrieve_reply, .destroy = tfs_destroy, }; static void update_fs(void) { struct tm *now; time_t t; t = time(NULL); now = localtime(&t); assert(now != NULL); file_size = strftime(file_contents, MAX_STR_LEN, "The current time is %H:%M:%S\n", now); assert(file_size != 0); } static void* update_fs_loop(void *data) { struct fuse_session *se = (struct fuse_session*) data; struct fuse_bufvec bufv; int ret; while(!is_umount) { update_fs(); pthread_mutex_lock(&lock); if (!options.no_notify && open_cnt && lookup_cnt) { /* Only send notification if the kernel is aware of the inode */ bufv.count = 1; bufv.idx = 0; bufv.off = 0; bufv.buf[0].size = file_size; bufv.buf[0].mem = file_contents; bufv.buf[0].flags = 0; /* * Some errors (ENOENT, EBADF, ENODEV) have to be accepted as they * might come up during umount, when kernel side already releases * all inodes, but does not send FUSE_DESTROY yet. */ ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0); if ((ret != 0 && !is_umount) && ret != -ENOENT && ret != -EBADF && ret != -ENODEV) { fprintf(stderr, "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n", strerror(-ret), -ret); abort(); } /* To make sure that everything worked correctly, ask the kernel to send us back the stored data */ ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN, 0, (void*) strdup(file_contents)); assert((ret == 0 || is_umount) || ret == -ENOENT || ret == -EBADF || ret != -ENODEV); if(retrieve_status == 0) retrieve_status = 1; } pthread_mutex_unlock(&lock); sleep(options.update_interval); } return NULL; } static void show_help(const char *progname) { printf("usage: %s [options] \n\n", progname); printf("File-system specific options:\n" " --update-interval= Update-rate of file system contents\n" " --no-notify Disable kernel notifications\n" "\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; struct fuse_loop_config *config; int ret = -1; if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) return 1; if (fuse_parse_cmdline(&args, &opts) != 0) return 1; if (opts.show_help) { show_help(argv[0]); fuse_cmdline_help(); 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; } /* Initial contents */ update_fs(); se = fuse_session_new(&args, &tfs_oper, sizeof(tfs_oper), NULL); if (se == NULL) goto err_out1; if (fuse_set_signal_handlers(se) != 0) goto err_out2; if (fuse_session_mount(se, opts.mountpoint) != 0) goto err_out3; fuse_daemonize(opts.foreground); /* Start thread to update file contents */ ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se); if (ret != 0) { fprintf(stderr, "pthread_create failed with %s\n", strerror(ret)); goto err_out3; } /* Block until ctrl+c or fusermount -u */ if (opts.singlethread) ret = fuse_session_loop(se); else { config = fuse_loop_cfg_create(); fuse_loop_cfg_set_clone_fd(config, opts.clone_fd); fuse_loop_cfg_set_max_threads(config, opts.max_threads); ret = fuse_session_loop_mt(se, config); fuse_loop_cfg_destroy(config); config = NULL; } assert(retrieve_status != 1); fuse_session_unmount(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; } /** * Local Variables: * mode: c * indent-tabs-mode: nil * c-basic-offset: 4 * End: */ fuse-3.18.2/example/null.c0000644000175000017500000000543015156613252014276 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This "filesystem" provides only a single file. The mountpoint * needs to be a file rather than a directory. All writes to the * file will be discarded, and reading the file always returns * \0. * * Compile with: * * gcc -Wall null.c `pkg-config fuse3 --cflags --libs` -o null * * ## Source code ## * \include passthrough_fh.c */ #define FUSE_USE_VERSION 31 #include #include #include #include #include #include #include #include static int null_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { (void) fi; if(strcmp(path, "/") != 0) return -ENOENT; stbuf->st_mode = S_IFREG | 0644; stbuf->st_nlink = 1; stbuf->st_uid = getuid(); stbuf->st_gid = getgid(); stbuf->st_size = (1ULL << 32); /* 4G */ stbuf->st_blocks = 0; stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL); return 0; } static int null_truncate(const char *path, off_t size, struct fuse_file_info *fi) { (void) size; (void) fi; if(strcmp(path, "/") != 0) return -ENOENT; return 0; } static int null_open(const char *path, struct fuse_file_info *fi) { (void) fi; if(strcmp(path, "/") != 0) return -ENOENT; return 0; } static int null_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { (void) buf; (void) offset; (void) fi; if(strcmp(path, "/") != 0) return -ENOENT; if (offset >= (1ULL << 32)) return 0; memset(buf, 0, size); return size; } static int null_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { (void) buf; (void) offset; (void) fi; if(strcmp(path, "/") != 0) return -ENOENT; return size; } static const struct fuse_operations null_oper = { .getattr = null_getattr, .truncate = null_truncate, .open = null_open, .read = null_read, .write = null_write, }; int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_cmdline_opts opts; struct stat stbuf; if (fuse_parse_cmdline(&args, &opts) != 0) return 1; fuse_opt_free_args(&args); if (!opts.mountpoint) { fprintf(stderr, "missing mountpoint parameter\n"); return 1; } if (stat(opts.mountpoint, &stbuf) == -1) { fprintf(stderr ,"failed to access mountpoint %s: %s\n", opts.mountpoint, strerror(errno)); free(opts.mountpoint); return 1; } free(opts.mountpoint); if (!S_ISREG(stbuf.st_mode)) { fprintf(stderr, "mountpoint is not a regular file\n"); return 1; } return fuse_main(argc, argv, &null_oper, NULL); } fuse-3.18.2/example/passthrough.c0000644000175000017500000002575715156613252015711 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Copyright (C) 2011 Sebastian Pipping This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This file system mirrors the existing file system hierarchy of the * system, starting at the root file system. This is implemented by * just "passing through" all requests to the corresponding user-space * libc functions. Its performance is terrible. * * Compile with * * gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough * * ## Source code ## * \include passthrough.c */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18) #define _GNU_SOURCE #ifdef linux /* For pread()/pwrite()/utimensat() */ #define _XOPEN_SOURCE 700 #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_SETXATTR #include #endif #include "passthrough_helpers.h" static int fill_dir_plus = 0; static int readdir_zero_ino; static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { (void) conn; cfg->use_ino = !readdir_zero_ino; /* parallel_direct_writes feature depends on direct_io features. To make parallel_direct_writes valid, need either set cfg->direct_io in current function (recommended in high level API) or set fi->direct_io in xmp_create() or xmp_open(). */ // cfg->direct_io = 1; cfg->parallel_direct_writes = 1; /* Pick up changes from lower filesystem right away. This is also necessary for better hardlink support. When the kernel calls the unlink() handler, it does not know the inode of the to-be-removed entry and can therefore not invalidate the cache of the associated inode - resulting in an incorrect st_nlink value being reported for any remaining hardlinks to this inode. */ if (!cfg->auto_cache) { 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) { (void) fi; int res; res = lstat(path, stbuf); if (res == -1) return -errno; return 0; } static int xmp_access(const char *path, int mask) { int res; res = access(path, mask); if (res == -1) return -errno; return 0; } static int xmp_readlink(const char *path, char *buf, size_t size) { int res; res = readlink(path, buf, size - 1); if (res == -1) return -errno; buf[res] = '\0'; return 0; } static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { DIR *dp; struct dirent *de; (void) offset; (void) fi; (void) flags; dp = opendir(path); if (dp == NULL) return -errno; while ((de = readdir(dp)) != NULL) { struct stat st; if (fill_dir_plus) { fstatat(dirfd(dp), de->d_name, &st, AT_SYMLINK_NOFOLLOW); } else { memset(&st, 0, sizeof(st)); st.st_ino = de->d_ino; st.st_mode = de->d_type << 12; } if (readdir_zero_ino) st.st_ino = 0; if (filler(buf, de->d_name, &st, 0, fill_dir_plus)) break; } closedir(dp); return 0; } static int xmp_mknod(const char *path, mode_t mode, dev_t rdev) { int res; res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev); if (res == -1) return -errno; return 0; } static int xmp_mkdir(const char *path, mode_t mode) { int res; res = mkdir(path, mode); 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_rmdir(const char *path) { int res; res = rmdir(path); if (res == -1) return -errno; return 0; } static int xmp_symlink(const char *from, const char *to) { int res; res = symlink(from, to); 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; res = rename(from, to); if (res == -1) return -errno; return 0; } static int xmp_link(const char *from, const char *to) { int res; res = link(from, to); if (res == -1) return -errno; return 0; } static int xmp_chmod(const char *path, mode_t mode, struct fuse_file_info *fi) { (void) fi; int res; res = chmod(path, mode); if (res == -1) return -errno; return 0; } static int xmp_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi) { (void) fi; int res; res = lchown(path, uid, gid); if (res == -1) return -errno; return 0; } static int xmp_truncate(const char *path, off_t size, struct fuse_file_info *fi) { int res; if (fi != NULL) res = ftruncate(fi->fh, size); else res = truncate(path, size); if (res == -1) return -errno; return 0; } #ifdef HAVE_UTIMENSAT static int xmp_utimens(const char *path, const struct timespec ts[2], struct fuse_file_info *fi) { (void) fi; int res; /* don't use utime/utimes since they follow symlinks */ res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); if (res == -1) return -errno; return 0; } #endif static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi) { int res; res = open(path, fi->flags, mode); if (res == -1) return -errno; fi->fh = res; return 0; } static int xmp_open(const char *path, struct fuse_file_info *fi) { int res; res = open(path, fi->flags); if (res == -1) return -errno; /* Enable direct_io when open has flags O_DIRECT to enjoy the feature parallel_direct_writes (i.e., to get a shared lock, not exclusive lock, for writes to the same file). */ if (fi->flags & O_DIRECT) { fi->direct_io = 1; fi->parallel_direct_writes = 1; } fi->fh = res; return 0; } static int xmp_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { int fd; int res; if(fi == NULL) fd = open(path, O_RDONLY); else fd = fi->fh; if (fd == -1) return -errno; res = pread(fd, buf, size, offset); if (res == -1) res = -errno; if(fi == NULL) close(fd); return res; } static int xmp_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { int fd; int res; (void) fi; if(fi == NULL) fd = open(path, O_WRONLY); else fd = fi->fh; if (fd == -1) return -errno; res = pwrite(fd, buf, size, offset); if (res == -1) res = -errno; if(fi == NULL) close(fd); return res; } static int xmp_statfs(const char *path, struct statvfs *stbuf) { int res; res = statvfs(path, stbuf); if (res == -1) return -errno; return 0; } static int xmp_release(const char *path, struct fuse_file_info *fi) { (void) path; close(fi->fh); return 0; } static int xmp_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) { /* Just a stub. This method is optional and can safely be left unimplemented */ (void) path; (void) isdatasync; (void) fi; return 0; } static int xmp_fallocate(const char *path, int mode, off_t offset, off_t length, struct fuse_file_info *fi) { int fd; int res; (void) fi; if(fi == NULL) fd = open(path, O_WRONLY); else fd = fi->fh; if (fd == -1) return -errno; res = do_fallocate(fd, mode, offset, length); if(fi == NULL) close(fd); return res; } #ifdef HAVE_SETXATTR /* xattr operations are optional and can safely be left unimplemented */ static int xmp_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { int res = lsetxattr(path, name, value, size, flags); if (res == -1) return -errno; return 0; } static int xmp_getxattr(const char *path, const char *name, char *value, size_t size) { int res = lgetxattr(path, name, value, size); if (res == -1) return -errno; return res; } static int xmp_listxattr(const char *path, char *list, size_t size) { int res = llistxattr(path, list, size); if (res == -1) return -errno; return res; } static int xmp_removexattr(const char *path, const char *name) { int res = lremovexattr(path, name); if (res == -1) return -errno; return 0; } #endif /* HAVE_SETXATTR */ #ifdef HAVE_COPY_FILE_RANGE static ssize_t xmp_copy_file_range(const char *path_in, struct fuse_file_info *fi_in, off_t offset_in, const char *path_out, struct fuse_file_info *fi_out, off_t offset_out, size_t len, int flags) { int fd_in, fd_out; ssize_t res; if(fi_in == NULL) fd_in = open(path_in, O_RDONLY); else fd_in = fi_in->fh; if (fd_in == -1) return -errno; if(fi_out == NULL) fd_out = open(path_out, O_WRONLY); else fd_out = fi_out->fh; if (fd_out == -1) { close(fd_in); return -errno; } res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len, flags); if (res == -1) res = -errno; if (fi_out == NULL) close(fd_out); if (fi_in == NULL) close(fd_in); return res; } #endif static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi) { int fd; off_t res; if (fi == NULL) fd = open(path, O_RDONLY); else fd = fi->fh; if (fd == -1) return -errno; res = lseek(fd, off, whence); if (res == -1) res = -errno; if (fi == NULL) close(fd); return res; } #ifdef HAVE_STATX static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi) { int fd = -1; int res; if (fi) fd = fi->fh; res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf); if (res == -1) return -errno; return 0; } #endif static const struct fuse_operations xmp_oper = { .init = xmp_init, .getattr = xmp_getattr, .access = xmp_access, .readlink = xmp_readlink, .readdir = xmp_readdir, .mknod = xmp_mknod, .mkdir = xmp_mkdir, .symlink = xmp_symlink, .unlink = xmp_unlink, .rmdir = xmp_rmdir, .rename = xmp_rename, .link = xmp_link, .chmod = xmp_chmod, .chown = xmp_chown, .truncate = xmp_truncate, #ifdef HAVE_UTIMENSAT .utimens = xmp_utimens, #endif .open = xmp_open, .create = xmp_create, .read = xmp_read, .write = xmp_write, .statfs = xmp_statfs, .release = xmp_release, .fsync = xmp_fsync, .fallocate = xmp_fallocate, #ifdef HAVE_SETXATTR .setxattr = xmp_setxattr, .getxattr = xmp_getxattr, .listxattr = xmp_listxattr, .removexattr = xmp_removexattr, #endif #ifdef HAVE_COPY_FILE_RANGE .copy_file_range = xmp_copy_file_range, #endif .lseek = xmp_lseek, #ifdef HAVE_STATX .statx = xmp_statx, #endif }; int main(int argc, char *argv[]) { enum { MAX_ARGS = 10 }; int i,new_argc; char *new_argv[MAX_ARGS]; umask(0); /* Process the "--plus" option apart */ for (i=0, new_argc=0; (i Copyright (C) 2011 Sebastian Pipping This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This file system mirrors the existing file system hierarchy of the * system, starting at the root file system. This is implemented by * just "passing through" all requests to the corresponding user-space * libc functions. This implementation is a little more sophisticated * than the one in passthrough.c, so performance is not quite as bad. * * Compile with: * * gcc -Wall passthrough_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o passthrough_fh * * ## Source code ## * \include passthrough_fh.c */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18) #define _GNU_SOURCE #include #ifdef HAVE_LIBULOCKMGR #include #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_SETXATTR #include #endif #include /* flock(2) */ #include "passthrough_helpers.h" static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { (void) conn; cfg->use_ino = 1; cfg->nullpath_ok = 1; /* parallel_direct_writes feature depends on direct_io features. To make parallel_direct_writes valid, need either set cfg->direct_io in current function (recommended in high level API) or set fi->direct_io in xmp_create() or xmp_open(). */ // cfg->direct_io = 1; cfg->parallel_direct_writes = 1; /* Pick up changes from lower filesystem right away. This is also necessary for better hardlink support. When the kernel calls the unlink() handler, it does not know the inode of the to-be-removed entry and can therefore not invalidate the cache of the associated inode - resulting in an incorrect st_nlink value being reported for any remaining hardlinks to this inode. */ 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_access(const char *path, int mask) { int res; res = access(path, mask); if (res == -1) return -errno; return 0; } static int xmp_readlink(const char *path, char *buf, size_t size) { int res; res = readlink(path, buf, size - 1); if (res == -1) return -errno; buf[res] = '\0'; return 0; } struct xmp_dirp { DIR *dp; struct dirent *entry; off_t offset; }; static int xmp_opendir(const char *path, struct fuse_file_info *fi) { int res; struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp)); if (d == NULL) return -ENOMEM; d->dp = opendir(path); if (d->dp == NULL) { res = -errno; free(d); return res; } d->offset = 0; d->entry = NULL; fi->fh = (unsigned long) d; return 0; } static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi) { return (struct xmp_dirp *) (uintptr_t) fi->fh; } static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { struct xmp_dirp *d = get_dirp(fi); (void) path; if (offset != d->offset) { #ifndef __FreeBSD__ seekdir(d->dp, offset); #else /* Subtract the one that we add when calling telldir() below */ seekdir(d->dp, offset-1); #endif d->entry = NULL; d->offset = offset; } while (1) { struct stat st; off_t nextoff; enum fuse_fill_dir_flags fill_flags = FUSE_FILL_DIR_DEFAULTS; if (!d->entry) { d->entry = readdir(d->dp); if (!d->entry) break; } #ifdef HAVE_FSTATAT if (flags & FUSE_READDIR_PLUS) { int res; res = fstatat(dirfd(d->dp), d->entry->d_name, &st, AT_SYMLINK_NOFOLLOW); if (res != -1) fill_flags |= FUSE_FILL_DIR_PLUS; } #endif if (!(fill_flags & FUSE_FILL_DIR_PLUS)) { memset(&st, 0, sizeof(st)); st.st_ino = d->entry->d_ino; st.st_mode = d->entry->d_type << 12; } nextoff = telldir(d->dp); #ifdef __FreeBSD__ /* Under FreeBSD, telldir() may return 0 the first time it is called. But for libfuse, an offset of zero means that offsets are not supported, so we shift everything by one. */ nextoff++; #endif if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags)) break; d->entry = NULL; d->offset = nextoff; } return 0; } static int xmp_releasedir(const char *path, struct fuse_file_info *fi) { struct xmp_dirp *d = get_dirp(fi); (void) path; closedir(d->dp); free(d); return 0; } static int xmp_mknod(const char *path, mode_t mode, dev_t rdev) { int res; if (S_ISFIFO(mode)) res = mkfifo(path, mode); else res = mknod(path, mode, rdev); if (res == -1) return -errno; return 0; } static int xmp_mkdir(const char *path, mode_t mode) { int res; res = mkdir(path, mode); 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_rmdir(const char *path) { int res; res = rmdir(path); if (res == -1) return -errno; return 0; } static int xmp_symlink(const char *from, const char *to) { int res; res = symlink(from, to); if (res == -1) return -errno; return 0; } static int xmp_rename(const char *from, const char *to, unsigned int flags) { int res; /* When we have renameat2() in libc, then we can implement flags */ if (flags) return -EINVAL; res = rename(from, to); if (res == -1) return -errno; return 0; } static int xmp_link(const char *from, const char *to) { int res; res = link(from, to); if (res == -1) return -errno; return 0; } static int xmp_chmod(const char *path, mode_t mode, struct fuse_file_info *fi) { int res; if(fi) res = fchmod(fi->fh, mode); else res = chmod(path, mode); if (res == -1) return -errno; return 0; } static int xmp_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi) { int res; if (fi) res = fchown(fi->fh, uid, gid); else res = lchown(path, uid, gid); if (res == -1) return -errno; return 0; } static int xmp_truncate(const char *path, off_t size, struct fuse_file_info *fi) { int res; if(fi) res = ftruncate(fi->fh, size); else res = truncate(path, size); if (res == -1) return -errno; return 0; } #ifdef HAVE_UTIMENSAT static int xmp_utimens(const char *path, const struct timespec ts[2], struct fuse_file_info *fi) { int res; /* don't use utime/utimes since they follow symlinks */ if (fi) res = futimens(fi->fh, ts); else res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW); if (res == -1) return -errno; return 0; } #endif 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_open(const char *path, struct fuse_file_info *fi) { int fd; fd = open(path, fi->flags); if (fd == -1) return -errno; /* Enable direct_io when open has flags O_DIRECT to enjoy the feature parallel_direct_writes (i.e., to get a shared lock, not exclusive lock, for writes to the same file). */ if (fi->flags & O_DIRECT) { fi->direct_io = 1; fi->parallel_direct_writes = 1; } fi->fh = fd; return 0; } static int xmp_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { int res; (void) path; res = pread(fi->fh, buf, size, offset); if (res == -1) res = -errno; return res; } static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp, size_t size, off_t offset, struct fuse_file_info *fi) { struct fuse_bufvec *src; (void) path; src = malloc(sizeof(struct fuse_bufvec)); if (src == NULL) return -ENOMEM; *src = FUSE_BUFVEC_INIT(size); src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; src->buf[0].fd = fi->fh; src->buf[0].pos = offset; *bufp = src; return 0; } static int xmp_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { int res; (void) path; res = pwrite(fi->fh, buf, size, offset); if (res == -1) res = -errno; return res; } static int xmp_write_buf(const char *path, struct fuse_bufvec *buf, off_t offset, struct fuse_file_info *fi) { struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf)); (void) path; dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; dst.buf[0].fd = fi->fh; dst.buf[0].pos = offset; return fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK); } static int xmp_statfs(const char *path, struct statvfs *stbuf) { int res; res = statvfs(path, stbuf); if (res == -1) return -errno; return 0; } static int xmp_flush(const char *path, struct fuse_file_info *fi) { int res; (void) path; /* This is called from every close on an open file, so call the close on the underlying filesystem. But since flush may be called multiple times for an open file, this must not really close the file. This is important if used on a network filesystem like NFS which flush the data/metadata on close() */ res = close(dup(fi->fh)); if (res == -1) return -errno; return 0; } static int xmp_release(const char *path, struct fuse_file_info *fi) { (void) path; close(fi->fh); return 0; } static int xmp_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) { int res; (void) path; #ifndef HAVE_FDATASYNC (void) isdatasync; #else if (isdatasync) res = fdatasync(fi->fh); else #endif res = fsync(fi->fh); if (res == -1) return -errno; return 0; } static int xmp_fallocate(const char *path, int mode, off_t offset, off_t length, struct fuse_file_info *fi) { (void) path; return do_fallocate(fi->fh, mode, offset, length); } #ifdef HAVE_SETXATTR /* xattr operations are optional and can safely be left unimplemented */ static int xmp_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { int res = lsetxattr(path, name, value, size, flags); if (res == -1) return -errno; return 0; } static int xmp_getxattr(const char *path, const char *name, char *value, size_t size) { int res = lgetxattr(path, name, value, size); if (res == -1) return -errno; return res; } static int xmp_listxattr(const char *path, char *list, size_t size) { int res = llistxattr(path, list, size); if (res == -1) return -errno; return res; } static int xmp_removexattr(const char *path, const char *name) { int res = lremovexattr(path, name); if (res == -1) return -errno; return 0; } #endif /* HAVE_SETXATTR */ #ifdef HAVE_LIBULOCKMGR static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd, struct flock *lock) { (void) path; return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner, sizeof(fi->lock_owner)); } #endif static int xmp_flock(const char *path, struct fuse_file_info *fi, int op) { int res; (void) path; res = flock(fi->fh, op); if (res == -1) return -errno; return 0; } #ifdef HAVE_COPY_FILE_RANGE static ssize_t xmp_copy_file_range(const char *path_in, struct fuse_file_info *fi_in, off_t off_in, const char *path_out, struct fuse_file_info *fi_out, off_t off_out, size_t len, int flags) { ssize_t res; (void) path_in; (void) path_out; res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags); if (res == -1) return -errno; return res; } #endif static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi) { off_t res; (void) path; res = lseek(fi->fh, off, whence); if (res == -1) return -errno; return res; } #ifdef HAVE_STATX static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi) { int fd = -1; int res; if (fi) fd = fi->fh; res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf); if (res == -1) return -errno; return 0; } #endif static const struct fuse_operations xmp_oper = { .init = xmp_init, .getattr = xmp_getattr, .access = xmp_access, .readlink = xmp_readlink, .opendir = xmp_opendir, .readdir = xmp_readdir, .releasedir = xmp_releasedir, .mknod = xmp_mknod, .mkdir = xmp_mkdir, .symlink = xmp_symlink, .unlink = xmp_unlink, .rmdir = xmp_rmdir, .rename = xmp_rename, .link = xmp_link, .chmod = xmp_chmod, .chown = xmp_chown, .truncate = xmp_truncate, #ifdef HAVE_UTIMENSAT .utimens = xmp_utimens, #endif .create = xmp_create, .open = xmp_open, .read = xmp_read, .read_buf = xmp_read_buf, .write = xmp_write, .write_buf = xmp_write_buf, .statfs = xmp_statfs, .flush = xmp_flush, .release = xmp_release, .fsync = xmp_fsync, .fallocate = xmp_fallocate, #ifdef HAVE_SETXATTR .setxattr = xmp_setxattr, .getxattr = xmp_getxattr, .listxattr = xmp_listxattr, .removexattr = xmp_removexattr, #endif #ifdef HAVE_LIBULOCKMGR .lock = xmp_lock, #endif .flock = xmp_flock, #ifdef HAVE_COPY_FILE_RANGE .copy_file_range = xmp_copy_file_range, #endif .lseek = xmp_lseek, #ifdef HAVE_STATX .statx = xmp_statx, #endif }; int main(int argc, char *argv[]) { umask(0); return fuse_main(argc, argv, &xmp_oper, NULL); } fuse-3.18.2/example/passthrough_helpers.h0000644000175000017500000000661715156613252017432 0ustar berndbernd/* * FUSE: Filesystem in Userspace * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE */ #ifndef FUSE_EXAMPLE_PASSTHROUGH_HELPERS_H_ #define FUSE_EXAMPLE_PASSTHROUGH_HELPERS_H_ #include #include #include #include #include #ifdef __FreeBSD__ #include #include #endif static inline int do_fallocate(int fd, int mode, off_t offset, off_t length) { #ifdef HAVE_FALLOCATE if (fallocate(fd, mode, offset, length) == -1) return -errno; return 0; #else // HAVE_FALLOCATE #ifdef HAVE_POSIX_FALLOCATE if (mode == 0) return -posix_fallocate(fd, offset, length); #endif #ifdef HAVE_FSPACECTL // 0x3 == FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE if (mode == 0x3) { struct spacectl_range sr; sr.r_offset = offset; sr.r_len = length; if (fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, NULL) == -1) return -errno; return 0; } #endif return -EOPNOTSUPP; #endif // HAVE_FALLOCATE } /* * Creates files on the underlying file system in response to a FUSE_MKNOD * operation */ static inline int mknod_wrapper(int dirfd, const char *path, const char *link, int mode, dev_t rdev) { int res; if (S_ISREG(mode)) { res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); if (res >= 0) res = close(res); } else if (S_ISDIR(mode)) { res = mkdirat(dirfd, path, mode); } else if (S_ISLNK(mode) && link != NULL) { res = symlinkat(link, dirfd, path); } else if (S_ISFIFO(mode)) { res = mkfifoat(dirfd, path, mode); #ifdef __FreeBSD__ } else if (S_ISSOCK(mode)) { struct sockaddr_un su; int fd; if (strlen(path) >= sizeof(su.sun_path)) { errno = ENAMETOOLONG; return -1; } fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd >= 0) { /* * We must bind the socket to the underlying file * system to create the socket file, even though * we'll never listen on this socket. */ su.sun_family = AF_UNIX; strncpy(su.sun_path, path, sizeof(su.sun_path)); res = bindat(dirfd, fd, (struct sockaddr*)&su, sizeof(su)); if (res == 0) close(fd); } else { res = -1; } #endif } else { res = mknodat(dirfd, path, mode, rdev); } return res; } #endif // FUSE_PASSTHROUGH_HELPERS_H_ fuse-3.18.2/example/passthrough_hp.cc0000644000175000017500000012415415156613252016532 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Copyright (C) 2017 Nikolaus Rath Copyright (C) 2018 Valve, Inc This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This is a "high-performance" version of passthrough_ll.c. While * passthrough_ll.c is designed to be as simple as possible, this * example intended to be as efficient and correct as possible. * * passthrough_hp.cc mirrors a specified "source" directory under a * specified the mountpoint with as much fidelity and performance as * possible. * * If --nocache is specified, the source directory may be changed * directly even while mounted and the filesystem will continue * to work correctly. * * Without --nocache, the source directory is assumed to be modified * only through the passthrough filesystem. This enables much better * performance, but if changes are made directly to the source, they * may not be immediately visible under the mountpoint and further * access to the mountpoint may result in incorrect behavior, * including data-loss. * * On its own, this filesystem fulfills no practical purpose. It is * intended as a template upon which additional functionality can be * built. * * Unless --nocache is specified, is only possible to write to files * for which the mounting user has read permissions. This is because * the writeback cache requires the kernel to be able to issue read * requests for all files (which the passthrough filesystem cannot * satisfy if it can't read the file in the underlying filesystem). * * ## Source code ## * \include passthrough_hp.cc */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif // C includes #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_XATTR_H #include #endif #include #include #include #include #ifdef __FreeBSD__ #include #include #endif // C++ includes #include #include #include #include "cxxopts.hpp" #include #include #include #include "passthrough_helpers.h" 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 variable and a uint64_t variable (used for file handles). */ static_assert(sizeof(fuse_ino_t) >= sizeof(void *), "void* must fit into fuse_ino_t"); static_assert(sizeof(fuse_ino_t) >= sizeof(uint64_t), "fuse_ino_t must be at least 64 bits"); /* Forward declarations */ struct Inode; static Inode &get_inode(fuse_ino_t ino); static void forget_one(fuse_ino_t ino, uint64_t n); // Uniquely identifies a file in the source directory tree. This could // be simplified to just ino_t since we require the source directory // not to contain any mountpoints. This hasn't been done yet in case // we need to reconsider this constraint (but relaxing this would have // the drawback that we can no longer reuse inode numbers, and thus // readdir() would need to do a full lookup() in order to report the // right inode number). typedef std::pair SrcId; // Define a hash function for SrcId namespace std { template <> struct hash { size_t operator()(const SrcId &id) const { return hash{}(id.first) ^ hash{}(id.second); } }; } // Maps files in the source directory tree to inodes typedef std::unordered_map InodeMap; struct Inode { int fd{ -1 }; dev_t src_dev{ 0 }; ino_t src_ino{ 0 }; int generation{ 0 }; int backing_id{ 0 }; uint64_t nopen{ 0 }; std::atomic nlookup{ 0 }; std::mutex m; // Delete copy constructor and assignments. We could implement // move if we need it. Inode() = default; Inode(const Inode &) = delete; Inode(Inode &&inode) = delete; Inode &operator=(Inode &&inode) = delete; Inode &operator=(const Inode &) = delete; ~Inode() { if (fd > 0) close(fd); } }; struct Fs { // Must be acquired *after* any Inode.m locks. std::mutex mutex; InodeMap inodes; // protected by mutex 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; bool direct_io; bool passthrough; }; static Fs fs{}; #define FUSE_BUF_COPY_FLAGS \ (fs.nosplice ? FUSE_BUF_NO_SPLICE : \ static_cast(FUSE_BUF_SPLICE_MOVE)) static Inode &get_inode(fuse_ino_t ino) { if (ino == FUSE_ROOT_ID) return fs.root; Inode *inode = reinterpret_cast(ino); if (inode->fd == -1) { cerr << "INTERNAL ERROR: Unknown inode " << ino << endl; abort(); } return *inode; } static int get_fs_fd(fuse_ino_t ino) { int fd = get_inode(ino).fd; return fd; } static void sfs_init(void *userdata, fuse_conn_info *conn) { (void)userdata; if (!fuse_set_feature_flag(conn, FUSE_CAP_PASSTHROUGH)) fs.passthrough = false; /* Passthrough and writeback cache are conflicting modes */ if (fs.timeout && !fs.passthrough) fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE); fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS); if (fs.nosplice) { // FUSE_CAP_SPLICE_READ is enabled in libfuse3 by default, // see do_init() in fuse_lowlevel.c // Just unset all, in case FUSE_CAP_SPLICE_WRITE or // FUSE_CAP_SPLICE_MOVE would also get enabled by default. fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ); fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_WRITE); fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_MOVE); } else { fuse_set_feature_flag(conn, FUSE_CAP_SPLICE_WRITE); fuse_set_feature_flag(conn, FUSE_CAP_SPLICE_READ); fuse_set_feature_flag(conn, FUSE_CAP_SPLICE_MOVE); } /* This is a local file system - no network coherency needed */ fuse_set_feature_flag(conn, FUSE_CAP_DIRECT_IO_ALLOW_MMAP); /* Disable NFS export support, which also disabled name_to_handle_at. * Goal is to make xfstests that test name_to_handle_at to fail with * the right error code (EOPNOTSUPP) than to open_by_handle_at to fail with * ESTALE and let those test fail. * Perfect NFS export support is not possible with this FUSE filesystem needs * more kernel work, in order to passthrough nfs handle encode/decode to * fuse-server/daemon. */ fuse_set_feature_flag(conn, FUSE_CAP_NO_EXPORT_SUPPORT); /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; /* Try a large IO by default */ conn->max_write = 4 * 1024 * 1024; } static void sfs_getattr(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) { struct stat attr; int fd = fi ? fi->fh : get_inode(ino).fd; auto res = fstatat(fd, "", &attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) { fuse_reply_err(req, errno); return; } fuse_reply_attr(req, &attr, fs.timeout); } static int with_fd_path(int fd, const std::function &f) { #ifdef __FreeBSD__ struct kinfo_file kf; kf.kf_structsize = sizeof(kf); int ret = fcntl(fd, F_KINFO, &kf); if (ret == -1) return ret; return f(kf.kf_path); #else // Linux char procname[64]; sprintf(procname, "/proc/self/fd/%i", fd); return f(procname); #endif } static void do_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int valid, struct fuse_file_info *fi) { Inode &inode = get_inode(ino); int ifd = inode.fd; int res; if (valid & FUSE_SET_ATTR_MODE) { if (fi) { res = fchmod(fi->fh, attr->st_mode); } else { res = with_fd_path(ifd, [attr](const char *procname) { return chmod(procname, attr->st_mode); }); } if (res == -1) goto out_err; } if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : static_cast(-1); gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : static_cast(-1); res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) goto out_err; } if (valid & FUSE_SET_ATTR_SIZE) { if (fi) { res = ftruncate(fi->fh, attr->st_size); } else { res = with_fd_path(ifd, [attr](const char *procname) { return truncate(procname, attr->st_size); }); } if (res == -1) goto out_err; } if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { struct timespec tv[2]; tv[0].tv_sec = 0; tv[1].tv_sec = 0; tv[0].tv_nsec = UTIME_OMIT; tv[1].tv_nsec = UTIME_OMIT; if (valid & FUSE_SET_ATTR_ATIME_NOW) tv[0].tv_nsec = UTIME_NOW; else if (valid & FUSE_SET_ATTR_ATIME) tv[0] = attr->st_atim; if (valid & FUSE_SET_ATTR_MTIME_NOW) tv[1].tv_nsec = UTIME_NOW; else if (valid & FUSE_SET_ATTR_MTIME) tv[1] = attr->st_mtim; if (fi) res = futimens(fi->fh, tv); else { #ifdef HAVE_UTIMENSAT res = with_fd_path(ifd, [&tv](const char *procname) { return utimensat(AT_FDCWD, procname, tv, 0); }); #else res = -1; errno = EOPNOTSUPP; #endif } if (res == -1) goto out_err; } return sfs_getattr(req, ino, fi); out_err: fuse_reply_err(req, errno); } static void sfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int valid, fuse_file_info *fi) { (void)ino; do_setattr(req, ino, attr, valid, fi); } static int do_lookup(fuse_ino_t parent, const char *name, fuse_entry_param *e) { if (fs.debug) cerr << "DEBUG: lookup(): name=" << name << ", parent=" << parent << endl; memset(e, 0, sizeof(*e)); e->attr_timeout = fs.timeout; e->entry_timeout = fs.timeout; auto newfd = openat(get_fs_fd(parent), name, O_PATH | O_NOFOLLOW); if (newfd == -1) return errno; auto res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) { auto saveerr = errno; close(newfd); if (fs.debug) cerr << "DEBUG: lookup(): fstatat failed" << endl; return saveerr; } if (e->attr.st_dev != fs.src_dev) { cerr << "WARNING: Mountpoints in the source directory tree will be hidden." << endl; return ENOTSUP; } else if (e->attr.st_ino == FUSE_ROOT_ID) { cerr << "ERROR: Source directory tree must not include inode " << FUSE_ROOT_ID << endl; return EIO; } SrcId id{ e->attr.st_ino, e->attr.st_dev }; unique_lock fs_lock{ fs.mutex }; Inode *inode_p; try { inode_p = &fs.inodes[id]; } catch (std::bad_alloc &) { return ENOMEM; } e->ino = reinterpret_cast(inode_p); Inode &inode{ *inode_p }; e->generation = inode.generation; if (inode.fd == -ENOENT) { // found unlinked inode if (fs.debug) cerr << "DEBUG: lookup(): inode " << e->attr.st_ino << " recycled; generation=" << inode.generation << endl; /* fallthrough to new inode but keep existing inode.nlookup */ } if (inode.fd > 0) { // found existing inode if (fs.debug) cerr << "DEBUG: lookup(): inode " << e->attr.st_ino << " (userspace) already known; fd = " << inode.fd << endl; inode.nlookup++; if (fs.debug) cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " " << "inode " << inode.src_ino << " count " << inode.nlookup << endl; fs_lock.unlock(); close(newfd); } else { // no existing inode /* This is just here to make Helgrind happy. It violates the * lock ordering requirement (inode.m must be acquired before * fs.mutex), but this is of no consequence because at this * point no other thread has access to the inode mutex */ lock_guard 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(); if (fs.debug) cerr << "DEBUG: lookup(): created userspace inode " << e->attr.st_ino << "; fd = " << inode.fd << endl; } return 0; } static void sfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { fuse_entry_param e{}; auto err = do_lookup(parent, name, &e); if (err == ENOENT) { e.attr_timeout = fs.timeout; e.entry_timeout = fs.timeout; e.ino = e.attr.st_ino = 0; fuse_reply_entry(req, &e); } else if (err) { if (err == ENFILE || err == EMFILE) cerr << "ERROR: Reached maximum number of file descriptors." << endl; fuse_reply_err(req, err); } else { fuse_reply_entry(req, &e); } } static void mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev, const char *link) { int res; Inode &inode_p = get_inode(parent); auto saverr = ENOMEM; if (S_ISDIR(mode)) res = mkdirat(inode_p.fd, name, mode); else if (S_ISLNK(mode)) res = symlinkat(link, inode_p.fd, name); else res = mknodat(inode_p.fd, name, mode, rdev); saverr = errno; if (res == -1) goto out; fuse_entry_param e; saverr = do_lookup(parent, name, &e); if (saverr) goto out; fuse_reply_entry(req, &e); return; out: if (saverr == ENFILE || saverr == EMFILE) cerr << "ERROR: Reached maximum number of file descriptors." << endl; fuse_reply_err(req, saverr); } static void sfs_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) { mknod_symlink(req, parent, name, mode, rdev, nullptr); } static void sfs_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) { mknod_symlink(req, parent, name, S_IFDIR | mode, 0, nullptr); } static void sfs_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name) { mknod_symlink(req, parent, name, S_IFLNK, 0, link); } static void sfs_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, const char *name) { Inode &inode = get_inode(ino); Inode &inode_p = get_inode(parent); fuse_entry_param e{}; e.attr_timeout = fs.timeout; e.entry_timeout = fs.timeout; char procname[64]; sprintf(procname, "/proc/self/fd/%i", inode.fd); auto res = linkat(AT_FDCWD, procname, inode_p.fd, name, AT_SYMLINK_FOLLOW); if (res == -1) { fuse_reply_err(req, errno); return; } res = fstatat(inode.fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) { fuse_reply_err(req, errno); return; } e.ino = reinterpret_cast(&inode); { inode.nlookup++; if (fs.debug) cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " " << "inode " << inode.src_ino << " count " << inode.nlookup << endl; } fuse_reply_entry(req, &e); return; } static void sfs_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) { Inode &inode_p = get_inode(parent); lock_guard g{ inode_p.m }; auto res = unlinkat(inode_p.fd, name, AT_REMOVEDIR); fuse_reply_err(req, res == -1 ? errno : 0); } static void sfs_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags) { Inode &inode_p = get_inode(parent); Inode &inode_np = get_inode(newparent); if (flags) { fuse_reply_err(req, EINVAL); return; } auto res = renameat(inode_p.fd, name, inode_np.fd, newname); fuse_reply_err(req, res == -1 ? errno : 0); } static void sfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) { Inode &inode_p = get_inode(parent); // Release inode.fd before last unlink like nfsd EXPORT_OP_CLOSE_BEFORE_UNLINK // to test reused inode numbers. // Skip this when inode has an open file and when writeback cache is enabled. if (!fs.timeout) { fuse_entry_param e; auto err = do_lookup(parent, name, &e); if (err) { fuse_reply_err(req, err); return; } if (e.attr.st_nlink == 1) { Inode &inode = get_inode(e.ino); lock_guard g{ inode.m }; if (inode.fd > 0 && !inode.nopen) { if (fs.debug) cerr << "DEBUG: unlink: release inode " << e.attr.st_ino << "; fd=" << inode.fd << endl; lock_guard g_fs{ fs.mutex }; close(inode.fd); inode.fd = -ENOENT; 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); } static void forget_one(fuse_ino_t ino, uint64_t n) { Inode &inode = get_inode(ino); unique_lock l{ inode.m }; if (n > inode.nlookup) { cerr << "INTERNAL ERROR: Negative lookup count for inode " << inode.src_ino << endl; abort(); } inode.nlookup -= n; if (fs.debug) cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " " << "inode " << inode.src_ino << " count " << inode.nlookup << endl; if (!inode.nlookup) { lock_guard g_fs{ fs.mutex }; l.unlock(); if (!inode.nlookup) { if (fs.debug) cerr << "DEBUG: forget: cleaning up inode " << inode.src_ino << endl; fs.inodes.erase({ inode.src_ino, inode.src_dev }); } } else if (fs.debug) cerr << "DEBUG: forget: inode " << inode.src_ino << " lookup count now " << inode.nlookup << endl; } static void sfs_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { forget_one(ino, nlookup); fuse_reply_none(req); } static void sfs_forget_multi(fuse_req_t req, size_t count, fuse_forget_data *forgets) { for (int i = 0; i < count; i++) forget_one(forgets[i].ino, forgets[i].nlookup); fuse_reply_none(req); } static void sfs_readlink(fuse_req_t req, fuse_ino_t ino) { Inode &inode = get_inode(ino); char buf[PATH_MAX + 1]; auto res = readlinkat(inode.fd, "", buf, sizeof(buf)); if (res == -1) fuse_reply_err(req, errno); else if (res == sizeof(buf)) fuse_reply_err(req, ENAMETOOLONG); else { buf[res] = '\0'; fuse_reply_readlink(req, buf); } } struct DirHandle { DIR *dp{ nullptr }; off_t offset; DirHandle() = default; DirHandle(const DirHandle &) = delete; DirHandle &operator=(const DirHandle &) = delete; ~DirHandle() { if (dp) closedir(dp); } }; static DirHandle *get_dir_handle(fuse_file_info *fi) { return reinterpret_cast(fi->fh); } static void sfs_opendir(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) { Inode &inode = get_inode(ino); auto d = new (nothrow) DirHandle; if (d == nullptr) { fuse_reply_err(req, ENOMEM); return; } // Make Helgrind happy - it can't know that there's an implicit // synchronization due to the fact that other threads cannot // access d until we've called fuse_reply_*. lock_guard g{ inode.m }; auto fd = openat(inode.fd, ".", O_RDONLY); if (fd == -1) goto out_errno; // On success, dir stream takes ownership of fd, so we // do not have to close it. d->dp = fdopendir(fd); if (d->dp == nullptr) goto out_errno; d->offset = 0; fi->fh = reinterpret_cast(d); if (fs.timeout) { fi->keep_cache = 1; fi->cache_readdir = 1; } fuse_reply_open(req, fi); return; out_errno: auto error = errno; delete d; if (error == ENFILE || error == EMFILE) cerr << "ERROR: Reached maximum number of file descriptors." << endl; fuse_reply_err(req, error); } static bool is_dot_or_dotdot(const char *name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } static void do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, fuse_file_info *fi, const int plus) { auto d = get_dir_handle(fi); Inode &inode = get_inode(ino); lock_guard g{ inode.m }; char *p; auto rem = size; int err = 0, count = 0; if (fs.debug) cerr << "DEBUG: readdir(): started with offset " << offset << endl; auto buf = new (nothrow) char[size]; if (!buf) { fuse_reply_err(req, ENOMEM); return; } p = buf; if (offset != d->offset) { if (fs.debug) cerr << "DEBUG: readdir(): seeking to " << offset << endl; seekdir(d->dp, offset); d->offset = offset; } while (1) { bool did_lookup = false; struct dirent *entry; errno = 0; entry = readdir(d->dp); if (!entry) { if (errno) { err = errno; if (fs.debug) warn("DEBUG: readdir(): readdir failed with"); goto error; } break; // End of stream } d->offset = entry->d_off; fuse_entry_param e{}; size_t entsize; if (plus) { if (is_dot_or_dotdot(entry->d_name)) { /* fuse kernel ignores attributes for these and also does * not increase lookup count (see fuse_direntplus_link) */ e.attr.st_ino = entry->d_ino; e.attr.st_mode = entry->d_type << 12; } else { err = do_lookup(ino, entry->d_name, &e); if (err) goto error; did_lookup = true; } entsize = fuse_add_direntry_plus( req, p, rem, entry->d_name, &e, entry->d_off); } 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; if (did_lookup) forget_one(e.ino, 1); break; } p += entsize; rem -= entsize; count++; if (fs.debug) { cerr << "DEBUG: readdir(): added to buffer: " << entry->d_name << ", ino " << e.attr.st_ino << ", offset " << entry->d_off << endl; } } err = 0; error: // If there's an error, we can only signal it if we haven't stored // any entries yet - otherwise we'd end up with wrong lookup // counts for the entries that are already in the buffer. So we // return what we've collected until that point. if (err && rem == size) { if (err == ENFILE || err == EMFILE) cerr << "ERROR: Reached maximum number of file descriptors." << endl; fuse_reply_err(req, err); } else { if (fs.debug) cerr << "DEBUG: readdir(): returning " << count << " entries, curr offset " << d->offset << endl; fuse_reply_buf(req, buf, size - rem); } delete[] buf; return; } static void sfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, fuse_file_info *fi) { // operation logging is done in readdir to reduce code duplication do_readdir(req, ino, size, offset, fi, 0); } static void sfs_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, fuse_file_info *fi) { // operation logging is done in readdir to reduce code duplication do_readdir(req, ino, size, offset, fi, 1); } static void sfs_releasedir(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) { (void)ino; auto d = get_dir_handle(fi); delete d; fuse_reply_err(req, 0); } static void do_passthrough_open(fuse_req_t req, fuse_ino_t ino, int fd, fuse_file_info *fi) { Inode &inode = get_inode(ino); /* Setup a shared backing file on first open of an inode */ if (inode.backing_id) { if (fs.debug) cerr << "DEBUG: reusing shared backing file " << inode.backing_id << " for inode " << ino << endl; fi->backing_id = inode.backing_id; } else if (!(inode.backing_id = fuse_passthrough_open(req, fd))) { cerr << "DEBUG: fuse_passthrough_open failed for inode " << ino << ", disabling rw passthrough." << endl; fs.passthrough = false; } else { if (fs.debug) cerr << "DEBUG: setup shared backing file " << inode.backing_id << " for inode " << ino << endl; fi->backing_id = inode.backing_id; } /* open in passthrough mode must drop old page cache */ if (fi->backing_id) fi->keep_cache = false; } static void sfs_create_open_flags(fuse_file_info *fi) { if (fs.direct_io) fi->direct_io = 1; /* * fi->direct_io (FOPEN_DIRECT_IO) is set to benefit from * parallel_direct_writes, which kernel cannot do for plain O_DIRECT. * However, passthrough is preferred, but which is not possible when * FOPEN_DIRECT_IO is set. */ if (!fs.passthrough) { if (fi->flags & O_DIRECT) fi->direct_io = 1; } /* parallel_direct_writes feature depends on direct_io features. * To make parallel_direct_writes valid, need set fi->direct_io * in current function. */ fi->parallel_direct_writes = 1; fi->keep_cache = (fs.timeout != 0); fi->noflush = (fs.timeout == 0 && (fi->flags & O_ACCMODE) == O_RDONLY); } static void sfs_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, fuse_file_info *fi) { Inode &inode_p = get_inode(parent); auto fd = openat(inode_p.fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode); if (fd == -1) { auto err = errno; if (err == ENFILE || err == EMFILE) cerr << "ERROR: Reached maximum number of file descriptors." << endl; fuse_reply_err(req, err); return; } fi->fh = fd; fuse_entry_param e; auto err = do_lookup(parent, name, &e); if (err) { if (err == ENFILE || err == EMFILE) cerr << "ERROR: Reached maximum number of file descriptors." << endl; fuse_reply_err(req, err); return; } Inode &inode = get_inode(e.ino); lock_guard g{ inode.m }; inode.nopen++; sfs_create_open_flags(fi); if (fs.passthrough) do_passthrough_open(req, e.ino, fd, fi); fuse_reply_create(req, &e, fi); } #ifdef O_TMPFILE static Inode *create_new_inode(int fd, fuse_entry_param *e) { memset(e, 0, sizeof(*e)); e->attr_timeout = fs.timeout; e->entry_timeout = fs.timeout; auto res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) { if (fs.debug) cerr << "DEBUG: lookup(): fstatat failed" << endl; return NULL; } SrcId id{ e->attr.st_ino, e->attr.st_dev }; unique_lock fs_lock{ fs.mutex }; Inode *p_inode; try { p_inode = &fs.inodes[id]; } catch (std::bad_alloc &) { return NULL; } e->ino = reinterpret_cast(p_inode); e->generation = p_inode->generation; lock_guard g{ p_inode->m }; p_inode->src_ino = e->attr.st_ino; p_inode->src_dev = e->attr.st_dev; p_inode->nlookup++; if (fs.debug) cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " " << "inode " << p_inode->src_ino << " count " << p_inode->nlookup << endl; p_inode->fd = fd; fs_lock.unlock(); if (fs.debug) cerr << "DEBUG: lookup(): created userspace inode " << e->attr.st_ino << "; fd = " << p_inode->fd << endl; return p_inode; } static void sfs_tmpfile(fuse_req_t req, fuse_ino_t parent, mode_t mode, fuse_file_info *fi) { Inode &parent_inode = get_inode(parent); auto fd = openat(parent_inode.fd, ".", (fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode); if (fd == -1) { auto err = errno; if (err == ENFILE || err == EMFILE) cerr << "ERROR: Reached maximum number of file descriptors." << endl; fuse_reply_err(req, err); return; } fi->fh = fd; fuse_entry_param e; Inode *inode = create_new_inode(dup(fd), &e); if (inode == NULL) { auto err = errno; cerr << "ERROR: could not create new inode." << endl; close(fd); fuse_reply_err(req, err); return; } lock_guard g{ inode->m }; sfs_create_open_flags(fi); if (fs.passthrough) do_passthrough_open(req, e.ino, fd, fi); fuse_reply_create(req, &e, fi); } #endif static void sfs_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, fuse_file_info *fi) { (void)ino; int res; int fd = dirfd(get_dir_handle(fi)->dp); if (datasync) res = fdatasync(fd); else res = fsync(fd); fuse_reply_err(req, res == -1 ? errno : 0); } static void sfs_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) { Inode &inode = get_inode(ino); /* With writeback cache, kernel may send read requests even when userspace opened write-only */ if (fs.timeout && (fi->flags & O_ACCMODE) == O_WRONLY) { fi->flags &= ~O_ACCMODE; fi->flags |= O_RDWR; } /* With writeback cache, O_APPEND is handled by the kernel. This * breaks atomicity (since the file may change in the underlying * filesystem, so that the kernel's idea of the end of the file * isn't accurate anymore). However, no process should modify the * file in the underlying filesystem once it has been read, so * this is not a problem. */ if (fs.timeout && fi->flags & O_APPEND) fi->flags &= ~O_APPEND; /* Unfortunately we cannot use inode.fd, because this was opened with O_PATH (so it doesn't allow read/write access). */ auto fd = with_fd_path(inode.fd, [fi](const char *buf) { return open(buf, fi->flags & ~O_NOFOLLOW); }); if (fd == -1) { auto err = errno; if (err == ENFILE || err == EMFILE) cerr << "ERROR: Reached maximum number of file descriptors." << endl; fuse_reply_err(req, err); return; } lock_guard g{ inode.m }; inode.nopen++; sfs_create_open_flags(fi); fi->fh = fd; if (fs.passthrough) do_passthrough_open(req, ino, fd, fi); fuse_reply_open(req, fi); } static void sfs_release(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) { Inode &inode = get_inode(ino); lock_guard g{ inode.m }; inode.nopen--; /* Close the shared backing file on last file close of an inode */ if (inode.backing_id && !inode.nopen) { if (fuse_passthrough_close(req, inode.backing_id) < 0) { cerr << "DEBUG: fuse_passthrough_close failed for inode " << ino << " backing file " << inode.backing_id << endl; } else if (fs.debug) { cerr << "DEBUG: closed backing file " << inode.backing_id << " for inode " << ino << endl; } inode.backing_id = 0; } close(fi->fh); fuse_reply_err(req, 0); } static void sfs_flush(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) { (void)ino; auto res = close(dup(fi->fh)); fuse_reply_err(req, res == -1 ? errno : 0); } static void sfs_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, fuse_file_info *fi) { (void)ino; int res; if (datasync) res = fdatasync(fi->fh); else res = fsync(fi->fh); fuse_reply_err(req, res == -1 ? errno : 0); } static void do_read(fuse_req_t req, size_t size, off_t off, fuse_file_info *fi) { fuse_bufvec buf = FUSE_BUFVEC_INIT(size); char *payload = NULL; size_t payload_size = 0; int res = fuse_req_get_payload(req, &payload, &payload_size, NULL); /* * This is a demonstration how to use io-uring payload. For FUSE_BUF_IS_FD * it shouldn't make much of a difference because fuse_reply_data() -> * fuse_reply_data_uring() also has access to the payload and will * read directly from the FD into the payload. * It is more useful for file systems that need a buffer for decryption, * decompression, etc. */ if (res == 0) { /* This is an io-uring request - write directly to the payload */ assert(payload_size >= size); buf.buf[0].mem = payload; buf.buf[0].size = payload_size; res = pread(fi->fh, payload, size, off); if (res < 0) { fuse_reply_err(req, errno); return; } buf.buf[0].size = res; } else { buf.buf[0].flags = static_cast( FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK); buf.buf[0].fd = fi->fh; buf.buf[0].pos = off; } fuse_reply_data(req, &buf, FUSE_BUF_COPY_FLAGS); } static void sfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, fuse_file_info *fi) { (void)ino; if (fs.passthrough && !fs.direct_io) { cerr << "ERROR: fuse_passthrough read failed." << endl; fuse_reply_err(req, EIO); return; } do_read(req, size, off, fi); } static void do_write_buf(fuse_req_t req, size_t size, off_t off, fuse_bufvec *in_buf, fuse_file_info *fi) { fuse_bufvec out_buf = FUSE_BUFVEC_INIT(size); out_buf.buf[0].flags = static_cast(FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK); out_buf.buf[0].fd = fi->fh; out_buf.buf[0].pos = off; auto res = fuse_buf_copy(&out_buf, in_buf, FUSE_BUF_COPY_FLAGS); if (res < 0) fuse_reply_err(req, -res); else fuse_reply_write(req, (size_t)res); } static void sfs_write_buf(fuse_req_t req, fuse_ino_t ino, fuse_bufvec *in_buf, off_t off, fuse_file_info *fi) { (void)ino; if (fs.passthrough && !fs.direct_io) { cerr << "ERROR: fuse_passthrough write failed." << endl; fuse_reply_err(req, EIO); return; } auto size{ fuse_buf_size(in_buf) }; do_write_buf(req, size, off, in_buf, fi); } static void sfs_statfs(fuse_req_t req, fuse_ino_t ino) { struct statvfs stbuf; auto res = fstatvfs(get_fs_fd(ino), &stbuf); if (res == -1) fuse_reply_err(req, errno); else fuse_reply_statfs(req, &stbuf); } static void sfs_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, fuse_file_info *fi) { (void)ino; auto err = -do_fallocate(fi->fh, mode, offset, length); fuse_reply_err(req, err); } static void sfs_flock(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi, int op) { (void)ino; auto res = flock(fi->fh, op); fuse_reply_err(req, res == -1 ? errno : 0); } #ifdef HAVE_SETXATTR static void sfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) { char *value = nullptr; Inode &inode = get_inode(ino); ssize_t ret; int saverr; char procname[64]; sprintf(procname, "/proc/self/fd/%i", inode.fd); if (size) { value = new (nothrow) char[size]; if (value == nullptr) { saverr = ENOMEM; goto out; } ret = getxattr(procname, name, value, size); if (ret == -1) goto out_err; saverr = 0; if (ret == 0) goto out; fuse_reply_buf(req, value, ret); } else { ret = getxattr(procname, name, nullptr, 0); if (ret == -1) goto out_err; fuse_reply_xattr(req, ret); } out_free: delete[] value; return; out_err: saverr = errno; out: fuse_reply_err(req, saverr); goto out_free; } static void sfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { char *value = nullptr; Inode &inode = get_inode(ino); ssize_t ret; int saverr; char procname[64]; sprintf(procname, "/proc/self/fd/%i", inode.fd); if (size) { value = new (nothrow) char[size]; if (value == nullptr) { saverr = ENOMEM; goto out; } ret = listxattr(procname, value, size); if (ret == -1) goto out_err; saverr = 0; if (ret == 0) goto out; fuse_reply_buf(req, value, ret); } else { ret = listxattr(procname, nullptr, 0); if (ret == -1) goto out_err; fuse_reply_xattr(req, ret); } out_free: delete[] value; return; out_err: saverr = errno; out: fuse_reply_err(req, saverr); goto out_free; } static void sfs_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags) { Inode &inode = get_inode(ino); ssize_t ret; int saverr; char procname[64]; sprintf(procname, "/proc/self/fd/%i", inode.fd); ret = setxattr(procname, name, value, size, flags); saverr = ret == -1 ? errno : 0; fuse_reply_err(req, saverr); } static void sfs_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) { char procname[64]; Inode &inode = get_inode(ino); ssize_t ret; int saverr; sprintf(procname, "/proc/self/fd/%i", inode.fd); ret = removexattr(procname, name); saverr = ret == -1 ? errno : 0; fuse_reply_err(req, saverr); } #endif static void assign_operations(fuse_lowlevel_ops &sfs_oper) { sfs_oper.init = sfs_init; sfs_oper.lookup = sfs_lookup; sfs_oper.mkdir = sfs_mkdir; sfs_oper.mknod = sfs_mknod; sfs_oper.symlink = sfs_symlink; sfs_oper.link = sfs_link; sfs_oper.unlink = sfs_unlink; sfs_oper.rmdir = sfs_rmdir; sfs_oper.rename = sfs_rename; sfs_oper.forget = sfs_forget; sfs_oper.forget_multi = sfs_forget_multi; sfs_oper.getattr = sfs_getattr; sfs_oper.setattr = sfs_setattr; sfs_oper.readlink = sfs_readlink; sfs_oper.opendir = sfs_opendir; sfs_oper.readdir = sfs_readdir; sfs_oper.readdirplus = sfs_readdirplus; sfs_oper.releasedir = sfs_releasedir; sfs_oper.fsyncdir = sfs_fsyncdir; sfs_oper.create = sfs_create; #ifdef O_TMPFILE sfs_oper.tmpfile = sfs_tmpfile; #endif sfs_oper.open = sfs_open; sfs_oper.release = sfs_release; sfs_oper.flush = sfs_flush; sfs_oper.fsync = sfs_fsync; sfs_oper.read = sfs_read; sfs_oper.write_buf = sfs_write_buf; sfs_oper.statfs = sfs_statfs; sfs_oper.fallocate = sfs_fallocate; sfs_oper.flock = sfs_flock; #ifdef HAVE_SETXATTR sfs_oper.setxattr = sfs_setxattr; sfs_oper.getxattr = sfs_getxattr; sfs_oper.listxattr = sfs_listxattr; sfs_oper.removexattr = sfs_removexattr; #endif } static void print_usage(char *prog_name) { cout << "Usage: " << prog_name << " --help\n" << " " << prog_name << " [options] \n"; } static cxxopts::ParseResult parse_wrapper(cxxopts::Options &parser, int &argc, char **&argv) { try { return parser.parse(argc, argv); } catch (cxxopts::option_not_exists_exception &exc) { std::cout << argv[0] << ": " << exc.what() << std::endl; print_usage(argv[0]); exit(2); } } static void string_split(std::string s, std::vector &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 &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 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 attribute all caching")( "nosplice", "Do not use splice(2) to transfer data")( "nopassthrough", "Do not use pass-through mode for read/write")( "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()->default_value(SFS_DEFAULT_THREADS))( "clone-fd", "use separate fuse device fd for each thread")( "direct-io", "enable fuse kernel internal direct-io"); // FIXME: Find a better way to limit the try clause to just // opt_parser.parse() (cf. https://github.com/jarro2783/cxxopts/issues/146) auto options = parse_wrapper(opt_parser, argc, argv); if (options.count("help")) { print_usage(argv[0]); // Strip everything before the option list from the // default help string. auto help = opt_parser.help(); std::cout << std::endl << "options:" << help.substr(help.find("\n\n") + 1, string::npos); std::cout << "\nFuse lowlevel options:\n"; fuse_lowlevel_help(); exit(0); } else if (argc != 3) { std::cout << argv[0] << ": invalid number of arguments\n"; print_usage(argv[0]); exit(2); } 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.passthrough = options.count("nopassthrough") == 0; fs.num_threads = options["num-threads"].as(); fs.clone_fd = options.count("clone-fd"); fs.direct_io = options.count("direct-io"); 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 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; } static void maximize_fd_limit() { struct rlimit lim {}; auto res = getrlimit(RLIMIT_NOFILE, &lim); if (res != 0) { warn("WARNING: getrlimit() failed with"); return; } lim.rlim_cur = lim.rlim_max; res = setrlimit(RLIMIT_NOFILE, &lim); if (res != 0) warn("WARNING: setrlimit() failed with"); } int main(int argc, char *argv[]) { struct fuse_loop_config *loop_config = NULL; // Parse command line options auto options{ parse_options(argc, argv) }; // We need an fd for every dentry in our the filesystem that the // kernel knows about. This is way more than most processes need, // so try to get rid of any resource softlimit. maximize_fd_limit(); // Initialize filesystem root fs.root.fd = -1; fs.root.nlookup = 9999; fs.timeout = options.count("nocache") ? 0 : 86400.0; struct stat stat; auto ret = lstat(fs.source.c_str(), &stat); if (ret == -1) err(1, "ERROR: failed to stat source (\"%s\")", fs.source.c_str()); if (!S_ISDIR(stat.st_mode)) errx(1, "ERROR: source is not a directory"); fs.src_dev = stat.st_dev; fs.root.fd = open(fs.source.c_str(), O_PATH); if (fs.root.fd == -1) err(1, "ERROR: open(\"%s\", O_PATH)", fs.source.c_str()); // Initialize fuse 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, fs.fuse_mount_options.c_str()) || (fs.debug_fuse && fuse_opt_add_arg(&args, "-odebug"))) errx(3, "ERROR: Out of memory adding arguments"); ret = -1; fuse_lowlevel_ops sfs_oper{}; assign_operations(sfs_oper); auto se = fuse_session_new(&args, &sfs_oper, sizeof(sfs_oper), &fs); if (se == nullptr) goto err_out1; if (fuse_set_signal_handlers(se) != 0) goto err_out2; if (fuse_set_fail_signal_handlers(se) != 0) goto err_out2; // Don't apply umask, use modes exactly as specified umask(0); // Mount and run main loop loop_config = fuse_loop_cfg_create(); if (fs.num_threads != -1) fuse_loop_cfg_set_max_threads(loop_config, fs.num_threads); fuse_loop_cfg_set_clone_fd(loop_config, fs.clone_fd); if (fuse_session_mount(se, argv[2]) != 0) goto err_out3; fuse_daemonize(fs.foreground); if (!fs.foreground) fuse_log_enable_syslog("passthrough-hp", LOG_PID | LOG_CONS, LOG_DAEMON); if (options.count("single")) ret = fuse_session_loop(se); else ret = fuse_session_loop_mt(se, loop_config); fuse_session_unmount(se); err_out3: fuse_remove_signal_handlers(se); err_out2: fuse_session_destroy(se); err_out1: fuse_loop_cfg_destroy(loop_config); fuse_opt_free_args(&args); if (!fs.foreground) fuse_log_close_syslog(); return ret ? 1 : 0; } fuse-3.18.2/example/passthrough_ll.c0000644000175000017500000007760315156613252016375 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This file system mirrors the existing file system hierarchy of the * system, starting at the root file system. This is implemented by * just "passing through" all requests to the corresponding user-space * libc functions. In contrast to passthrough.c and passthrough_fh.c, * this implementation uses the low-level API. Its performance should * be the least bad among the three. * * When writeback caching is enabled (-o writeback mount option), it * is only possible to write to files for which the mounting user has * read permissions. This is because the writeback cache requires the * kernel to be able to issue read requests for all files (which the * passthrough filesystem cannot satisfy if it can't read the file in * the underlying filesystem). * * Compile with: * * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll * * ## Source code ## * \include passthrough_ll.c */ #define _GNU_SOURCE #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 18) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "passthrough_helpers.h" /* We are re-using pointers to our `struct lo_inode` and `struct lo_dirp` elements as inodes. This means that we must be able to store uintptr_t values in a fuse_ino_t variable. The following incantation checks this condition at compile time. */ #if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus _Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), "fuse_ino_t too small to hold uintptr_t values!"); #else struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \ { unsigned _uintptr_to_must_hold_fuse_ino_t: ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; #endif struct lo_inode { struct lo_inode *next; /* protected by lo->mutex */ struct lo_inode *prev; /* protected by lo->mutex */ int fd; ino_t ino; dev_t dev; uint64_t refcount; /* protected by lo->mutex */ }; enum { CACHE_NEVER, CACHE_NORMAL, CACHE_ALWAYS, }; struct lo_data { pthread_mutex_t mutex; int debug; int writeback; int flock; int xattr; char *source; double timeout; int cache; int timeout_set; struct lo_inode root; /* protected by lo->mutex */ }; static const struct fuse_opt lo_opts[] = { { "writeback", offsetof(struct lo_data, writeback), 1 }, { "no_writeback", offsetof(struct lo_data, writeback), 0 }, { "source=%s", offsetof(struct lo_data, source), 0 }, { "flock", offsetof(struct lo_data, flock), 1 }, { "no_flock", offsetof(struct lo_data, flock), 0 }, { "xattr", offsetof(struct lo_data, xattr), 1 }, { "no_xattr", offsetof(struct lo_data, xattr), 0 }, { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, { "timeout=", offsetof(struct lo_data, timeout_set), 1 }, { "cache=never", offsetof(struct lo_data, cache), CACHE_NEVER }, { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL }, { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, 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); } static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) { if (ino == FUSE_ROOT_ID) return &lo_data(req)->root; else return (struct lo_inode *) (uintptr_t) ino; } static int lo_fd(fuse_req_t req, fuse_ino_t ino) { return lo_inode(req, ino)->fd; } static bool lo_debug(fuse_req_t req) { return lo_data(req)->debug != 0; } static void lo_init(void *userdata, struct fuse_conn_info *conn) { struct lo_data *lo = (struct lo_data *)userdata; bool has_flag; if (lo->writeback) { has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE); if (lo->debug && has_flag) fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); } if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { has_flag = fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS); if (lo->debug && has_flag) fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); } /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; } static void lo_destroy(void *userdata) { struct lo_data *lo = (struct lo_data*) userdata; while (lo->root.next != &lo->root) { struct lo_inode* next = lo->root.next; lo->root.next = next->next; close(next->fd); free(next); } } static void lo_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { int res; struct stat buf; struct lo_data *lo = lo_data(req); int fd = fi ? fi->fh : lo_fd(req, ino); (void) fi; res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) return (void) fuse_reply_err(req, errno); fuse_reply_attr(req, &buf, lo->timeout); } static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int valid, struct fuse_file_info *fi) { int saverr; char procname[64]; struct lo_inode *inode = lo_inode(req, ino); int ifd = inode->fd; int res; if (valid & FUSE_SET_ATTR_MODE) { if (fi) { res = fchmod(fi->fh, attr->st_mode); } else { sprintf(procname, "/proc/self/fd/%i", ifd); res = chmod(procname, attr->st_mode); } if (res == -1) goto out_err; } if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) goto out_err; } if (valid & FUSE_SET_ATTR_SIZE) { if (fi) { res = ftruncate(fi->fh, attr->st_size); } else { sprintf(procname, "/proc/self/fd/%i", ifd); res = truncate(procname, attr->st_size); } if (res == -1) goto out_err; } if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { struct timespec tv[2]; tv[0].tv_sec = 0; tv[1].tv_sec = 0; tv[0].tv_nsec = UTIME_OMIT; tv[1].tv_nsec = UTIME_OMIT; if (valid & FUSE_SET_ATTR_ATIME_NOW) tv[0].tv_nsec = UTIME_NOW; else if (valid & FUSE_SET_ATTR_ATIME) tv[0] = attr->st_atim; if (valid & FUSE_SET_ATTR_MTIME_NOW) tv[1].tv_nsec = UTIME_NOW; else if (valid & FUSE_SET_ATTR_MTIME) tv[1] = attr->st_mtim; if (fi) res = futimens(fi->fh, tv); else { sprintf(procname, "/proc/self/fd/%i", ifd); res = utimensat(AT_FDCWD, procname, tv, 0); } if (res == -1) goto out_err; } return lo_getattr(req, ino, fi); out_err: saverr = errno; fuse_reply_err(req, saverr); } static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) { struct lo_inode *p; struct lo_inode *ret = NULL; pthread_mutex_lock(&lo->mutex); for (p = lo->root.next; p != &lo->root; p = p->next) { if (p->ino == st->st_ino && p->dev == st->st_dev) { assert(p->refcount > 0); ret = p; ret->refcount++; break; } } pthread_mutex_unlock(&lo->mutex); return ret; } static struct lo_inode *create_new_inode(int fd, struct fuse_entry_param *e, struct lo_data* lo) { struct lo_inode *inode = NULL; struct lo_inode *prev, *next; inode = calloc(1, sizeof(struct lo_inode)); if (!inode) return NULL; inode->refcount = 1; inode->fd = fd; inode->ino = e->attr.st_ino; inode->dev = e->attr.st_dev; pthread_mutex_lock(&lo->mutex); prev = &lo->root; next = prev->next; next->prev = inode; inode->next = next; inode->prev = prev; prev->next = inode; pthread_mutex_unlock(&lo->mutex); return inode; } static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param *e) { int res; struct lo_data *lo = lo_data(req); memset(e, 0, sizeof(*e)); e->attr_timeout = lo->timeout; e->entry_timeout = lo->timeout; res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) return errno; e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo); if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, " %lli/%d -> %lli\n", (unsigned long long) parent, fd, (unsigned long long) e->ino); return 0; } static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, struct fuse_entry_param *e) { int newfd; int res; int saverr; struct lo_data *lo = lo_data(req); struct lo_inode *inode; memset(e, 0, sizeof(*e)); e->attr_timeout = lo->timeout; e->entry_timeout = lo->timeout; newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); if (newfd == -1) goto out_err; res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) goto out_err; inode = lo_find(lo_data(req), &e->attr); if (inode) { close(newfd); newfd = -1; } else { inode = create_new_inode(newfd, e, lo); if (!inode) goto out_err; } e->ino = (uintptr_t) inode; if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long) parent, name, (unsigned long long) e->ino); return 0; out_err: saverr = errno; if (newfd != -1) close(newfd); return saverr; } static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse_entry_param e; int err; if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, name); err = lo_do_lookup(req, parent, name, &e); if (err) fuse_reply_err(req, err); else fuse_reply_entry(req, &e); } static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev, const char *link) { int res; int saverr; struct lo_inode *dir = lo_inode(req, parent); struct fuse_entry_param e; res = mknod_wrapper(dir->fd, name, link, mode, rdev); saverr = errno; if (res == -1) goto out; saverr = lo_do_lookup(req, parent, name, &e); if (saverr) goto out; if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long) parent, name, (unsigned long long) e.ino); fuse_reply_entry(req, &e); return; out: fuse_reply_err(req, saverr); } static void lo_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) { lo_mknod_symlink(req, parent, name, mode, rdev, NULL); } static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) { lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); } static void lo_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name) { lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); } static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, const char *name) { int res; struct lo_data *lo = lo_data(req); struct lo_inode *inode = lo_inode(req, ino); struct fuse_entry_param e; char procname[64]; int saverr; memset(&e, 0, sizeof(struct fuse_entry_param)); e.attr_timeout = lo->timeout; e.entry_timeout = lo->timeout; sprintf(procname, "/proc/self/fd/%i", inode->fd); res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name, AT_SYMLINK_FOLLOW); if (res == -1) goto out_err; res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); if (res == -1) goto out_err; pthread_mutex_lock(&lo->mutex); inode->refcount++; pthread_mutex_unlock(&lo->mutex); e.ino = (uintptr_t) inode; if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long) parent, name, (unsigned long long) e.ino); fuse_reply_entry(req, &e); return; out_err: saverr = errno; fuse_reply_err(req, saverr); } static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) { int res; res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); fuse_reply_err(req, res == -1 ? errno : 0); } static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags) { int res; if (flags) { fuse_reply_err(req, EINVAL); return; } res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); fuse_reply_err(req, res == -1 ? errno : 0); } static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) { int res; res = unlinkat(lo_fd(req, parent), name, 0); fuse_reply_err(req, res == -1 ? errno : 0); } static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) { if (!inode) return; pthread_mutex_lock(&lo->mutex); assert(inode->refcount >= n); inode->refcount -= n; if (!inode->refcount) { struct lo_inode *prev, *next; prev = inode->prev; next = inode->next; next->prev = prev; prev->next = next; pthread_mutex_unlock(&lo->mutex); close(inode->fd); free(inode); } else { pthread_mutex_unlock(&lo->mutex); } } static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { struct lo_data *lo = lo_data(req); struct lo_inode *inode = lo_inode(req, ino); if (lo_debug(req)) { fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", (unsigned long long) ino, (unsigned long long) inode->refcount, (unsigned long long) nlookup); } unref_inode(lo, inode, nlookup); } static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { lo_forget_one(req, ino, nlookup); fuse_reply_none(req); } static void lo_forget_multi(fuse_req_t req, size_t count, struct fuse_forget_data *forgets) { int i; for (i = 0; i < count; i++) lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); fuse_reply_none(req); } static void lo_readlink(fuse_req_t req, fuse_ino_t ino) { char buf[PATH_MAX + 1]; int res; res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); if (res == -1) return (void) fuse_reply_err(req, errno); if (res == sizeof(buf)) return (void) fuse_reply_err(req, ENAMETOOLONG); buf[res] = '\0'; fuse_reply_readlink(req, buf); } struct lo_dirp { DIR *dp; struct dirent *entry; off_t offset; }; static struct lo_dirp *lo_dirp(struct fuse_file_info *fi) { return (struct lo_dirp *) (uintptr_t) fi->fh; } static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { int error = ENOMEM; struct lo_data *lo = lo_data(req); struct lo_dirp *d; int fd = -1; d = calloc(1, sizeof(struct lo_dirp)); if (d == NULL) goto out_err; fd = openat(lo_fd(req, ino), ".", O_RDONLY); if (fd == -1) goto out_errno; d->dp = fdopendir(fd); if (d->dp == NULL) goto out_errno; d->offset = 0; d->entry = NULL; fi->fh = (uintptr_t) d; if (lo->cache != CACHE_NEVER) fi->cache_readdir = 1; if (lo->cache == CACHE_ALWAYS) fi->keep_cache = 1; fuse_reply_open(req, fi); return; out_errno: error = errno; out_err: if (d) { if (fd != -1) close(fd); free(d); } fuse_reply_err(req, error); } static int is_dot_or_dotdot(const char *name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi, int plus) { struct lo_dirp *d = lo_dirp(fi); char *buf; char *p; size_t rem = size; int err; (void) ino; buf = calloc(1, size); if (!buf) { err = ENOMEM; goto error; } p = buf; if (offset != d->offset) { seekdir(d->dp, offset); d->entry = NULL; d->offset = offset; } while (1) { size_t entsize; off_t nextoff; const char *name; if (!d->entry) { errno = 0; d->entry = readdir(d->dp); if (!d->entry) { if (errno) { // Error err = errno; goto error; } else { // End of stream break; } } } nextoff = d->entry->d_off; name = d->entry->d_name; fuse_ino_t entry_ino = 0; if (plus) { struct fuse_entry_param e; if (is_dot_or_dotdot(name)) { e = (struct fuse_entry_param) { .attr.st_ino = d->entry->d_ino, .attr.st_mode = d->entry->d_type << 12, }; } else { err = lo_do_lookup(req, ino, name, &e); if (err) goto error; entry_ino = e.ino; } entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff); } else { struct stat st = { .st_ino = d->entry->d_ino, .st_mode = d->entry->d_type << 12, }; entsize = fuse_add_direntry(req, p, rem, name, &st, nextoff); } if (entsize > rem) { if (entry_ino != 0) lo_forget_one(req, entry_ino, 1); break; } p += entsize; rem -= entsize; d->entry = NULL; d->offset = nextoff; } err = 0; error: // If there's an error, we can only signal it if we haven't stored // any entries yet - otherwise we'd end up with wrong lookup // counts for the entries that are already in the buffer. So we // return what we've collected until that point. if (err && rem == size) fuse_reply_err(req, err); else fuse_reply_buf(req, buf, size - rem); free(buf); } static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi) { lo_do_readdir(req, ino, size, offset, fi, 0); } static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi) { lo_do_readdir(req, ino, size, offset, fi, 1); } static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { struct lo_dirp *d = lo_dirp(fi); (void) ino; closedir(d->dp); free(d); fuse_reply_err(req, 0); } static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info *fi) { int fd; struct lo_data *lo = lo_data(req); struct fuse_entry_param e; int err; if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n", parent); fd = openat(lo_fd(req, parent), ".", (fi->flags | O_TMPFILE) & ~O_NOFOLLOW, mode); if (fd == -1) return (void) fuse_reply_err(req, errno); fi->fh = fd; if (lo->cache == CACHE_NEVER) fi->direct_io = 1; else if (lo->cache == CACHE_ALWAYS) fi->keep_cache = 1; /* parallel_direct_writes feature depends on direct_io features. To make parallel_direct_writes valid, need set fi->direct_io in current function. */ fi->parallel_direct_writes = 1; err = fill_entry_param_new_inode(req, parent, fd, &e); if (err) fuse_reply_err(req, err); else fuse_reply_create(req, &e, fi); } static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) { int fd; struct lo_data *lo = lo_data(req); struct fuse_entry_param e; int err; if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", parent, name); fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode); if (fd == -1) return (void) fuse_reply_err(req, errno); fi->fh = fd; if (lo->cache == CACHE_NEVER) fi->direct_io = 1; else if (lo->cache == CACHE_ALWAYS) fi->keep_cache = 1; /* parallel_direct_writes feature depends on direct_io features. To make parallel_direct_writes valid, need set fi->direct_io in current function. */ fi->parallel_direct_writes = 1; err = lo_do_lookup(req, parent, name, &e); if (err) fuse_reply_err(req, err); else fuse_reply_create(req, &e, fi); } static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { int res; int fd = dirfd(lo_dirp(fi)->dp); (void) ino; if (datasync) res = fdatasync(fd); else res = fsync(fd); fuse_reply_err(req, res == -1 ? errno : 0); } static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { int fd; char buf[64]; struct lo_data *lo = lo_data(req); if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, fi->flags); /* With writeback cache, kernel may send read requests even when userspace opened write-only */ if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { fi->flags &= ~O_ACCMODE; fi->flags |= O_RDWR; } /* With writeback cache, O_APPEND is handled by the kernel. This breaks atomicity (since the file may change in the underlying filesystem, so that the kernel's idea of the end of the file isn't accurate anymore). In this example, we just accept that. A more rigorous filesystem may want to return an error here */ if (lo->writeback && (fi->flags & O_APPEND)) fi->flags &= ~O_APPEND; sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); fd = open(buf, fi->flags & ~O_NOFOLLOW); if (fd == -1) return (void) fuse_reply_err(req, errno); fi->fh = fd; if (lo->cache == CACHE_NEVER) fi->direct_io = 1; else if (lo->cache == CACHE_ALWAYS) fi->keep_cache = 1; /* Enable direct_io when open has flags O_DIRECT to enjoy the feature parallel_direct_writes (i.e., to get a shared lock, not exclusive lock, for writes to the same file in the kernel). */ if (fi->flags & O_DIRECT) fi->direct_io = 1; /* parallel_direct_writes feature depends on direct_io features. To make parallel_direct_writes valid, need set fi->direct_io in current function. */ fi->parallel_direct_writes = 1; fuse_reply_open(req, fi); } static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { (void) ino; close(fi->fh); fuse_reply_err(req, 0); } static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { int res; (void) ino; res = close(dup(fi->fh)); fuse_reply_err(req, res == -1 ? errno : 0); } static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { int res; (void) ino; if (datasync) res = fdatasync(fi->fh); else res = fsync(fi->fh); fuse_reply_err(req, res == -1 ? errno : 0); } static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi) { struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, " "off=%lu)\n", ino, size, (unsigned long) offset); buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; buf.buf[0].fd = fi->fh; buf.buf[0].pos = offset; fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); } static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *in_buf, off_t off, struct fuse_file_info *fi) { (void) ino; ssize_t res; struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; out_buf.buf[0].fd = fi->fh; out_buf.buf[0].pos = off; if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%jd)\n", ino, out_buf.buf[0].size, (intmax_t) off); res = fuse_buf_copy(&out_buf, in_buf, 0); if(res < 0) fuse_reply_err(req, -res); else fuse_reply_write(req, (size_t) res); } static void lo_statfs(fuse_req_t req, fuse_ino_t ino) { int res; struct statvfs stbuf; res = fstatvfs(lo_fd(req, ino), &stbuf); if (res == -1) fuse_reply_err(req, errno); else fuse_reply_statfs(req, &stbuf); } static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi) { int err; (void) ino; err = -do_fallocate(fi->fh, mode, offset, length); fuse_reply_err(req, err); } static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op) { int res; (void) ino; res = flock(fi->fh, op); fuse_reply_err(req, res == -1 ? errno : 0); } static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) { char *value = NULL; char procname[64]; struct lo_inode *inode = lo_inode(req, ino); ssize_t ret; int saverr; saverr = ENOSYS; if (!lo_data(req)->xattr) goto out; if (lo_debug(req)) { fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ino, name, size); } sprintf(procname, "/proc/self/fd/%i", inode->fd); if (size) { value = malloc(size); if (!value) goto out_err; ret = getxattr(procname, name, value, size); if (ret == -1) goto out_err; saverr = 0; if (ret == 0) goto out; fuse_reply_buf(req, value, ret); } else { ret = getxattr(procname, name, NULL, 0); if (ret == -1) goto out_err; fuse_reply_xattr(req, ret); } out_free: free(value); return; out_err: saverr = errno; out: fuse_reply_err(req, saverr); goto out_free; } static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { char *value = NULL; char procname[64]; struct lo_inode *inode = lo_inode(req, ino); ssize_t ret; int saverr; saverr = ENOSYS; if (!lo_data(req)->xattr) goto out; if (lo_debug(req)) { fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", ino, size); } sprintf(procname, "/proc/self/fd/%i", inode->fd); if (size) { value = malloc(size); if (!value) goto out_err; ret = listxattr(procname, value, size); if (ret == -1) goto out_err; saverr = 0; if (ret == 0) goto out; fuse_reply_buf(req, value, ret); } else { ret = listxattr(procname, NULL, 0); if (ret == -1) goto out_err; fuse_reply_xattr(req, ret); } out_free: free(value); return; out_err: saverr = errno; out: fuse_reply_err(req, saverr); goto out_free; } static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags) { char procname[64]; struct lo_inode *inode = lo_inode(req, ino); ssize_t ret; int saverr; saverr = ENOSYS; if (!lo_data(req)->xattr) goto out; if (lo_debug(req)) { fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", ino, name, value, size); } sprintf(procname, "/proc/self/fd/%i", inode->fd); ret = setxattr(procname, name, value, size, flags); saverr = ret == -1 ? errno : 0; out: fuse_reply_err(req, saverr); } static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) { char procname[64]; struct lo_inode *inode = lo_inode(req, ino); ssize_t ret; int saverr; saverr = ENOSYS; if (!lo_data(req)->xattr) goto out; if (lo_debug(req)) { fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", ino, name); } sprintf(procname, "/proc/self/fd/%i", inode->fd); ret = removexattr(procname, name); saverr = ret == -1 ? errno : 0; out: fuse_reply_err(req, saverr); } #ifdef HAVE_COPY_FILE_RANGE static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fuse_file_info *fi_in, fuse_ino_t ino_out, off_t off_out, struct fuse_file_info *fi_out, size_t len, int flags) { ssize_t res; if (lo_debug(req)) fuse_log(FUSE_LOG_DEBUG, "%s(ino=%lld fd=%lld off=%jd ino=%lld fd=%lld off=%jd, size=%zd, flags=0x%x)\n", __func__, (unsigned long long)ino_in, (unsigned long long)fi_in->fh, (intmax_t) off_in, (unsigned long long)ino_out, (unsigned long long)fi_out->fh, (intmax_t) off_out, len, flags); res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags); if (res < 0) fuse_reply_err(req, errno); else fuse_reply_write(req, res); } #endif static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi) { off_t res; (void)ino; res = lseek(fi->fh, off, whence); if (res != -1) fuse_reply_lseek(req, res); else fuse_reply_err(req, errno); } #ifdef HAVE_STATX static void lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask, struct fuse_file_info *fi) { struct lo_data *lo = lo_data(req); struct statx buf; int res; int fd; if (fi) fd = fi->fh; else fd = lo_fd(req, ino); res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, &buf); if (res == -1) fuse_reply_err(req, errno); else fuse_reply_statx(req, 0, &buf, lo->timeout); } #endif static const struct fuse_lowlevel_ops lo_oper = { .init = lo_init, .destroy = lo_destroy, .lookup = lo_lookup, .mkdir = lo_mkdir, .mknod = lo_mknod, .symlink = lo_symlink, .link = lo_link, .unlink = lo_unlink, .rmdir = lo_rmdir, .rename = lo_rename, .forget = lo_forget, .forget_multi = lo_forget_multi, .getattr = lo_getattr, .setattr = lo_setattr, .readlink = lo_readlink, .opendir = lo_opendir, .readdir = lo_readdir, .readdirplus = lo_readdirplus, .releasedir = lo_releasedir, .fsyncdir = lo_fsyncdir, .create = lo_create, .tmpfile = lo_tmpfile, .open = lo_open, .release = lo_release, .flush = lo_flush, .fsync = lo_fsync, .read = lo_read, .write_buf = lo_write_buf, .statfs = lo_statfs, .fallocate = lo_fallocate, .flock = lo_flock, .getxattr = lo_getxattr, .listxattr = lo_listxattr, .setxattr = lo_setxattr, .removexattr = lo_removexattr, #ifdef HAVE_COPY_FILE_RANGE .copy_file_range = lo_copy_file_range, #endif .lseek = lo_lseek, #ifdef HAVE_STATX .statx = lo_statx, #endif }; int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_session *se; struct fuse_cmdline_opts opts; struct fuse_loop_config *config; struct lo_data lo = { .debug = 0, .writeback = 0 }; int ret = -1; /* Don't mask creation mode, kernel already did that */ umask(0); pthread_mutex_init(&lo.mutex, NULL); lo.root.next = lo.root.prev = &lo.root; lo.root.fd = -1; lo.cache = CACHE_NORMAL; if (fuse_parse_cmdline(&args, &opts) != 0) return 1; if (opts.show_help) { printf("usage: %s [options] \n\n", argv[0]); fuse_cmdline_help(); fuse_lowlevel_help(); passthrough_ll_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; } if(opts.mountpoint == NULL) { printf("usage: %s [options] \n", argv[0]); printf(" %s --help\n", argv[0]); ret = 1; goto err_out1; } if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1) return 1; lo.debug = opts.debug; lo.root.refcount = 2; if (lo.source) { struct stat stat; int res; res = lstat(lo.source, &stat); if (res == -1) { fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", lo.source); exit(1); } if (!S_ISDIR(stat.st_mode)) { fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); exit(1); } } else { 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) { case CACHE_NEVER: lo.timeout = 0.0; break; case CACHE_NORMAL: lo.timeout = 1.0; break; case CACHE_ALWAYS: lo.timeout = 86400.0; break; } } else if (lo.timeout < 0) { fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", lo.timeout); exit(1); } lo.root.fd = open(lo.source, O_PATH); if (lo.root.fd == -1) { fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); exit(1); } se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); if (se == NULL) goto err_out1; if (fuse_set_signal_handlers(se) != 0) goto err_out2; if (fuse_session_mount(se, opts.mountpoint) != 0) goto err_out3; fuse_daemonize(opts.foreground); /* Block until ctrl+c or fusermount -u */ if (opts.singlethread) ret = fuse_session_loop(se); else { config = fuse_loop_cfg_create(); fuse_loop_cfg_set_clone_fd(config, opts.clone_fd); fuse_loop_cfg_set_max_threads(config, opts.max_threads); ret = fuse_session_loop_mt(se, config); fuse_loop_cfg_destroy(config); config = NULL; } fuse_session_unmount(se); err_out3: fuse_remove_signal_handlers(se); err_out2: fuse_session_destroy(se); err_out1: free(opts.mountpoint); fuse_opt_free_args(&args); if (lo.root.fd >= 0) close(lo.root.fd); free(lo.source); return ret ? 1 : 0; } fuse-3.18.2/example/poll.c0000644000175000017500000001502215156613252014270 0ustar berndbernd/* FUSE fsel: FUSE select example Copyright (C) 2008 SUSE Linux Products GmbH Copyright (C) 2008 Tejun Heo This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This example illustrates how to write a FUSE file system that * supports polling for changes that don't come through the kernel. It * can be tested with the poll_client.c program. * * Compile with: * * gcc -Wall poll.c `pkg-config fuse3 --cflags --libs` -o poll * * ## Source code ## * \include poll.c */ #define FUSE_USE_VERSION 31 #include #include #include #include #include #include #include #include #include #include #include /* * fsel_open_mask is used to limit the number of opens to 1 per file. * This is to use file index (0-F) as fh as poll support requires * unique fh per open file. Lifting this would require proper open * file management. */ 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 up to 10 chars */ #define FSEL_FILES 16 static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */ static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */ static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */ static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */ static _Atomic bool fsel_stop = false; static pthread_t fsel_producer_thread; static int fsel_path_index(const char *path) { char ch = path[1]; if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch)) return -1; return ch <= '9' ? ch - '0' : ch - 'A' + 10; } static void fsel_destroy(void *private_data) { (void)private_data; fsel_stop = true; pthread_join(fsel_producer_thread, NULL); } static int fsel_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { (void) fi; int idx; memset(stbuf, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { stbuf->st_mode = S_IFDIR | 0555; stbuf->st_nlink = 2; return 0; } idx = fsel_path_index(path); if (idx < 0) return -ENOENT; stbuf->st_mode = S_IFREG | 0444; stbuf->st_nlink = 1; stbuf->st_size = fsel_cnt[idx]; return 0; } static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { char name[2] = { }; int i; (void) offset; (void) fi; (void) flags; if (strcmp(path, "/") != 0) return -ENOENT; for (i = 0; i < FSEL_FILES; i++) { name[0] = fsel_hex_map[i]; filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS); } return 0; } static int fsel_open(const char *path, struct fuse_file_info *fi) { int idx = fsel_path_index(path); if (idx < 0) return -ENOENT; if ((fi->flags & O_ACCMODE) != O_RDONLY) return -EACCES; if (fsel_open_mask & (1 << idx)) return -EBUSY; fsel_open_mask |= (1 << idx); /* * fsel files are nonseekable somewhat pipe-like files which * gets filled up periodically by producer thread and consumed * on read. Tell FUSE as such. */ fi->fh = idx; fi->direct_io = 1; fi->nonseekable = 1; return 0; } static int fsel_release(const char *path, struct fuse_file_info *fi) { int idx = fi->fh; (void) path; fsel_open_mask &= ~(1 << idx); return 0; } static int fsel_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { int idx = fi->fh; (void) path; (void) offset; pthread_mutex_lock(&fsel_mutex); if (fsel_cnt[idx] < size) size = fsel_cnt[idx]; printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]); fsel_cnt[idx] -= size; pthread_mutex_unlock(&fsel_mutex); memset(buf, fsel_hex_map[idx], size); return size; } static int fsel_poll(const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp) { static unsigned polled_zero; int idx = fi->fh; (void) path; /* * Poll notification requires pointer to struct fuse which * can't be obtained when using fuse_main(). As notification * happens only after poll is called, fill it here from * fuse_context. */ if (!fsel_fuse) { struct fuse_context *cxt = fuse_get_context(); if (cxt) fsel_fuse = cxt->fuse; } pthread_mutex_lock(&fsel_mutex); if (ph != NULL) { struct fuse_pollhandle *oldph = fsel_poll_handle[idx]; if (oldph) fuse_pollhandle_destroy(oldph); fsel_poll_notify_mask |= (1 << idx); fsel_poll_handle[idx] = ph; } if (fsel_cnt[idx]) { *reventsp |= POLLIN; printf("POLL %X cnt=%u polled_zero=%u\n", idx, fsel_cnt[idx], polled_zero); polled_zero = 0; } else polled_zero++; pthread_mutex_unlock(&fsel_mutex); return 0; } static const struct fuse_operations fsel_oper = { .destroy = fsel_destroy, .getattr = fsel_getattr, .readdir = fsel_readdir, .open = fsel_open, .release = fsel_release, .read = fsel_read, .poll = fsel_poll, }; static void *fsel_producer(void *data) { const struct timespec interval = { 0, 250000000 }; unsigned idx = 0, nr = 1; (void) data; while (!fsel_stop) { int i, t; pthread_mutex_lock(&fsel_mutex); /* * This is the main producer loop which is executed * ever 500ms. On each iteration, it fills one byte * to 1, 2 or 4 files and sends poll notification if * requested. */ for (i = 0, t = idx; i < nr; i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) { if (fsel_cnt[t] == FSEL_CNT_MAX) continue; fsel_cnt[t]++; if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) { struct fuse_pollhandle *ph; printf("NOTIFY %X\n", t); ph = fsel_poll_handle[t]; fuse_notify_poll(ph); fuse_pollhandle_destroy(ph); fsel_poll_notify_mask &= ~(1 << t); fsel_poll_handle[t] = NULL; } } idx = (idx + 1) % FSEL_FILES; if (idx == 0) nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */ pthread_mutex_unlock(&fsel_mutex); nanosleep(&interval, NULL); } return NULL; } int main(int argc, char *argv[]) { pthread_attr_t attr; int ret; errno = pthread_mutex_init(&fsel_mutex, NULL); if (errno) { perror("pthread_mutex_init"); return 1; } errno = pthread_attr_init(&attr); if (errno) { perror("pthread_attr_init"); return 1; } errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL); if (errno) { perror("pthread_create"); return 1; } ret = fuse_main(argc, argv, &fsel_oper, NULL); return ret; } fuse-3.18.2/example/poll_client.c0000644000175000017500000000302615156613252015627 0ustar berndbernd/* FUSE fselclient: FUSE select example client Copyright (C) 2008 SUSE Linux Products GmbH Copyright (C) 2008 Tejun Heo This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * This program tests the poll.c example file systsem. * * Compile with: * * gcc -Wall poll_client.c -o poll_client * * ## Source code ## * \include poll_client.c */ #include #include #include #include #include #include #include #include #include #include #define FSEL_FILES 16 int main(void) { static const char hex_map[FSEL_FILES] = "0123456789ABCDEF"; int fds[FSEL_FILES]; int i, nfds, tries; for (i = 0; i < FSEL_FILES; i++) { char name[] = { hex_map[i], '\0' }; fds[i] = open(name, O_RDONLY); if (fds[i] < 0) { perror("open"); return 1; } } nfds = fds[FSEL_FILES - 1] + 1; for(tries=0; tries < 16; tries++) { static char buf[4096]; fd_set rfds; int rc; FD_ZERO(&rfds); for (i = 0; i < FSEL_FILES; i++) FD_SET(fds[i], &rfds); rc = select(nfds, &rfds, NULL, NULL, NULL); if (rc < 0) { perror("select"); return 1; } for (i = 0; i < FSEL_FILES; i++) { if (!FD_ISSET(fds[i], &rfds)) { printf("_: "); continue; } printf("%X:", i); rc = read(fds[i], buf, sizeof(buf)); if (rc < 0) { perror("read"); return 1; } printf("%02d ", rc); } printf("\n"); } return 0; } fuse-3.18.2/example/printcap.c0000644000175000017500000000745215156613252015152 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2017 Nikolaus Rath This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /** @file * * minimal example filesystem that prints out all capabilities * supported by the kernel and then exits. * * Compile with: * * gcc -Wall printcap.c `pkg-config fuse3 --cflags --libs` -o printcap * * ## Source code ## * \include printcap.c */ #define FUSE_USE_VERSION 31 #include #include #include #include #include struct fuse_session *se; // Define a structure to hold capability information struct cap_info { uint64_t flag; const char *name; }; // Define an array of all capabilities static const struct cap_info capabilities[] = { {FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"}, {FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"}, {FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"}, {FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"}, {FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"}, {FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"}, {FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"}, {FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"}, {FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"}, {FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"}, {FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"}, {FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"}, {FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"}, {FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"}, {FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"}, {FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"}, {FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"}, {FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"}, {FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"}, {FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"}, {FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"}, {FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"}, {FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"}, {FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"}, {FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"}, {FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"}, {FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"}, {FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"}, // Add any new capabilities here {0, NULL} // Sentinel to mark the end of the array }; static void print_capabilities(struct fuse_conn_info *conn) { printf("Capabilities:\n"); for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) { if (fuse_get_feature_flag(conn, cap->flag)) { printf("\t%s\n", cap->name); } } } static void pc_init(void *userdata, struct fuse_conn_info *conn) { (void) userdata; printf("Protocol version: %d.%d\n", conn->proto_major, conn->proto_minor); print_capabilities(conn); fuse_session_exit(se); } static const struct fuse_lowlevel_ops pc_oper = { .init = pc_init, }; int main(int argc, char **argv) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); char *mountpoint; int ret = -1; mountpoint = strdup("/tmp/fuse_printcap_XXXXXX"); if(mkdtemp(mountpoint) == NULL) { perror("mkdtemp"); return 1; } printf("FUSE library version %s\n", fuse_pkgversion()); fuse_lowlevel_version(); se = fuse_session_new(&args, &pc_oper, sizeof(pc_oper), NULL); if (se == NULL) goto err_out1; if (fuse_set_signal_handlers(se) != 0) goto err_out2; if (fuse_session_mount(se, mountpoint) != 0) goto err_out3; ret = fuse_session_loop(se); fuse_session_unmount(se); err_out3: fuse_remove_signal_handlers(se); err_out2: fuse_session_destroy(se); err_out1: rmdir(mountpoint); free(mountpoint); fuse_opt_free_args(&args); return ret ? 1 : 0; } fuse-3.18.2/example/usdt.bt0000644000175000017500000000075215156613252014470 0ustar berndbernd#!/usr/bin/env bpftrace // To run, do `sudo bpftrace usdt.bt` usdt:../build/lib/libfuse3.so:libfuse:request_receive { printf("libfuse:request_receive hit, err=%d\n", arg0); } usdt:../build/lib/libfuse3.so:libfuse:request_process { printf("libfuse:request_process hit, opcode=%u, unique=%u\n", arg0, arg1); } usdt:../build/lib/libfuse3.so:libfuse:request_reply { printf("libfuse:request_reply hit, unique=%lu, len=%u, err=%u, reply_err=%d\n", arg0, arg1, arg2, arg3); } fuse-3.18.2/include/0000755000175000017500000000000015156613252013146 5ustar berndberndfuse-3.18.2/include/cuse_lowlevel.h0000644000175000017500000000501315156613252016166 0ustar berndbernd/* CUSE: Character device in Userspace Copyright (C) 2008-2009 SUSE Linux Products GmbH Copyright (C) 2008-2009 Tejun Heo This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. Read example/cusexmp.c for usages. */ #ifndef CUSE_LOWLEVEL_H_ #define CUSE_LOWLEVEL_H_ #ifndef FUSE_USE_VERSION #define FUSE_USE_VERSION 29 #endif #include "fuse_lowlevel.h" #include #include #include #ifdef __cplusplus extern "C" { #endif #define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */ struct fuse_session; struct cuse_info { unsigned dev_major; unsigned dev_minor; unsigned dev_info_argc; const char **dev_info_argv; unsigned flags; }; /* * Most ops behave almost identically to the matching fuse_lowlevel * ops except that they don't take @ino. * * init_done : called after initialization is complete * read/write : always direct IO, simultaneous operations allowed * ioctl : might be in unrestricted mode depending on ci->flags */ struct cuse_lowlevel_ops { void (*init) (void *userdata, struct fuse_conn_info *conn); void (*init_done) (void *userdata); void (*destroy) (void *userdata); void (*open) (fuse_req_t req, struct fuse_file_info *fi); void (*read) (fuse_req_t req, size_t size, off_t off, struct fuse_file_info *fi); void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off, struct fuse_file_info *fi); void (*flush) (fuse_req_t req, struct fuse_file_info *fi); void (*release) (fuse_req_t req, struct fuse_file_info *fi); void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi); void (*ioctl) (fuse_req_t req, int cmd, void *arg, struct fuse_file_info *fi, unsigned int flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz); void (*poll) (fuse_req_t req, struct fuse_file_info *fi, struct fuse_pollhandle *ph); }; struct fuse_session *cuse_lowlevel_new(struct fuse_args *args, const struct cuse_info *ci, const struct cuse_lowlevel_ops *clop, void *userdata); struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], const struct cuse_info *ci, const struct cuse_lowlevel_ops *clop, int *multithreaded, void *userdata); void cuse_lowlevel_teardown(struct fuse_session *se); int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, const struct cuse_lowlevel_ops *clop, void *userdata); #ifdef __cplusplus } #endif #endif /* CUSE_LOWLEVEL_H_ */ fuse-3.18.2/include/fuse.h0000644000175000017500000014063315156613252014270 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ #ifndef FUSE_H_ #define FUSE_H_ /** @file * * This file defines the library interface of FUSE * * IMPORTANT: you should define FUSE_USE_VERSION before including this header. */ #include "fuse_common.h" #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* ----------------------------------------------------------- * * Basic FUSE API * * ----------------------------------------------------------- */ /* Forward declaration */ struct statx; /** Handle for a FUSE filesystem */ struct fuse; /** * Readdir flags, passed to ->readdir() */ enum fuse_readdir_flags { /** * "Plus" mode. * * The kernel wants to prefill the inode cache during readdir. The * filesystem may honour this by filling in the attributes and setting * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also * just ignore this flag completely. */ FUSE_READDIR_DEFAULTS = 0, FUSE_READDIR_PLUS = (1 << 0) }; /** * Readdir flags, passed to fuse_fill_dir_t callback. */ enum fuse_fill_dir_flags { /** * "Plus" mode: file attributes are valid * * The attributes are used by the kernel to prefill the inode cache * during a readdir. * * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set * and vice versa. * * This does not make libfuse honor the 'st_ino' field. That is * controlled by the 'use_ino' option instead. */ FUSE_FILL_DIR_DEFAULTS = 0, FUSE_FILL_DIR_PLUS = (1 << 1) }; /** Function to add an entry in a readdir() operation * * The *off* parameter can be any non-zero value that enables the * filesystem to identify the current point in the directory * stream. It does not need to be the actual physical position. A * value of zero is reserved to indicate that seeking in directories * is not supported. * * @param buf the buffer passed to the readdir() operation * @param name the file name of the directory entry * @param stbuf file attributes, can be NULL * @param off offset of the next entry or zero * @param flags fill flags * @return 1 if buffer is full, zero otherwise */ typedef int (*fuse_fill_dir_t) (void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags); /** * Configuration of the high-level API * * This structure is initialized from the arguments passed to * fuse_new(), and then passed to the file system's init() handler * which should ensure that the configuration is compatible with the * file system implementation. * * Note: this data structure is ABI sensitive, new options have to be * appended at the end of the structure */ struct fuse_config { /** * If `set_gid` is non-zero, the st_gid attribute of each file * is overwritten with the value of `gid`. */ int32_t set_gid; uint32_t gid; /** * If `set_uid` is non-zero, the st_uid attribute of each file * is overwritten with the value of `uid`. */ int32_t set_uid; uint32_t uid; /** * If `set_mode` is non-zero, the any permissions bits set in * `umask` are unset in the st_mode attribute of each file. */ int32_t set_mode; uint32_t umask; /** * The timeout in seconds for which name lookups will be * cached. */ double entry_timeout; /** * The timeout in seconds for which a negative lookup will be * cached. This means, that if file did not exist (lookup * returned ENOENT), the lookup will only be redone after the * timeout, and the file/directory will be assumed to not * exist until then. A value of zero means that negative * lookups are not cached. */ double negative_timeout; /** * The timeout in seconds for which file/directory attributes * (as returned by e.g. the `getattr` handler) are cached. */ double attr_timeout; /** * Allow requests to be interrupted */ int32_t intr; /** * Specify which signal number to send to the filesystem when * a request is interrupted. The default is hardcoded to * USR1. */ int32_t intr_signal; /** * Normally, FUSE assigns inodes to paths only for as long as * the kernel is aware of them. With this option inodes are * instead remembered for at least this many seconds. This * will require more memory, but may be necessary when using * applications that make use of inode numbers. * * A number of -1 means that inodes will be remembered for the * entire life-time of the file-system process. */ int32_t remember; /** * The default behavior is that if an open file is deleted, * the file is renamed to a hidden file (.fuse_hiddenXXX), and * only removed when the file is finally released. This * relieves the filesystem implementation of having to deal * with this problem. This option disables the hiding * behavior, and files are removed immediately in an unlink * operation (or in a rename operation which overwrites an * existing file). * * It is recommended that you not use the hard_remove * option. When hard_remove is set, the following libc * functions fail on unlinked files (returning errno of * ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2), * ftruncate(2), fstat(2), fchmod(2), fchown(2) */ int32_t hard_remove; /** * Honor the st_ino field in the functions getattr() and * fill_dir(). This value is used to fill in the st_ino field * in the stat(2), lstat(2), fstat(2) functions and the d_ino * field in the readdir(2) function. The filesystem does not * have to guarantee uniqueness, however some applications * rely on this value being unique for the whole filesystem. * * Note that this does *not* affect the inode that libfuse * and the kernel use internally (also called the "nodeid"). */ int32_t use_ino; /** * If use_ino option is not given, still try to fill in the * d_ino field in readdir(2). If the name was previously * looked up, and is still in the cache, the inode number * found there will be used. Otherwise it will be set to -1. * If use_ino option is given, this option is ignored. */ int32_t readdir_ino; /** * This option disables the use of page cache (file content cache) * in the kernel for this filesystem. This has several affects: * * 1. Each read(2) or write(2) system call will initiate one * or more read or write operations, data will not be * cached in the kernel. * * 2. The return value of the read() and write() system calls * will correspond to the return values of the read and * write operations. This is useful for example if the * file size is not known in advance (before reading it). * * Internally, enabling this option causes fuse to set the * `direct_io` field of `struct fuse_file_info` - overwriting * any value that was put there by the file system. */ int32_t direct_io; /** * This option disables flushing the cache of the file * contents on every open(2). This should only be enabled on * filesystems where the file data is never changed * externally (not through the mounted FUSE filesystem). Thus * it is not suitable for network filesystems and other * intermediate filesystems. * * NOTE: if this option is not specified (and neither * direct_io) data is still cached after the open(2), so a * read(2) system call will not always initiate a read * operation. * * Internally, enabling this option causes fuse to set the * `keep_cache` field of `struct fuse_file_info` - overwriting * any value that was put there by the file system. */ int32_t kernel_cache; /** * This option is an alternative to `kernel_cache`. Instead of * unconditionally keeping cached data, the cached data is * invalidated on open(2) if if the modification time or the * size of the file has changed since it was last opened. */ int32_t auto_cache; /* * The timeout in seconds for which file attributes are cached * for the purpose of checking if auto_cache should flush the * file data on open. */ int32_t ac_attr_timeout_set; double ac_attr_timeout; /** * If this option is given the file-system handlers for the * following operations will not receive path information: * read, write, flush, release, fallocate, fsync, readdir, * releasedir, fsyncdir, lock, ioctl and poll. * * For the truncate, getattr, chmod, chown and utimens * operations the path will be provided only if the struct * fuse_file_info argument is NULL. */ int32_t nullpath_ok; /** * These 3 options are used by libfuse internally and * should not be touched. */ int32_t show_help; char *modules; int32_t debug; /** * `fmask` and `dmask` function the same way as `umask`, but apply * to files and directories separately. If non-zero, `fmask` and * `dmask` take precedence over the `umask` setting. */ uint32_t fmask; uint32_t dmask; /** * 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. */ int32_t no_rofd_flush; /** * 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). */ int32_t parallel_direct_writes; /** * Reserved for future use. */ uint32_t flags; /** * Reserved for future use. */ uint64_t reserved[48]; }; /** * The file system operations: * * Most of these should work very similarly to the well known UNIX * file system operations. A major exception is that instead of * returning an error in 'errno', the operation should return the * negated error value (-errno) directly. * * All methods are optional, but some are essential for a useful * filesystem (e.g. getattr). Open, flush, release, fsync, opendir, * releasedir, fsyncdir, access, create, truncate, lock, init and * destroy are special purpose methods, without which a full featured * filesystem can still be implemented. * * In general, all methods are expected to perform any necessary * permission checking. However, a filesystem may delegate this task * to the kernel by passing the `default_permissions` mount option to * `fuse_new()`. In this case, methods will only be called if * the kernel's permission check has succeeded. * * Almost all operations take a path which can be of any length. */ struct fuse_operations { /** Get file attributes. * * Similar to stat(). The 'st_dev' and 'st_blksize' fields are * ignored. The 'st_ino' field is ignored except if the 'use_ino' * mount option is given. In that case it is passed to userspace, * but libfuse and the kernel will still assign a different * inode for internal use (called the "nodeid"). * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. */ int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi); /** Read the target of a symbolic link * * The buffer should be filled with a null terminated string. The * buffer size argument includes the space for the terminating * null character. If the linkname is too long to fit in the * buffer, it should be truncated. The return value should be 0 * for success. */ int (*readlink) (const char *, char *, size_t); /** Create a file node * * This is called for creation of all non-directory, non-symlink * nodes. If the filesystem defines a create() method, then for * regular files that will be called instead. */ int (*mknod) (const char *, mode_t, dev_t); /** Create a directory * * Note that the mode argument may not have the type specification * bits set, i.e. S_ISDIR(mode) can be false. To obtain the * correct directory type bits use mode|S_IFDIR * */ int (*mkdir) (const char *, mode_t); /** Remove a file */ int (*unlink) (const char *); /** Remove a directory */ int (*rmdir) (const char *); /** Create a symbolic link */ int (*symlink) (const char *, const char *); /** Rename a file * * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If * RENAME_NOREPLACE is specified, the filesystem must not * overwrite *newname* if it exists and return an error * instead. If `RENAME_EXCHANGE` is specified, the filesystem * must atomically exchange the two files, i.e. both must * exist and neither may be deleted. */ int (*rename) (const char *, const char *, unsigned int flags); /** Create a hard link to a file */ int (*link) (const char *, const char *); /** Change the permission bits of a file * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. */ int (*chmod) (const char *, mode_t, struct fuse_file_info *fi); /** Change the owner and group of a file * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * expected to reset the setuid and setgid bits. */ int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi); /** Change the size of a file * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * expected to reset the setuid and setgid bits. */ int (*truncate) (const char *, off_t, struct fuse_file_info *fi); /** Open a file * * Open flags are available in fi->flags. The following rules * apply. * * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be * filtered out / handled by the kernel. * * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH) * should be used by the filesystem to check if the operation is * permitted. If the ``-o default_permissions`` mount option is * given, this check is already done by the kernel before calling * open() and may thus be omitted by the filesystem. * * - When writeback caching is enabled, the kernel may send * read requests even for files opened with O_WRONLY. The * filesystem should be prepared to handle this. * * - When writeback caching is disabled, the filesystem is * expected to properly handle the O_APPEND flag and ensure * that each write is appending to the end of the file. * * - When writeback caching is enabled, the kernel will * handle O_APPEND. However, unless all changes to the file * come through the kernel this will not work reliably. The * filesystem should thus either ignore the O_APPEND flag * (and let the kernel handle it), or return an error * (indicating that reliably O_APPEND is not available). * * Filesystem may store an arbitrary file handle (pointer, * index, etc) in fi->fh, and use this in other all other file * operations (read, write, flush, release, fsync). * * Filesystem may also implement stateless file I/O and not store * anything in fi->fh. * * There are also some flags (direct_io, keep_cache) which the * filesystem may set in fi, to change the way the file is opened. * See fuse_file_info structure in for more details. * * If this request is answered with an error code of ENOSYS * and FUSE_CAP_NO_OPEN_SUPPORT is set in * `fuse_conn_info.capable`, this is treated as success and * future calls to open will also succeed without being sent * to the filesystem process. * */ int (*open) (const char *, struct fuse_file_info *); /** Read data from an open file * * Read should return exactly the number of bytes requested except * on EOF or error, otherwise the rest of the data will be * substituted with zeroes. An exception to this is when the * 'direct_io' mount option is specified, in which case the return * value of the read system call will reflect the return value of * this operation. */ int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *); /** Write data to an open file * * Write should return exactly the number of bytes requested * except on error. An exception to this is when the 'direct_io' * mount option is specified (see read operation). * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * expected to reset the setuid and setgid bits. */ int (*write) (const char *, const char *, size_t, off_t, struct fuse_file_info *); /** Get file system statistics * * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored */ int (*statfs) (const char *, struct statvfs *); /** Possibly flush cached data * * BIG NOTE: This is not equivalent to fsync(). It's not a * request to sync dirty data. * * Flush is called on each close() of a file descriptor, as opposed to * release which is called on the close of the last file descriptor for * a file. Under Linux, errors returned by flush() will be passed to * userspace as errors from close(), so flush() is a good place to write * back any cached dirty data. However, many applications ignore errors * on close(), and on non-Linux systems, close() may succeed even if flush() * returns an error. For these reasons, filesystems should not assume * that errors returned by flush will ever be noticed or even * delivered. * * NOTE: The flush() method may be called more than once for each * open(). This happens if more than one file descriptor refers to an * open file handle, e.g. due to dup(), dup2() or fork() calls. It is * not possible to determine if a flush is final, so each flush should * be treated equally. Multiple write-flush sequences are relatively * rare, so this shouldn't be a problem. * * Filesystems shouldn't assume that flush will be called at any * particular point. It may be called more times than expected, or not * at all. * * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ int (*flush) (const char *, struct fuse_file_info *); /** Release an open file * * Release is called when there are no more references to an open * file: all file descriptors are closed and all memory mappings * are unmapped. * * For every open() call there will be exactly one release() call * with the same flags and file handle. It is possible to * have a file opened more than once, in which case only the last * release will mean, that no more reads/writes will happen on the * file. The return value of release is ignored. */ int (*release) (const char *, struct fuse_file_info *); /** Synchronize file contents * * If the datasync parameter is non-zero, then only the user data * should be flushed, not the meta data. */ int (*fsync) (const char *, int, struct fuse_file_info *); /** Set extended attributes */ int (*setxattr) (const char *, const char *, const char *, size_t, int); /** Get extended attributes */ int (*getxattr) (const char *, const char *, char *, size_t); /** List extended attributes */ int (*listxattr) (const char *, char *, size_t); /** Remove extended attributes */ int (*removexattr) (const char *, const char *); /** Open directory * * Unless the 'default_permissions' mount option is given, * this method should check if opendir is permitted for this * directory. Optionally opendir may also return an arbitrary * filehandle in the fuse_file_info structure, which will be * passed to readdir, releasedir and fsyncdir. */ int (*opendir) (const char *, struct fuse_file_info *); /** Read directory * * The filesystem may choose between two modes of operation: * * 1) The readdir implementation ignores the offset parameter, and * passes zero to the filler function's offset. The filler * function will not return '1' (unless an error happens), so the * whole directory is read in a single readdir operation. * * 2) The readdir implementation keeps track of the offsets of the * directory entries. It uses the offset parameter and always * 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 */ int (*fsyncdir) (const char *, int, struct fuse_file_info *); /** * Initialize filesystem * * The return value will passed in the `private_data` field of * `struct fuse_context` to all file operations, and as a * parameter to the destroy() method. It overrides the initial * value provided to fuse_main() / fuse_new(). */ void *(*init) (struct fuse_conn_info *conn, struct fuse_config *cfg); /** * Clean up filesystem * * Called on filesystem exit. */ void (*destroy) (void *private_data); /** * Check file access permissions * * This will be called for the access() system call. If the * 'default_permissions' mount option is given, this method is not * called. * * This method is not called under Linux kernel versions 2.4.x */ int (*access) (const char *, int); /** * Create and open a file * * If the file does not exist, first create it with the specified * mode, and then open it. * * If this method is not implemented or under Linux kernel * versions earlier than 2.6.15, the mknod() and open() methods * will be called instead. */ int (*create) (const char *, mode_t, struct fuse_file_info *); /** * Perform POSIX file locking operation * * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. * * For the meaning of fields in 'struct flock' see the man page * for fcntl(2). The l_whence field will always be set to * SEEK_SET. * * For checking lock ownership, the 'fuse_file_info->owner' * argument must be used. * * For F_GETLK operation, the library will first check currently * held locks, and if a conflicting lock is found it will return * information without calling this method. This ensures, that * for local locks the l_pid field is correctly filled in. The * results may not be accurate in case of race conditions and in * the presence of hard links, but it's unlikely that an * application would rely on accurate GETLK results in these * cases. If a conflicting lock is not found, this method will be * called, and the filesystem may fill out l_pid by a meaningful * value, or it may leave this field zero. * * For F_SETLK and F_SETLKW the l_pid field will be set to the pid * of the process performing the locking operation. * * Note: if this method is not implemented, the kernel will still * allow file locking to work locally. Hence it is only * interesting for network filesystems and similar. */ int (*lock) (const char *, struct fuse_file_info *, int cmd, struct flock *); /** * Change the access and modification times of a file with * nanosecond resolution * * This supersedes the old utime() interface. New applications * should use this. * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. * * See the utimensat(2) man page for details. */ int (*utimens) (const char *, const struct timespec tv[2], struct fuse_file_info *fi); /** * Map block index within file to block index within device * * Note: This makes sense only for block device backed filesystems * mounted with the 'blkdev' option */ int (*bmap) (const char *, size_t blocksize, uint64_t *idx); #if FUSE_USE_VERSION < 35 int (*ioctl) (const char *, int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data); #else /** * Ioctl * * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in * 64bit environment. The size and direction of data is * determined by _IOC_*() decoding of cmd. For _IOC_NONE, * data will be NULL, for _IOC_WRITE data is out area, for * _IOC_READ in area and if both are set in/out area. In all * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. * * If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a * directory file handle. * * Note : the unsigned long request submitted by the application * is truncated to 32 bits. */ int (*ioctl) (const char *, unsigned int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data); #endif /** * Poll for IO readiness events * * Note: If ph is non-NULL, the client should notify * when IO readiness events occur by calling * fuse_notify_poll() with the specified ph. * * Regardless of the number of times poll with a non-NULL ph * is received, single notification is enough to clear all. * Notifying more times incurs overhead but doesn't harm * correctness. * * The callee is responsible for destroying ph with * fuse_pollhandle_destroy() when no longer in use. */ int (*poll) (const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp); /** Write contents of buffer to an open file * * Similar to the write() method, but data is supplied in a * generic buffer. Use fuse_buf_copy() to transfer data to * the destination. * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * expected to reset the setuid and setgid bits. */ int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *); /** Store data from an open file in a buffer * * Similar to the read() method, but data is stored and * returned in a generic buffer. * * No actual copying of data has to take place, the source * file descriptor may simply be stored in the buffer for * later data transfer. * * The buffer must be allocated dynamically and stored at the * location pointed to by bufp. If the buffer contains memory * regions, they too must be allocated using malloc(). The * allocated memory will be freed by the caller. */ int (*read_buf) (const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *); /** * Perform BSD file locking operation * * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN * * Nonblocking requests will be indicated by ORing LOCK_NB to * the above operations * * For more information see the flock(2) manual page. * * Additionally fi->owner will be set to a value unique to * this open file. This same value will be supplied to * ->release() when the file is released. * * Note: if this method is not implemented, the kernel will still * allow file locking to work locally. Hence it is only * interesting for network filesystems and similar. */ int (*flock) (const char *, struct fuse_file_info *, int op); /** * Allocates space for an open file * * This function ensures that required space is allocated for specified * file. If this function returns success then any subsequent write * request to specified range is guaranteed not to fail because of lack * of space on the file system media. */ int (*fallocate) (const char *, int, off_t, off_t, struct fuse_file_info *); /** * Copy a range of data from one file to another * * Performs an optimized copy between two file descriptors without the * additional cost of transferring data through the FUSE kernel module * to user space (glibc) and then back into the FUSE filesystem again. * * In case this method is not implemented, applications are expected to * fall back to a regular file copy. (Some glibc versions did this * emulation automatically, but the emulation has been removed from all * glibc release branches.) */ ssize_t (*copy_file_range) (const char *path_in, struct fuse_file_info *fi_in, off_t offset_in, const char *path_out, struct fuse_file_info *fi_out, off_t offset_out, size_t size, int flags); /** * Find next data or hole after the specified offset */ off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *); /** * Get extended file attributes. * * fi may be NULL. * * If path is NULL, then the AT_EMPTY_PATH bit in flags will be * already set. */ int (*statx)(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi); }; /** Extra context that may be needed by some filesystems * * The uid, gid and pid fields are not filled in case of a writepage * operation. */ struct fuse_context { /** Pointer to the fuse object */ struct fuse *fuse; /** User ID of the calling process */ uid_t uid; /** Group ID of the calling process */ gid_t gid; /** Process ID of the calling thread */ pid_t pid; /** Private filesystem data */ void *private_data; /** Umask of the calling process */ mode_t umask; }; /** * The real main function * * Do not call this directly, use fuse_main() */ int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data); static inline int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data) { struct libfuse_version version = { .major = FUSE_MAJOR_VERSION, .minor = FUSE_MINOR_VERSION, .hotfix = FUSE_HOTFIX_VERSION, .padding = 0 }; fuse_log(FUSE_LOG_ERR, "%s is a libfuse internal function, please use fuse_main()\n", __func__); return fuse_main_real_versioned(argc, argv, op, op_size, &version, user_data); } /** * Main function of FUSE. * * This is for the lazy. This is all that has to be called from the * main() function. * * This function does the following: * - parses command line options, and handles --help and * --version * - installs signal handlers for INT, HUP, TERM and PIPE * - registers an exit handler to unmount the filesystem on program exit * - creates a fuse handle * - registers the operations * - calls either the single-threaded or the multi-threaded event loop * * Most file systems will have to parse some file-system specific * arguments before calling this function. It is recommended to do * this with fuse_opt_parse() and a processing function that passes * through any unknown options (this can also be achieved by just * passing NULL as the processing function). That way, the remaining * options can be passed directly to fuse_main(). * * fuse_main() accepts all options that can be passed to * fuse_parse_cmdline(), fuse_new(), or fuse_session_new(). * * Option parsing skips argv[0], which is assumed to contain the * program name. This element must always be present and is used to * construct a basic ``usage: `` message for the --help * output. argv[0] may also be set to the empty string. In this case * the usage message is suppressed. This can be used by file systems * to print their own usage line first. See hello.c for an example of * how to do this. * * Note: this is currently implemented as a macro. * * The following error codes may be returned from fuse_main(): * 1: Invalid option arguments * 2: No mount point specified * 3: FUSE setup failed * 4: Mounting failed * 5: Failed to daemonize (detach from session) * 6: Failed to set up signal handlers * 7: An error occurred during the life of the file system * * @param argc the argument counter passed to the main() function * @param argv the argument vector passed to the main() function * @param op the file system operation * @param private_data Initial value for the `private_data` * field of `struct fuse_context`. May be overridden by the * `struct fuse_operations.init` handler. * @return 0 on success, nonzero on failure * * Example usage, see hello.c */ static inline int fuse_main_fn(int argc, char *argv[], const struct fuse_operations *op, void *user_data) { struct libfuse_version version = { .major = FUSE_MAJOR_VERSION, .minor = FUSE_MINOR_VERSION, .hotfix = FUSE_HOTFIX_VERSION, .padding = 0 }; return fuse_main_real_versioned(argc, argv, op, sizeof(*(op)), &version, user_data); } #define fuse_main(argc, argv, op, user_data) \ fuse_main_fn(argc, argv, op, user_data) /* ----------------------------------------------------------- * * More detailed API * * ----------------------------------------------------------- */ /** * Print available options (high- and low-level) to stdout. This is * not an exhaustive list, but includes only those options that may be * of interest to an end-user of a file system. * * The function looks at the argument vector only to determine if * there are additional modules to be loaded (module=foo option), * and attempts to call their help functions as well. * * @param args the argument vector. */ void fuse_lib_help(struct fuse_args *args); /* Do not call this directly, use fuse_new() instead */ struct fuse *_fuse_new_30(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data); struct fuse *_fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data); /** * Create a new FUSE filesystem. * * This function accepts most file-system independent mount options * (like context, nodev, ro - see mount(8)), as well as the * FUSE-specific mount options from mount.fuse(8). * * If the --help option is specified, the function writes a help text * to stdout and returns NULL. * * Option parsing skips argv[0], which is assumed to contain the * program name. This element must always be present and is used to * construct a basic ``usage: `` message for the --help output. If * argv[0] is set to the empty string, no usage message is included in * the --help output. * * If an unknown option is passed in, an error message is written to * stderr and the function returns NULL. * * @param args argument vector * @param op the filesystem operations * @param op_size the size of the fuse_operations structure * @param private_data Initial value for the `private_data` * field of `struct fuse_context`. May be overridden by the * `struct fuse_operations.init` handler. * @return the created FUSE handle */ #if FUSE_USE_VERSION == 30 static inline struct fuse *fuse_new_fn(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data) { struct libfuse_version version = { .major = FUSE_MAJOR_VERSION, .minor = FUSE_MINOR_VERSION, .hotfix = FUSE_HOTFIX_VERSION, .padding = 0 }; return _fuse_new_30(args, op, op_size, &version, user_data); } #else /* FUSE_USE_VERSION */ static inline struct fuse *fuse_new_fn(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data) { struct libfuse_version version = { .major = FUSE_MAJOR_VERSION, .minor = FUSE_MINOR_VERSION, .hotfix = FUSE_HOTFIX_VERSION, .padding = 0 }; return _fuse_new_31(args, op, op_size, &version, user_data); } #endif #define fuse_new(args, op, size, data) fuse_new_fn(args, op, size, data) /** * Mount a FUSE file system. * * @param mountpoint the mount point path * @param f the FUSE handle * * @return 0 on success, -1 on failure. **/ int fuse_mount(struct fuse *f, const char *mountpoint); /** * Unmount a FUSE file system. * * See fuse_session_unmount() for additional information. * * @param f the FUSE handle **/ void fuse_unmount(struct fuse *f); /** * Destroy the FUSE handle. * * NOTE: This function does not unmount the filesystem. If this is * needed, call fuse_unmount() before calling this function. * * @param f the FUSE handle */ void fuse_destroy(struct fuse *f); /** * FUSE event loop. * * Requests from the kernel are processed, and the appropriate * operations are called. * * For a description of the return value and the conditions when the * event loop exits, refer to the documentation of * fuse_session_loop(). * * @param f the FUSE handle * @return see fuse_session_loop() * * See also: fuse_loop_mt() */ int fuse_loop(struct fuse *f); /** * Flag session as terminated * * This function will cause any running event loops to exit on * the next opportunity. * * @param f the FUSE handle */ 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 * * Requests from the kernel are processed, and the appropriate * operations are called. Request are processed in parallel by * distributing them between multiple threads. * * For a description of the return value and the conditions when the * event loop exits, refer to the documentation of * fuse_session_loop(). * * Note: using fuse_loop() instead of fuse_loop_mt() means you are running in * single-threaded mode, and that you will not have to worry about reentrancy, * though you will have to worry about recursive lookups. In single-threaded * mode, FUSE will wait for one callback to return before calling another. * * Enabling multiple threads, by using fuse_loop_mt(), will cause FUSE to make * multiple simultaneous calls into the various callback functions given by your * fuse_operations record. * * If you are using multiple threads, you can enjoy all the parallel execution * and interactive response benefits of threads, and you get to enjoy all the * benefits of race conditions and locking bugs, too. Ensure that any code used * in the callback function of fuse_operations is also thread-safe. * * @param f the FUSE handle * @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 * * The context is only valid for the duration of a filesystem * operation, and thus must not be stored and used later. * * @return the context */ struct fuse_context *fuse_get_context(void); /** * Get the current supplementary group IDs for the current request * * Similar to the getgroups(2) system call, except the return value is * always the total number of group IDs, even if it is larger than the * specified size. * * The current fuse kernel module in linux (as of 2.6.30) doesn't pass * the group list to userspace, hence this function needs to parse * "/proc/$TID/task/$TID/status" to get the group IDs. * * This feature may not be supported on all operating systems. In * such a case this function will return -ENOSYS. * * @param size size of given array * @param list array of group IDs to be filled in * @return the total number of supplementary group IDs or -errno on failure */ int fuse_getgroups(int size, gid_t list[]); /** * Check if the current request has already been interrupted * * @return 1 if the request has been interrupted, 0 otherwise */ int fuse_interrupted(void); /** * Invalidates cache for the given path. * * This calls fuse_lowlevel_notify_inval_inode internally. * * @return 0 on successful invalidation, negative error value otherwise. * This routine may return -ENOENT to indicate that there was * no entry to be invalidated, e.g., because the path has not * been seen before or has been forgotten; this should not be * considered to be an error. */ int fuse_invalidate_path(struct fuse *f, const char *path); /** * Start the cleanup thread when using option "remember". * * This is done automatically by fuse_loop_mt() * @param fuse struct fuse pointer for fuse instance * @return 0 on success and -1 on error */ int fuse_start_cleanup_thread(struct fuse *fuse); /** * Stop the cleanup thread when using option "remember". * * This is done automatically by fuse_loop_mt() * @param fuse struct fuse pointer for fuse instance */ void fuse_stop_cleanup_thread(struct fuse *fuse); /** * Iterate over cache removing stale entries * use in conjunction with "-oremember" * * NOTE: This is already done for the standard sessions * * @param fuse struct fuse pointer for fuse instance * @return the number of seconds until the next cleanup */ int fuse_clean_cache(struct fuse *fuse); /* * Stacking API */ /** * Fuse filesystem object * * This is opaque object represents a filesystem layer */ struct fuse_fs; /* * These functions call the relevant filesystem operation, and return * the result. * * If the operation is not defined, they return -ENOSYS, with the * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir, * fuse_fs_releasedir and fuse_fs_statfs, which return 0. */ int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf, struct fuse_file_info *fi); int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, const char *newpath, unsigned int flags); int fuse_fs_unlink(struct fuse_fs *fs, const char *path); int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path); int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); int fuse_fs_release(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi); int fuse_fs_open(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi); int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, off_t off, struct fuse_file_info *fi); int fuse_fs_read_buf(struct fuse_fs *fs, const char *path, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *fi); int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, size_t size, off_t off, struct fuse_file_info *fi); int fuse_fs_write_buf(struct fuse_fs *fs, const char *path, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *fi); int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, struct fuse_file_info *fi); int fuse_fs_flush(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi); int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); int fuse_fs_opendir(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi); int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, fuse_fill_dir_t filler, off_t off, struct fuse_file_info *fi, enum fuse_readdir_flags flags); int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, struct fuse_file_info *fi); int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi); int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, struct fuse_file_info *fi); int fuse_fs_lock(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, int cmd, struct flock *lock); int fuse_fs_flock(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, int op); int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode, struct fuse_file_info *fi); int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi); int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size, struct fuse_file_info *fi); int fuse_fs_utimens(struct fuse_fs *fs, const char *path, const struct timespec tv[2], struct fuse_file_info *fi); int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, size_t len); int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, dev_t rdev); int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode); int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, const char *value, size_t size, int flags); int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, char *value, size_t size); int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, size_t size); int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name); int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, uint64_t *idx); #if FUSE_USE_VERSION < 35 int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg, struct fuse_file_info *fi, unsigned int flags, void *data); #else int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned int flags, void *data); #endif int fuse_fs_poll(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp); int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode, off_t offset, off_t length, struct fuse_file_info *fi); ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in, struct fuse_file_info *fi_in, off_t off_in, const char *path_out, struct fuse_file_info *fi_out, off_t off_out, size_t len, int flags); off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence, struct fuse_file_info *fi); int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi); void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn, struct fuse_config *cfg); void fuse_fs_destroy(struct fuse_fs *fs); int fuse_notify_poll(struct fuse_pollhandle *ph); /** * Create a new fuse filesystem object * * This is usually called from the factory of a fuse module to create * a new instance of a filesystem. * * @param op the filesystem operations * @param op_size the size of the fuse_operations structure * @param private_data Initial value for the `private_data` * field of `struct fuse_context`. May be overridden by the * `struct fuse_operations.init` handler. * @return a new filesystem object */ struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data); /** * Factory for creating filesystem objects * * The function may use and remove options from 'args' that belong * to this module. * * For now the 'fs' vector always contains exactly one filesystem. * This is the filesystem which will be below the newly created * filesystem in the stack. * * @param args the command line arguments * @param fs NULL terminated filesystem object vector * @return the new filesystem object */ typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[]); /** * Register filesystem module * * If the "-omodules=*name*_:..." option is present, filesystem * objects are created and pushed onto the stack with the *factory_* * function. * * @param name_ the name of this filesystem module * @param factory_ the factory function for this filesystem module */ #define FUSE_REGISTER_MODULE(name_, factory_) \ fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_ /** Get session from fuse object */ struct fuse_session *fuse_get_session(struct fuse *f); /** * Open a FUSE file descriptor and set up the mount for the given * mountpoint and flags. * * @param mountpoint reference to the mount in the file system * @param options mount options * @return the FUSE file descriptor or -1 upon error */ int fuse_open_channel(const char *mountpoint, const char *options); #ifdef __cplusplus } #endif #endif /* FUSE_H_ */ fuse-3.18.2/include/fuse_common.h0000644000175000017500000011076715156613252015645 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ /** @file */ #if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_) #error "Never include directly; use or instead." #endif #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 #include #include #include #define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min)) #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) #ifdef __cplusplus extern "C" { #endif /** * Information about an open file. * * File Handles are created by the open, opendir, and create methods and closed * by the release and releasedir methods. Multiple file handles may be * concurrently open for the same file. Generally, a client will create one * file handle per file descriptor, though in some cases multiple file * descriptors can share a single file handle. * * Note: This data structure is ABI sensitive, new parameters have to be * added within padding/padding2 bits and always below existing * parameters. */ struct fuse_file_info { /** Open flags. Available in open(), release() and create() */ int32_t flags; /** In case of a write operation indicates if this was caused by a delayed write from the page cache. If so, then the context's pid, uid, and gid fields will not be valid, and the *fh* value may not match the *fh* value that would have been sent with the corresponding individual write requests if write caching had been disabled. */ uint32_t writepage : 1; /** Can be filled in by open/create, to use direct I/O on this file. */ uint32_t direct_io : 1; /** 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. */ uint32_t keep_cache : 1; /** Indicates a flush operation. Set in flush operation, also maybe set in highlevel lock operation and lowlevel release operation. */ uint32_t flush : 1; /** Can be filled in by open, to indicate that the file is not seekable. */ uint32_t nonseekable : 1; /* Indicates that flock locks for this file should be released. If set, lock_owner shall contain a valid value. May only be set in ->release(). */ uint32_t flock_release : 1; /** Can be filled in by opendir. It signals the kernel to enable caching of entries returned by readdir(). Has no effect when set in other contexts (in particular it does nothing when set by open()). */ uint32_t cache_readdir : 1; /** Can be filled in by open, to indicate that flush is not needed on close. */ uint32_t noflush : 1; /** Can be filled by open/create, to allow parallel direct writes on this file */ uint32_t parallel_direct_writes : 1; /** Padding. Reserved for future use*/ uint32_t padding : 23; uint32_t padding2 : 32; uint32_t padding3 : 32; /** File handle id. May be filled in by filesystem in create, * open, and opendir(). Available in most other file operations on the * same file handle. */ uint64_t fh; /** Lock owner id. Available in locking operations and flush */ uint64_t lock_owner; /** Requested poll events. Available in ->poll. Only set on kernels which support it. If unsupported, this field is set to zero. */ uint32_t poll_events; /** Passthrough backing file id. May be filled in by filesystem in * create and open. It is used to create a passthrough connection * between FUSE file and backing file. */ int32_t backing_id; /** struct fuse_file_info api and abi flags */ uint64_t compat_flags; uint64_t reserved[2]; }; /** * Configuration parameters passed to fuse_session_loop_mt() and * fuse_loop_mt(). * For FUSE API versions less than 312, use a public struct * fuse_loop_config in applications and struct fuse_loop_config_v1 * is used in library (i.e., libfuse.so). These two structs are binary * compatible in earlier API versions and can be linked. * 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 declaration */ struct fuse_loop_config { #else struct fuse_loop_config_v1 { #endif /** * 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. */ unsigned int max_idle_threads; }; /************************************************************************** * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' * **************************************************************************/ /** * Indicates that the filesystem supports asynchronous read requests. * * If this capability is not requested/available, the kernel will * ensure that there is at most one pending read request per * file-handle at any time, and will attempt to order read requests by * increasing offset. * * This feature is enabled by default when supported by the kernel. */ #define FUSE_CAP_ASYNC_READ (1UL << 0) /** * Indicates that the filesystem supports "remote" locking. * * This feature is enabled by default when supported by the kernel, * and if getlk() and setlk() handlers are implemented. */ #define FUSE_CAP_POSIX_LOCKS (1UL << 1) /** * Indicates that the filesystem supports the O_TRUNC open flag. If * disabled, and an application specifies O_TRUNC, fuse first calls * truncate() and then open() with O_TRUNC filtered out. * * This feature is enabled by default when supported by the kernel. */ #define FUSE_CAP_ATOMIC_O_TRUNC (1UL << 3) /** * Indicates that the filesystem supports lookups of "." and "..". * * When this flag is set, the filesystem must be prepared to receive requests * for invalid inodes (i.e., for which a FORGET request was received or * which have been used in a previous instance of the filesystem daemon) and * must not reuse node-ids (even when setting generation numbers). * * This feature is disabled by default. */ #define FUSE_CAP_EXPORT_SUPPORT (1UL << 4) /** * Indicates that the kernel should not apply the umask to the * file mode on create operations. * * This feature is disabled by default. */ #define FUSE_CAP_DONT_MASK (1UL << 6) /** * Indicates that libfuse should try to use splice() when writing to * the fuse device. This may improve performance. * * This feature is disabled by default. */ #define FUSE_CAP_SPLICE_WRITE (1UL << 7) /** * Indicates that libfuse should try to move pages instead of copying when * writing to / reading from the fuse device. This may improve performance. * * This feature is disabled by default. */ #define FUSE_CAP_SPLICE_MOVE (1UL << 8) /** * Indicates that libfuse should try to use splice() when reading from * the fuse device. This may improve performance. * * This feature is enabled by default when supported by the kernel and * if the filesystem implements a write_buf() handler. */ #define FUSE_CAP_SPLICE_READ (1UL << 9) /** * If set, the calls to flock(2) will be emulated using POSIX locks and must * then be handled by the filesystem's setlock() handler. * * If not set, flock(2) calls will be handled by the FUSE kernel module * internally (so any access that does not go through the kernel cannot be taken * into account). * * This feature is enabled by default when supported by the kernel and * if the filesystem implements a flock() handler. */ #define FUSE_CAP_FLOCK_LOCKS (1UL << 10) /** * Indicates that the filesystem supports ioctl's on directories. * * This feature is enabled by default when supported by the kernel. */ #define FUSE_CAP_IOCTL_DIR (1UL << 11) /** * Traditionally, while a file is open the FUSE kernel module only * asks the filesystem for an update of the file's attributes when a * client attempts to read beyond EOF. This is unsuitable for * e.g. network filesystems, where the file contents may change * without the kernel knowing about it. * * If this flag is set, FUSE will check the validity of the attributes * on every read. If the attributes are no longer valid (i.e., if the * *attr_timeout* passed to fuse_reply_attr() or set in `struct * fuse_entry_param` has passed), it will first issue a `getattr` * request. If the new mtime differs from the previous value, any * cached file *contents* will be invalidated as well. * * This flag should always be set when available. If all file changes * go through the kernel, *attr_timeout* should be set to a very large * number to avoid unnecessary getattr() calls. * * This feature is enabled by default when supported by the kernel. */ #define FUSE_CAP_AUTO_INVAL_DATA (1UL << 12) /** * Indicates that the filesystem supports readdirplus. * * This feature is enabled by default when supported by the kernel and if the * filesystem implements a readdirplus() handler. */ #define FUSE_CAP_READDIRPLUS (1UL << 13) /** * Indicates that the filesystem supports adaptive readdirplus. * * If FUSE_CAP_READDIRPLUS is not set, this flag has no effect. * * If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel * will always issue readdirplus() requests to retrieve directory * contents. * * If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel * will issue both readdir() and readdirplus() requests, depending on * how much information is expected to be required. * * As of Linux 4.20, the algorithm is as follows: when userspace * starts to read directory entries, issue a READDIRPLUS request to * the filesystem. If any entry attributes have been looked up by the * time userspace requests the next batch of entries continue with * READDIRPLUS, otherwise switch to plain READDIR. This will reasult * in eg plain "ls" triggering READDIRPLUS first then READDIR after * that because it doesn't do lookups. "ls -l" should result in all * READDIRPLUS, except if dentries are already cached. * * This feature is enabled by default when supported by the kernel and * if the filesystem implements both a readdirplus() and a readdir() * handler. */ #define FUSE_CAP_READDIRPLUS_AUTO (1UL << 14) /** * Indicates that the filesystem supports asynchronous direct I/O submission. * * If this capability is not requested/available, the kernel will ensure that * there is at most one pending read and one pending write request per direct * I/O file-handle at any time. * * This feature is enabled by default when supported by the kernel. */ #define FUSE_CAP_ASYNC_DIO (1UL << 15) /** * Indicates that writeback caching should be enabled. This means that * individual write request may be buffered and merged in the kernel * before they are send to the filesystem. * * This feature is disabled by default. */ #define FUSE_CAP_WRITEBACK_CACHE (1UL << 16) /** * Indicates support for zero-message opens. If this flag is set in * the `capable` field of the `fuse_conn_info` structure, then the * filesystem may return `ENOSYS` from the open() handler to indicate * success. Further attempts to open files will be handled in the * kernel. (If this flag is not set, returning ENOSYS will be treated * as an error and signaled to the caller). * * Setting this flag in the `want` field enables this behavior automatically * within libfuse for low level API users. If non-low level users wish to have * this behavior you must return `ENOSYS` from the open() handler on supporting * kernels. */ #define FUSE_CAP_NO_OPEN_SUPPORT (1UL << 17) /** * Indicates support for parallel directory operations. If this flag * is unset, the FUSE kernel module will ensure that lookup() and * readdir() requests are never issued concurrently for the same * directory. */ #define FUSE_CAP_PARALLEL_DIROPS (1UL << 18) /** * Indicates support for POSIX ACLs. * * If this feature is enabled, the kernel will cache and have * responsibility for enforcing ACLs. ACL will be stored as xattrs and * passed to userspace, which is responsible for updating the ACLs in * the filesystem, keeping the file mode in sync with the ACL, and * ensuring inheritance of default ACLs when new filesystem nodes are * created. Note that this requires that the file system is able to * parse and interpret the xattr representation of ACLs. * * Enabling this feature implicitly turns on the * ``default_permissions`` mount option (even if it was not passed to * mount(2)). * * This feature is disabled by default. */ #define FUSE_CAP_POSIX_ACL (1UL << 19) /** * Indicates that the filesystem is responsible for unsetting * setuid and setgid bits when a file is written, truncated, or * its owner is changed. * * This feature is disabled by default. */ #define FUSE_CAP_HANDLE_KILLPRIV (1UL << 20) /** * Indicates that the filesystem is responsible for unsetting * setuid and setgid bit and additionally cap (stored as xattr) when a * file is written, truncated, or its owner is changed. * 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). * KILLPRIV_V2 requires handling of * - FUSE_OPEN_KILL_SUIDGID (set in struct fuse_create_in::open_flags) * - FATTR_KILL_SUIDGID (set in struct fuse_setattr_in::valid) * - FUSE_WRITE_KILL_SUIDGID (set in struct fuse_write_in::write_flags) * * This feature is disabled by default. */ #define FUSE_CAP_HANDLE_KILLPRIV_V2 (1UL << 21) /** * Indicates that the kernel supports caching symlinks in its page cache. * * When this feature is enabled, symlink targets are saved in the page cache. * You can invalidate a cached link by calling: * `fuse_lowlevel_notify_inval_inode(se, ino, 0, 0);` * * This feature is disabled by default. * If the kernel supports it (>= 4.20), you can enable this feature by * setting this flag in the `want` field of the `fuse_conn_info` structure. */ #define FUSE_CAP_CACHE_SYMLINKS (1UL << 23) /** * Indicates support for zero-message opendirs. If this flag is set in * the `capable` field of the `fuse_conn_info` structure, then the filesystem * may return `ENOSYS` from the opendir() handler to indicate success. Further * opendir and releasedir messages will be handled in the kernel. (If this * flag is not set, returning ENOSYS will be treated as an error and signalled * to the caller.) * * Setting this flag in the `want` field enables this behavior automatically * within libfuse for low level API users. If non-low level users with to have * this behavior you must return `ENOSYS` from the opendir() handler on * supporting kernels. */ #define FUSE_CAP_NO_OPENDIR_SUPPORT (1UL << 24) /** * Indicates support for invalidating cached pages only on explicit request. * * If this flag is set in the `capable` field of the `fuse_conn_info` structure, * then the FUSE kernel module supports invalidating cached pages only on * explicit request by the filesystem through fuse_lowlevel_notify_inval_inode() * or fuse_invalidate_path(). * * By setting this flag in the `want` field of the `fuse_conn_info` structure, * the filesystem is responsible for invalidating cached pages through explicit * requests to the kernel. * * Note that setting this flag does not prevent the cached pages from being * flushed by OS itself and/or through user actions. * * Note that if both FUSE_CAP_EXPLICIT_INVAL_DATA and FUSE_CAP_AUTO_INVAL_DATA * are set in the `capable` field of the `fuse_conn_info` structure then * FUSE_CAP_AUTO_INVAL_DATA takes precedence. * * This feature is disabled by default. */ #define FUSE_CAP_EXPLICIT_INVAL_DATA (1UL << 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 (1UL << 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 (1UL << 27) /** * Files opened with FUSE_DIRECT_IO do not support MAP_SHARED mmap. This restriction * is relaxed through FUSE_CAP_DIRECT_IO_RELAX (kernel flag: FUSE_DIRECT_IO_RELAX). * MAP_SHARED is disabled by default for FUSE_DIRECT_IO, as this flag can be used to * ensure coherency between mount points (or network clients) and with kernel page * cache as enforced by mmap that cannot be guaranteed anymore. */ #define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1UL << 28) /** * Indicates support for passthrough mode access for read/write operations. * * If this flag is set in the `capable` field of the `fuse_conn_info` * structure, then the FUSE kernel module supports redirecting read/write * operations to the backing file instead of letting them to be handled * by the FUSE daemon. * * This feature is disabled by default. */ #define FUSE_CAP_PASSTHROUGH (1UL << 29) /** * Indicates that the file system cannot handle NFS export * * If this flag is set NFS export and name_to_handle_at * is not going to work at all and will fail with EOPNOTSUPP. */ #define FUSE_CAP_NO_EXPORT_SUPPORT (1UL << 30) /** * Indicates support for io-uring between fuse-server and fuse-client */ #define FUSE_CAP_OVER_IO_URING (1UL << 31) /** * Ioctl flags * * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed * FUSE_IOCTL_RETRY: retry with new iovecs * FUSE_IOCTL_DIR: is a directory * * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs */ #define FUSE_IOCTL_COMPAT (1 << 0) #define FUSE_IOCTL_UNRESTRICTED (1 << 1) #define FUSE_IOCTL_RETRY (1 << 2) #define FUSE_IOCTL_DIR (1 << 4) #define FUSE_IOCTL_MAX_IOV 256 /** * Connection information, passed to the ->init() method * * Some of the elements are read-write, these can be changed to * indicate the value requested by the filesystem. The requested * value must usually be smaller than the indicated value. * * Note: The `capable` and `want` fields are limited to 32 bits for * ABI compatibility. For full 64-bit capability support, use the * `capable_ext` and `want_ext` fields instead. */ struct fuse_conn_info { /** * Major version of the protocol (read-only) */ uint32_t proto_major; /** * Minor version of the protocol (read-only) */ uint32_t proto_minor; /** * Maximum size of the write buffer */ uint32_t max_write; /** * Maximum size of read requests. A value of zero indicates no * limit. However, even if the filesystem does not specify a * limit, the maximum size of read requests will still be * limited by the kernel. * * NOTE: For the time being, the maximum size of read requests * must be set both here *and* passed to fuse_session_new() * using the ``-o max_read=`` mount option. At some point * in the future, specifying the mount option will no longer * be necessary. */ uint32_t max_read; /** * Maximum readahead */ uint32_t max_readahead; /** * Capability flags that the kernel supports (read-only) * * Deprecated left over for ABI compatibility, use capable_ext */ uint32_t capable; /** * Capability flags that the filesystem wants to enable. * * libfuse attempts to initialize this field with * reasonable default values before calling the init() handler. * * Deprecated left over for ABI compatibility. * Use want_ext with the helper functions * fuse_set_feature_flag() / fuse_unset_feature_flag() */ uint32_t want; /** * Maximum number of pending "background" requests. A * background request is any type of request for which the * total number is not limited by other means. As of kernel * 4.8, only two types of requests fall into this category: * * 1. Read-ahead requests * 2. Asynchronous direct I/O requests * * Read-ahead requests are generated (if max_readahead is * non-zero) by the kernel to preemptively fill its caches * when it anticipates that userspace will soon read more * data. * * Asynchronous direct I/O requests are generated if * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large * direct I/O request. In this case the kernel will internally * split it up into multiple smaller requests and submit them * to the filesystem concurrently. * * Note that the following requests are *not* background * requests: writeback requests (limited by the kernel's * flusher algorithm), regular (i.e., synchronous and * buffered) userspace read/write requests (limited to one per * thread), asynchronous read requests (Linux's io_submit(2) * call actually blocks, so these are also limited to one per * thread). */ uint32_t max_background; /** * Kernel congestion threshold parameter. If the number of pending * background requests exceeds this number, the FUSE kernel module will * mark the filesystem as "congested". This instructs the kernel to * expect that queued requests will take some time to complete, and to * adjust its algorithms accordingly (e.g. by putting a waiting thread * to sleep instead of using a busy-loop). */ uint32_t congestion_threshold; /** * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible * for updating mtime and ctime when write requests are received. The * updated values are passed to the filesystem with setattr() requests. * However, if the filesystem does not support the full resolution of * the kernel timestamps (nanoseconds), the mtime and ctime values used * by kernel and filesystem will differ (and result in an apparent * change of times after a cache flush). * * To prevent this problem, this variable can be used to inform the * kernel about the timestamp granularity supported by the file-system. * The value should be power of 10. The default is 1, i.e. full * nano-second resolution. Filesystems supporting only second resolution * should set this to 1000000000. */ uint32_t time_gran; /** * When FUSE_CAP_PASSTHROUGH is enabled, this is the maximum allowed * stacking depth of the backing files. In current kernel, the maximum * allowed stack depth if FILESYSTEM_MAX_STACK_DEPTH (2), which includes * the FUSE passthrough layer, so the maximum stacking depth for backing * files is 1. * * The default is FUSE_BACKING_STACKED_UNDER (0), meaning that the * backing files cannot be on a stacked filesystem, but another stacked * filesystem can be stacked over this FUSE passthrough filesystem. * * Set this to FUSE_BACKING_STACKED_OVER (1) if backing files may be on * a stacked filesystem, such as overlayfs or another FUSE passthrough. * In this configuration, another stacked filesystem cannot be stacked * over this FUSE passthrough filesystem. */ #define FUSE_BACKING_STACKED_UNDER (0) #define FUSE_BACKING_STACKED_OVER (1) uint32_t max_backing_stack_depth; /** * Disable FUSE_INTERRUPT requests. * * Enable `no_interrupt` option to: * 1) Avoid unnecessary locking operations and list operations, * 2) Return ENOSYS for the reply of FUSE_INTERRUPT request to * inform the kernel not to send the FUSE_INTERRUPT request. */ uint32_t no_interrupt : 1; /* reserved bits for future use */ uint32_t padding : 31; /** * Extended capability flags that the kernel supports (read-only) * This field provides full 64-bit capability support. */ uint64_t capable_ext; /** * Extended capability flags that the filesystem wants to enable. * This field provides full 64-bit capability support. * * Don't set this field directly, but use the helper functions * fuse_set_feature_flag() / fuse_unset_feature_flag() * */ uint64_t want_ext; /** * Request timeout (in seconds). If the request is not answered by * this timeout, the connection will be aborted by the kernel. */ uint16_t request_timeout; /** * For future use. */ uint16_t reserved[31]; }; struct fuse_session; struct fuse_pollhandle; struct fuse_conn_info_opts; /** * This function parses several command-line options that can be used * to override elements of struct fuse_conn_info. The pointer returned * by this function should be passed to the * fuse_apply_conn_info_opts() method by the file system's init() * handler. * * Before using this function, think twice if you really want these * parameters to be adjustable from the command line. In most cases, * they should be determined by the file system internally. * * The following options are recognized: * * -o max_write=N sets conn->max_write * -o max_readahead=N sets conn->max_readahead * -o max_background=N sets conn->max_background * -o congestion_threshold=N sets conn->congestion_threshold * -o async_read sets FUSE_CAP_ASYNC_READ in conn->want * -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want * -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want * -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock * -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want * -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want * -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want * -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want * -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want * -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want * -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want * -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets * FUSE_CAP_READDIRPLUS_AUTO in conn->want * -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and * FUSE_CAP_READDIRPLUS_AUTO in conn->want * -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want * -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want * -o time_gran=N sets conn->time_gran * * Known options will be removed from *args*, unknown options will be * passed through unchanged. * * @param args argument vector (input+output) * @return parsed options **/ struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args); /** * This function applies the (parsed) parameters in *opts* to the * *conn* pointer. It may modify the following fields: wants, * max_write, max_readahead, congestion_threshold, max_background, * time_gran. A field is only set (or unset) if the corresponding * option has been explicitly set. */ void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn); /** * Go into the background * * @param foreground if true, stay in the foreground * @return 0 on success, -1 on failure */ int fuse_daemonize(int foreground); /** * Get the version of the library * * @return the version */ int fuse_version(void); /** * Get the full package version string of the library * * @return the package version */ const char *fuse_pkgversion(void); /** * Destroy poll handle * * @param ph the poll handle */ void fuse_pollhandle_destroy(struct fuse_pollhandle *ph); /* ----------------------------------------------------------- * * Data buffer * * ----------------------------------------------------------- */ /** * Buffer flags */ enum fuse_buf_flags { /** * Buffer contains a file descriptor * * If this flag is set, the .fd field is valid, otherwise the * .mem fields is valid. */ FUSE_BUF_IS_FD = (1 << 1), /** * Seek on the file descriptor * * If this flag is set then the .pos field is valid and is * used to seek to the given offset before performing * operation on file descriptor. */ FUSE_BUF_FD_SEEK = (1 << 2), /** * Retry operation on file descriptor * * If this flag is set then retry operation on file descriptor * until .size bytes have been copied or an error or EOF is * detected. */ FUSE_BUF_FD_RETRY = (1 << 3) }; /** * Buffer copy flags */ enum fuse_buf_copy_flags { /** * Don't use splice(2) * * Always fall back to using read and write instead of * splice(2) to copy data from one file descriptor to another. * * If this flag is not set, then only fall back if splice is * unavailable. */ FUSE_BUF_NO_SPLICE = (1 << 1), /** * Force splice * * Always use splice(2) to copy data from one file descriptor * to another. If splice is not available, return -EINVAL. */ FUSE_BUF_FORCE_SPLICE = (1 << 2), /** * Try to move data with splice. * * If splice is used, try to move pages from the source to the * destination instead of copying. See documentation of * SPLICE_F_MOVE in splice(2) man page. */ FUSE_BUF_SPLICE_MOVE = (1 << 3), /** * Don't block on the pipe when copying data with splice * * Makes the operations on the pipe non-blocking (if the pipe * is full or empty). See SPLICE_F_NONBLOCK in the splice(2) * man page. */ FUSE_BUF_SPLICE_NONBLOCK= (1 << 4) }; /** * Single data buffer * * Generic data buffer for I/O, extended attributes, etc... Data may * be supplied as a memory pointer or as a file descriptor */ struct fuse_buf { /** * Size of data in bytes */ size_t size; /** * Buffer flags */ enum fuse_buf_flags flags; /** * Memory pointer * * Used unless FUSE_BUF_IS_FD flag is set. */ void *mem; /** * File descriptor * * Used if FUSE_BUF_IS_FD flag is set. */ int fd; /** * File position * * Used if FUSE_BUF_FD_SEEK flag is set. */ off_t pos; /** * Size of memory pointer * * Used only if mem was internally allocated. * Not used if mem was user-provided. */ size_t mem_size; }; /** * Data buffer vector * * An array of data buffers, each containing a memory pointer or a * file descriptor. * * Allocate dynamically to add more than one buffer. */ struct fuse_bufvec { /** * Number of buffers in the array */ size_t count; /** * Index of current buffer within the array */ size_t idx; /** * Current offset within the current buffer */ size_t off; /** * Array of buffers */ struct fuse_buf buf[1]; }; /** * libfuse version a file system was compiled with. Should be filled in from * defines in 'libfuse_config.h' */ struct libfuse_version { uint32_t major; uint32_t minor; uint32_t hotfix; uint32_t padding; }; /* Initialize bufvec with a single buffer of given size */ #define FUSE_BUFVEC_INIT(size__) \ ((struct fuse_bufvec) { \ /* .count= */ 1, \ /* .idx = */ 0, \ /* .off = */ 0, \ /* .buf = */ { /* [0] = */ { \ /* .size = */ (size__), \ /* .flags = */ (enum fuse_buf_flags) 0, \ /* .mem = */ NULL, \ /* .fd = */ -1, \ /* .pos = */ 0, \ /* .mem_size = */ 0, \ } } \ } ) /** * Get total size of data in a fuse buffer vector * * @param bufv buffer vector * @return size of data */ size_t fuse_buf_size(const struct fuse_bufvec *bufv); /** * Copy data from one buffer vector to another * * @param dst destination buffer vector * @param src source buffer vector * @param flags flags controlling the copy * @return actual number of bytes copied or -errno on error */ ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags); /* ----------------------------------------------------------- * * Signal handling * * ----------------------------------------------------------- */ /** * Exit session on HUP, TERM and INT signals and ignore PIPE signal * * Stores session in a global variable. May only be called once per * process until fuse_remove_signal_handlers() is called. * * Once either of the POSIX signals arrives, the signal handler calls * fuse_session_exit(). * * @param se the session to exit * @return 0 on success, -1 on failure * * See also: * fuse_remove_signal_handlers() */ int fuse_set_signal_handlers(struct fuse_session *se); /** * Print a stack backtrace diagnostic on critical signals () * * Stores session in a global variable. May only be called once per * process until fuse_remove_signal_handlers() is called. * * Once either of the POSIX signals arrives, the signal handler calls * fuse_session_exit(). * * @param se the session to exit * @return 0 on success, -1 on failure * * See also: * fuse_remove_signal_handlers() */ int fuse_set_fail_signal_handlers(struct fuse_session *se); /** * Restore default signal handlers * * Resets global session. After this fuse_set_signal_handlers() may * be called again. * * @param se the same session as given in fuse_set_signal_handlers() * * See also: * fuse_set_signal_handlers() */ void fuse_remove_signal_handlers(struct fuse_session *se); /** * Config operations. * These APIs are introduced in version 312 (FUSE_MAKE_VERSION(3, 12)). * Using them in earlier versions will result in errors. */ #if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12) /** * 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); #endif /** * Set a feature flag in the want_ext field of fuse_conn_info. * * @param conn connection information * @param flag feature flag to be set * @return true if the flag was set, false if the flag is not supported */ bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag); /** * Unset a feature flag in the want_ext field of fuse_conn_info. * * @param conn connection information * @param flag feature flag to be unset */ void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag); /** * Get the value of a feature flag in the want_ext field of fuse_conn_info. * * @param conn connection information * @param flag feature flag to be checked * @return true if the flag is set, false otherwise */ bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag); /* * DO NOT USE: Not part of public API, for internal test use only. * The function signature or any use of it is not guaranteeed to * remain stable. And neither are results of what this function does. */ int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn); /* ----------------------------------------------------------- * * Compatibility stuff * * ----------------------------------------------------------- */ #if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30 # error only API version 30 or greater is supported #endif #ifdef __cplusplus } #endif /* * This interface uses 64 bit off_t. * * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags! */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) _Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit"); #else struct _fuse_off_t_must_be_64bit_dummy_struct \ { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); }; #endif #endif /* FUSE_COMMON_H_ */ fuse-3.18.2/include/fuse_kernel.h0000644000175000017500000007601215156613252015627 0ustar berndbernd/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ /* This file defines the kernel interface of FUSE Copyright (C) 2001-2008 Miklos Szeredi This program can be distributed under the terms of the GNU GPL. See the file COPYING. This -- and only this -- header file may also be distributed under the terms of the BSD Licence as follows: Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * This file defines the kernel interface of FUSE * * Protocol changelog: * * 7.1: * - add the following messages: * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK, * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE, * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR, * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR, * FUSE_RELEASEDIR * - add padding to messages to accommodate 32-bit servers on 64-bit kernels * * 7.2: * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags * - add FUSE_FSYNCDIR message * * 7.3: * - add FUSE_ACCESS message * - add FUSE_CREATE message * - add filehandle to fuse_setattr_in * * 7.4: * - add frsize to fuse_kstatfs * - clean up request size limit checking * * 7.5: * - add flags and max_write to fuse_init_out * * 7.6: * - add max_readahead to fuse_init_in and fuse_init_out * * 7.7: * - add FUSE_INTERRUPT message * - add POSIX file lock support * * 7.8: * - add lock_owner and flags fields to fuse_release_in * - add FUSE_BMAP message * - add FUSE_DESTROY message * * 7.9: * - new fuse_getattr_in input argument of GETATTR * - add lk_flags in fuse_lk_in * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in * - add blksize field to fuse_attr * - add file flags field to fuse_read_in and fuse_write_in * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in * * 7.10 * - add nonseekable open flag * * 7.11 * - add IOCTL message * - add unsolicited notification support * - add POLL message and NOTIFY_POLL notification * * 7.12 * - add umask flag to input argument of create, mknod and mkdir * - add notification messages for invalidation of inodes and * directory entries * * 7.13 * - make max number of background requests and congestion threshold * tunables * * 7.14 * - add splice support to fuse device * * 7.15 * - add store notify * - add retrieve notify * * 7.16 * - add BATCH_FORGET request * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct * fuse_ioctl_iovec' instead of ambiguous 'struct iovec' * - add FUSE_IOCTL_32BIT flag * * 7.17 * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK * * 7.18 * - add FUSE_IOCTL_DIR flag * - add FUSE_NOTIFY_DELETE * * 7.19 * - add FUSE_FALLOCATE * * 7.20 * - add FUSE_AUTO_INVAL_DATA * * 7.21 * - add FUSE_READDIRPLUS * - send the requested events in POLL request * * 7.22 * - add FUSE_ASYNC_DIO * * 7.23 * - add FUSE_WRITEBACK_CACHE * - add time_gran to fuse_init_out * - add reserved space to fuse_init_out * - add FATTR_CTIME * - add ctime and ctimensec to fuse_setattr_in * - add FUSE_RENAME2 request * - add FUSE_NO_OPEN_SUPPORT flag * * 7.24 * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support * * 7.25 * - add FUSE_PARALLEL_DIROPS * * 7.26 * - add FUSE_HANDLE_KILLPRIV * - add FUSE_POSIX_ACL * * 7.27 * - add FUSE_ABORT_ERROR * * 7.28 * - add FUSE_COPY_FILE_RANGE * - add FOPEN_CACHE_DIR * - add FUSE_MAX_PAGES, add max_pages to init_out * - add FUSE_CACHE_SYMLINKS * * 7.29 * - add FUSE_NO_OPENDIR_SUPPORT flag * * 7.30 * - add FUSE_EXPLICIT_INVAL_DATA * - add FUSE_IOCTL_COMPAT_X32 * * 7.31 * - add FUSE_WRITE_KILL_PRIV flag * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag * * 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 * - add FUSE_HAS_EXPIRE_ONLY * * 7.39 * - add FUSE_DIRECT_IO_ALLOW_MMAP * - add FUSE_STATX and related structures * * 7.40 * - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag * - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag * - add FUSE_NO_EXPORT_SUPPORT init flag * - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag * * 7.41 * - add FUSE_ALLOW_IDMAP * 7.42 * - Add FUSE_OVER_IO_URING and all other io-uring related flags and data * structures: * - struct fuse_uring_ent_in_out * - struct fuse_uring_req_header * - struct fuse_uring_cmd_req * - FUSE_URING_IN_OUT_HEADER_SZ * - FUSE_URING_OP_IN_OUT_SZ * - enum fuse_uring_cmd * * 7.43 * - add FUSE_REQUEST_TIMEOUT * * 7.44 * - add FUSE_NOTIFY_INC_EPOCH * * 7.45 * - add FUSE_COPY_FILE_RANGE_64 * - add struct fuse_copy_file_range_out */ #ifndef _LINUX_FUSE_H #define _LINUX_FUSE_H #ifdef __KERNEL__ #include #else #include #endif /* * Version negotiation: * * Both the kernel and userspace send the version they support in the * INIT request and reply respectively. * * If the major versions match then both shall use the smallest * of the two minor versions for communication. * * If the kernel supports a larger major version, then userspace shall * reply with the major version it supports, ignore the rest of the * INIT message and expect a new INIT message from the kernel with a * matching major version. * * If the library supports a larger major version, then it shall fall * back to the major protocol version sent by the kernel for * communication and reply with that major version (and an arbitrary * supported minor version). */ /** Version number of this interface */ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ #define FUSE_KERNEL_MINOR_VERSION 45 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 /* Make sure all structures are padded to 64bit boundary, so 32bit userspace works under 64bit kernels */ struct fuse_attr { uint64_t ino; uint64_t size; uint64_t blocks; uint64_t atime; uint64_t mtime; uint64_t ctime; uint32_t atimensec; uint32_t mtimensec; uint32_t ctimensec; uint32_t mode; uint32_t nlink; uint32_t uid; uint32_t gid; uint32_t rdev; uint32_t blksize; uint32_t flags; }; /* * The following structures are bit-for-bit compatible with the statx(2) ABI in * Linux. */ struct fuse_sx_time { int64_t tv_sec; uint32_t tv_nsec; int32_t __reserved; }; struct fuse_statx { uint32_t mask; uint32_t blksize; uint64_t attributes; uint32_t nlink; uint32_t uid; uint32_t gid; uint16_t mode; uint16_t __spare0[1]; uint64_t ino; uint64_t size; uint64_t blocks; uint64_t attributes_mask; struct fuse_sx_time atime; struct fuse_sx_time btime; struct fuse_sx_time ctime; struct fuse_sx_time mtime; uint32_t rdev_major; uint32_t rdev_minor; uint32_t dev_major; uint32_t dev_minor; uint64_t __spare2[14]; }; struct fuse_kstatfs { uint64_t blocks; uint64_t bfree; uint64_t bavail; uint64_t files; uint64_t ffree; uint32_t bsize; uint32_t namelen; uint32_t frsize; uint32_t padding; uint32_t spare[6]; }; struct fuse_file_lock { uint64_t start; uint64_t end; uint32_t type; uint32_t pid; /* tgid */ }; /** * Bitmasks for fuse_setattr_in.valid */ #define FATTR_MODE (1 << 0) #define FATTR_UID (1 << 1) #define FATTR_GID (1 << 2) #define FATTR_SIZE (1 << 3) #define FATTR_ATIME (1 << 4) #define FATTR_MTIME (1 << 5) #define FATTR_FH (1 << 6) #define FATTR_ATIME_NOW (1 << 7) #define FATTR_MTIME_NOW (1 << 8) #define FATTR_LOCKOWNER (1 << 9) #define FATTR_CTIME (1 << 10) #define FATTR_KILL_SUIDGID (1 << 11) /** * Flags returned by the OPEN request * * FOPEN_DIRECT_IO: bypass page cache for this open file * FOPEN_KEEP_CACHE: don't invalidate the data cache on open * FOPEN_NONSEEKABLE: the file is not seekable * FOPEN_CACHE_DIR: allow caching this directory * FOPEN_STREAM: the file is stream-like (no file position at all) * 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 * FOPEN_PASSTHROUGH: passthrough read/write io for this open file */ #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) #define FOPEN_PASSTHROUGH (1 << 7) /** * INIT request/reply flags * * FUSE_ASYNC_READ: asynchronous read requests * FUSE_POSIX_LOCKS: remote locking for POSIX file locks * FUSE_FILE_OPS: kernel sends file handle for fstat, etc... (not yet supported) * FUSE_ATOMIC_O_TRUNC: handles the O_TRUNC open flag in the filesystem * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." * FUSE_BIG_WRITES: filesystem can handle write size larger than 4kB * FUSE_DONT_MASK: don't apply umask to file mode on create operations * FUSE_SPLICE_WRITE: kernel supports splice write on the device * FUSE_SPLICE_MOVE: kernel supports splice move on the device * FUSE_SPLICE_READ: kernel supports splice read on the device * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages * FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one) * FUSE_READDIRPLUS_AUTO: adaptive readdirplus * FUSE_ASYNC_DIO: asynchronous direct I/O submission * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir * FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc * FUSE_POSIX_ACL: filesystem supports posix acls * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages * FUSE_CACHE_SYMLINKS: cache READLINK responses * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request * FUSE_MAP_ALIGNMENT: 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) * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation * FUSE_DIRECT_IO_ALLOW_MMAP: allow shared mmap in FOPEN_DIRECT_IO mode. * FUSE_NO_EXPORT_SUPPORT: explicitly disable export support * FUSE_HAS_RESEND: kernel supports resending pending requests, and the high bit * of the request ID indicates resend requests * FUSE_ALLOW_IDMAP: allow creation of idmapped mounts * FUSE_OVER_IO_URING: Indicate that client supports io-uring * FUSE_REQUEST_TIMEOUT: kernel supports timing out requests. * init_out.request_timeout contains the timeout (in secs) */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) #define FUSE_FILE_OPS (1 << 2) #define FUSE_ATOMIC_O_TRUNC (1 << 3) #define FUSE_EXPORT_SUPPORT (1 << 4) #define FUSE_BIG_WRITES (1 << 5) #define FUSE_DONT_MASK (1 << 6) #define FUSE_SPLICE_WRITE (1 << 7) #define FUSE_SPLICE_MOVE (1 << 8) #define FUSE_SPLICE_READ (1 << 9) #define FUSE_FLOCK_LOCKS (1 << 10) #define FUSE_HAS_IOCTL_DIR (1 << 11) #define FUSE_AUTO_INVAL_DATA (1 << 12) #define FUSE_DO_READDIRPLUS (1 << 13) #define FUSE_READDIRPLUS_AUTO (1 << 14) #define FUSE_ASYNC_DIO (1 << 15) #define FUSE_WRITEBACK_CACHE (1 << 16) #define FUSE_NO_OPEN_SUPPORT (1 << 17) #define FUSE_PARALLEL_DIROPS (1 << 18) #define FUSE_HANDLE_KILLPRIV (1 << 19) #define FUSE_POSIX_ACL (1 << 20) #define FUSE_ABORT_ERROR (1 << 21) #define FUSE_MAX_PAGES (1 << 22) #define FUSE_CACHE_SYMLINKS (1 << 23) #define FUSE_NO_OPENDIR_SUPPORT (1 << 24) #define FUSE_EXPLICIT_INVAL_DATA (1 << 25) #define FUSE_MAP_ALIGNMENT (1 << 26) #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) #define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) #define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36) #define FUSE_PASSTHROUGH (1ULL << 37) #define FUSE_NO_EXPORT_SUPPORT (1ULL << 38) #define FUSE_HAS_RESEND (1ULL << 39) /* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */ #define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP #define FUSE_ALLOW_IDMAP (1ULL << 40) #define FUSE_OVER_IO_URING (1ULL << 41) #define FUSE_REQUEST_TIMEOUT (1ULL << 42) /** * CUSE INIT request/reply flags * * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl */ #define CUSE_UNRESTRICTED_IOCTL (1 << 0) /** * Release flags */ #define FUSE_RELEASE_FLUSH (1 << 0) #define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1) /** * Getattr flags */ #define FUSE_GETATTR_FH (1 << 0) /** * Lock flags */ #define FUSE_LK_FLOCK (1 << 0) /** * WRITE flags * * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed * FUSE_WRITE_LOCKOWNER: lock_owner field is valid * 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_SUIDGID (1 << 2) /* Obsolete alias; this flag implies killing suid/sgid only. */ #define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID /** * Read flags */ #define FUSE_READ_LOCKOWNER (1 << 1) /** * Ioctl flags * * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed * FUSE_IOCTL_RETRY: retry with new iovecs * FUSE_IOCTL_32BIT: 32bit ioctl * FUSE_IOCTL_DIR: is a directory * FUSE_IOCTL_COMPAT_X32: x32 compat ioctl on 64bit machine (64bit time_t) * * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs */ #define FUSE_IOCTL_COMPAT (1 << 0) #define FUSE_IOCTL_UNRESTRICTED (1 << 1) #define FUSE_IOCTL_RETRY (1 << 2) #define FUSE_IOCTL_32BIT (1 << 3) #define FUSE_IOCTL_DIR (1 << 4) #define FUSE_IOCTL_COMPAT_X32 (1 << 5) #define FUSE_IOCTL_MAX_IOV 256 /** * Poll flags * * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify */ #define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) /** * Fsync flags * * FUSE_FSYNC_FDATASYNC: Sync data only, not metadata */ #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 */ FUSE_GETATTR = 3, FUSE_SETATTR = 4, FUSE_READLINK = 5, FUSE_SYMLINK = 6, FUSE_MKNOD = 8, FUSE_MKDIR = 9, FUSE_UNLINK = 10, FUSE_RMDIR = 11, FUSE_RENAME = 12, FUSE_LINK = 13, FUSE_OPEN = 14, FUSE_READ = 15, FUSE_WRITE = 16, FUSE_STATFS = 17, FUSE_RELEASE = 18, FUSE_FSYNC = 20, FUSE_SETXATTR = 21, FUSE_GETXATTR = 22, FUSE_LISTXATTR = 23, FUSE_REMOVEXATTR = 24, FUSE_FLUSH = 25, FUSE_INIT = 26, FUSE_OPENDIR = 27, FUSE_READDIR = 28, FUSE_RELEASEDIR = 29, FUSE_FSYNCDIR = 30, FUSE_GETLK = 31, FUSE_SETLK = 32, FUSE_SETLKW = 33, FUSE_ACCESS = 34, FUSE_CREATE = 35, FUSE_INTERRUPT = 36, FUSE_BMAP = 37, FUSE_DESTROY = 38, FUSE_IOCTL = 39, FUSE_POLL = 40, FUSE_NOTIFY_REPLY = 41, FUSE_BATCH_FORGET = 42, FUSE_FALLOCATE = 43, FUSE_READDIRPLUS = 44, FUSE_RENAME2 = 45, FUSE_LSEEK = 46, FUSE_COPY_FILE_RANGE = 47, FUSE_SETUPMAPPING = 48, FUSE_REMOVEMAPPING = 49, FUSE_SYNCFS = 50, FUSE_TMPFILE = 51, FUSE_STATX = 52, FUSE_COPY_FILE_RANGE_64 = 53, /* CUSE specific operations */ CUSE_INIT = 4096, /* Reserved opcodes: helpful to detect structure endian-ness */ CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */ FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */ }; enum fuse_notify_code { FUSE_NOTIFY_POLL = 1, FUSE_NOTIFY_INVAL_INODE = 2, FUSE_NOTIFY_INVAL_ENTRY = 3, FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_DELETE = 6, FUSE_NOTIFY_RESEND = 7, FUSE_NOTIFY_INC_EPOCH = 8, FUSE_NOTIFY_CODE_MAX, }; /* The read buffer is required to be at least 8k, but may be much larger */ #define FUSE_MIN_READ_BUFFER 8192 #define FUSE_COMPAT_ENTRY_OUT_SIZE 120 struct fuse_entry_out { uint64_t nodeid; /* Inode ID */ uint64_t generation; /* Inode generation: nodeid:gen must be unique for the fs's lifetime */ uint64_t entry_valid; /* Cache timeout for the name */ uint64_t attr_valid; /* Cache timeout for the attributes */ uint32_t entry_valid_nsec; uint32_t attr_valid_nsec; struct fuse_attr attr; }; struct fuse_forget_in { uint64_t nlookup; }; struct fuse_forget_one { uint64_t nodeid; uint64_t nlookup; }; struct fuse_batch_forget_in { uint32_t count; uint32_t dummy; }; struct fuse_getattr_in { uint32_t getattr_flags; uint32_t dummy; uint64_t fh; }; #define FUSE_COMPAT_ATTR_OUT_SIZE 96 struct fuse_attr_out { uint64_t attr_valid; /* Cache timeout for the attributes */ uint32_t attr_valid_nsec; uint32_t dummy; struct fuse_attr attr; }; struct fuse_statx_in { uint32_t getattr_flags; uint32_t reserved; uint64_t fh; uint32_t sx_flags; uint32_t sx_mask; }; struct fuse_statx_out { uint64_t attr_valid; /* Cache timeout for the attributes */ uint32_t attr_valid_nsec; uint32_t flags; uint64_t spare[2]; struct fuse_statx stat; }; #define FUSE_COMPAT_MKNOD_IN_SIZE 8 struct fuse_mknod_in { uint32_t mode; uint32_t rdev; uint32_t umask; uint32_t padding; }; struct fuse_mkdir_in { uint32_t mode; uint32_t umask; }; struct fuse_rename_in { uint64_t newdir; }; struct fuse_rename2_in { uint64_t newdir; uint32_t flags; uint32_t padding; }; struct fuse_link_in { uint64_t oldnodeid; }; struct fuse_setattr_in { uint32_t valid; uint32_t padding; uint64_t fh; uint64_t size; uint64_t lock_owner; uint64_t atime; uint64_t mtime; uint64_t ctime; uint32_t atimensec; uint32_t mtimensec; uint32_t ctimensec; uint32_t mode; uint32_t unused4; uint32_t uid; uint32_t gid; uint32_t unused5; }; struct fuse_open_in { uint32_t flags; uint32_t open_flags; /* FUSE_OPEN_... */ }; struct fuse_create_in { uint32_t flags; uint32_t mode; uint32_t umask; uint32_t open_flags; /* FUSE_OPEN_... */ }; struct fuse_open_out { uint64_t fh; uint32_t open_flags; int32_t backing_id; }; struct fuse_release_in { uint64_t fh; uint32_t flags; uint32_t release_flags; uint64_t lock_owner; }; struct fuse_flush_in { uint64_t fh; uint32_t unused; uint32_t padding; uint64_t lock_owner; }; struct fuse_read_in { uint64_t fh; uint64_t offset; uint32_t size; uint32_t read_flags; uint64_t lock_owner; uint32_t flags; uint32_t padding; }; #define FUSE_COMPAT_WRITE_IN_SIZE 24 struct fuse_write_in { uint64_t fh; uint64_t offset; uint32_t size; uint32_t write_flags; uint64_t lock_owner; uint32_t flags; uint32_t padding; }; struct fuse_write_out { uint32_t size; uint32_t padding; }; #define FUSE_COMPAT_STATFS_SIZE 48 struct fuse_statfs_out { struct fuse_kstatfs st; }; struct fuse_fsync_in { uint64_t fh; uint32_t fsync_flags; 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 { uint32_t size; uint32_t padding; }; struct fuse_getxattr_out { uint32_t size; uint32_t padding; }; struct fuse_lk_in { uint64_t fh; uint64_t owner; struct fuse_file_lock lk; uint32_t lk_flags; uint32_t padding; }; struct fuse_lk_out { struct fuse_file_lock lk; }; struct fuse_access_in { uint32_t mask; uint32_t padding; }; struct fuse_init_in { uint32_t major; uint32_t minor; uint32_t max_readahead; uint32_t flags; uint32_t flags2; uint32_t unused[11]; }; #define FUSE_COMPAT_INIT_OUT_SIZE 8 #define FUSE_COMPAT_22_INIT_OUT_SIZE 24 struct fuse_init_out { uint32_t major; uint32_t minor; uint32_t max_readahead; uint32_t flags; uint16_t max_background; uint16_t congestion_threshold; uint32_t max_write; uint32_t time_gran; uint16_t max_pages; uint16_t map_alignment; uint32_t flags2; uint32_t max_stack_depth; uint16_t request_timeout; uint16_t unused[11]; }; #define CUSE_INIT_INFO_MAX 4096 struct cuse_init_in { uint32_t major; uint32_t minor; uint32_t unused; uint32_t flags; }; struct cuse_init_out { uint32_t major; uint32_t minor; uint32_t unused; uint32_t flags; uint32_t max_read; uint32_t max_write; uint32_t dev_major; /* chardev major */ uint32_t dev_minor; /* chardev minor */ uint32_t spare[10]; }; struct fuse_interrupt_in { uint64_t unique; }; struct fuse_bmap_in { uint64_t block; uint32_t blocksize; uint32_t padding; }; struct fuse_bmap_out { uint64_t block; }; struct fuse_ioctl_in { uint64_t fh; uint32_t flags; uint32_t cmd; uint64_t arg; uint32_t in_size; uint32_t out_size; }; struct fuse_ioctl_iovec { uint64_t base; uint64_t len; }; struct fuse_ioctl_out { int32_t result; uint32_t flags; uint32_t in_iovs; uint32_t out_iovs; }; struct fuse_poll_in { uint64_t fh; uint64_t kh; uint32_t flags; uint32_t events; }; struct fuse_poll_out { uint32_t revents; uint32_t padding; }; struct fuse_notify_poll_wakeup_out { uint64_t kh; }; struct fuse_fallocate_in { uint64_t fh; uint64_t offset; uint64_t length; uint32_t mode; uint32_t padding; }; /** * FUSE request unique ID flag * * Indicates whether this is a resend request. The receiver should handle this * request accordingly. */ #define FUSE_UNIQUE_RESEND (1ULL << 63) /** * This value will be set by the kernel to * (struct fuse_in_header).{uid,gid} fields in * case when: * - fuse daemon enabled FUSE_ALLOW_IDMAP * - idmapping information is not available and uid/gid * can not be mapped in accordance with an idmapping. * * Note: an idmapping information always available * for inode creation operations like: * FUSE_MKNOD, FUSE_SYMLINK, FUSE_MKDIR, FUSE_TMPFILE, * FUSE_CREATE and FUSE_RENAME2 (with RENAME_WHITEOUT). */ #define FUSE_INVALID_UIDGID ((uint32_t)(-1)) struct fuse_in_header { uint32_t len; uint32_t opcode; uint64_t unique; uint64_t nodeid; uint32_t uid; uint32_t gid; uint32_t pid; uint16_t total_extlen; /* length of extensions in 8byte units */ uint16_t padding; }; struct fuse_out_header { uint32_t len; int32_t error; uint64_t unique; }; struct fuse_dirent { uint64_t ino; uint64_t off; uint32_t namelen; uint32_t type; char name[]; }; /* 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) struct fuse_direntplus { struct fuse_entry_out entry_out; struct fuse_dirent dirent; }; #define FUSE_NAME_OFFSET_DIRENTPLUS \ offsetof(struct fuse_direntplus, dirent.name) #define FUSE_DIRENTPLUS_SIZE(d) \ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen) struct fuse_notify_inval_inode_out { uint64_t ino; int64_t off; int64_t len; }; struct fuse_notify_inval_entry_out { uint64_t parent; uint32_t namelen; uint32_t flags; }; struct fuse_notify_delete_out { uint64_t parent; uint64_t child; uint32_t namelen; uint32_t padding; }; struct fuse_notify_store_out { uint64_t nodeid; uint64_t offset; uint32_t size; uint32_t padding; }; struct fuse_notify_retrieve_out { uint64_t notify_unique; uint64_t nodeid; uint64_t offset; uint32_t size; uint32_t padding; }; /* Matches the size of fuse_write_in */ struct fuse_notify_retrieve_in { uint64_t dummy1; uint64_t offset; uint32_t size; uint32_t dummy2; uint64_t dummy3; uint64_t dummy4; }; struct fuse_backing_map { int32_t fd; uint32_t flags; uint64_t padding; }; /* Device ioctls: */ #define FUSE_DEV_IOC_MAGIC 229 #define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t) #define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \ struct fuse_backing_map) #define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t) struct fuse_lseek_in { uint64_t fh; uint64_t offset; uint32_t whence; uint32_t padding; }; struct fuse_lseek_out { uint64_t offset; }; struct fuse_copy_file_range_in { uint64_t fh_in; uint64_t off_in; uint64_t nodeid_out; uint64_t fh_out; uint64_t off_out; uint64_t len; uint64_t flags; }; /* For FUSE_COPY_FILE_RANGE_64 */ struct fuse_copy_file_range_out { uint64_t bytes_copied; }; #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[]; }; /** * Size of the ring buffer header */ #define FUSE_URING_IN_OUT_HEADER_SZ 128 #define FUSE_URING_OP_IN_OUT_SZ 128 /* Used as part of the fuse_uring_req_header */ struct fuse_uring_ent_in_out { uint64_t flags; /* * commit ID to be used in a reply to a ring request (see also * struct fuse_uring_cmd_req) */ uint64_t commit_id; /* size of user payload buffer */ uint32_t payload_sz; uint32_t padding; uint64_t reserved; }; /** * Header for all fuse-io-uring requests */ struct fuse_uring_req_header { /* struct fuse_in_header / struct fuse_out_header */ char in_out[FUSE_URING_IN_OUT_HEADER_SZ]; /* per op code header */ char op_in[FUSE_URING_OP_IN_OUT_SZ]; struct fuse_uring_ent_in_out ring_ent_in_out; }; /** * sqe commands to the kernel */ enum fuse_uring_cmd { FUSE_IO_URING_CMD_INVALID = 0, /* register the request buffer and fetch a fuse request */ FUSE_IO_URING_CMD_REGISTER = 1, /* commit fuse request result and fetch next request */ FUSE_IO_URING_CMD_COMMIT_AND_FETCH = 2, }; /** * In the 80B command area of the SQE. */ struct fuse_uring_cmd_req { uint64_t flags; /* entry identifier for commits */ uint64_t commit_id; /* queue the command is for (queue index) */ uint16_t qid; uint8_t padding[6]; }; #endif /* _LINUX_FUSE_H */ fuse-3.18.2/include/fuse_log.h0000644000175000017500000000435115156613252015125 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2019 Red Hat, Inc. This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ #ifndef FUSE_LOG_H_ #define FUSE_LOG_H_ /** @file * * This file defines the logging interface of FUSE */ #include #ifdef __cplusplus extern "C" { #endif /** * Log severity level * * These levels correspond to syslog(2) log levels since they are widely used. */ enum fuse_log_level { FUSE_LOG_EMERG, FUSE_LOG_ALERT, FUSE_LOG_CRIT, FUSE_LOG_ERR, FUSE_LOG_WARNING, FUSE_LOG_NOTICE, FUSE_LOG_INFO, FUSE_LOG_DEBUG }; /** * Log message handler function. * * This function must be thread-safe. It may be called from any libfuse * function, including fuse_parse_cmdline() and other functions invoked before * a FUSE filesystem is created. * * Install a custom log message handler function using fuse_set_log_func(). * * @param level log severity level * @param fmt sprintf-style format string including newline * @param ap format string arguments */ typedef void (*fuse_log_func_t)(enum fuse_log_level level, const char *fmt, va_list ap); /** * Install a custom log handler function. * * Log messages are emitted by libfuse functions to report errors and debug * information. Messages are printed to stderr by default but this can be * overridden by installing a custom log message handler function. * * The log message handler function is global and affects all FUSE filesystems * created within this process. * * @param func a custom log message handler function or NULL to revert to * the default */ void fuse_set_log_func(fuse_log_func_t func); /** * Emit a log message * * @param level severity level (FUSE_LOG_ERR, FUSE_LOG_DEBUG, etc) * @param fmt sprintf-style format string including newline */ void fuse_log(enum fuse_log_level level, const char *fmt, ...) __attribute__((format(printf, 2, 3))); /** * Switch default log handler from stderr to syslog * * Passed options are according to 'man 3 openlog' */ void fuse_log_enable_syslog(const char *ident, int option, int facility); /** * To be called at teardown to close syslog. */ void fuse_log_close_syslog(void); #ifdef __cplusplus } #endif #endif /* FUSE_LOG_H_ */ fuse-3.18.2/include/fuse_lowlevel.h0000644000175000017500000023206115156613252016176 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ #ifndef FUSE_LOWLEVEL_H_ #define FUSE_LOWLEVEL_H_ /** @file * * Low level API * * IMPORTANT: you should define FUSE_USE_VERSION before including this * header. To use the newest API define it to 35 (recommended for any * new application). */ #ifndef FUSE_USE_VERSION #error FUSE_USE_VERSION not defined #endif #include "fuse_common.h" #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* ----------------------------------------------------------- * * Miscellaneous definitions * * ----------------------------------------------------------- */ /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 /** Inode number type */ typedef uint64_t fuse_ino_t; /** Request pointer type */ typedef struct fuse_req *fuse_req_t; /* Forward declaration */ struct statx; /** * Session * * This provides hooks for processing requests, and exiting */ struct fuse_session; /** Directory entry parameters supplied to fuse_reply_entry() */ struct fuse_entry_param { /** Unique inode number * * In lookup, zero means negative entry (from version 2.5) * Returning ENOENT also means negative entry, but by setting zero * ino the kernel may cache negative entries for entry_timeout * seconds. */ fuse_ino_t ino; /** Generation number for this entry. * * If the file system will be exported over NFS, the * ino/generation pairs need to be unique over the file * system's lifetime (rather than just the mount time). So if * the file system reuses an inode after it has been deleted, * it must assign a new, previously unused generation number * to the inode at the same time. * */ uint64_t generation; /** Inode attributes. * * Even if attr_timeout == 0, attr must be correct. For example, * for open(), FUSE uses attr.st_size from lookup() to determine * how many bytes to request. If this value is not correct, * incorrect data will be returned. */ struct stat attr; /** Validity timeout (in seconds) for inode attributes. If attributes only change as a result of requests that come through the kernel, this should be set to a very large value. */ double attr_timeout; /** Validity timeout (in seconds) for the name. If directory entries are changed/deleted only as a result of requests that come through the kernel, this should be set to a very large value. */ double entry_timeout; }; /** * Additional context associated with requests. * * Note that the reported client uid, gid and pid may be zero in some * situations. For example, if the FUSE file system is running in a * PID or user namespace but then accessed from outside the namespace, * there is no valid uid/pid/gid that could be reported. */ struct fuse_ctx { /** User ID of the calling process */ uid_t uid; /** Group ID of the calling process */ gid_t gid; /** Thread ID of the calling process */ pid_t pid; /** Umask of the calling process */ mode_t umask; }; struct fuse_forget_data { fuse_ino_t ino; 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); int (*clone_fd)(int master_fd); }; /** * 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) #define FUSE_SET_ATTR_GID (1 << 2) #define FUSE_SET_ATTR_SIZE (1 << 3) #define FUSE_SET_ATTR_ATIME (1 << 4) #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 * * ----------------------------------------------------------- */ /** * Low level filesystem operations * * Most of the methods (with the exception of init and destroy) * receive a request handle (fuse_req_t) as their first argument. * This handle must be passed to one of the specified reply functions. * * This may be done inside the method invocation, or after the call * has returned. The request handle is valid until one of the reply * functions is called. * * Other pointer arguments (name, fuse_file_info, etc) are not valid * after the call has returned, so if they are needed later, their * contents have to be copied. * * In general, all methods are expected to perform any necessary * permission checking. However, a filesystem may delegate this task * to the kernel by passing the `default_permissions` mount option to * `fuse_session_new()`. In this case, methods will only be called if * the kernel's permission check has succeeded. * * It is generally not really necessary to check the fuse_reply_* return * values for errors, as any error in sending a reply indicates an * unrecoverable problem with the kernel fuse connection, which will also * terminate the session loop anyway. * * This data structure is ABI sensitive, on adding new functions these need to * be appended at the end of the struct */ struct fuse_lowlevel_ops { /** * Initialize filesystem * * This function is called when libfuse establishes * communication with the FUSE kernel module. The file system * should use this module to inspect and/or modify the * connection parameters provided in the `conn` structure. * * Note that some parameters may be overwritten by options * passed to fuse_session_new() which take precedence over the * values set in this handler. * * There's no reply to this function * * @param userdata the user data passed to fuse_session_new() */ void (*init) (void *userdata, struct fuse_conn_info *conn); /** * Clean up filesystem. * * Called on filesystem exit. When this method is called, the * connection to the kernel may be gone already, so that eg. calls * to fuse_lowlevel_notify_* will fail. * * There's no reply to this function * * @param userdata the user data passed to fuse_session_new() */ void (*destroy) (void *userdata); /** * Look up a directory entry by name and get its attributes. * * Valid replies: * fuse_reply_entry * fuse_reply_err * * @param req request handle * @param parent inode number of the parent directory * @param name the name to look up */ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); /** * Forget about an inode * * This function is called when the kernel removes an inode * from its internal caches. * * The inode's lookup count increases by one for every call to * fuse_reply_entry and fuse_reply_create. The nlookup parameter * indicates by how much the lookup count should be decreased. * * Inodes with a non-zero lookup count may receive request from * the kernel even after calls to unlink, rmdir or (when * overwriting an existing file) rename. Filesystems must handle * such requests properly and it is recommended to defer removal * of the inode until the lookup count reaches zero. Calls to * unlink, rmdir or rename will be followed closely by forget * unless the file or directory is open, in which case the * kernel issues forget only after the release or releasedir * calls. * * Note that if a file system will be exported over NFS the * inodes lifetime must extend even beyond forget. See the * generation field in struct fuse_entry_param above. * * On unmount the lookup count for all inodes implicitly drops * to zero. It is not guaranteed that the file system will * receive corresponding forget messages for the affected * inodes. * * Valid replies: * fuse_reply_none * * @param req request handle * @param ino the inode number * @param nlookup the number of lookups to forget */ void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); /** * Get file attributes. * * If writeback caching is enabled, the kernel may have a * better idea of a file's length than the FUSE file system * (eg if there has been a write that extended the file size, * but that has not yet been passed to the filesystem. * * In this case, the st_size value provided by the file system * will be ignored. * * Valid replies: * fuse_reply_attr * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information, or NULL */ void (*getattr) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** * Set file attributes * * In the 'attr' argument only members indicated by the 'to_set' * bitmask contain valid values. Other members contain undefined * values. * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * 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 * if the open method didn't set any value. Otherwise (not * ftruncate call, or kernel version earlier than 2.6.15) the fi * parameter will be NULL. * * Valid replies: * fuse_reply_attr * fuse_reply_err * * @param req request handle * @param ino the inode number * @param attr the attributes * @param to_set bit mask of attributes which should be set * @param fi file information, or NULL */ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi); /** * Read symbolic link * * Valid replies: * fuse_reply_readlink * fuse_reply_err * * @param req request handle * @param ino the inode number */ void (*readlink) (fuse_req_t req, fuse_ino_t ino); /** * Create file node * * Create a regular file, character device, block device, fifo or * socket node. * * Valid replies: * fuse_reply_entry * fuse_reply_err * * @param req request handle * @param parent inode number of the parent directory * @param name to create * @param mode file type and mode with which to create the new file * @param rdev the device number (only valid if created file is a device) */ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev); /** * Create a directory * * Valid replies: * fuse_reply_entry * fuse_reply_err * * @param req request handle * @param parent inode number of the parent directory * @param name to create * @param mode with which to create the new file */ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode); /** * Remove a file * * If the file's inode's lookup count is non-zero, the file * system is expected to postpone any removal of the inode * until the lookup count reaches zero (see description of the * forget function). * * Valid replies: * fuse_reply_err * * @param req request handle * @param parent inode number of the parent directory * @param name to remove */ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); /** * Remove a directory * * If the directory's inode's lookup count is non-zero, the * file system is expected to postpone any removal of the * inode until the lookup count reaches zero (see description * of the forget function). * * Valid replies: * fuse_reply_err * * @param req request handle * @param parent inode number of the parent directory * @param name to remove */ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); /** * Create a symbolic link * * Valid replies: * fuse_reply_entry * fuse_reply_err * * @param req request handle * @param link the contents of the symbolic link * @param parent inode number of the parent directory * @param name to create */ void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, const char *name); /** Rename a file * * If the target exists it should be atomically replaced. If * the target's inode's lookup count is non-zero, the file * system is expected to postpone any removal of the inode * until the lookup count reaches zero (see description of the * forget function). * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent failure with error code EINVAL, i.e. all * future rename requests will fail with EINVAL without being * send to the filesystem process. * * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If * RENAME_NOREPLACE is specified, the filesystem must not * overwrite *newname* if it exists and return an error * instead. If `RENAME_EXCHANGE` is specified, the filesystem * must atomically exchange the two files, i.e. both must * exist and neither may be deleted. * * Valid replies: * fuse_reply_err * * @param req request handle * @param parent inode number of the old parent directory * @param name old name * @param newparent inode number of the new parent directory * @param newname new name */ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags); /** * Create a hard link * * Valid replies: * fuse_reply_entry * fuse_reply_err * * @param req request handle * @param ino the old inode number * @param newparent inode number of the new parent directory * @param newname new name to create */ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname); /** * Open a file * * Open flags are available in fi->flags. The following rules * apply. * * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be * filtered out / handled by the kernel. * * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used * by the filesystem to check if the operation is * permitted. If the ``-o default_permissions`` mount * option is given, this check is already done by the * kernel before calling open() and may thus be omitted by * the filesystem. * * - When writeback caching is enabled, the kernel may send * read requests even for files opened with O_WRONLY. The * filesystem should be prepared to handle this. * * - When writeback caching is disabled, the filesystem is * expected to properly handle the O_APPEND flag and ensure * that each write is appending to the end of the file. * * - When writeback caching is enabled, the kernel will * handle O_APPEND. However, unless all changes to the file * come through the kernel this will not work reliably. The * filesystem should thus either ignore the O_APPEND flag * (and let the kernel handle it), or return an error * (indicating that reliably O_APPEND is not available). * * Filesystem may store an arbitrary file handle (pointer, * index, etc) in fi->fh, and use this in other all other file * operations (read, write, flush, release, fsync). * * Filesystem may also implement stateless file I/O and not store * anything in fi->fh. * * There are also some flags (direct_io, keep_cache) which the * filesystem may set in fi, to change the way the file is opened. * See fuse_file_info structure in for more details. * * If this request is answered with an error code of ENOSYS * and FUSE_CAP_NO_OPEN_SUPPORT is set in * `fuse_conn_info.capable`, this is treated as success and * future calls to open and release will also succeed without being * sent to the filesystem process. * * To get this behavior without providing an opendir handler, you may * set FUSE_CAP_NO_OPEN_SUPPORT in `fuse_conn_info.want` on supported * kernels to automatically get the zero message open(). * * If this callback is not provided and FUSE_CAP_NO_OPEN_SUPPORT is not * set in `fuse_conn_info.want` then an empty reply will be sent. * * Valid replies: * fuse_reply_open * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information */ void (*open) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** * Read data * * Read should send exactly the number of bytes requested except * on EOF or error, otherwise the rest of the data will be * substituted with zeroes. An exception to this is when the file * has been opened in 'direct_io' mode, in which case the return * value of the read system call will reflect the return value of * this operation. * * fi->fh will contain the value set by the open method, or will * be undefined if the open method didn't set any value. * * Valid replies: * fuse_reply_buf * fuse_reply_iov * fuse_reply_data * fuse_reply_err * * @param req request handle * @param ino the inode number * @param size number of bytes to read * @param off offset to read from * @param fi file information */ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); /** * Write data * * Write should return exactly the number of bytes requested * except on error. An exception to this is when the file has * been opened in 'direct_io' mode, in which case the return value * of the write system call will reflect the return value of this * operation. * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * expected to reset the setuid and setgid bits. * * fi->fh will contain the value set by the open method, or will * be undefined if the open method didn't set any value. * * Valid replies: * fuse_reply_write * fuse_reply_err * * @param req request handle * @param ino the inode number * @param buf data to write * @param size number of bytes to write * @param off offset to write to * @param fi file information */ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi); /** * Flush method * * This is called on each close() of the opened file. * * Since file descriptors can be duplicated (dup, dup2, fork), for * one open call there may be many flush calls. * * Filesystems shouldn't assume that flush will always be called * after some writes, or that if will be called at all. * * fi->fh will contain the value set by the open method, or will * be undefined if the open method didn't set any value. * * NOTE: the name of the method is misleading, since (unlike * fsync) the filesystem is not forced to flush pending writes. * One reason to flush data is if the filesystem wants to return * write errors during close. However, such use is non-portable * because POSIX does not require [close] to wait for delayed I/O to * complete. * * If the filesystem supports file locking operations (setlk, * getlk) it should remove all locks belonging to 'fi->owner'. * * If this request is answered with an error code of ENOSYS, * this is treated as success and future calls to flush() will * succeed automatically without being send to the filesystem * process. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information * * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ void (*flush) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** * Release an open file * * Release is called when there are no more references to an open * file: all file descriptors are closed and all memory mappings * are unmapped. * * For every open call there will be exactly one release call (unless * the filesystem is force-unmounted). * * The filesystem may reply with an error, but error values are * not returned to close() or munmap() which triggered the * release. * * fi->fh will contain the value set by the open method, or will * be undefined if the open method didn't set any value. * fi->flags will contain the same flags as for open. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information */ void (*release) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** * Synchronize file contents * * If the datasync parameter is non-zero, then only the user data * should be flushed, not the meta data. * * If this request is answered with an error code of ENOSYS, * this is treated as success and future calls to fsync() will * succeed automatically without being send to the filesystem * process. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param datasync flag indicating if only data should be flushed * @param fi file information */ void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi); /** * Open a directory * * Filesystem may store an arbitrary file handle (pointer, index, * etc) in fi->fh, and use this in other all other directory * stream operations (readdir, releasedir, fsyncdir). * * If this request is answered with an error code of ENOSYS and * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`, * this is treated as success and future calls to opendir and * releasedir will also succeed without being sent to the filesystem * process. In addition, the kernel will cache readdir results * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR. * * To get this behavior without providing an opendir handler, you may * set FUSE_CAP_NO_OPENDIR_SUPPORT in `fuse_conn_info.want` on supported * kernels to automatically get the zero message opendir(). * * If this callback is not provided and FUSE_CAP_NO_OPENDIR_SUPPORT is * not set in `fuse_conn_info.want` then an empty reply will be sent. * * Valid replies: * fuse_reply_open * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information */ void (*opendir) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** * Read directory * * Send a buffer filled using fuse_add_direntry(), with size not * exceeding the requested size. Send an empty buffer on end of * stream. * * fi->fh will contain the value set by the opendir method, or * will be undefined if the opendir method didn't set any value. * * Returning a directory entry from readdir() does not affect * its lookup count. * * If off_t is non-zero, then it will correspond to one of the off_t * values that was previously returned by readdir() for the same * directory handle. In this case, readdir() should skip over entries * coming before the position defined by the off_t value. If entries * are added or removed while the directory handle is open, the filesystem * may still include the entries that have been removed, and may not * report the entries that have been created. However, addition or * removal of entries must never cause readdir() to skip over unrelated * entries or to report them more than once. This means * that off_t can not be a simple index that enumerates the entries * that have been returned but must contain sufficient information to * uniquely determine the next directory entry to return even when the * set of entries is changing. * * The function does not have to report the '.' and '..' * entries, but is allowed to do so. Note that, if readdir does * not return '.' or '..', they will not be implicitly returned, * and this behavior is observable by the caller. * * Valid replies: * fuse_reply_buf * fuse_reply_data * fuse_reply_err * * @param req request handle * @param ino the inode number * @param size maximum number of bytes to send * @param off offset to continue reading the directory stream * @param fi file information */ void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); /** * Release an open directory * * For every opendir call there will be exactly one releasedir * call (unless the filesystem is force-unmounted). * * fi->fh will contain the value set by the opendir method, or * will be undefined if the opendir method didn't set any value. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information */ void (*releasedir) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** * Synchronize directory contents * * If the datasync parameter is non-zero, then only the directory * contents should be flushed, not the meta data. * * fi->fh will contain the value set by the opendir method, or * will be undefined if the opendir method didn't set any value. * * If this request is answered with an error code of ENOSYS, * this is treated as success and future calls to fsyncdir() will * succeed automatically without being send to the filesystem * process. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param datasync flag indicating if only data should be flushed * @param fi file information */ void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi); /** * Get file system statistics * * Valid replies: * fuse_reply_statfs * fuse_reply_err * * @param req request handle * @param ino the inode number, zero means "undefined" */ void (*statfs) (fuse_req_t req, fuse_ino_t ino); /** * Set an extended attribute * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent failure with error code EOPNOTSUPP, i.e. all * future setxattr() requests will fail with EOPNOTSUPP without being * send to the filesystem process. * * Valid replies: * fuse_reply_err */ void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags); /** * Get an extended attribute * * If size is zero, the size of the value should be sent with * fuse_reply_xattr. * * If the size is non-zero, and the value fits in the buffer, the * value should be sent with fuse_reply_buf. * * If the size is too small for the value, the ERANGE error should * be sent. * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent failure with error code EOPNOTSUPP, i.e. all * future getxattr() requests will fail with EOPNOTSUPP without being * send to the filesystem process. * * Valid replies: * fuse_reply_buf * fuse_reply_data * fuse_reply_xattr * fuse_reply_err * * @param req request handle * @param ino the inode number * @param name of the extended attribute * @param size maximum size of the value to send */ void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size); /** * List extended attribute names * * If size is zero, the total size of the attribute list should be * sent with fuse_reply_xattr. * * If the size is non-zero, and the null character separated * attribute list fits in the buffer, the list should be sent with * fuse_reply_buf. * * If the size is too small for the list, the ERANGE error should * be sent. * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent failure with error code EOPNOTSUPP, i.e. all * future listxattr() requests will fail with EOPNOTSUPP without being * send to the filesystem process. * * Valid replies: * fuse_reply_buf * fuse_reply_data * fuse_reply_xattr * fuse_reply_err * * @param req request handle * @param ino the inode number * @param size maximum size of the list to send */ void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); /** * Remove an extended attribute * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent failure with error code EOPNOTSUPP, i.e. all * future removexattr() requests will fail with EOPNOTSUPP without being * send to the filesystem process. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param name of the extended attribute */ void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); /** * Check file access permissions * * This will be called for the access() and chdir() system * calls. If the 'default_permissions' mount option is given, * this method is not called. * * This method is not called under Linux kernel versions 2.4.x * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent success, i.e. this and all future access() * requests will succeed without being send to the filesystem process. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param mask requested access mode */ void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); /** * Create and open a file * * If the file does not exist, first create it with the specified * mode, and then open it. * * See the description of the open handler for more * information. * * If this method is not implemented or under Linux kernel * versions earlier than 2.6.15, the mknod() and open() methods * will be called instead. * * If this request is answered with an error code of ENOSYS, the handler * is treated as not implemented (i.e., for this and future requests the * mknod() and open() handlers will be called instead). * * Valid replies: * fuse_reply_create * fuse_reply_err * * @param req request handle * @param parent inode number of the parent directory * @param name to create * @param mode file type and mode with which to create the new file * @param fi file information */ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi); /** * Test for a POSIX file lock * * Valid replies: * fuse_reply_lock * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information * @param lock the region/type to test */ void (*getlk) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock); /** * Acquire, modify or release a POSIX file lock * * For POSIX threads (NPTL) there's a 1-1 relation between pid and * owner, but otherwise this is not always the case. For checking * lock ownership, 'fi->owner' must be used. The l_pid field in * 'struct flock' should only be used to fill in this field in * getlk(). * * Note: if the locking methods are not implemented, the kernel * will still allow file locking to work locally. Hence these are * only interesting for network filesystems and similar. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information * @param lock the region/type to set * @param sleep locking operation may sleep */ void (*setlk) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep); /** * Map block index within file to block index within device * * Note: This makes sense only for block device backed filesystems * mounted with the 'blkdev' option * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent failure, i.e. all future bmap() requests will * fail with the same error code without being send to the filesystem * process. * * Valid replies: * fuse_reply_bmap * fuse_reply_err * * @param req request handle * @param ino the inode number * @param blocksize unit of block index * @param idx block index within file */ void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx); #if FUSE_USE_VERSION < 35 void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz); #else /** * Ioctl * * Note: For unrestricted ioctls (not allowed for FUSE * servers), data in and out areas can be discovered by giving * iovs and setting FUSE_IOCTL_RETRY in *flags*. For * restricted ioctls, kernel prepares in/out data area * according to the information encoded in cmd. * * Valid replies: * fuse_reply_ioctl_retry * fuse_reply_ioctl * fuse_reply_ioctl_iov * fuse_reply_err * * @param req request handle * @param ino the inode number * @param cmd ioctl command * @param arg ioctl argument * @param fi file information * @param flags for FUSE_IOCTL_* flags * @param in_buf data fetched from the caller * @param in_bufsz number of fetched bytes * @param out_bufsz maximum size of output data * * Note : the unsigned long request submitted by the application * is truncated to 32 bits. */ void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz); #endif /** * Poll for IO readiness * * The client should immediately respond with fuse_reply_poll(), * setting revents appropriately according to which events are ready. * * Additionally, if ph is non-NULL, the client must retain it and * notify when all future IO readiness events occur by calling * fuse_lowlevel_notify_poll() with the specified ph. * * Regardless of the number of times poll with a non-NULL ph is * received, a single notify_poll is enough to service all. (Notifying * more times incurs overhead but doesn't harm correctness.) Any * additional received handles can be immediately destroyed. * * The callee is responsible for destroying ph with * fuse_pollhandle_destroy() when no longer in use. * * If this request is answered with an error code of ENOSYS, this is * treated as success (with a kernel-defined default poll-mask) and * future calls to poll() will succeed the same way without being send * to the filesystem process. * * Valid replies: * fuse_reply_poll * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information * @param ph poll handle to be used for notification */ void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph); /** * Write data made available in a buffer * * This is a more generic version of the ->write() method. If * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the * kernel supports splicing from the fuse device, then the * data will be made available in pipe for supporting zero * copy data transfer. * * buf->count is guaranteed to be one (and thus buf->idx is * always zero). The write_buf handler must ensure that * bufv->off is correctly updated (reflecting the number of * bytes read from bufv->buf[0]). * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * expected to reset the setuid and setgid bits. * * Valid replies: * fuse_reply_write * fuse_reply_err * * @param req request handle * @param ino the inode number * @param bufv buffer containing the data * @param off offset to write to * @param fi file information */ void (*write_buf) (fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, off_t off, struct fuse_file_info *fi); /** * Callback function for the retrieve request * * Valid replies: * fuse_reply_none * * @param req request handle * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() * @param bufv the buffer containing the returned data */ void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv); /** * Forget about multiple inodes * * See description of the forget function for more * information. * * Valid replies: * fuse_reply_none * * @param req request handle */ void (*forget_multi) (fuse_req_t req, size_t count, struct fuse_forget_data *forgets); /** * Acquire, modify or release a BSD file lock * * Note: if the locking methods are not implemented, the kernel * will still allow file locking to work locally. Hence these are * only interesting for network filesystems and similar. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param fi file information * @param op the locking operation, see flock(2) */ void (*flock) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op); /** * Allocate requested space. If this function returns success then * subsequent writes to the specified range shall not fail due to the lack * of free space on the file system storage media. * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent failure with error code EOPNOTSUPP, i.e. all * future fallocate() requests will fail with EOPNOTSUPP without being * send to the filesystem process. * * Valid replies: * fuse_reply_err * * @param req request handle * @param ino the inode number * @param offset starting point for allocated region * @param length size of allocated region * @param mode determines the operation to be performed on the given range, * see fallocate(2) */ void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi); /** * Read directory with attributes * * Send a buffer filled using fuse_add_direntry_plus(), with size not * exceeding the requested size. Send an empty buffer on end of * stream. * * fi->fh will contain the value set by the opendir method, or * will be undefined if the opendir method didn't set any value. * * In contrast to readdir() (which does not affect the lookup counts), * the lookup count of every entry returned by readdirplus(), except "." * and "..", is incremented by one. * * Valid replies: * fuse_reply_buf * fuse_reply_data * fuse_reply_err * * @param req request handle * @param ino the inode number * @param size maximum number of bytes to send * @param off offset to continue reading the directory stream * @param fi file information */ void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); /** * Copy a range of data from one file to another * * Performs an optimized copy between two file descriptors without the * additional cost of transferring data through the FUSE kernel module * to user space (glibc) and then back into the FUSE filesystem again. * * In case this method is not implemented, glibc falls back to reading * data from the source and writing to the destination. Effectively * doing an inefficient copy of the data. * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent failure with error code EOPNOTSUPP, i.e. all * future copy_file_range() requests will fail with EOPNOTSUPP without * being send to the filesystem process. * * Valid replies: * fuse_reply_write * fuse_reply_err * * @param req request handle * @param ino_in the inode number or the source file * @param off_in starting point from were the data should be read * @param fi_in file information of the source file * @param ino_out the inode number or the destination file * @param off_out starting point where the data should be written * @param fi_out file information of the destination file * @param len maximum size of the data to copy * @param flags passed along with the copy_file_range() syscall */ void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in, off_t off_in, struct fuse_file_info *fi_in, fuse_ino_t ino_out, off_t off_out, struct fuse_file_info *fi_out, size_t len, int flags); /** * Find next data or hole after the specified offset * * If this request is answered with an error code of ENOSYS, this is * treated as a permanent failure, i.e. all future lseek() requests will * fail with the same error code without being send to the filesystem * process. * * Valid replies: * fuse_reply_lseek * fuse_reply_err * * @param req request handle * @param ino the inode number * @param off offset to start search from * @param whence either SEEK_DATA or SEEK_HOLE * @param fi file information */ void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi); /** * Create a tempfile * * Tempfile means an anonymous file. It can be made into a normal file later * by using linkat or such. * * If this is answered with an error ENOSYS this is treated by the kernel as * a permanent failure and it will disable the feature and not ask again. * * Valid replies: * fuse_reply_create * fuse_reply_err * * @param req request handle * @param parent inode number of the parent directory * @param mode file type and mode with which to create the new file * @param fi file information */ void (*tmpfile) (fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info *fi); /** * Get extended file attributes. * * Valid replies: * fuse_reply_statx * fuse_reply_err * * @param req request handle * @param ino the inode number * @param flags bitmask of requested flags * @param mask bitmask of requested fields * @param fi file information (may be NULL) */ void (*statx)(fuse_req_t req, fuse_ino_t ino, int flags, int mask, struct fuse_file_info *fi); }; /** * Reply with an error code or success. * * Possible requests: * all except forget, forget_multi, retrieve_reply * * Wherever possible, error codes should be chosen from the list of * documented error conditions in the corresponding system calls * manpage. * * An error code of ENOSYS is sometimes treated specially. This is * indicated in the documentation of the affected handler functions. * * The following requests may be answered with a zero error code: * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, * removexattr, setlk. * * @param req request handle * @param err the positive error value, or zero for success * @return zero for success, -errno for failure to send reply */ int fuse_reply_err(fuse_req_t req, int err); /** * Don't send reply * * Possible requests: * forget * forget_multi * retrieve_reply * * @param req request handle */ void fuse_reply_none(fuse_req_t req); /** * Reply with a directory entry * * Possible requests: * lookup, mknod, mkdir, symlink, link * * Side effects: * increments the lookup count on success * * @param req request handle * @param e the entry parameters * @return zero for success, -errno for failure to send reply */ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); /** * Reply with a directory entry and open parameters * * currently the following members of 'fi' are used: * fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, * parallel_direct_writes * * Possible requests: * create * * Side effects: * increments the lookup count on success * * @param req request handle * @param e the entry parameters * @param fi file information * @return zero for success, -errno for failure to send reply */ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi); /** * Reply with attributes * * Possible requests: * getattr, setattr * * @param req request handle * @param attr the attributes * @param attr_timeout validity timeout (in seconds) for the attributes * @return zero for success, -errno for failure to send reply */ int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout); /** * Reply with the contents of a symbolic link * * Possible requests: * readlink * * @param req request handle * @param link symbolic link contents * @return zero for success, -errno for failure to send reply */ int fuse_reply_readlink(fuse_req_t req, const char *link); /** * Setup passthrough backing file for open reply * * Currently there should be only one backing id per node / backing file. * * Possible requests: * open, opendir, create * * @param req request handle * @param fd backing file descriptor * @return positive backing id for success, 0 for failure */ int fuse_passthrough_open(fuse_req_t req, int fd); int fuse_passthrough_close(fuse_req_t req, int backing_id); /** * Reply with open parameters * * currently the following members of 'fi' are used: * fh, direct_io, keep_cache, cache_readdir, nonseekable, noflush, * parallel_direct_writes, * * Possible requests: * open, opendir * * @param req request handle * @param fi file information * @return zero for success, -errno for failure to send reply */ int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi); /** * Reply with number of bytes written * * Possible requests: * write * * @param req request handle * @param count the number of bytes written * @return zero for success, -errno for failure to send reply */ int fuse_reply_write(fuse_req_t req, size_t count); /** * Reply with data * * Possible requests: * read, readdir, getxattr, listxattr * * @param req request handle * @param buf buffer containing data * @param size the size of data in bytes * @return zero for success, -errno for failure to send reply */ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); /** * Reply with data copied/moved from buffer(s) * * Zero copy data transfer ("splicing") will be used under * the following circumstances: * * 1. FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and * 2. the kernel supports splicing from the fuse device * (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and * 3. *flags* does not contain FUSE_BUF_NO_SPLICE * 4. The amount of data that is provided in file-descriptor backed * buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) * is at least twice the page size. * * In order for SPLICE_F_MOVE to be used, the following additional * conditions have to be fulfilled: * * 1. FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and * 2. the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.capable), and * 3. *flags* contains FUSE_BUF_SPLICE_MOVE * * Note that, if splice is used, the data is actually spliced twice: * once into a temporary pipe (to prepend header data), and then again * into the kernel. If some of the provided buffers are memory-backed, * the data in them is copied in step one and spliced in step two. * * The FUSE_BUF_SPLICE_FORCE_SPLICE and FUSE_BUF_SPLICE_NONBLOCK flags * are silently ignored. * * Possible requests: * read, readdir, getxattr, listxattr * * Side effects: * when used to return data from a readdirplus() (but not readdir()) * call, increments the lookup count of each returned entry by one * on success. * * @param req request handle * @param bufv buffer vector * @param flags flags controlling the copy * @return zero for success, -errno for failure to send reply */ int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags); /** * Reply with data vector * * Possible requests: * read, readdir, getxattr, listxattr * * @param req request handle * @param iov the vector containing the data * @param count the size of vector * @return zero for success, -errno for failure to send reply */ int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count); /** * Reply with filesystem statistics * * Possible requests: * statfs * * @param req request handle * @param stbuf filesystem statistics * @return zero for success, -errno for failure to send reply */ int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf); /** * Reply with needed buffer size * * Possible requests: * getxattr, listxattr * * @param req request handle * @param count the buffer size needed in bytes * @return zero for success, -errno for failure to send reply */ int fuse_reply_xattr(fuse_req_t req, size_t count); /** * Reply with file lock information * * Possible requests: * getlk * * @param req request handle * @param lock the lock information * @return zero for success, -errno for failure to send reply */ int fuse_reply_lock(fuse_req_t req, const struct flock *lock); /** * Reply with block index * * Possible requests: * bmap * * @param req request handle * @param idx block index within device * @return zero for success, -errno for failure to send reply */ int fuse_reply_bmap(fuse_req_t req, uint64_t idx); /* ----------------------------------------------------------- * * Filling a buffer in readdir * * ----------------------------------------------------------- */ /** * Add a directory entry to the buffer * * Buffer needs to be large enough to hold the entry. If it's not, * then the entry is not filled in but the size of the entry is still * returned. The caller can check this by comparing the bufsize * parameter with the returned entry size. If the entry size is * larger than the buffer size, the operation failed. * * From the 'stbuf' argument the st_ino field and bits 12-15 of the * st_mode field are used. The other fields are ignored. * * *off* should be any non-zero value that the filesystem can use to * identify the current point in the directory stream. It does not * need to be the actual physical position. A value of zero is * reserved to mean "from the beginning", and should therefore never * be used (the first call to fuse_add_direntry should be passed the * offset of the second directory entry). * * @param req request handle * @param buf the point where the new entry will be added to the buffer * @param bufsize remaining size of the buffer * @param name the name of the entry * @param stbuf the file attributes * @param off the offset of the next entry * @return the space needed for the entry */ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off); /** * Add a directory entry to the buffer with the attributes * * See documentation of `fuse_add_direntry()` for more details. * * @param req request handle * @param buf the point where the new entry will be added to the buffer * @param bufsize remaining size of the buffer * @param name the name of the entry * @param e the directory entry * @param off the offset of the next entry * @return the space needed for the entry */ size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off); /** * Reply to ask for data fetch and output buffer preparation. ioctl * will be retried with the specified input data fetched and output * buffer prepared. * * Possible requests: * ioctl * * @param req request handle * @param in_iov iovec specifying data to fetch from the caller * @param in_count number of entries in in_iov * @param out_iov iovec specifying addresses to write output to * @param out_count number of entries in out_iov * @return zero for success, -errno for failure to send reply */ int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count); /** * Reply to finish ioctl * * Possible requests: * ioctl * * @param req request handle * @param result result to be passed to the caller * @param buf buffer containing output data * @param size length of output data */ int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size); /** * Reply to finish ioctl with iov buffer * * Possible requests: * ioctl * * @param req request handle * @param result result to be passed to the caller * @param iov the vector containing the data * @param count the size of vector */ int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count); /** * Reply with poll result event mask * * @param req request handle * @param revents poll result event mask */ int fuse_reply_poll(fuse_req_t req, unsigned revents); /** * Reply with offset * * Possible requests: * lseek * * @param req request handle * @param off offset of next data or hole * @return zero for success, -errno for failure to send reply */ int fuse_reply_lseek(fuse_req_t req, off_t off); /** * Reply with extended file attributes. * * Possible requests: * statx * * @param req request handle * @param flags statx flags * @param statx the attributes * @param attr_timeout validity timeout (in seconds) for the attributes * @return zero for success, -errno for failure to send reply */ int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout); /* ----------------------------------------------------------- * * Notification * * ----------------------------------------------------------- */ /** * Notify IO readiness event * * For more information, please read comment for poll operation. * * @param ph poll handle to notify IO readiness event for */ int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph); /** * Notify to invalidate cache for an inode. * * Added in FUSE protocol version 7.12. If the kernel does not support * this (or a newer) version, the function will return -ENOSYS and do * nothing. * * If the filesystem has writeback caching enabled, invalidating an * inode will first trigger a writeback of all dirty pages. The call * will block until all writeback requests have completed and the * inode has been invalidated. It will, however, not wait for * completion of pending writeback requests that have been issued * before. * * If there are no dirty pages, this function will never block. * * @param se the session object * @param ino the inode number * @param off the offset in the inode where to start invalidating * or negative to invalidate attributes only * @param len the amount of cache to invalidate or 0 for all * @return zero for success, -errno for failure */ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len); /** * Notify to increment the epoch for the current * * Each fuse connection has an 'epoch', which is initialized during INIT. * Caching will then be validated against the epoch value: if the current epoch * is higher than an object being revalidated, the object is invalid. * * This function simply increment the current epoch value. * * @param se the session object * @return zero for success, -errno for failure */ int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se); /** * 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 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() * request for the parent, and a setattr(), unlink(), rmdir(), * rename(), setxattr(), removexattr(), readdir() or readdirplus() * request for the inode itself. * * When called correctly, this function will never block. * * Added in FUSE protocol version 7.12. 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 */ 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): * * If the provided *child* inode matches the inode that is currently * associated with the cached dentry, and if there are any inotify * watches registered for the dentry, then the watchers are informed * that the dentry has been deleted. * * To avoid a deadlock this function must not be called while * 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). * * When called correctly, this function will never block. * * Added in FUSE protocol version 7.18. 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 child inode number * @param name file name * @param namelen strlen() of file name * @return zero for success, -errno for failure */ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, fuse_ino_t child, const char *name, size_t namelen); /** * Store data to the kernel buffers * * Synchronously store data in the kernel buffers belonging to the * given inode. The stored data is marked up-to-date (no read will be * performed against it, unless it's invalidated or evicted from the * cache). * * If the stored data overflows the current file size, then the size * is extended, similarly to a write(2) on the filesystem. * * If this function returns an error, then the store wasn't fully * completed, but it may have been partially completed. * * Added in FUSE protocol version 7.15. 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 ino the inode number * @param offset the starting offset into the file to store to * @param bufv buffer vector * @param flags flags controlling the copy * @return zero for success, -errno for failure */ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags); /** * Retrieve data from the kernel buffers * * Retrieve data in the kernel buffers belonging to the given inode. * If successful then the retrieve_reply() method will be called with * the returned data. * * Only present pages are returned in the retrieve reply. Retrieving * stops when it finds a non-present page and only data prior to that * is returned. * * If this function returns an error, then the retrieve will not be * completed and no reply will be sent. * * This function doesn't change the dirty state of pages in the kernel * buffer. For dirty pages the write() method will be called * regardless of having been retrieved previously. * * Added in FUSE protocol version 7.15. 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 ino the inode number * @param size the number of bytes to retrieve * @param offset the starting offset into the file to retrieve from * @param cookie user data to supply to the reply callback * @return zero for success, -errno for failure */ int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie); /* ----------------------------------------------------------- * * Utility functions * * ----------------------------------------------------------- */ /** * Get the userdata from the request * * @param req request handle * @return the user data passed to fuse_session_new() */ void *fuse_req_userdata(fuse_req_t req); /** * Get the context from the request * * The pointer returned by this function will only be valid for the * request's lifetime * * @param req request handle * @return the context structure */ const struct fuse_ctx *fuse_req_ctx(fuse_req_t req); /** * Get the current supplementary group IDs for the specified request * * Similar to the getgroups(2) system call, except the return value is * always the total number of group IDs, even if it is larger than the * specified size. * * The current fuse kernel module in linux (as of 2.6.30) doesn't pass * the group list to userspace, hence this function needs to parse * "/proc/$TID/task/$TID/status" to get the group IDs. * * This feature may not be supported on all operating systems. In * such a case this function will return -ENOSYS. * * @param req request handle * @param size size of given array * @param list array of group IDs to be filled in * @return the total number of supplementary group IDs or -errno on failure */ int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]); /** * Callback function for an interrupt * * @param req interrupted request * @param data user data */ typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); /** * Register/unregister callback for an interrupt * * If an interrupt has already happened, then the callback function is * called from within this function, hence it's not possible for * interrupts to be lost. * * @param req request handle * @param func the callback function or NULL for unregister * @param data user data passed to the callback function */ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data); /** * Check if a request has already been interrupted * * @param req request handle * @return 1 if the request has been interrupted, 0 otherwise */ int fuse_req_interrupted(fuse_req_t req); /* ----------------------------------------------------------- * * Inquiry functions * * ----------------------------------------------------------- */ /** * Print low-level version information to stdout. */ void fuse_lowlevel_version(void); /** * Print available low-level options to stdout. This is not an * exhaustive list, but includes only those options that may be of * interest to an end-user of a file system. */ void fuse_lowlevel_help(void); /** * Print available options for `fuse_parse_cmdline()`. */ 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; int debug; int nodefault_subtype; char *mountpoint; int show_version; int show_help; int clone_fd; unsigned int max_idle_threads; /* discouraged, due to thread * destruct overhead */ /* Added in libfuse-3.12 */ unsigned int max_threads; }; /** * Utility function to parse common options for simple file systems * using the low-level API. A help text that describes the available * options can be printed with `fuse_cmdline_help`. A single * non-option argument is treated as the mountpoint. Multiple * non-option arguments will result in an error. * * If neither -o subtype= or -o fsname= options are given, a new * subtype option will be added and set to the basename of the program * (the fsname will remain unset, and then defaults to "fuse"). * * Known options will be removed from *args*, unknown options will * remain. * * @param args argument vector (input+output) * @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 /* Do not call this directly, use fuse_session_new() instead */ struct fuse_session * fuse_session_new_versioned(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, struct libfuse_version *version, void *userdata); /** * Create a low level session. * * Returns a session structure suitable for passing to * fuse_session_mount() and fuse_session_loop(). * * This function accepts most file-system independent mount options * (like context, nodev, ro - see mount(8)), as well as the general * fuse mount options listed in mount.fuse(8) (e.g. -o allow_root and * -o default_permissions, but not ``-o use_ino``). Instead of `-o * debug`, debugging may also enabled with `-d` or `--debug`. * * If not all options are known, an error message is written to stderr * and the function returns NULL. * * To create a no-op session just for mounting pass op as NULL. * * Option parsing skips argv[0], which is assumed to contain the * program name. To prevent accidentally passing an option in * argv[0], this element must always be present (even if no options * are specified). It may be set to the empty string ('\0') if no * reasonable value can be provided. * * @param args argument vector * @param op the (low-level) filesystem operations * @param op_size sizeof(struct fuse_lowlevel_ops) * @param version the libfuse version a file system server was compiled against * @param userdata user data * @return the fuse session on success, NULL on failure **/ static inline struct fuse_session * fuse_session_new_fn(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata) { struct libfuse_version version = { .major = FUSE_MAJOR_VERSION, .minor = FUSE_MINOR_VERSION, .hotfix = FUSE_HOTFIX_VERSION, .padding = 0 }; return fuse_session_new_versioned(args, op, op_size, &version, userdata); } #define fuse_session_new(args, op, op_size, userdata) \ fuse_session_new_fn(args, op, op_size, userdata) /* * This should mostly not be called directly, but instead the * fuse_session_custom_io() should be used. */ int fuse_session_custom_io_317(struct fuse_session *se, const struct fuse_custom_io *io, size_t op_size, int fd); /** * 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` * **/ #if FUSE_MAKE_VERSION(3, 17) <= FUSE_USE_VERSION static inline int fuse_session_custom_io(struct fuse_session *se, const struct fuse_custom_io *io, size_t op_size, int fd) { return fuse_session_custom_io_317(se, io, op_size, fd); } #else static inline int fuse_session_custom_io(struct fuse_session *se, const struct fuse_custom_io *io, int fd) { return fuse_session_custom_io_317(se, io, offsetof(struct fuse_custom_io, clone_fd), fd); } #endif /** * Mount a FUSE file system. * * @param mountpoint the mount point path * @param se session object * * @return 0 on success, -1 on failure. **/ int fuse_session_mount(struct fuse_session *se, const char *mountpoint); /** * Enter a single threaded, blocking event loop. * * When the event loop terminates because the connection to the FUSE * kernel module has been closed, this function returns zero. This * happens when the filesystem is unmounted regularly (by the * filesystem owner or root running the umount(8) or fusermount(1) * command), or if connection is explicitly severed by writing ``1`` * to the``abort`` file in ``/sys/fs/fuse/connections/NNN``. The only * way to distinguish between these two conditions is to check if the * filesystem is still mounted after the session loop returns. * * When some error occurs during request processing, the function * returns a negated errno(3) value. * * If the loop has been terminated because of a signal handler * installed by fuse_set_signal_handlers(), this function returns the * (positive) signal value that triggered the exit. * * @param se the session * @return 0, -errno, or a signal value */ 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) #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(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 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 */ void fuse_session_exit(struct fuse_session *se); /** * Reset the terminated flag of a session * * @param se the session */ void fuse_session_reset(struct fuse_session *se); /** * Query the terminated flag of a session * * @param se the session * @return 1 if exited, 0 if not exited */ int fuse_session_exited(struct fuse_session *se); /** * Ensure that file system is unmounted. * * In regular operation, the file system is typically unmounted by the * user calling umount(8) or fusermount(1), which then terminates the * FUSE session loop. However, the session loop may also terminate as * a result of an explicit call to fuse_session_exit() (e.g. by a * signal handler installed by fuse_set_signal_handler()). In this * case the filesystem remains mounted, but any attempt to access it * will block (while the filesystem process is still running) or give * an ESHUTDOWN error (after the filesystem process has terminated). * * If the communication channel with the FUSE kernel module is still * open (i.e., if the session loop was terminated by an explicit call * to fuse_session_exit()), this function will close it and unmount * the filesystem. If the communication channel has been closed by the * kernel, this method will do (almost) nothing. * * NOTE: The above semantics mean that if the connection to the kernel * is terminated via the ``/sys/fs/fuse/connections/NNN/abort`` file, * this method will *not* unmount the filesystem. * * @param se the session */ void fuse_session_unmount(struct fuse_session *se); /** * Destroy a session * * @param se the session */ void fuse_session_destroy(struct fuse_session *se); /* ----------------------------------------------------------- * * Custom event loop support * * ----------------------------------------------------------- */ /** * Return file descriptor for communication with kernel. * * The file selector can be used to integrate FUSE with a custom event * loop. Whenever data is available for reading on the provided fd, * the event loop should call `fuse_session_receive_buf` followed by * `fuse_session_process_buf` to process the request. * * The returned file descriptor is valid until `fuse_session_unmount` * is called. * * @param se the session * @return a file descriptor */ int fuse_session_fd(struct fuse_session *se); /** * Process a raw request supplied in a generic buffer * * The fuse_buf may contain a memory buffer or a pipe file descriptor. * * @param se the session * @param buf the fuse_buf containing the request */ void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf); /** * Read a raw request from the kernel into the supplied buffer. * * Depending on file system options, system capabilities, and request * size the request is either read into a memory buffer or spliced * into a temporary pipe. * * @param se the session * @param buf the fuse_buf to store the request in * @return the actual size of the raw request, or -errno on error */ int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf); /** * Check if the request is submitted through fuse-io-uring */ bool fuse_req_is_uring(fuse_req_t req); /** * Get the payload of a request * (for requests submitted through fuse-io-uring only) * * This is useful for a file system that wants to write data directly * to the request buffer. With io-uring the req is the buffer owner * and the file system can write directly to the buffer and avoid * extra copying. For example useful for network file systems. * * @param req the request * @param payload pointer to the payload * @param payload_sz size of the payload * @param mr memory registration handle, currently unused * @return 0 on success, -errno on failure */ int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr); #ifdef __cplusplus } #endif #endif /* FUSE_LOWLEVEL_H_ */ fuse-3.18.2/include/fuse_mount_compat.h0000644000175000017500000000204615156613252017050 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2023 Giulio Benetti 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 /* Some libc don't define MS_*, so define them manually * (values taken from https://elixir.bootlin.com/linux/v6.10/source/include/uapi/linux/mount.h#L13 on) */ #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 #endif #ifndef MS_NOSYMFOLLOW #define MS_NOSYMFOLLOW 256 #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_ */ fuse-3.18.2/include/fuse_opt.h0000644000175000017500000001657315156613252015157 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ #ifndef FUSE_OPT_H_ #define FUSE_OPT_H_ /** @file * * This file defines the option parsing interface of FUSE */ #ifdef __cplusplus extern "C" { #endif /** * Option description * * This structure describes a single option, and action associated * with it, in case it matches. * * More than one such match may occur, in which case the action for * each match is executed. * * There are three possible actions in case of a match: * * i) An integer (int or unsigned) variable determined by 'offset' is * set to 'value' * * ii) The processing function is called, with 'value' as the key * * iii) An integer (any) or string (char *) variable determined by * 'offset' is set to the value of an option parameter * * 'offset' should normally be either set to * * - 'offsetof(struct foo, member)' actions i) and iii) * * - -1 action ii) * * The 'offsetof()' macro is defined in the header. * * The template determines which options match, and also have an * effect on the action. Normally the action is either i) or ii), but * if a format is present in the template, then action iii) is * performed. * * The types of templates are: * * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only * themselves. Invalid values are "--" and anything beginning * with "-o" * * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or * the relevant option in a comma separated option list * * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) * which have a parameter * * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform * action iii). * * 5) "-x ", etc. Matches either "-xparam" or "-x param" as * two separate arguments * * 6) "-x %s", etc. Combination of 4) and 5) * * If the format is "%s", memory is allocated for the string unlike with * scanf(). The previous value (if non-NULL) stored at the this location is * freed. */ struct fuse_opt { /** Matching template and optional parameter formatting */ const char *templ; /** * Offset of variable within 'data' parameter of fuse_opt_parse() * or -1 */ unsigned long offset; /** * Value to set the variable to, or to be passed as 'key' to the * processing function. Ignored if template has a format */ int value; }; /** * Key option. In case of a match, the processing function will be * called with the specified key. */ #define FUSE_OPT_KEY(templ, key) { templ, -1U, key } /** * Last option. An array of 'struct fuse_opt' must end with a NULL * template value */ #define FUSE_OPT_END { NULL, 0, 0 } /** * Argument list */ struct fuse_args { /** Argument count */ int argc; /** Argument vector. NULL terminated */ char **argv; /** Is 'argv' allocated? */ int allocated; }; /** * Initializer for 'struct fuse_args' */ #define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } /** * Key value passed to the processing function if an option did not * match any template */ #define FUSE_OPT_KEY_OPT -1 /** * Key value passed to the processing function for all non-options * * Non-options are the arguments beginning with a character other than * '-' or all arguments after the special '--' option */ #define FUSE_OPT_KEY_NONOPT -2 /** * Special key value for options to keep * * Argument is not passed to processing function, but behave as if the * processing function returned 1 */ #define FUSE_OPT_KEY_KEEP -3 /** * Special key value for options to discard * * Argument is not passed to processing function, but behave as if the * processing function returned zero */ #define FUSE_OPT_KEY_DISCARD -4 /** * Processing function * * This function is called if * - option did not match any 'struct fuse_opt' * - argument is a non-option * - option did match and offset was set to -1 * * The 'arg' parameter will always contain the whole argument or * option including the parameter if exists. A two-argument option * ("-x foo") is always converted to single argument option of the * form "-xfoo" before this function is called. * * Options of the form '-ofoo' are passed to this function without the * '-o' prefix. * * The return value of this function determines whether this argument * is to be inserted into the output argument vector, or discarded. * * @param data is the user data passed to the fuse_opt_parse() function * @param arg is the whole argument or option * @param key determines why the processing function was called * @param outargs the current output argument list * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept */ typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs); /** * Option parsing function * * If 'args' was returned from a previous call to fuse_opt_parse() or * it was constructed from * * A NULL 'args' is equivalent to an empty argument vector * * A NULL 'opts' is equivalent to an 'opts' array containing a single * end marker * * A NULL 'proc' is equivalent to a processing function always * returning '1' * * @param args is the input and output argument list * @param data is the user data * @param opts is the option description array * @param proc is the processing function * @return -1 on error, 0 on success */ int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc); /** * Add an option to a comma separated option list * * @param opts is a pointer to an option list, may point to a NULL value * @param opt is the option to add * @return -1 on allocation error, 0 on success */ int fuse_opt_add_opt(char **opts, const char *opt); /** * Add an option, escaping commas, to a comma separated option list * * @param opts is a pointer to an option list, may point to a NULL value * @param opt is the option to add * @return -1 on allocation error, 0 on success */ int fuse_opt_add_opt_escaped(char **opts, const char *opt); /** * Add an argument to a NULL terminated argument vector * * @param args is the structure containing the current argument list * @param arg is the new argument to add * @return -1 on allocation error, 0 on success */ int fuse_opt_add_arg(struct fuse_args *args, const char *arg); /** * Add an argument at the specified position in a NULL terminated * argument vector * * Adds the argument to the N-th position. This is useful for adding * options at the beginning of the array which must not come after the * special '--' option. * * @param args is the structure containing the current argument list * @param pos is the position at which to add the argument * @param arg is the new argument to add * @return -1 on allocation error, 0 on success */ int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg); /** * Free the contents of argument list * * The structure itself is not freed * * @param args is the structure containing the argument list */ void fuse_opt_free_args(struct fuse_args *args); /** * Check if an option matches * * @param opts is the option description array * @param opt is the option to match * @return 1 if a match is found, 0 if not */ int fuse_opt_match(const struct fuse_opt opts[], const char *opt); #ifdef __cplusplus } #endif #endif /* FUSE_OPT_H_ */ fuse-3.18.2/include/meson.build0000644000175000017500000000026215156613252015310 0ustar berndberndlibfuse_headers = [ 'fuse.h', 'fuse_common.h', 'fuse_lowlevel.h', 'fuse_opt.h', 'cuse_lowlevel.h', 'fuse_log.h' ] install_headers(libfuse_headers, subdir: 'fuse3') fuse-3.18.2/lib/0000755000175000017500000000000015156613252012271 5ustar berndberndfuse-3.18.2/lib/buffer.c0000644000175000017500000001471415156613252013715 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2010 Miklos Szeredi Functions for dealing with `struct fuse_buf` and `struct fuse_bufvec`. This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #define _GNU_SOURCE #include "fuse_config.h" #include "fuse_i.h" #include "fuse_lowlevel.h" #include #include #include #include size_t fuse_buf_size(const struct fuse_bufvec *bufv) { size_t i; size_t size = 0; for (i = 0; i < bufv->count; i++) { if (bufv->buf[i].size >= SIZE_MAX - size) return SIZE_MAX; size += bufv->buf[i].size; } return size; } static size_t min_size(size_t s1, size_t s2) { return s1 < s2 ? s1 : s2; } static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off, const struct fuse_buf *src, size_t src_off, size_t len) { ssize_t res = 0; size_t copied = 0; while (len) { if (dst->flags & FUSE_BUF_FD_SEEK) { res = pwrite(dst->fd, (char *)src->mem + src_off, len, dst->pos + dst_off); } else { res = write(dst->fd, (char *)src->mem + src_off, len); } if (res == -1) { if (!copied) return -errno; break; } if (res == 0) break; copied += res; if (!(dst->flags & FUSE_BUF_FD_RETRY)) break; src_off += res; dst_off += res; len -= res; } return copied; } static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off, const struct fuse_buf *src, size_t src_off, size_t len) { ssize_t res = 0; size_t copied = 0; while (len) { if (src->flags & FUSE_BUF_FD_SEEK) { res = pread(src->fd, (char *)dst->mem + dst_off, len, src->pos + src_off); } else { res = read(src->fd, (char *)dst->mem + dst_off, len); } if (res == -1) { if (!copied) return -errno; break; } if (res == 0) break; copied += res; if (!(src->flags & FUSE_BUF_FD_RETRY)) break; dst_off += res; src_off += res; len -= res; } return copied; } static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, const struct fuse_buf *src, size_t src_off, size_t len) { char buf[4096]; struct fuse_buf tmp = { .size = sizeof(buf), .flags = 0, }; ssize_t res; size_t copied = 0; tmp.mem = buf; while (len) { size_t this_len = min_size(tmp.size, len); size_t read_len; res = fuse_buf_read(&tmp, 0, src, src_off, this_len); if (res < 0) { if (!copied) return res; break; } if (res == 0) break; read_len = res; res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); if (res < 0) { if (!copied) return res; break; } if (res == 0) break; copied += res; if (res < this_len) break; dst_off += res; src_off += res; len -= res; } return copied; } #ifdef HAVE_SPLICE static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, const struct fuse_buf *src, size_t src_off, size_t len, enum fuse_buf_copy_flags flags) { int splice_flags = 0; off_t *srcpos = NULL; off_t *dstpos = NULL; off_t srcpos_val; off_t dstpos_val; ssize_t res; size_t copied = 0; if (flags & FUSE_BUF_SPLICE_MOVE) splice_flags |= SPLICE_F_MOVE; if (flags & FUSE_BUF_SPLICE_NONBLOCK) splice_flags |= SPLICE_F_NONBLOCK; if (src->flags & FUSE_BUF_FD_SEEK) { srcpos_val = src->pos + src_off; srcpos = &srcpos_val; } if (dst->flags & FUSE_BUF_FD_SEEK) { dstpos_val = dst->pos + dst_off; dstpos = &dstpos_val; } while (len) { res = splice(src->fd, srcpos, dst->fd, dstpos, len, splice_flags); if (res == -1) { if (copied) break; if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE)) return -errno; /* Maybe splice is not supported for this combination */ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); } if (res == 0) break; copied += res; if (!(src->flags & FUSE_BUF_FD_RETRY) && !(dst->flags & FUSE_BUF_FD_RETRY)) { break; } len -= res; } return copied; } #else static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, const struct fuse_buf *src, size_t src_off, size_t len, enum fuse_buf_copy_flags flags) { (void) flags; return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); } #endif static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, const struct fuse_buf *src, size_t src_off, size_t len, enum fuse_buf_copy_flags flags) { int src_is_fd = src->flags & FUSE_BUF_IS_FD; int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; if (!src_is_fd && !dst_is_fd) { char *dstmem = (char *)dst->mem + dst_off; char *srcmem = (char *)src->mem + src_off; if (dstmem != srcmem) { if (dstmem + len <= srcmem || srcmem + len <= dstmem) memcpy(dstmem, srcmem, len); else memmove(dstmem, srcmem, len); } return len; } else if (!src_is_fd) { return fuse_buf_write(dst, dst_off, src, src_off, len); } else if (!dst_is_fd) { return fuse_buf_read(dst, dst_off, src, src_off, len); } else if (flags & FUSE_BUF_NO_SPLICE) { return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); } else { return fuse_buf_splice(dst, dst_off, src, src_off, len, flags); } } static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv) { if (bufv->idx < bufv->count) return &bufv->buf[bufv->idx]; else return NULL; } static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) { const struct fuse_buf *buf = fuse_bufvec_current(bufv); if (!buf) return 0; bufv->off += len; assert(bufv->off <= buf->size); if (bufv->off == buf->size) { assert(bufv->idx < bufv->count); bufv->idx++; if (bufv->idx == bufv->count) return 0; bufv->off = 0; } return 1; } ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, enum fuse_buf_copy_flags flags) { size_t copied = 0; if (dstv == srcv) return fuse_buf_size(dstv); for (;;) { const struct fuse_buf *src = fuse_bufvec_current(srcv); const struct fuse_buf *dst = fuse_bufvec_current(dstv); size_t src_len; size_t dst_len; size_t len; ssize_t res; if (src == NULL || dst == NULL) break; src_len = src->size - srcv->off; dst_len = dst->size - dstv->off; len = min_size(src_len, dst_len); res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); if (res < 0) { if (!copied) return res; break; } copied += res; if (!fuse_bufvec_advance(srcv, res) || !fuse_bufvec_advance(dstv, res)) break; if (res < len) break; } return copied; } fuse-3.18.2/lib/compat.c0000644000175000017500000000533615156613252013727 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi 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 LGPL2.txt. */ /* Description: This file has compatibility symbols for platforms that do not support version symboling */ #include "libfuse_config.h" #include #include struct fuse_args; struct fuse_cmdline_opts; struct fuse_cmdline_opts; struct fuse_session; struct fuse_custom_io; struct fuse_operations; struct fuse_lowlevel_ops; /** * 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); } int fuse_session_custom_io_30(struct fuse_session *se, const struct fuse_custom_io *io, int fd); int fuse_session_custom_io(struct fuse_session *se, const struct fuse_custom_io *io, int fd); int fuse_session_custom_io(struct fuse_session *se, const struct fuse_custom_io *io, int fd) { return fuse_session_custom_io_30(se, io, fd); } #endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */ int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data); int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data); int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data) { return fuse_main_real_30(argc, argv, op, op_size, user_data); } struct fuse_session *fuse_session_new_30(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata); struct fuse_session *fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata); struct fuse_session *fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata) { return fuse_session_new_30(args, op, op_size, userdata); } fuse-3.18.2/lib/cuse_lowlevel.c0000644000175000017500000002172315156613252015312 0ustar berndbernd/* CUSE: Character device in Userspace Copyright (C) 2008 SUSE Linux Products GmbH Copyright (C) 2008 Tejun Heo This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ #include "fuse_config.h" #include "cuse_lowlevel.h" #include "fuse_kernel.h" #include "fuse_i.h" #include "fuse_opt.h" #include #include #include #include #include #include struct cuse_data { struct cuse_lowlevel_ops clop; unsigned max_read; unsigned dev_major; unsigned dev_minor; unsigned flags; unsigned dev_info_len; char dev_info[]; }; static struct cuse_lowlevel_ops *req_clop(fuse_req_t req) { return &req->se->cuse_data->clop; } static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { (void)ino; req_clop(req)->open(req, fi); } static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { (void)ino; req_clop(req)->read(req, size, off, fi); } static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) { (void)ino; req_clop(req)->write(req, buf, size, off, fi); } static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { (void)ino; req_clop(req)->flush(req, fi); } static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { (void)ino; req_clop(req)->release(req, fi); } static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { (void)ino; req_clop(req)->fsync(req, datasync, fi); } static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned int flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz) { (void)ino; req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz); } static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph) { (void)ino; req_clop(req)->poll(req, fi, ph); } static size_t cuse_pack_info(int argc, const char **argv, char *buf) { size_t size = 0; int i; for (i = 0; i < argc; i++) { size_t len; len = strlen(argv[i]) + 1; size += len; if (buf) { memcpy(buf, argv[i], len); buf += len; } } return size; } static struct cuse_data *cuse_prep_data(const struct cuse_info *ci, const struct cuse_lowlevel_ops *clop) { struct cuse_data *cd; size_t dev_info_len; dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, NULL); if (dev_info_len > CUSE_INIT_INFO_MAX) { fuse_log(FUSE_LOG_ERR, "cuse: dev_info (%zu) too large, limit=%u\n", dev_info_len, CUSE_INIT_INFO_MAX); return NULL; } cd = calloc(1, sizeof(*cd) + dev_info_len); if (!cd) { fuse_log(FUSE_LOG_ERR, "cuse: failed to allocate cuse_data\n"); return NULL; } memcpy(&cd->clop, clop, sizeof(cd->clop)); cd->max_read = 131072; cd->dev_major = ci->dev_major; cd->dev_minor = ci->dev_minor; cd->dev_info_len = dev_info_len; cd->flags = ci->flags; cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info); return cd; } struct fuse_session *cuse_lowlevel_new(struct fuse_args *args, const struct cuse_info *ci, const struct cuse_lowlevel_ops *clop, void *userdata) { struct fuse_lowlevel_ops lop; struct cuse_data *cd; struct fuse_session *se; cd = cuse_prep_data(ci, clop); if (!cd) return NULL; memset(&lop, 0, sizeof(lop)); lop.init = clop->init; lop.destroy = clop->destroy; lop.open = clop->open ? cuse_fll_open : NULL; lop.read = clop->read ? cuse_fll_read : NULL; lop.write = clop->write ? cuse_fll_write : NULL; lop.flush = clop->flush ? cuse_fll_flush : NULL; lop.release = clop->release ? cuse_fll_release : NULL; lop.fsync = clop->fsync ? cuse_fll_fsync : NULL; lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL; lop.poll = clop->poll ? cuse_fll_poll : NULL; se = fuse_session_new(args, &lop, sizeof(lop), userdata); if (!se) { free(cd); return NULL; } se->cuse_data = cd; return se; } static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg, char *dev_info, unsigned dev_info_len) { struct iovec iov[3]; iov[1].iov_base = arg; iov[1].iov_len = sizeof(struct cuse_init_out); iov[2].iov_base = dev_info; iov[2].iov_len = dev_info_len; return fuse_send_reply_iov_nofree(req, 0, iov, 3); } void _cuse_lowlevel_init(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *req_payload) { const struct fuse_init_in *arg = op_in; (void)req_payload; struct cuse_init_out outarg; struct fuse_session *se = req->se; struct cuse_data *cd = se->cuse_data; size_t bufsize = se->bufsize; struct cuse_lowlevel_ops *clop = req_clop(req); (void) nodeid; if (se->debug) { fuse_log(FUSE_LOG_DEBUG, "CUSE_INIT: %u.%u\n", arg->major, arg->minor); fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); } se->conn.proto_major = arg->major; se->conn.proto_minor = arg->minor; /* XXX This is not right.*/ se->conn.capable_ext = 0; se->conn.want_ext = 0; if (arg->major < 7) { fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n", arg->major, arg->minor); fuse_reply_err(req, EPROTO); return; } if (bufsize < FUSE_MIN_READ_BUFFER) { fuse_log(FUSE_LOG_ERR, "cuse: warning: buffer size too small: %zu\n", bufsize); bufsize = FUSE_MIN_READ_BUFFER; } bufsize -= 4096; if (bufsize < se->conn.max_write) se->conn.max_write = bufsize; se->got_init = 1; if (se->op.init) se->op.init(se->userdata, &se->conn); memset(&outarg, 0, sizeof(outarg)); outarg.major = FUSE_KERNEL_VERSION; outarg.minor = FUSE_KERNEL_MINOR_VERSION; outarg.flags = cd->flags; outarg.max_read = cd->max_read; outarg.max_write = se->conn.max_write; outarg.dev_major = cd->dev_major; outarg.dev_minor = cd->dev_minor; if (se->debug) { fuse_log(FUSE_LOG_DEBUG, " CUSE_INIT: %u.%u\n", outarg.major, outarg.minor); fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); fuse_log(FUSE_LOG_DEBUG, " max_read=0x%08x\n", outarg.max_read); fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); fuse_log(FUSE_LOG_DEBUG, " dev_major=%u\n", outarg.dev_major); fuse_log(FUSE_LOG_DEBUG, " dev_minor=%u\n", outarg.dev_minor); fuse_log(FUSE_LOG_DEBUG, " dev_info: %.*s\n", cd->dev_info_len, cd->dev_info); } cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len); if (clop->init_done) clop->init_done(se->userdata); fuse_free_req(req); } void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { _cuse_lowlevel_init(req, nodeid, inarg, NULL); } struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[], const struct cuse_info *ci, const struct cuse_lowlevel_ops *clop, int *multithreaded, void *userdata) { const char *devname = "/dev/cuse"; static const struct fuse_opt kill_subtype_opts[] = { FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD), FUSE_OPT_END }; struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_session *se; struct fuse_cmdline_opts opts; int fd; int res; if (fuse_parse_cmdline(&args, &opts) == -1) return NULL; *multithreaded = !opts.singlethread; /* Remove subtype= option */ res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL); if (res == -1) goto out1; /* * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos * would ensue. */ do { fd = open("/dev/null", O_RDWR); if (fd > 2) close(fd); } while (fd >= 0 && fd <= 2); se = cuse_lowlevel_new(&args, ci, clop, userdata); if (se == NULL) goto out1; fd = open(devname, O_RDWR); if (fd == -1) { if (errno == ENODEV || errno == ENOENT) fuse_log(FUSE_LOG_ERR, "cuse: device not found, try 'modprobe cuse' first\n"); else fuse_log(FUSE_LOG_ERR, "cuse: failed to open %s: %s\n", devname, strerror(errno)); goto err_se; } se->fd = fd; res = fuse_set_signal_handlers(se); if (res == -1) goto err_se; res = fuse_daemonize(opts.foreground); if (res == -1) goto err_sig; fuse_opt_free_args(&args); return se; err_sig: fuse_remove_signal_handlers(se); err_se: fuse_session_destroy(se); out1: free(opts.mountpoint); fuse_opt_free_args(&args); return NULL; } void cuse_lowlevel_teardown(struct fuse_session *se) { fuse_remove_signal_handlers(se); fuse_session_destroy(se); } int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, const struct cuse_lowlevel_ops *clop, void *userdata) { struct fuse_session *se; int multithreaded; int res; se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded, userdata); if (se == NULL) return 1; if (multithreaded) { 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); cuse_lowlevel_teardown(se); if (res == -1) return 1; return 0; } fuse-3.18.2/lib/fuse.c0000644000175000017500000035410415156613252013406 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Implementation of the high-level FUSE API on top of the low-level API. This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #define _GNU_SOURCE #include "fuse.h" #include #include "fuse_config.h" #include "fuse_i.h" #include "fuse_lowlevel.h" #include "fuse_opt.h" #include "fuse_misc.h" #include "fuse_kernel.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FUSE_NODE_SLAB 1 #ifndef MAP_ANONYMOUS #undef FUSE_NODE_SLAB #endif #ifndef RENAME_EXCHANGE #define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */ #endif #define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1 #define FUSE_UNKNOWN_INO 0xffffffff #define OFFSET_MAX 0x7fffffffffffffffLL #define NODE_TABLE_MIN_SIZE 8192 struct fuse_fs { struct fuse_operations op; void *user_data; int debug; }; struct fusemod_so { void *handle; int ctr; }; struct lock_queue_element { struct lock_queue_element *next; pthread_cond_t cond; fuse_ino_t nodeid1; const char *name1; char **path1; struct node **wnode1; fuse_ino_t nodeid2; const char *name2; char **path2; struct node **wnode2; int err; bool done : 1; }; struct node_table { struct node **array; size_t use; size_t size; size_t split; }; #define list_entry(ptr, type, member) \ container_of(ptr, type, member) struct list_head { struct list_head *next; struct list_head *prev; }; struct node_slab { struct list_head list; /* must be the first member */ struct list_head freelist; int used; }; struct fuse { struct fuse_session *se; struct node_table name_table; struct node_table id_table; struct list_head lru_table; fuse_ino_t ctr; unsigned int generation; unsigned int hidectr; pthread_mutex_t lock; struct fuse_config conf; int intr_installed; struct fuse_fs *fs; struct lock_queue_element *lockq; int pagesize; struct list_head partial_slabs; struct list_head full_slabs; pthread_t prune_thread; }; struct lock { int type; off_t start; off_t end; pid_t pid; uint64_t owner; struct lock *next; }; struct node { struct node *name_next; struct node *id_next; fuse_ino_t nodeid; unsigned int generation; int refctr; struct node *parent; char *name; uint64_t nlookup; int open_count; struct timespec stat_updated; struct timespec mtime; off_t size; struct lock *locks; unsigned int is_hidden : 1; unsigned int cache_valid : 1; int treelock; char inline_name[32]; }; #define TREELOCK_WRITE -1 #define TREELOCK_WAIT_OFFSET INT_MIN struct node_lru { struct node node; struct list_head lru; struct timespec forget_time; }; struct fuse_direntry { struct stat stat; enum fuse_fill_dir_flags flags; char *name; struct fuse_direntry *next; }; struct fuse_dh { pthread_mutex_t lock; struct fuse *fuse; fuse_req_t req; char *contents; struct fuse_direntry *first; struct fuse_direntry **last; unsigned len; unsigned size; unsigned needlen; int filled; uint64_t fh; int error; fuse_ino_t nodeid; }; struct fuse_context_i { struct fuse_context ctx; fuse_req_t req; }; /* Defined by FUSE_REGISTER_MODULE() in lib/modules/subdir.c and iconv.c. */ extern fuse_module_factory_t fuse_module_subdir_factory; #ifdef HAVE_ICONV extern fuse_module_factory_t fuse_module_iconv_factory; #endif static pthread_key_t fuse_context_key; static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER; static int fuse_context_ref; static struct fuse_module *fuse_modules = NULL; static int fuse_register_module(const char *name, fuse_module_factory_t factory, struct fusemod_so *so) { struct fuse_module *mod; mod = calloc(1, sizeof(struct fuse_module)); if (!mod) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module\n"); return -1; } mod->name = strdup(name); if (!mod->name) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module name\n"); free(mod); return -1; } mod->factory = factory; mod->ctr = 0; mod->so = so; if (mod->so) mod->so->ctr++; mod->next = fuse_modules; fuse_modules = mod; return 0; } static void fuse_unregister_module(struct fuse_module *m) { struct fuse_module **mp; for (mp = &fuse_modules; *mp; mp = &(*mp)->next) { if (*mp == m) { *mp = (*mp)->next; break; } } free(m->name); free(m); } static int fuse_load_so_module(const char *module) { int ret = -1; char *tmp; struct fusemod_so *so; fuse_module_factory_t *factory; tmp = malloc(strlen(module) + 64); if (!tmp) { fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); return -1; } sprintf(tmp, "libfusemod_%s.so", module); so = calloc(1, sizeof(struct fusemod_so)); if (!so) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module so\n"); goto out; } so->handle = dlopen(tmp, RTLD_NOW); if (so->handle == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: dlopen(%s) failed: %s\n", tmp, dlerror()); goto out_free_so; } sprintf(tmp, "fuse_module_%s_factory", module); 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); if (ret) goto out_dlclose; out: free(tmp); return ret; out_dlclose: dlclose(so->handle); out_free_so: free(so); goto out; } static struct fuse_module *fuse_find_module(const char *module) { struct fuse_module *m; for (m = fuse_modules; m; m = m->next) { if (strcmp(module, m->name) == 0) { m->ctr++; break; } } return m; } static struct fuse_module *fuse_get_module(const char *module) { struct fuse_module *m; pthread_mutex_lock(&fuse_context_lock); m = fuse_find_module(module); if (!m) { int err = fuse_load_so_module(module); if (!err) m = fuse_find_module(module); } pthread_mutex_unlock(&fuse_context_lock); return m; } static void fuse_put_module(struct fuse_module *m) { pthread_mutex_lock(&fuse_context_lock); if (m->so) assert(m->ctr > 0); /* Builtin modules may already have m->ctr == 0 */ if (m->ctr > 0) m->ctr--; if (!m->ctr && m->so) { struct fusemod_so *so = m->so; assert(so->ctr > 0); so->ctr--; if (!so->ctr) { struct fuse_module **mp; for (mp = &fuse_modules; *mp;) { if ((*mp)->so == so) fuse_unregister_module(*mp); else mp = &(*mp)->next; } dlclose(so->handle); free(so); } } else if (!m->ctr) { fuse_unregister_module(m); } pthread_mutex_unlock(&fuse_context_lock); } static void init_list_head(struct list_head *list) { list->next = list; list->prev = list; } static int list_empty(const struct list_head *head) { return head->next == head; } static void list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } static inline void list_add_head(struct list_head *new, struct list_head *head) { list_add(new, head, head->next); } static inline void list_add_tail(struct list_head *new, struct list_head *head) { list_add(new, head->prev, head); } static inline void list_del(struct list_head *entry) { struct list_head *prev = entry->prev; struct list_head *next = entry->next; next->prev = prev; prev->next = next; } static inline int lru_enabled(struct fuse *f) { return f->conf.remember > 0; } static struct node_lru *node_lru(struct node *node) { return (struct node_lru *) node; } static size_t get_node_size(struct fuse *f) { if (lru_enabled(f)) return sizeof(struct node_lru); else return sizeof(struct node); } #ifdef FUSE_NODE_SLAB static struct node_slab *list_to_slab(struct list_head *head) { return (struct node_slab *) head; } static struct node_slab *node_to_slab(struct fuse *f, struct node *node) { return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1)); } static int alloc_slab(struct fuse *f) { void *mem; struct node_slab *slab; char *start; size_t num; size_t i; size_t node_size = get_node_size(f); mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mem == MAP_FAILED) return -1; slab = mem; init_list_head(&slab->freelist); slab->used = 0; num = (f->pagesize - sizeof(struct node_slab)) / node_size; start = (char *) mem + f->pagesize - num * node_size; for (i = 0; i < num; i++) { struct list_head *n; n = (struct list_head *) (start + i * node_size); list_add_tail(n, &slab->freelist); } list_add_tail(&slab->list, &f->partial_slabs); return 0; } static struct node *alloc_node(struct fuse *f) { struct node_slab *slab; struct list_head *node; if (list_empty(&f->partial_slabs)) { int res = alloc_slab(f); if (res != 0) return NULL; } slab = list_to_slab(f->partial_slabs.next); slab->used++; node = slab->freelist.next; list_del(node); if (list_empty(&slab->freelist)) { list_del(&slab->list); list_add_tail(&slab->list, &f->full_slabs); } memset(node, 0, sizeof(struct node)); return (struct node *) node; } static void free_slab(struct fuse *f, struct node_slab *slab) { int res; list_del(&slab->list); res = munmap(slab, f->pagesize); if (res == -1) fuse_log(FUSE_LOG_WARNING, "fuse warning: munmap(%p) failed\n", slab); } static void free_node_mem(struct fuse *f, struct node *node) { struct node_slab *slab = node_to_slab(f, node); struct list_head *n = (struct list_head *) node; slab->used--; if (slab->used) { if (list_empty(&slab->freelist)) { list_del(&slab->list); list_add_tail(&slab->list, &f->partial_slabs); } list_add_head(n, &slab->freelist); } else { free_slab(f, slab); } } #else static struct node *alloc_node(struct fuse *f) { return (struct node *) calloc(1, get_node_size(f)); } static void free_node_mem(struct fuse *f, struct node *node) { (void) f; free(node); } #endif static size_t id_hash(struct fuse *f, fuse_ino_t ino) { uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size; uint64_t oldhash = hash % (f->id_table.size / 2); if (oldhash >= f->id_table.split) return oldhash; else return hash; } static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid) { size_t hash = id_hash(f, nodeid); struct node *node; for (node = f->id_table.array[hash]; node != NULL; node = node->id_next) if (node->nodeid == nodeid) return node; return NULL; } static struct node *get_node(struct fuse *f, fuse_ino_t nodeid) { struct node *node = get_node_nocheck(f, nodeid); if (!node) { fuse_log(FUSE_LOG_ERR, "fuse internal error: node %llu not found\n", (unsigned long long) nodeid); abort(); } return node; } static void curr_time(struct timespec *now); static double diff_timespec(const struct timespec *t1, const struct timespec *t2); static void remove_node_lru(struct node *node) { struct node_lru *lnode = node_lru(node); list_del(&lnode->lru); init_list_head(&lnode->lru); } static void set_forget_time(struct fuse *f, struct node *node) { struct node_lru *lnode = node_lru(node); list_del(&lnode->lru); list_add_tail(&lnode->lru, &f->lru_table); curr_time(&lnode->forget_time); } static void free_node(struct fuse *f, struct node *node) { if (node->name != node->inline_name) free(node->name); free_node_mem(f, node); } static void node_table_reduce(struct node_table *t) { size_t newsize = t->size / 2; void *newarray; if (newsize < NODE_TABLE_MIN_SIZE) return; newarray = realloc(t->array, sizeof(struct node *) * newsize); if (newarray != NULL) t->array = newarray; t->size = newsize; t->split = t->size / 2; } static void remerge_id(struct fuse *f) { struct node_table *t = &f->id_table; int iter; if (t->split == 0) node_table_reduce(t); for (iter = 8; t->split > 0 && iter; iter--) { struct node **upper; t->split--; upper = &t->array[t->split + t->size / 2]; if (*upper) { struct node **nodep; for (nodep = &t->array[t->split]; *nodep; nodep = &(*nodep)->id_next); *nodep = *upper; *upper = NULL; break; } } } static void unhash_id(struct fuse *f, struct node *node) { struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)]; for (; *nodep != NULL; nodep = &(*nodep)->id_next) if (*nodep == node) { *nodep = node->id_next; f->id_table.use--; if(f->id_table.use < f->id_table.size / 4) remerge_id(f); return; } } static int node_table_resize(struct node_table *t) { size_t newsize = t->size * 2; void *newarray; newarray = realloc(t->array, sizeof(struct node *) * newsize); if (newarray == NULL) return -1; t->array = newarray; memset(t->array + t->size, 0, t->size * sizeof(struct node *)); t->size = newsize; t->split = 0; return 0; } static void rehash_id(struct fuse *f) { struct node_table *t = &f->id_table; struct node **nodep; struct node **next; size_t hash; if (t->split == t->size / 2) return; hash = t->split; t->split++; for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) { struct node *node = *nodep; size_t newhash = id_hash(f, node->nodeid); if (newhash != hash) { next = nodep; *nodep = node->id_next; node->id_next = t->array[newhash]; t->array[newhash] = node; } else { next = &node->id_next; } } if (t->split == t->size / 2) node_table_resize(t); } static void hash_id(struct fuse *f, struct node *node) { size_t hash = id_hash(f, node->nodeid); node->id_next = f->id_table.array[hash]; f->id_table.array[hash] = node; f->id_table.use++; if (f->id_table.use >= f->id_table.size / 2) rehash_id(f); } static size_t name_hash(struct fuse *f, fuse_ino_t parent, const char *name) { uint64_t hash = parent; uint64_t oldhash; for (; *name; name++) hash = hash * 31 + (unsigned char) *name; hash %= f->name_table.size; oldhash = hash % (f->name_table.size / 2); if (oldhash >= f->name_table.split) return oldhash; else return hash; } static void unref_node(struct fuse *f, struct node *node); static void remerge_name(struct fuse *f) { struct node_table *t = &f->name_table; int iter; if (t->split == 0) node_table_reduce(t); for (iter = 8; t->split > 0 && iter; iter--) { struct node **upper; t->split--; upper = &t->array[t->split + t->size / 2]; if (*upper) { struct node **nodep; for (nodep = &t->array[t->split]; *nodep; nodep = &(*nodep)->name_next); *nodep = *upper; *upper = NULL; break; } } } static void unhash_name(struct fuse *f, struct node *node) { if (node->name) { size_t hash = name_hash(f, node->parent->nodeid, node->name); struct node **nodep = &f->name_table.array[hash]; for (; *nodep != NULL; nodep = &(*nodep)->name_next) if (*nodep == node) { *nodep = node->name_next; node->name_next = NULL; unref_node(f, node->parent); if (node->name != node->inline_name) free(node->name); node->name = NULL; node->parent = NULL; f->name_table.use--; if (f->name_table.use < f->name_table.size / 4) remerge_name(f); return; } fuse_log(FUSE_LOG_ERR, "fuse internal error: unable to unhash node: %llu\n", (unsigned long long) node->nodeid); abort(); } } static void rehash_name(struct fuse *f) { struct node_table *t = &f->name_table; struct node **nodep; struct node **next; size_t hash; if (t->split == t->size / 2) return; hash = t->split; t->split++; for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) { struct node *node = *nodep; size_t newhash = name_hash(f, node->parent->nodeid, node->name); if (newhash != hash) { next = nodep; *nodep = node->name_next; node->name_next = t->array[newhash]; t->array[newhash] = node; } else { next = &node->name_next; } } if (t->split == t->size / 2) node_table_resize(t); } static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid, const char *name) { size_t hash = name_hash(f, parentid, name); struct node *parent = get_node(f, parentid); if (strlen(name) < sizeof(node->inline_name)) { strcpy(node->inline_name, name); node->name = node->inline_name; } else { node->name = strdup(name); if (node->name == NULL) return -1; } parent->refctr ++; node->parent = parent; node->name_next = f->name_table.array[hash]; f->name_table.array[hash] = node; f->name_table.use++; if (f->name_table.use >= f->name_table.size / 2) rehash_name(f); return 0; } static void delete_node(struct fuse *f, struct node *node) { if (f->conf.debug) fuse_log(FUSE_LOG_DEBUG, "DELETE: %llu\n", (unsigned long long) node->nodeid); assert(node->treelock == 0); unhash_name(f, node); if (lru_enabled(f)) remove_node_lru(node); unhash_id(f, node); free_node(f, node); } static void unref_node(struct fuse *f, struct node *node) { assert(node->refctr > 0); node->refctr --; if (!node->refctr) delete_node(f, node); } static fuse_ino_t next_id(struct fuse *f) { do { f->ctr = (f->ctr + 1) & 0xffffffff; if (!f->ctr) f->generation ++; } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO || get_node_nocheck(f, f->ctr) != NULL); return f->ctr; } static struct node *lookup_node(struct fuse *f, fuse_ino_t parent, const char *name) { size_t hash = name_hash(f, parent, name); struct node *node; for (node = f->name_table.array[hash]; node != NULL; node = node->name_next) if (node->parent->nodeid == parent && strcmp(node->name, name) == 0) return node; return NULL; } static void inc_nlookup(struct node *node) { if (!node->nlookup) node->refctr++; node->nlookup++; } static struct node *find_node(struct fuse *f, fuse_ino_t parent, const char *name) { struct node *node; pthread_mutex_lock(&f->lock); if (!name) node = get_node(f, parent); else node = lookup_node(f, parent, name); if (node == NULL) { node = alloc_node(f); if (node == NULL) goto out_err; node->nodeid = next_id(f); node->generation = f->generation; if (f->conf.remember) inc_nlookup(node); if (hash_name(f, node, parent, name) == -1) { free_node(f, node); node = NULL; goto out_err; } hash_id(f, node); if (lru_enabled(f)) { struct node_lru *lnode = node_lru(node); init_list_head(&lnode->lru); } } else if (lru_enabled(f) && node->nlookup == 1) { remove_node_lru(node); } inc_nlookup(node); out_err: pthread_mutex_unlock(&f->lock); return node; } static int lookup_path_in_cache(struct fuse *f, const char *path, fuse_ino_t *inop) { char *tmp = strdup(path); if (!tmp) return -ENOMEM; pthread_mutex_lock(&f->lock); fuse_ino_t ino = FUSE_ROOT_ID; int err = 0; char *save_ptr; char *path_element = strtok_r(tmp, "/", &save_ptr); while (path_element != NULL) { struct node *node = lookup_node(f, ino, path_element); if (node == NULL) { err = -ENOENT; break; } ino = node->nodeid; path_element = strtok_r(NULL, "/", &save_ptr); } pthread_mutex_unlock(&f->lock); free(tmp); if (!err) *inop = ino; return err; } static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name) { size_t len = strlen(name); if (s - len <= *buf) { unsigned pathlen = *bufsize - (s - *buf); unsigned newbufsize = *bufsize; char *newbuf; while (newbufsize < pathlen + len + 1) { if (newbufsize >= 0x80000000) newbufsize = 0xffffffff; else newbufsize *= 2; } newbuf = realloc(*buf, newbufsize); if (newbuf == NULL) return NULL; *buf = newbuf; s = newbuf + newbufsize - pathlen; memmove(s, newbuf + *bufsize - pathlen, pathlen); *bufsize = newbufsize; } s -= len; memcpy(s, name, len); s--; *s = '/'; return s; } static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode, struct node *end) { struct node *node; if (wnode) { assert(wnode->treelock == TREELOCK_WRITE); wnode->treelock = 0; } for (node = get_node(f, nodeid); node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) { assert(node->treelock != 0); assert(node->treelock != TREELOCK_WAIT_OFFSET); assert(node->treelock != TREELOCK_WRITE); node->treelock--; if (node->treelock == TREELOCK_WAIT_OFFSET) node->treelock = 0; } } static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name, char **path, struct node **wnodep, bool need_lock) { unsigned bufsize = 256; char *buf; char *s; struct node *node; struct node *wnode = NULL; int err; *path = NULL; err = -ENOMEM; buf = malloc(bufsize); if (buf == NULL) goto out_err; s = buf + bufsize - 1; *s = '\0'; if (name != NULL) { s = add_name(&buf, &bufsize, s, name); err = -ENOMEM; if (s == NULL) goto out_free; } if (wnodep) { assert(need_lock); wnode = lookup_node(f, nodeid, name); if (wnode) { if (wnode->treelock != 0) { if (wnode->treelock > 0) wnode->treelock += TREELOCK_WAIT_OFFSET; err = -EAGAIN; goto out_free; } wnode->treelock = TREELOCK_WRITE; } } for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID; node = node->parent) { err = -ESTALE; if (node->name == NULL || node->parent == NULL) goto out_unlock; err = -ENOMEM; s = add_name(&buf, &bufsize, s, node->name); if (s == NULL) goto out_unlock; if (need_lock) { err = -EAGAIN; if (node->treelock < 0) goto out_unlock; node->treelock++; } } if (s[0]) memmove(buf, s, bufsize - (s - buf)); else strcpy(buf, "/"); *path = buf; if (wnodep) *wnodep = wnode; return 0; out_unlock: if (need_lock) unlock_path(f, nodeid, wnode, node); out_free: free(buf); out_err: return err; } 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 void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe) { int err; if (!qe->path1) { /* Just waiting for it to be unlocked */ if (get_node(f, qe->nodeid1)->treelock == 0) pthread_cond_signal(&qe->cond); return; } 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); } else { err = try_get_path2(f, qe->nodeid1, qe->name1, qe->nodeid2, qe->name2, qe->path1, qe->path2, qe->wnode1, qe->wnode2); } if (err == -EAGAIN) return; /* keep trying */ qe->err = err; qe->done = true; pthread_cond_signal(&qe->cond); } static void wake_up_queued(struct fuse *f) { struct lock_queue_element *qe; for (qe = f->lockq; qe != NULL; qe = qe->next) queue_element_wakeup(f, qe); } static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid, const char *name, bool wr) { if (f->conf.debug) { struct node *wnode = NULL; if (wr) wnode = lookup_node(f, nodeid, name); if (wnode) { fuse_log(FUSE_LOG_DEBUG, "%s %llu (w)\n", msg, (unsigned long long) wnode->nodeid); } else { fuse_log(FUSE_LOG_DEBUG, "%s %llu\n", msg, (unsigned long long) nodeid); } } } static void queue_path(struct fuse *f, struct lock_queue_element *qe) { struct lock_queue_element **qp; qe->done = false; pthread_cond_init(&qe->cond, NULL); qe->next = NULL; for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next); *qp = qe; } static void dequeue_path(struct fuse *f, struct lock_queue_element *qe) { struct lock_queue_element **qp; pthread_cond_destroy(&qe->cond); for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next); *qp = qe->next; } static int wait_path(struct fuse *f, struct lock_queue_element *qe) { queue_path(f, qe); do { pthread_cond_wait(&qe->cond, &f->lock); } while (!qe->done); dequeue_path(f, qe); return qe->err; } static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name, char **path, struct node **wnode) { int err; pthread_mutex_lock(&f->lock); err = try_get_path(f, nodeid, name, path, wnode, true); if (err == -EAGAIN) { struct lock_queue_element qe = { .nodeid1 = nodeid, .name1 = name, .path1 = path, .wnode1 = wnode, }; debug_path(f, "QUEUE PATH", nodeid, name, !!wnode); err = wait_path(f, &qe); debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode); } pthread_mutex_unlock(&f->lock); return err; } static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path) { return get_path_common(f, nodeid, NULL, path, NULL); } static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path) { int err = 0; if (f->conf.nullpath_ok) { *path = NULL; } else { err = get_path_common(f, nodeid, NULL, path, NULL); if (err == -ESTALE) err = 0; } return err; } static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name, char **path) { return get_path_common(f, nodeid, name, path, NULL); } static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name, char **path, struct node **wnode) { return get_path_common(f, nodeid, name, path, wnode); } #if defined(__FreeBSD__) #define CHECK_DIR_LOOP #endif #if defined(CHECK_DIR_LOOP) static int check_dir_loop(struct fuse *f, fuse_ino_t nodeid1, const char *name1, fuse_ino_t nodeid2, const char *name2) { struct node *node, *node1, *node2; fuse_ino_t id1, id2; node1 = lookup_node(f, nodeid1, name1); id1 = node1 ? node1->nodeid : nodeid1; node2 = lookup_node(f, nodeid2, name2); id2 = node2 ? node2->nodeid : nodeid2; for (node = get_node(f, id2); node->nodeid != FUSE_ROOT_ID; node = node->parent) { if (node->name == NULL || node->parent == NULL) break; if (node->nodeid != id2 && node->nodeid == id1) return -EINVAL; } if (node2) { for (node = get_node(f, id1); node->nodeid != FUSE_ROOT_ID; node = node->parent) { if (node->name == NULL || node->parent == NULL) break; if (node->nodeid != id1 && node->nodeid == id2) return -ENOTEMPTY; } } return 0; } #endif 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, struct node **wnode1, struct node **wnode2) { int err; pthread_mutex_lock(&f->lock); #if defined(CHECK_DIR_LOOP) if (name1) { // called during rename; perform dir loop check err = check_dir_loop(f, nodeid1, name1, nodeid2, name2); if (err) goto out_unlock; } #endif err = try_get_path2(f, nodeid1, name1, nodeid2, name2, path1, path2, wnode1, wnode2); if (err == -EAGAIN) { struct lock_queue_element qe = { .nodeid1 = nodeid1, .name1 = name1, .path1 = path1, .wnode1 = wnode1, .nodeid2 = nodeid2, .name2 = name2, .path2 = path2, .wnode2 = wnode2, }; debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1); debug_path(f, " PATH2", nodeid2, name2, !!wnode2); err = wait_path(f, &qe); debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1); debug_path(f, " PATH2", nodeid2, name2, !!wnode2); } #if defined(CHECK_DIR_LOOP) out_unlock: #endif pthread_mutex_unlock(&f->lock); return err; } static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid, struct node *wnode, char *path) { pthread_mutex_lock(&f->lock); unlock_path(f, nodeid, wnode, NULL); if (f->lockq) wake_up_queued(f); pthread_mutex_unlock(&f->lock); free(path); } static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path) { if (path) free_path_wrlock(f, nodeid, NULL, path); } static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2, struct node *wnode1, struct node *wnode2, char *path1, char *path2) { pthread_mutex_lock(&f->lock); unlock_path(f, nodeid1, wnode1, NULL); unlock_path(f, nodeid2, wnode2, NULL); wake_up_queued(f); pthread_mutex_unlock(&f->lock); free(path1); free(path2); } static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup) { struct node *node; if (nodeid == FUSE_ROOT_ID) return; pthread_mutex_lock(&f->lock); node = get_node(f, nodeid); /* * Node may still be locked due to interrupt idiocy in open, * create and opendir */ while (node->nlookup == nlookup && node->treelock) { struct lock_queue_element qe = { .nodeid1 = nodeid, }; debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false); queue_path(f, &qe); do { pthread_cond_wait(&qe.cond, &f->lock); } while (node->nlookup == nlookup && node->treelock); dequeue_path(f, &qe); debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false); } assert(node->nlookup >= nlookup); node->nlookup -= nlookup; if (!node->nlookup) { unref_node(f, node); } else if (lru_enabled(f) && node->nlookup == 1) { set_forget_time(f, node); } pthread_mutex_unlock(&f->lock); } static void unlink_node(struct fuse *f, struct node *node) { if (f->conf.remember) { assert(node->nlookup > 1); node->nlookup--; } unhash_name(f, node); } static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name) { struct node *node; pthread_mutex_lock(&f->lock); node = lookup_node(f, dir, name); if (node != NULL) unlink_node(f, node); pthread_mutex_unlock(&f->lock); } static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname, fuse_ino_t newdir, const char *newname, int hide) { struct node *node; struct node *newnode; int err = 0; pthread_mutex_lock(&f->lock); node = lookup_node(f, olddir, oldname); newnode = lookup_node(f, newdir, newname); if (node == NULL) goto out; if (newnode != NULL) { if (hide) { fuse_log(FUSE_LOG_ERR, "fuse: hidden file got created during hiding\n"); err = -EBUSY; goto out; } unlink_node(f, newnode); } unhash_name(f, node); if (hash_name(f, node, newdir, newname) == -1) { err = -ENOMEM; goto out; } if (hide) node->is_hidden = 1; out: pthread_mutex_unlock(&f->lock); return err; } static int exchange_node(struct fuse *f, fuse_ino_t olddir, const char *oldname, fuse_ino_t newdir, const char *newname) { struct node *oldnode; struct node *newnode; int err; pthread_mutex_lock(&f->lock); oldnode = lookup_node(f, olddir, oldname); newnode = lookup_node(f, newdir, newname); if (oldnode) unhash_name(f, oldnode); if (newnode) unhash_name(f, newnode); err = -ENOMEM; if (oldnode) { if (hash_name(f, oldnode, newdir, newname) == -1) goto out; } if (newnode) { if (hash_name(f, newnode, olddir, oldname) == -1) goto out; } err = 0; out: pthread_mutex_unlock(&f->lock); return err; } static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf) { if (!f->conf.use_ino) stbuf->st_ino = nodeid; if (f->conf.set_mode) { if (f->conf.dmask && S_ISDIR(stbuf->st_mode)) stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->conf.dmask); else if (f->conf.fmask) stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->conf.fmask); else stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->conf.umask); } if (f->conf.set_uid) stbuf->st_uid = f->conf.uid; if (f->conf.set_gid) stbuf->st_gid = f->conf.gid; } #ifdef HAVE_STATX static void set_statx(struct fuse *f, fuse_ino_t nodeid, struct statx *stxbuf) { if (!f->conf.use_ino) stxbuf->stx_ino = nodeid; if (f->conf.set_mode) { if (f->conf.dmask && S_ISDIR(stxbuf->stx_mode)) stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) | (0777 & ~f->conf.dmask); else if (f->conf.fmask) stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) | (0777 & ~f->conf.fmask); else stxbuf->stx_mode = (stxbuf->stx_mode & S_IFMT) | (0777 & ~f->conf.umask); } if (f->conf.set_uid) stxbuf->stx_uid = f->conf.uid; if (f->conf.set_gid) stxbuf->stx_gid = f->conf.gid; } #endif static struct fuse *req_fuse(fuse_req_t req) { return (struct fuse *) fuse_req_userdata(req); } static void fuse_intr_sighandler(int sig) { (void) sig; /* Nothing to do */ } struct fuse_intr_data { pthread_t id; pthread_cond_t cond; int finished; }; static void fuse_interrupt(fuse_req_t req, void *d_) { struct fuse_intr_data *d = d_; struct fuse *f = req_fuse(req); if (d->id == pthread_self()) return; pthread_mutex_lock(&f->lock); while (!d->finished) { struct timeval now; struct timespec timeout; pthread_kill(d->id, f->conf.intr_signal); gettimeofday(&now, NULL); timeout.tv_sec = now.tv_sec + 1; timeout.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait(&d->cond, &f->lock, &timeout); } pthread_mutex_unlock(&f->lock); } static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req, struct fuse_intr_data *d) { pthread_mutex_lock(&f->lock); d->finished = 1; pthread_cond_broadcast(&d->cond); pthread_mutex_unlock(&f->lock); fuse_req_interrupt_func(req, NULL, NULL); pthread_cond_destroy(&d->cond); } static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d) { d->id = pthread_self(); pthread_cond_init(&d->cond, NULL); d->finished = 0; fuse_req_interrupt_func(req, fuse_interrupt, d); } static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req, struct fuse_intr_data *d) { if (f->conf.intr) fuse_do_finish_interrupt(f, req, d); } static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req, struct fuse_intr_data *d) { if (f->conf.intr) fuse_do_prepare_interrupt(req, d); } static const char* file_info_string(struct fuse_file_info *fi, char* buf, size_t len) { if(fi == NULL) return "NULL"; snprintf(buf, len, "%llu", (unsigned long long) fi->fh); return buf; } int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.getattr) return -ENOSYS; if (fs->debug) { char buf[10]; fuse_log(FUSE_LOG_DEBUG, "getattr[%s] %s\n", file_info_string(fi, buf, sizeof(buf)), path); } return fs->op.getattr(path, buf, fi); } int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, const char *newpath, unsigned int flags) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.rename) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath, flags); return fs->op.rename(oldpath, newpath, flags); } int fuse_fs_unlink(struct fuse_fs *fs, const char *path) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.unlink) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path); return fs->op.unlink(path); } int fuse_fs_rmdir(struct fuse_fs *fs, const char *path) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.rmdir) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path); return fs->op.rmdir(path); } int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.symlink) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path); return fs->op.symlink(linkname, path); } int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.link) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath); return fs->op.link(oldpath, newpath); } int fuse_fs_release(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.release) return 0; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "release%s[%llu] flags: 0x%x\n", fi->flush ? "+flush" : "", (unsigned long long) fi->fh, fi->flags); return fs->op.release(path, fi); } int fuse_fs_opendir(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi) { int err; fuse_get_context()->private_data = fs->user_data; if (!fs->op.opendir) return 0; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "opendir flags: 0x%x %s\n", fi->flags, path); err = fs->op.opendir(path, fi); if (fs->debug && !err) fuse_log(FUSE_LOG_DEBUG, " opendir[%llu] flags: 0x%x %s\n", (unsigned long long) fi->fh, fi->flags, path); return err; } int fuse_fs_open(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi) { int err; fuse_get_context()->private_data = fs->user_data; if (!fs->op.open) return 0; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "open flags: 0x%x %s\n", fi->flags, path); err = fs->op.open(path, fi); if (fs->debug && !err) fuse_log(FUSE_LOG_DEBUG, " open[%llu] flags: 0x%x %s\n", (unsigned long long) fi->fh, fi->flags, path); return err; } static void fuse_free_buf(struct fuse_bufvec *buf) { if (buf != NULL) { size_t i; for (i = 0; i < buf->count; i++) if (!(buf->buf[i].flags & FUSE_BUF_IS_FD)) free(buf->buf[i].mem); free(buf); } } int fuse_fs_read_buf(struct fuse_fs *fs, const char *path, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *fi) { int res; fuse_get_context()->private_data = fs->user_data; if (!fs->op.read && !fs->op.read_buf) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "read[%llu] %zu bytes from %llu flags: 0x%x\n", (unsigned long long) fi->fh, size, (unsigned long long) off, fi->flags); if (fs->op.read_buf) { res = fs->op.read_buf(path, bufp, size, off, fi); } else { struct fuse_bufvec *buf; void *mem; buf = malloc(sizeof(struct fuse_bufvec)); if (buf == NULL) return -ENOMEM; mem = malloc(size); if (mem == NULL) { free(buf); return -ENOMEM; } *buf = FUSE_BUFVEC_INIT(size); buf->buf[0].mem = mem; *bufp = buf; res = fs->op.read(path, mem, size, off, fi); if (res >= 0) buf->buf[0].size = res; } if (fs->debug && res >= 0) fuse_log(FUSE_LOG_DEBUG, " read[%llu] %zu bytes from %llu\n", (unsigned long long) fi->fh, fuse_buf_size(*bufp), (unsigned long long) off); if (res >= 0 && fuse_buf_size(*bufp) > size) fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n"); if (res < 0) return res; return 0; } int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size, off_t off, struct fuse_file_info *fi) { int res; fuse_get_context()->private_data = fs->user_data; if (!fs->op.read && !fs->op.read_buf) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "read[%llu] %zu bytes from %llu flags: 0x%x\n", (unsigned long long) fi->fh, size, (unsigned long long) off, fi->flags); if (fs->op.read_buf) { struct fuse_bufvec *buf = NULL; res = fs->op.read_buf(path, &buf, size, off, fi); if (res == 0) { struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size); dst.buf[0].mem = mem; res = fuse_buf_copy(&dst, buf, 0); } fuse_free_buf(buf); } else { res = fs->op.read(path, mem, size, off, fi); } if (fs->debug && res >= 0) fuse_log(FUSE_LOG_DEBUG, " read[%llu] %u bytes from %llu\n", (unsigned long long) fi->fh, res, (unsigned long long) off); if (res >= 0 && res > (int) size) fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n"); return res; } int fuse_fs_write_buf(struct fuse_fs *fs, const char *path, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *fi) { int res; size_t size; fuse_get_context()->private_data = fs->user_data; if (!fs->op.write_buf && !fs->op.write) return -ENOSYS; size = fuse_buf_size(buf); assert(buf->idx == 0 && buf->off == 0); if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "write%s[%llu] %zu bytes to %llu flags: 0x%x\n", fi->writepage ? "page" : "", (unsigned long long) fi->fh, size, (unsigned long long) off, fi->flags); if (fs->op.write_buf) { res = fs->op.write_buf(path, buf, off, fi); } else { void *mem = NULL; struct fuse_buf *flatbuf; struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size); if (buf->count == 1 && !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { flatbuf = &buf->buf[0]; } else { res = -ENOMEM; mem = malloc(size); if (mem == NULL) goto out; tmp.buf[0].mem = mem; res = fuse_buf_copy(&tmp, buf, 0); if (res <= 0) goto out_free; tmp.buf[0].size = res; flatbuf = &tmp.buf[0]; } res = fs->op.write(path, flatbuf->mem, flatbuf->size, off, fi); out_free: free(mem); } out: if (fs->debug && res >= 0) fuse_log(FUSE_LOG_DEBUG, " write%s[%llu] %u bytes to %llu\n", fi->writepage ? "page" : "", (unsigned long long) fi->fh, res, (unsigned long long) off); if (res > (int) size) fuse_log(FUSE_LOG_ERR, "fuse: wrote too many bytes\n"); return res; } int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem, size_t size, off_t off, struct fuse_file_info *fi) { struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size); bufv.buf[0].mem = (void *) mem; return fuse_fs_write_buf(fs, path, &bufv, off, fi); } int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.fsync) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "fsync[%llu] datasync: %i\n", (unsigned long long) fi->fh, datasync); return fs->op.fsync(path, datasync, fi); } int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.fsyncdir) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "fsyncdir[%llu] datasync: %i\n", (unsigned long long) fi->fh, datasync); return fs->op.fsyncdir(path, datasync, fi); } int fuse_fs_flush(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.flush) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n", (unsigned long long) fi->fh); return fs->op.flush(path, fi); } int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf) { fuse_get_context()->private_data = fs->user_data; if (fs->op.statfs) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "statfs %s\n", path); return fs->op.statfs(path, buf); } else { buf->f_namemax = 255; buf->f_bsize = 512; return 0; } } int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.releasedir) return 0; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "releasedir[%llu] flags: 0x%x\n", (unsigned long long) fi->fh, fi->flags); return fs->op.releasedir(path, fi); } int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, fuse_fill_dir_t filler, off_t off, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.readdir) return -ENOSYS; if (fs->debug) { fuse_log(FUSE_LOG_DEBUG, "readdir%s[%llu] from %llu\n", (flags & FUSE_READDIR_PLUS) ? "plus" : "", (unsigned long long) fi->fh, (unsigned long long) off); } return fs->op.readdir(path, buf, filler, off, fi, flags); } int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, struct fuse_file_info *fi) { int err; fuse_get_context()->private_data = fs->user_data; if (!fs->op.create) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "create flags: 0x%x %s 0%o umask=0%03o\n", fi->flags, path, mode, fuse_get_context()->umask); err = fs->op.create(path, mode, fi); if (fs->debug && !err) fuse_log(FUSE_LOG_DEBUG, " create[%llu] flags: 0x%x %s\n", (unsigned long long) fi->fh, fi->flags, path); return err; } int fuse_fs_lock(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, int cmd, struct flock *lock) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.lock) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n", (unsigned long long) fi->fh, (cmd == F_GETLK ? "F_GETLK" : (cmd == F_SETLK ? "F_SETLK" : (cmd == F_SETLKW ? "F_SETLKW" : "???"))), (lock->l_type == F_RDLCK ? "F_RDLCK" : (lock->l_type == F_WRLCK ? "F_WRLCK" : (lock->l_type == F_UNLCK ? "F_UNLCK" : "???"))), (unsigned long long) lock->l_start, (unsigned long long) lock->l_len, (unsigned long long) lock->l_pid); return fs->op.lock(path, fi, cmd, lock); } int fuse_fs_flock(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, int op) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.flock) return -ENOSYS; if (fs->debug) { int xop = op & ~LOCK_NB; fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s%s\n", (unsigned long long) fi->fh, xop == LOCK_SH ? "LOCK_SH" : (xop == LOCK_EX ? "LOCK_EX" : (xop == LOCK_UN ? "LOCK_UN" : "???")), (op & LOCK_NB) ? "|LOCK_NB" : ""); } return fs->op.flock(path, fi, op); } int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.chown) return -ENOSYS; if (fs->debug) { char buf[10]; fuse_log(FUSE_LOG_DEBUG, "chown[%s] %s %lu %lu\n", file_info_string(fi, buf, sizeof(buf)), path, (unsigned long) uid, (unsigned long) gid); } return fs->op.chown(path, uid, gid, fi); } int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.truncate) return -ENOSYS; if (fs->debug) { char buf[10]; fuse_log(FUSE_LOG_DEBUG, "truncate[%s] %llu\n", file_info_string(fi, buf, sizeof(buf)), (unsigned long long) size); } return fs->op.truncate(path, size, fi); } int fuse_fs_utimens(struct fuse_fs *fs, const char *path, const struct timespec tv[2], struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.utimens) return -ENOSYS; if (fs->debug) { char buf[10]; fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %jd.%09ld %jd.%09ld\n", file_info_string(fi, buf, sizeof(buf)), path, (intmax_t)tv[0].tv_sec, tv[0].tv_nsec, (intmax_t)tv[1].tv_sec, tv[1].tv_nsec); } return fs->op.utimens(path, tv, fi); } int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.access) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask); return fs->op.access(path, mask); } int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, size_t len) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.readlink) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path, (unsigned long) len); return fs->op.readlink(path, buf, len); } int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, dev_t rdev) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.mknod) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "mknod %s 0%o 0x%llx umask=0%03o\n", path, mode, (unsigned long long) rdev, fuse_get_context()->umask); return fs->op.mknod(path, mode, rdev); } int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.mkdir) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "mkdir %s 0%o umask=0%03o\n", path, mode, fuse_get_context()->umask); return fs->op.mkdir(path, mode); } int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, const char *value, size_t size, int flags) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.setxattr) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "setxattr %s %s %lu 0x%x\n", path, name, (unsigned long) size, flags); return fs->op.setxattr(path, name, value, size, flags); } int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, char *value, size_t size) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.getxattr) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "getxattr %s %s %lu\n", path, name, (unsigned long) size); return fs->op.getxattr(path, name, value, size); } int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, size_t size) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.listxattr) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n", path, (unsigned long) size); return fs->op.listxattr(path, list, size); } int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, uint64_t *idx) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.bmap) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "bmap %s blocksize: %lu index: %llu\n", path, (unsigned long) blocksize, (unsigned long long) *idx); return fs->op.bmap(path, blocksize, idx); } int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.removexattr) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name); return fs->op.removexattr(path, name); } int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd, void *arg, struct fuse_file_info *fi, unsigned int flags, void *data) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.ioctl) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "ioctl[%llu] 0x%x flags: 0x%x\n", (unsigned long long) fi->fh, cmd, flags); return fs->op.ioctl(path, cmd, arg, fi, flags, data); } int fuse_fs_poll(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp) { int res; fuse_get_context()->private_data = fs->user_data; if (!fs->op.poll) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "poll[%llu] ph: %p, events 0x%x\n", (unsigned long long) fi->fh, ph, fi->poll_events); res = fs->op.poll(path, fi, ph, reventsp); if (fs->debug && !res) fuse_log(FUSE_LOG_DEBUG, " poll[%llu] revents: 0x%x\n", (unsigned long long) fi->fh, *reventsp); return res; } int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode, off_t offset, off_t length, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.fallocate) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "fallocate %s mode %x, offset: %llu, length: %llu\n", path, mode, (unsigned long long) offset, (unsigned long long) length); return fs->op.fallocate(path, mode, offset, length, fi); } ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in, struct fuse_file_info *fi_in, off_t off_in, const char *path_out, struct fuse_file_info *fi_out, off_t off_out, size_t len, int flags) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.copy_file_range) return -ENOSYS; if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "copy_file_range from %s:%llu to " "%s:%llu, length: %llu\n", path_in, (unsigned long long) off_in, path_out, (unsigned long long) off_out, (unsigned long long) len); return fs->op.copy_file_range(path_in, fi_in, off_in, path_out, fi_out, off_out, len, flags); } off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.lseek) return -ENOSYS; if (fs->debug) { char buf[10]; fuse_log(FUSE_LOG_DEBUG, "lseek[%s] %llu %d\n", file_info_string(fi, buf, sizeof(buf)), (unsigned long long) off, whence); } return fs->op.lseek(path, off, whence, fi); } #ifdef HAVE_STATX int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (fs->op.statx) { if (fs->debug) { char buf[10]; fuse_log(FUSE_LOG_DEBUG, "statx[%s] %s %d %d\n", file_info_string(fi, buf, sizeof(buf)), path, flags, mask); } return fs->op.statx(path, flags, mask, stxbuf, fi); } return -ENOSYS; } #else int fuse_fs_statx(struct fuse_fs *fs, const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi) { (void)fs; (void)path; (void)flags; (void)mask; (void)stxbuf; (void)fi; return -ENOSYS; } #endif static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) { struct node *node; int isopen = 0; pthread_mutex_lock(&f->lock); node = lookup_node(f, dir, name); if (node && node->open_count > 0) isopen = 1; pthread_mutex_unlock(&f->lock); return isopen; } static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname, char *newname, size_t bufsize) { struct stat buf; struct node *node; struct node *newnode; char *newpath; int res; int failctr = 10; do { pthread_mutex_lock(&f->lock); node = lookup_node(f, dir, oldname); if (node == NULL) { pthread_mutex_unlock(&f->lock); return NULL; } do { f->hidectr ++; snprintf(newname, bufsize, ".fuse_hidden%08x%08x", (unsigned int) node->nodeid, f->hidectr); newnode = lookup_node(f, dir, newname); } while(newnode); res = try_get_path(f, dir, newname, &newpath, NULL, false); pthread_mutex_unlock(&f->lock); if (res) break; memset(&buf, 0, sizeof(buf)); res = fuse_fs_getattr(f->fs, newpath, &buf, NULL); if (res == -ENOENT) break; free(newpath); newpath = NULL; } while(res == 0 && --failctr); return newpath; } static int hide_node(struct fuse *f, const char *oldpath, fuse_ino_t dir, const char *oldname) { char newname[64]; char *newpath; int err = -EBUSY; newpath = hidden_name(f, dir, oldname, newname, sizeof(newname)); if (newpath) { err = fuse_fs_rename(f->fs, oldpath, newpath, 0); if (!err) err = rename_node(f, dir, oldname, dir, newname, 1); free(newpath); } return err; } static int mtime_eq(const struct stat *stbuf, const struct timespec *ts) { return stbuf->st_mtime == ts->tv_sec && ST_MTIM_NSEC(stbuf) == ts->tv_nsec; } #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC CLOCK_REALTIME #endif static void curr_time(struct timespec *now) { static clockid_t clockid = CLOCK_MONOTONIC; int res = clock_gettime(clockid, now); if (res == -1 && errno == EINVAL) { clockid = CLOCK_REALTIME; res = clock_gettime(clockid, now); } if (res == -1) { perror("fuse: clock_gettime"); abort(); } } static void update_stat(struct node *node, const struct stat *stbuf) { if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) || stbuf->st_size != node->size)) node->cache_valid = 0; node->mtime.tv_sec = stbuf->st_mtime; node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf); node->size = stbuf->st_size; curr_time(&node->stat_updated); } static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name, struct fuse_entry_param *e) { struct node *node; node = find_node(f, nodeid, name); if (node == NULL) return -ENOMEM; e->ino = node->nodeid; e->generation = node->generation; e->entry_timeout = f->conf.entry_timeout; e->attr_timeout = f->conf.attr_timeout; if (f->conf.auto_cache) { pthread_mutex_lock(&f->lock); update_stat(node, &e->attr); pthread_mutex_unlock(&f->lock); } set_stat(f, e->ino, &e->attr); return 0; } static int lookup_path(struct fuse *f, fuse_ino_t nodeid, const char *name, const char *path, struct fuse_entry_param *e, struct fuse_file_info *fi) { int res; memset(e, 0, sizeof(struct fuse_entry_param)); res = fuse_fs_getattr(f->fs, path, &e->attr, fi); if (res == 0) { res = do_lookup(f, nodeid, name, e); if (res == 0 && f->conf.debug) { fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu\n", (unsigned long long) e->ino); } } return res; } static struct fuse_context_i *fuse_get_context_internal(void) { return (struct fuse_context_i *) pthread_getspecific(fuse_context_key); } static struct fuse_context_i *fuse_create_context(struct fuse *f) { struct fuse_context_i *c = fuse_get_context_internal(); if (c == NULL) { c = (struct fuse_context_i *) calloc(1, sizeof(struct fuse_context_i)); if (c == NULL) { /* This is hard to deal with properly, so just abort. If memory is so low that the context cannot be allocated, there's not much hope for the filesystem anyway */ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate thread specific data\n"); abort(); } pthread_setspecific(fuse_context_key, c); } else { memset(c, 0, sizeof(*c)); } c->ctx.fuse = f; return c; } static void fuse_freecontext(void *data) { free(data); } static int fuse_create_context_key(void) { int err = 0; pthread_mutex_lock(&fuse_context_lock); if (!fuse_context_ref) { err = pthread_key_create(&fuse_context_key, fuse_freecontext); if (err) { fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n", strerror(err)); pthread_mutex_unlock(&fuse_context_lock); return -1; } } fuse_context_ref++; pthread_mutex_unlock(&fuse_context_lock); return 0; } static void fuse_delete_context_key(void) { pthread_mutex_lock(&fuse_context_lock); fuse_context_ref--; if (!fuse_context_ref) { free(pthread_getspecific(fuse_context_key)); pthread_key_delete(fuse_context_key); } pthread_mutex_unlock(&fuse_context_lock); } static struct fuse *req_fuse_prepare(fuse_req_t req) { struct fuse_context_i *c = fuse_create_context(req_fuse(req)); const struct fuse_ctx *ctx = fuse_req_ctx(req); c->req = req; c->ctx.uid = ctx->uid; c->ctx.gid = ctx->gid; c->ctx.pid = ctx->pid; c->ctx.umask = ctx->umask; return c->ctx.fuse; } static inline void reply_err(fuse_req_t req, int err) { /* fuse_reply_err() uses non-negated errno values */ fuse_reply_err(req, -err); } static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e, int err) { if (!err) { struct fuse *f = req_fuse(req); if (fuse_reply_entry(req, e) == -ENOENT) { /* Skip forget for negative result */ if (e->ino != 0) forget_node(f, e->ino, 1); } } else reply_err(req, err); } void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn, struct fuse_config *cfg) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.write_buf) fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ); if (!fs->op.lock) fuse_unset_feature_flag(conn, FUSE_CAP_POSIX_LOCKS); if (!fs->op.flock) fuse_unset_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS); if (fs->op.init) fs->user_data = fs->op.init(conn, cfg); } static int fuse_init_intr_signal(int signum, int *installed); static void fuse_lib_init(void *data, struct fuse_conn_info *conn) { struct fuse *f = (struct fuse *) data; fuse_create_context(f); fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT); fuse_fs_init(f->fs, conn, &f->conf); if (f->conf.intr) { if (fuse_init_intr_signal(f->conf.intr_signal, &f->intr_installed) == -1) fuse_log(FUSE_LOG_ERR, "fuse: failed to init interrupt signal\n"); } else { /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; } } 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); } static void fuse_lib_destroy(void *data) { struct fuse *f = (struct fuse *) data; fuse_create_context(f); fuse_fs_destroy(f->fs); } static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e = { .ino = 0 }; /* invalid ino */ char *path; int err; struct node *dot = NULL; if (name[0] == '.') { int len = strlen(name); if (len == 1 || (name[1] == '.' && len == 2)) { pthread_mutex_lock(&f->lock); if (len == 1) { if (f->conf.debug) fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOT\n"); dot = get_node_nocheck(f, parent); if (dot == NULL) { pthread_mutex_unlock(&f->lock); reply_entry(req, &e, -ESTALE); return; } dot->refctr++; } else { if (f->conf.debug) fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOTDOT\n"); parent = get_node(f, parent)->parent->nodeid; } pthread_mutex_unlock(&f->lock); name = NULL; } } err = get_path_name(f, parent, name, &path); if (!err) { struct fuse_intr_data d; if (f->conf.debug) fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path); fuse_prepare_interrupt(f, req, &d); err = lookup_path(f, parent, name, path, &e, NULL); if (err == -ENOENT && f->conf.negative_timeout != 0.0) { e.ino = 0; e.entry_timeout = f->conf.negative_timeout; err = 0; } fuse_finish_interrupt(f, req, &d); free_path(f, parent, path); } if (dot) { pthread_mutex_lock(&f->lock); unref_node(f, dot); pthread_mutex_unlock(&f->lock); } reply_entry(req, &e, err); } static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup) { if (f->conf.debug) fuse_log(FUSE_LOG_DEBUG, "FORGET %llu/%llu\n", (unsigned long long)ino, (unsigned long long) nlookup); forget_node(f, ino, nlookup); } static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { do_forget(req_fuse(req), ino, nlookup); fuse_reply_none(req); } static void fuse_lib_forget_multi(fuse_req_t req, size_t count, struct fuse_forget_data *forgets) { struct fuse *f = req_fuse(req); size_t i; for (i = 0; i < count; i++) do_forget(f, forgets[i].ino, forgets[i].nlookup); fuse_reply_none(req); } static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct stat buf; char *path; int err; memset(&buf, 0, sizeof(buf)); if (fi != NULL) err = get_path_nullok(f, ino, &path); else err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_getattr(f->fs, path, &buf, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (!err) { struct node *node; pthread_mutex_lock(&f->lock); node = get_node(f, ino); if (node->is_hidden && buf.st_nlink > 0) buf.st_nlink--; if (f->conf.auto_cache) update_stat(node, &buf); pthread_mutex_unlock(&f->lock); set_stat(f, ino, &buf); fuse_reply_attr(req, &buf, f->conf.attr_timeout); } else reply_err(req, err); } int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (!fs->op.chmod) return -ENOSYS; if (fs->debug) { char buf[10]; fuse_log(FUSE_LOG_DEBUG, "chmod[%s] %s %llo\n", file_info_string(fi, buf, sizeof(buf)), path, (unsigned long long) mode); } return fs->op.chmod(path, mode, fi); } static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int valid, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct stat buf; char *path; int err; memset(&buf, 0, sizeof(buf)); if (fi != NULL) err = get_path_nullok(f, ino, &path); else err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = 0; if (!err && (valid & FUSE_SET_ATTR_MODE)) err = fuse_fs_chmod(f->fs, path, attr->st_mode, fi); if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) { uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; err = fuse_fs_chown(f->fs, path, uid, gid, fi); } if (!err && (valid & FUSE_SET_ATTR_SIZE)) { err = fuse_fs_truncate(f->fs, path, attr->st_size, fi); } #ifdef HAVE_UTIMENSAT if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) { struct timespec tv[2]; tv[0].tv_sec = 0; tv[1].tv_sec = 0; tv[0].tv_nsec = UTIME_OMIT; tv[1].tv_nsec = UTIME_OMIT; if (valid & FUSE_SET_ATTR_ATIME_NOW) tv[0].tv_nsec = UTIME_NOW; else if (valid & FUSE_SET_ATTR_ATIME) tv[0] = attr->st_atim; if (valid & FUSE_SET_ATTR_MTIME_NOW) tv[1].tv_nsec = UTIME_NOW; else if (valid & FUSE_SET_ATTR_MTIME) tv[1] = attr->st_mtim; err = fuse_fs_utimens(f->fs, path, tv, fi); } else #endif if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) == (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { struct timespec tv[2]; tv[0].tv_sec = attr->st_atime; tv[0].tv_nsec = ST_ATIM_NSEC(attr); tv[1].tv_sec = attr->st_mtime; tv[1].tv_nsec = ST_MTIM_NSEC(attr); err = fuse_fs_utimens(f->fs, path, tv, fi); } if (!err) { err = fuse_fs_getattr(f->fs, path, &buf, fi); } fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (!err) { if (f->conf.auto_cache) { pthread_mutex_lock(&f->lock); update_stat(get_node(f, ino), &buf); pthread_mutex_unlock(&f->lock); } set_stat(f, ino, &buf); fuse_reply_attr(req, &buf, f->conf.attr_timeout); } else reply_err(req, err); } static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask) { struct fuse *f = req_fuse_prepare(req); char *path; int err; err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_access(f->fs, path, mask); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } reply_err(req, err); } static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino) { struct fuse *f = req_fuse_prepare(req); char linkname[PATH_MAX + 1]; char *path; int err; err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname)); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (!err) { linkname[PATH_MAX] = '\0'; fuse_reply_readlink(req, linkname); } else reply_err(req, err); } static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; char *path; int err; err = get_path_name(f, parent, name, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = -ENOSYS; if (S_ISREG(mode)) { struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = O_CREAT | O_EXCL | O_WRONLY; err = fuse_fs_create(f->fs, path, mode, &fi); if (!err) { err = lookup_path(f, parent, name, path, &e, &fi); fuse_fs_release(f->fs, path, &fi); } } if (err == -ENOSYS) { err = fuse_fs_mknod(f->fs, path, mode, rdev); if (!err) err = lookup_path(f, parent, name, path, &e, NULL); } fuse_finish_interrupt(f, req, &d); free_path(f, parent, path); } reply_entry(req, &e, err); } static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; char *path; int err; err = get_path_name(f, parent, name, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_mkdir(f->fs, path, mode); if (!err) err = lookup_path(f, parent, name, path, &e, NULL); fuse_finish_interrupt(f, req, &d); free_path(f, parent, path); } reply_entry(req, &e, err); } static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse *f = req_fuse_prepare(req); struct node *wnode; char *path; int err; err = get_path_wrlock(f, parent, name, &path, &wnode); if (!err) { struct fuse_intr_data d; 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) remove_node(f, parent, name); } fuse_finish_interrupt(f, req, &d); free_path_wrlock(f, parent, wnode, path); } reply_err(req, err); } static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse *f = req_fuse_prepare(req); struct node *wnode; char *path; int err; err = get_path_wrlock(f, parent, name, &path, &wnode); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_rmdir(f->fs, path); fuse_finish_interrupt(f, req, &d); if (!err) remove_node(f, parent, name); free_path_wrlock(f, parent, wnode, path); } reply_err(req, err); } static void fuse_lib_symlink(fuse_req_t req, const char *linkname, fuse_ino_t parent, const char *name) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; char *path; int err; err = get_path_name(f, parent, name, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_symlink(f->fs, linkname, path); if (!err) err = lookup_path(f, parent, name, path, &e, NULL); fuse_finish_interrupt(f, req, &d); free_path(f, parent, path); } reply_entry(req, &e, err); } static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir, const char *oldname, fuse_ino_t newdir, const char *newname, unsigned int flags) { struct fuse *f = req_fuse_prepare(req); char *oldpath; char *newpath; struct node *wnode1; struct node *wnode2; int err; err = get_path2(f, olddir, oldname, newdir, newname, &oldpath, &newpath, &wnode1, &wnode2); if (!err) { struct fuse_intr_data d; err = 0; fuse_prepare_interrupt(f, req, &d); if (!f->conf.hard_remove && !(flags & RENAME_EXCHANGE) && is_open(f, newdir, newname)) err = hide_node(f, newpath, newdir, newname); if (!err) { err = fuse_fs_rename(f->fs, oldpath, newpath, flags); if (!err) { if (flags & RENAME_EXCHANGE) { err = exchange_node(f, olddir, oldname, newdir, newname); } else { err = rename_node(f, olddir, oldname, newdir, newname, 0); } } } fuse_finish_interrupt(f, req, &d); free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath); } reply_err(req, err); } static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname) { struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; char *oldpath; char *newpath; int err; err = get_path2(f, ino, NULL, newparent, newname, &oldpath, &newpath, NULL, NULL); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_link(f->fs, oldpath, newpath); if (!err) err = lookup_path(f, newparent, newname, newpath, &e, NULL); fuse_finish_interrupt(f, req, &d); free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath); } reply_entry(req, &e, err); } static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path, struct fuse_file_info *fi) { struct node *node; int unlink_hidden = 0; fuse_fs_release(f->fs, path, fi); pthread_mutex_lock(&f->lock); node = get_node(f, ino); assert(node->open_count > 0); --node->open_count; if (node->is_hidden && !node->open_count) { unlink_hidden = 1; node->is_hidden = 0; } pthread_mutex_unlock(&f->lock); if(unlink_hidden) { if (path) { fuse_fs_unlink(f->fs, path); } else if (f->conf.nullpath_ok) { char *unlinkpath; if (get_path(f, ino, &unlinkpath) == 0) fuse_fs_unlink(f->fs, unlinkpath); free_path(f, ino, unlinkpath); } } } static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; struct fuse_entry_param e; char *path; int err; err = get_path_name(f, parent, name, &path); if (!err) { fuse_prepare_interrupt(f, req, &d); err = fuse_fs_create(f->fs, path, mode, fi); if (!err) { err = lookup_path(f, parent, name, path, &e, fi); if (err) fuse_fs_release(f->fs, path, fi); else if (!S_ISREG(e.attr.st_mode)) { err = -EIO; fuse_fs_release(f->fs, path, fi); forget_node(f, e.ino, 1); } else { if (f->conf.direct_io) 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); } if (!err) { pthread_mutex_lock(&f->lock); get_node(f, e.ino)->open_count++; pthread_mutex_unlock(&f->lock); if (fuse_reply_create(req, &e, fi) == -ENOENT) { /* The open syscall was interrupted, so it must be cancelled */ fuse_do_release(f, e.ino, path, fi); forget_node(f, e.ino, 1); } } else { reply_err(req, err); } free_path(f, parent, path); } static double diff_timespec(const struct timespec *t1, const struct timespec *t2) { return (t1->tv_sec - t2->tv_sec) + ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0; } static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path, struct fuse_file_info *fi) { struct node *node; pthread_mutex_lock(&f->lock); node = get_node(f, ino); if (node->cache_valid) { struct timespec now; curr_time(&now); if (diff_timespec(&now, &node->stat_updated) > f->conf.ac_attr_timeout) { struct stat stbuf; int err; pthread_mutex_unlock(&f->lock); err = fuse_fs_getattr(f->fs, path, &stbuf, fi); pthread_mutex_lock(&f->lock); if (!err) update_stat(node, &stbuf); else node->cache_valid = 0; } } if (node->cache_valid) fi->keep_cache = 1; node->cache_valid = 1; pthread_mutex_unlock(&f->lock); } static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; char *path; int err; err = get_path(f, ino, &path); if (!err) { fuse_prepare_interrupt(f, req, &d); err = fuse_fs_open(f->fs, path, fi); if (!err) { if (f->conf.direct_io) fi->direct_io = 1; if (f->conf.kernel_cache) fi->keep_cache = 1; 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); } if (!err) { pthread_mutex_lock(&f->lock); get_node(f, ino)->open_count++; pthread_mutex_unlock(&f->lock); if (fuse_reply_open(req, fi) == -ENOENT) { /* The open syscall was interrupted, so it must be cancelled */ fuse_do_release(f, ino, path, fi); } } else reply_err(req, err); free_path(f, ino, path); } static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct fuse_bufvec *buf = NULL; char *path; int res; res = get_path_nullok(f, ino, &path); if (res == 0) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (res == 0) fuse_reply_data(req, buf, FUSE_BUF_SPLICE_MOVE); else reply_err(req, res); fuse_free_buf(buf); } static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); char *path; int res; res = get_path_nullok(f, ino, &path); if (res == 0) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); res = fuse_fs_write_buf(f->fs, path, buf, off, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (res >= 0) fuse_reply_write(req, res); else reply_err(req, res); } static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); char *path; int err; err = get_path_nullok(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_fsync(f->fs, path, datasync, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } reply_err(req, err); } static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi, struct fuse_file_info *fi) { struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh; memset(fi, 0, sizeof(struct fuse_file_info)); fi->fh = dh->fh; return dh; } static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *llfi) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; struct fuse_dh *dh; struct fuse_file_info fi; char *path; int err; dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh)); if (dh == NULL) { reply_err(req, -ENOMEM); return; } memset(dh, 0, sizeof(struct fuse_dh)); dh->fuse = f; dh->contents = NULL; dh->first = NULL; dh->len = 0; dh->filled = 0; dh->nodeid = ino; pthread_mutex_init(&dh->lock, NULL); llfi->fh = (uintptr_t) dh; memset(&fi, 0, sizeof(fi)); fi.flags = llfi->flags; err = get_path(f, ino, &path); if (!err) { fuse_prepare_interrupt(f, req, &d); 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) { /* The opendir syscall was interrupted, so it must be cancelled */ fuse_fs_releasedir(f->fs, path, &fi); pthread_mutex_destroy(&dh->lock); free(dh); } } else { reply_err(req, err); pthread_mutex_destroy(&dh->lock); free(dh); } free_path(f, ino, path); } static int extend_contents(struct fuse_dh *dh, unsigned minsize) { if (minsize > dh->size) { char *newptr; unsigned newsize = dh->size; if (!newsize) newsize = 1024; while (newsize < minsize) { if (newsize >= 0x80000000) newsize = 0xffffffff; else newsize *= 2; } newptr = (char *) realloc(dh->contents, newsize); if (!newptr) { dh->error = -ENOMEM; return -1; } dh->contents = newptr; dh->size = newsize; } return 0; } static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name, struct stat *st, enum fuse_fill_dir_flags flags) { struct fuse_direntry *de; de = malloc(sizeof(struct fuse_direntry)); if (!de) { dh->error = -ENOMEM; return -1; } de->name = strdup(name); if (!de->name) { dh->error = -ENOMEM; free(de); return -1; } de->flags = flags; de->stat = *st; de->next = NULL; *dh->last = de; dh->last = &de->next; return 0; } static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent, const char *name) { struct node *node; fuse_ino_t res = FUSE_UNKNOWN_INO; pthread_mutex_lock(&f->lock); node = lookup_node(f, parent, name); if (node) res = node->nodeid; pthread_mutex_unlock(&f->lock); return res; } static int fill_dir(void *dh_, const char *name, const struct stat *statp, off_t off, enum fuse_fill_dir_flags flags) { struct fuse_dh *dh = (struct fuse_dh *) dh_; struct stat stbuf; if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) { dh->error = -EIO; return 1; } if (statp) stbuf = *statp; else { memset(&stbuf, 0, sizeof(stbuf)); stbuf.st_ino = FUSE_UNKNOWN_INO; } if (!dh->fuse->conf.use_ino) { stbuf.st_ino = FUSE_UNKNOWN_INO; if (dh->fuse->conf.readdir_ino) { stbuf.st_ino = (ino_t) lookup_nodeid(dh->fuse, dh->nodeid, name); } } if (off) { size_t newlen; if (dh->filled) { dh->error = -EIO; return 1; } if (dh->first) { dh->error = -EIO; return 1; } if (extend_contents(dh, dh->needlen) == -1) return 1; newlen = dh->len + fuse_add_direntry(dh->req, dh->contents + dh->len, dh->needlen - dh->len, name, &stbuf, off); if (newlen > dh->needlen) return 1; dh->len = newlen; } else { dh->filled = 1; if (fuse_add_direntry_to_dh(dh, name, &stbuf, flags) == -1) return 1; } return 0; } static int is_dot_or_dotdot(const char *name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp, off_t off, enum fuse_fill_dir_flags flags) { struct fuse_dh *dh = (struct fuse_dh *) dh_; struct fuse_entry_param e = { /* ino=0 tells the kernel to ignore readdirplus stat info */ .ino = 0, }; struct fuse *f = dh->fuse; int res; if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) { dh->error = -EIO; return 1; } if (statp && (flags & FUSE_FILL_DIR_PLUS)) { e.attr = *statp; } e.attr.st_ino = FUSE_UNKNOWN_INO; if (statp) { e.attr.st_mode = statp->st_mode; if (f->conf.use_ino) e.attr.st_ino = statp->st_ino; } if (!f->conf.use_ino && f->conf.readdir_ino) { e.attr.st_ino = (ino_t) lookup_nodeid(f, dh->nodeid, name); } if (off) { size_t newlen; if (dh->filled) { dh->error = -EIO; return 1; } if (dh->first) { dh->error = -EIO; return 1; } if (extend_contents(dh, dh->needlen) == -1) return 1; if (statp && (flags & FUSE_FILL_DIR_PLUS)) { if (!is_dot_or_dotdot(name)) { res = do_lookup(f, dh->nodeid, name, &e); if (res) { dh->error = res; return 1; } } } newlen = dh->len + fuse_add_direntry_plus(dh->req, dh->contents + dh->len, dh->needlen - dh->len, name, &e, off); if (newlen > dh->needlen) return 1; dh->len = newlen; } else { dh->filled = 1; if (fuse_add_direntry_to_dh(dh, name, &e.attr, flags) == -1) return 1; } return 0; } static void free_direntries(struct fuse_direntry *de) { while (de) { struct fuse_direntry *next = de->next; free(de->name); free(de); de = next; } } static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_dh *dh, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { char *path; int err; if (f->fs->op.readdir) err = get_path_nullok(f, ino, &path); else err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_fill_dir_t filler = fill_dir; if (flags & FUSE_READDIR_PLUS) filler = fill_dir_plus; free_direntries(dh->first); dh->first = NULL; dh->last = &dh->first; dh->len = 0; dh->error = 0; dh->needlen = size; dh->filled = 0; dh->req = req; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags); fuse_finish_interrupt(f, req, &d); dh->req = NULL; if (!err) err = dh->error; if (err) dh->filled = 0; free_path(f, ino, path); } return err; } static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh, off_t off, enum fuse_readdir_flags flags) { off_t pos; struct fuse_direntry *de = dh->first; int res; dh->len = 0; if (extend_contents(dh, dh->needlen) == -1) return dh->error; for (pos = 0; pos < off; pos++) { if (!de) break; de = de->next; } while (de) { char *p = dh->contents + dh->len; unsigned rem = dh->needlen - dh->len; unsigned thislen; unsigned newlen; pos++; if (flags & FUSE_READDIR_PLUS) { struct fuse_entry_param e = { .ino = 0, .attr = de->stat, }; if (de->flags & FUSE_FILL_DIR_PLUS && !is_dot_or_dotdot(de->name)) { res = do_lookup(dh->fuse, dh->nodeid, de->name, &e); if (res) { dh->error = res; return 1; } } thislen = fuse_add_direntry_plus(req, p, rem, de->name, &e, pos); } else { thislen = fuse_add_direntry(req, p, rem, de->name, &de->stat, pos); } newlen = dh->len + thislen; if (newlen > dh->needlen) break; dh->len = newlen; de = de->next; } return 0; } static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *llfi, enum fuse_readdir_flags flags) { struct fuse *f = req_fuse_prepare(req); struct fuse_file_info fi; struct fuse_dh *dh = get_dirhandle(llfi, &fi); int err; pthread_mutex_lock(&dh->lock); /* According to SUS, directory contents need to be refreshed on rewinddir() */ if (!off) dh->filled = 0; if (!dh->filled) { err = readdir_fill(f, req, ino, size, off, dh, &fi, flags); if (err) { reply_err(req, err); goto out; } } if (dh->filled) { dh->needlen = size; err = readdir_fill_from_list(req, dh, off, flags); if (err) { reply_err(req, err); goto out; } } fuse_reply_buf(req, dh->contents, dh->len); out: pthread_mutex_unlock(&dh->lock); } static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *llfi) { fuse_readdir_common(req, ino, size, off, llfi, 0); } static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *llfi) { fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS); } static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *llfi) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; struct fuse_file_info fi; struct fuse_dh *dh = get_dirhandle(llfi, &fi); char *path; get_path_nullok(f, ino, &path); fuse_prepare_interrupt(f, req, &d); fuse_fs_releasedir(f->fs, path, &fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); pthread_mutex_lock(&dh->lock); pthread_mutex_unlock(&dh->lock); pthread_mutex_destroy(&dh->lock); free_direntries(dh->first); free(dh->contents); free(dh); reply_err(req, 0); } static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *llfi) { struct fuse *f = req_fuse_prepare(req); struct fuse_file_info fi; char *path; int err; get_dirhandle(llfi, &fi); err = get_path_nullok(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } reply_err(req, err); } static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino) { struct fuse *f = req_fuse_prepare(req); struct statvfs buf; char *path = NULL; int err = 0; memset(&buf, 0, sizeof(buf)); if (ino) err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_statfs(f->fs, path ? path : "/", &buf); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (!err) fuse_reply_statfs(req, &buf); else reply_err(req, err); } static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags) { struct fuse *f = req_fuse_prepare(req); char *path; int err; err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_setxattr(f->fs, path, name, value, size, flags); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } reply_err(req, err); } static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, const char *name, char *value, size_t size) { int err; char *path; err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_getxattr(f->fs, path, name, value, size); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } return err; } static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) { struct fuse *f = req_fuse_prepare(req); int res; if (size) { char *value = (char *) malloc(size); if (value == NULL) { reply_err(req, -ENOMEM); return; } res = common_getxattr(f, req, ino, name, value, size); if (res > 0) fuse_reply_buf(req, value, res); else reply_err(req, res); free(value); } else { res = common_getxattr(f, req, ino, name, NULL, 0); if (res >= 0) fuse_reply_xattr(req, res); else reply_err(req, res); } } static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, char *list, size_t size) { char *path; int err; err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_listxattr(f->fs, path, list, size); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } return err; } static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { struct fuse *f = req_fuse_prepare(req); int res; if (size) { char *list = (char *) malloc(size); if (list == NULL) { reply_err(req, -ENOMEM); return; } res = common_listxattr(f, req, ino, list, size); if (res > 0) fuse_reply_buf(req, list, res); else reply_err(req, res); free(list); } else { res = common_listxattr(f, req, ino, NULL, 0); if (res >= 0) fuse_reply_xattr(req, res); else reply_err(req, res); } } static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) { struct fuse *f = req_fuse_prepare(req); char *path; int err; err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_removexattr(f->fs, path, name); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } reply_err(req, err); } static struct lock *locks_conflict(struct node *node, const struct lock *lock) { struct lock *l; for (l = node->locks; l; l = l->next) if (l->owner != lock->owner && lock->start <= l->end && l->start <= lock->end && (l->type == F_WRLCK || lock->type == F_WRLCK)) break; return l; } static void delete_lock(struct lock **lockp) { struct lock *l = *lockp; *lockp = l->next; free(l); } static void insert_lock(struct lock **pos, struct lock *lock) { lock->next = *pos; *pos = lock; } static int locks_insert(struct node *node, struct lock *lock) { struct lock **lp; struct lock *newl1 = NULL; struct lock *newl2 = NULL; if (lock->type != F_UNLCK || lock->start != 0 || lock->end != OFFSET_MAX) { newl1 = malloc(sizeof(struct lock)); newl2 = malloc(sizeof(struct lock)); if (!newl1 || !newl2) { free(newl1); free(newl2); return -ENOLCK; } } for (lp = &node->locks; *lp;) { struct lock *l = *lp; if (l->owner != lock->owner) goto skip; if (lock->type == l->type) { if (l->end < lock->start - 1) goto skip; if (lock->end < l->start - 1) break; if (l->start <= lock->start && lock->end <= l->end) goto out; if (l->start < lock->start) lock->start = l->start; if (lock->end < l->end) lock->end = l->end; goto delete; } else { if (l->end < lock->start) goto skip; if (lock->end < l->start) break; if (lock->start <= l->start && l->end <= lock->end) goto delete; if (l->end <= lock->end) { l->end = lock->start - 1; goto skip; } if (lock->start <= l->start) { l->start = lock->end + 1; break; } *newl2 = *l; newl2->start = lock->end + 1; l->end = lock->start - 1; insert_lock(&l->next, newl2); newl2 = NULL; } skip: lp = &l->next; continue; delete: delete_lock(lp); } if (lock->type != F_UNLCK) { *newl1 = *lock; insert_lock(lp, newl1); newl1 = NULL; } out: free(newl1); free(newl2); return 0; } static void flock_to_lock(struct flock *flock, struct lock *lock) { memset(lock, 0, sizeof(struct lock)); lock->type = flock->l_type; lock->start = flock->l_start; lock->end = flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX; lock->pid = flock->l_pid; } static void lock_to_flock(struct lock *lock, struct flock *flock) { flock->l_type = lock->type; flock->l_start = lock->start; flock->l_len = (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1; flock->l_pid = lock->pid; } static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino, const char *path, struct fuse_file_info *fi) { struct fuse_intr_data d; struct flock lock; struct lock l; int err; int errlock; fuse_prepare_interrupt(f, req, &d); memset(&lock, 0, sizeof(lock)); lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; err = fuse_fs_flush(f->fs, path, fi); errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock); fuse_finish_interrupt(f, req, &d); if (errlock != -ENOSYS) { flock_to_lock(&lock, &l); l.owner = fi->lock_owner; pthread_mutex_lock(&f->lock); locks_insert(get_node(f, ino), &l); pthread_mutex_unlock(&f->lock); /* if op.lock() is defined FLUSH is needed regardless of op.flush() */ if (err == -ENOSYS) err = 0; } return err; } static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; char *path; int err = 0; get_path_nullok(f, ino, &path); if (fi->flush) { err = fuse_flush_common(f, req, ino, path, fi); if (err == -ENOSYS) err = 0; } fuse_prepare_interrupt(f, req, &d); fuse_do_release(f, ino, path, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); reply_err(req, err); } static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); char *path; int err; get_path_nullok(f, ino, &path); err = fuse_flush_common(f, req, ino, path, fi); free_path(f, ino, path); reply_err(req, err); } static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int cmd) { struct fuse *f = req_fuse_prepare(req); char *path; int err; err = get_path_nullok(f, ino, &path); if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_lock(f->fs, path, fi, cmd, lock); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } return err; } static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock) { int err; struct lock l; struct lock *conflict; struct fuse *f = req_fuse(req); flock_to_lock(lock, &l); l.owner = fi->lock_owner; pthread_mutex_lock(&f->lock); conflict = locks_conflict(get_node(f, ino), &l); if (conflict) lock_to_flock(conflict, lock); pthread_mutex_unlock(&f->lock); if (!conflict) err = fuse_lock_common(req, ino, fi, lock, F_GETLK); else err = 0; if (!err) fuse_reply_lock(req, lock); else reply_err(req, err); } static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep) { int err = fuse_lock_common(req, ino, fi, lock, sleep ? F_SETLKW : F_SETLK); if (!err) { struct fuse *f = req_fuse(req); struct lock l; flock_to_lock(lock, &l); l.owner = fi->lock_owner; pthread_mutex_lock(&f->lock); locks_insert(get_node(f, ino), &l); pthread_mutex_unlock(&f->lock); } reply_err(req, err); } static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op) { struct fuse *f = req_fuse_prepare(req); char *path; int err; err = get_path_nullok(f, ino, &path); if (err == 0) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_flock(f->fs, path, fi, op); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } reply_err(req, err); } static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; char *path; int err; err = get_path(f, ino, &path); if (!err) { fuse_prepare_interrupt(f, req, &d); err = fuse_fs_bmap(f->fs, path, blocksize, &idx); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (!err) fuse_reply_bmap(req, idx); else reply_err(req, err); } static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, struct fuse_file_info *llfi, unsigned int flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; struct fuse_file_info fi; char *path, *out_buf = NULL; int err; err = -EPERM; if (flags & FUSE_IOCTL_UNRESTRICTED) goto err; if (flags & FUSE_IOCTL_DIR) get_dirhandle(llfi, &fi); else fi = *llfi; if (out_bufsz) { err = -ENOMEM; out_buf = malloc(out_bufsz); if (!out_buf) goto err; } assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz); if (out_buf && in_bufsz) memcpy(out_buf, in_buf, in_bufsz); err = get_path_nullok(f, ino, &path); if (err) goto err; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags, out_buf ? out_buf : (void *)in_buf); 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: reply_err(req, err); out: free(out_buf); } static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; char *path; int err; unsigned revents = 0; err = get_path_nullok(f, ino, &path); if (!err) { fuse_prepare_interrupt(f, req, &d); err = fuse_fs_poll(f->fs, path, fi, ph, &revents); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (!err) fuse_reply_poll(req, revents); else reply_err(req, err); } static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; char *path; int err; err = get_path_nullok(f, ino, &path); if (!err) { fuse_prepare_interrupt(f, req, &d); err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } reply_err(req, err); } static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, off_t off_in, struct fuse_file_info *fi_in, fuse_ino_t nodeid_out, off_t off_out, struct fuse_file_info *fi_out, size_t len, int flags) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; char *path_in, *path_out; int err; ssize_t res; err = get_path_nullok(f, nodeid_in, &path_in); if (err) { reply_err(req, err); return; } err = get_path_nullok(f, nodeid_out, &path_out); if (err) { free_path(f, nodeid_in, path_in); reply_err(req, err); return; } fuse_prepare_interrupt(f, req, &d); res = fuse_fs_copy_file_range(f->fs, path_in, fi_in, off_in, path_out, fi_out, off_out, len, flags); fuse_finish_interrupt(f, req, &d); if (res >= 0) fuse_reply_write(req, res); else reply_err(req, res); free_path(f, nodeid_in, path_in); free_path(f, nodeid_out, path_out); } static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct fuse_intr_data d; char *path; int err; off_t res; err = get_path(f, ino, &path); if (err) { reply_err(req, err); return; } fuse_prepare_interrupt(f, req, &d); res = fuse_fs_lseek(f->fs, path, off, whence, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); if (res >= 0) fuse_reply_lseek(req, res); else reply_err(req, res); } #ifdef HAVE_STATX static void fuse_lib_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask, struct fuse_file_info *fi) { struct fuse *f = req_fuse_prepare(req); struct statx stxbuf; char *path; int err; memset(&stxbuf, 0, sizeof(stxbuf)); if (fi != NULL) err = get_path_nullok(f, ino, &path); else err = get_path(f, ino, &path); if (!err) { struct fuse_intr_data d; if (!path) flags |= AT_EMPTY_PATH; fuse_prepare_interrupt(f, req, &d); err = fuse_fs_statx(f->fs, path, flags, mask, &stxbuf, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (!err) { struct node *node; pthread_mutex_lock(&f->lock); node = get_node(f, ino); if (node->is_hidden && stxbuf.stx_nlink > 0) stxbuf.stx_nlink--; if (f->conf.auto_cache) { struct stat stbuf; stbuf.st_mtime = stxbuf.stx_mtime.tv_nsec; ST_MTIM_NSEC(&stbuf) = stxbuf.stx_mtime.tv_nsec; stbuf.st_size = stxbuf.stx_size; update_stat(node, &stbuf); } pthread_mutex_unlock(&f->lock); set_statx(f, ino, &stxbuf); fuse_reply_statx(req, 0, &stxbuf, f->conf.attr_timeout); } else reply_err(req, err); } #endif static int clean_delay(struct fuse *f) { /* * This is calculating the delay between clean runs. To * reduce the number of cleans we are doing them 10 times * within the remember window. */ int min_sleep = 60; int max_sleep = 3600; int sleep_time = f->conf.remember / 10; if (sleep_time > max_sleep) return max_sleep; if (sleep_time < min_sleep) return min_sleep; return sleep_time; } int fuse_clean_cache(struct fuse *f) { struct node_lru *lnode; struct list_head *curr, *next; struct node *node; struct timespec now; pthread_mutex_lock(&f->lock); curr_time(&now); for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) { double age; next = curr->next; lnode = list_entry(curr, struct node_lru, lru); node = &lnode->node; age = diff_timespec(&now, &lnode->forget_time); if (age <= f->conf.remember) break; assert(node->nlookup == 1); /* Don't forget active directories */ if (node->refctr > 1) continue; node->nlookup = 0; unhash_name(f, node); unref_node(f, node); } pthread_mutex_unlock(&f->lock); return clean_delay(f); } static struct fuse_lowlevel_ops fuse_path_ops = { .init = fuse_lib_init, .destroy = fuse_lib_destroy, .lookup = fuse_lib_lookup, .forget = fuse_lib_forget, .forget_multi = fuse_lib_forget_multi, .getattr = fuse_lib_getattr, .setattr = fuse_lib_setattr, .access = fuse_lib_access, .readlink = fuse_lib_readlink, .mknod = fuse_lib_mknod, .mkdir = fuse_lib_mkdir, .unlink = fuse_lib_unlink, .rmdir = fuse_lib_rmdir, .symlink = fuse_lib_symlink, .rename = fuse_lib_rename, .link = fuse_lib_link, .create = fuse_lib_create, .open = fuse_lib_open, .read = fuse_lib_read, .write_buf = fuse_lib_write_buf, .flush = fuse_lib_flush, .release = fuse_lib_release, .fsync = fuse_lib_fsync, .opendir = fuse_lib_opendir, .readdir = fuse_lib_readdir, .readdirplus = fuse_lib_readdirplus, .releasedir = fuse_lib_releasedir, .fsyncdir = fuse_lib_fsyncdir, .statfs = fuse_lib_statfs, .setxattr = fuse_lib_setxattr, .getxattr = fuse_lib_getxattr, .listxattr = fuse_lib_listxattr, .removexattr = fuse_lib_removexattr, .getlk = fuse_lib_getlk, .setlk = fuse_lib_setlk, .flock = fuse_lib_flock, .bmap = fuse_lib_bmap, .ioctl = fuse_lib_ioctl, .poll = fuse_lib_poll, .fallocate = fuse_lib_fallocate, .copy_file_range = fuse_lib_copy_file_range, .lseek = fuse_lib_lseek, #ifdef HAVE_STATX .statx = fuse_lib_statx, #endif }; int fuse_notify_poll(struct fuse_pollhandle *ph) { return fuse_lowlevel_notify_poll(ph); } struct fuse_session *fuse_get_session(struct fuse *f) { return f->se; } static int fuse_session_loop_remember(struct fuse *f) { struct fuse_session *se = f->se; int res = 0; struct timespec now; time_t next_clean; struct pollfd fds = { .fd = se->fd, .events = POLLIN }; struct fuse_buf fbuf = { .mem = NULL, }; curr_time(&now); next_clean = now.tv_sec; while (!fuse_session_exited(se)) { unsigned timeout; curr_time(&now); if (now.tv_sec < next_clean) timeout = next_clean - now.tv_sec; else timeout = 0; res = poll(&fds, 1, timeout * 1000); if (res == -1) { if (errno == EINTR) continue; else break; } else if (res > 0) { res = fuse_session_receive_buf_internal(se, &fbuf, NULL); if (res == -EINTR) continue; if (res <= 0) break; fuse_session_process_buf_internal(se, &fbuf, NULL); } else { timeout = fuse_clean_cache(f); curr_time(&now); next_clean = now.tv_sec + timeout; } } fuse_buf_free(&fbuf); return res < 0 ? -1 : 0; } int fuse_loop(struct fuse *f) { if (!f) return -1; if (lru_enabled(f)) return fuse_session_loop_remember(f); return fuse_session_loop(f->se); } 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; int res = fuse_start_cleanup_thread(f); if (res) return -1; 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) { 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) { fuse_session_exit(f->se); } struct fuse_context *fuse_get_context(void) { struct fuse_context_i *c = fuse_get_context_internal(); if (c) return &c->ctx; else return NULL; } int fuse_getgroups(int size, gid_t list[]) { struct fuse_context_i *c = fuse_get_context_internal(); if (!c) return -EINVAL; return fuse_req_getgroups(c->req, size, list); } int fuse_interrupted(void) { struct fuse_context_i *c = fuse_get_context_internal(); if (c) return fuse_req_interrupted(c->req); else return 0; } int fuse_invalidate_path(struct fuse *f, const char *path) { fuse_ino_t ino; int err = lookup_path_in_cache(f, path, &ino); if (err) { return err; } return fuse_lowlevel_notify_inval_inode(f->se, ino, 0, 0); } #define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v } static const struct fuse_opt fuse_lib_opts[] = { FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), FUSE_LIB_OPT("debug", debug, 1), FUSE_LIB_OPT("-d", debug, 1), 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("fmask=", set_mode, 1), FUSE_LIB_OPT("fmask=%o", fmask, 0), FUSE_LIB_OPT("dmask=", set_mode, 1), FUSE_LIB_OPT("dmask=%o", dmask, 0), FUSE_LIB_OPT("uid=", set_uid, 1), FUSE_LIB_OPT("uid=%d", uid, 0), FUSE_LIB_OPT("gid=", set_gid, 1), FUSE_LIB_OPT("gid=%d", gid, 0), FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0), FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0), FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0), FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1), FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0), 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 }; static int fuse_lib_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { (void) arg; (void) outargs; (void) data; (void) key; /* Pass through unknown options */ return 1; } static const struct fuse_opt fuse_help_opts[] = { FUSE_LIB_OPT("modules=%s", modules, 1), FUSE_OPT_KEY("modules=%s", FUSE_OPT_KEY_KEEP), FUSE_OPT_END }; static void print_module_help(const char *name, fuse_module_factory_t *fac) { struct fuse_args a = FUSE_ARGS_INIT(0, NULL); if (fuse_opt_add_arg(&a, "") == -1 || fuse_opt_add_arg(&a, "-h") == -1) return; printf("\nOptions for %s module:\n", name); (*fac)(&a, NULL); fuse_opt_free_args(&a); } void fuse_lib_help(struct fuse_args *args) { /* These are not all options, but only the ones that may be of interest to an end-user */ 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 fmask=M set file permissions (octal)\n" " -o dmask=M set dir permissions (octal)\n" " -o uid=N set file owner\n" " -o gid=N set file group\n" " -o entry_timeout=T cache timeout for names (1.0s)\n" " -o negative_timeout=T cache timeout for deleted names (0.0s)\n" " -o attr_timeout=T cache timeout for attributes (1.0s)\n" " -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n" " -o noforget never forget cached inodes\n" " -o remember=T remember cached inodes for T seconds (0s)\n" " -o modules=M1[:M2...] names of modules to push onto filesystem stack\n"); /* Print low-level help */ fuse_lowlevel_help(); /* Print help for builtin modules */ print_module_help("subdir", &fuse_module_subdir_factory); #ifdef HAVE_ICONV print_module_help("iconv", &fuse_module_iconv_factory); #endif /* Parse command line options in case we need to activate more modules */ struct fuse_config conf = { .modules = NULL }; if (fuse_opt_parse(args, &conf, fuse_help_opts, fuse_lib_opt_proc) == -1 || !conf.modules) return; char *module; char *next; struct fuse_module *m; // Iterate over all modules for (module = conf.modules; module; module = next) { char *p; for (p = module; *p && *p != ':'; p++); next = *p ? p + 1 : NULL; *p = '\0'; m = fuse_get_module(module); if (m) print_module_help(module, &m->factory); } } static int fuse_init_intr_signal(int signum, int *installed) { struct sigaction old_sa; if (sigaction(signum, NULL, &old_sa) == -1) { perror("fuse: cannot get old signal handler"); return -1; } if (old_sa.sa_handler == SIG_DFL) { struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = fuse_intr_sighandler; sigemptyset(&sa.sa_mask); if (sigaction(signum, &sa, NULL) == -1) { perror("fuse: cannot set interrupt signal handler"); return -1; } *installed = 1; } return 0; } static void fuse_restore_intr_signal(int signum) { struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = SIG_DFL; sigaction(signum, &sa, NULL); } static int fuse_push_module(struct fuse *f, const char *module, struct fuse_args *args) { struct fuse_fs *fs[2] = { f->fs, NULL }; struct fuse_fs *newfs; struct fuse_module *m = fuse_get_module(module); if (!m) return -1; newfs = m->factory(args, fs); if (!newfs) { fuse_put_module(m); return -1; } f->fs = newfs; return 0; } struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *user_data) { struct fuse_fs *fs; if (sizeof(struct fuse_operations) < op_size) { fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not not work\n"); op_size = sizeof(struct fuse_operations); } fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs)); if (!fs) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse_fs object\n"); return NULL; } fs->user_data = user_data; if (op) memcpy(&fs->op, op, op_size); return fs; } static int node_table_init(struct node_table *t) { t->size = NODE_TABLE_MIN_SIZE; t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size); if (t->array == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); return -1; } t->use = 0; t->split = 0; return 0; } static void *fuse_prune_nodes(void *fuse) { struct fuse *f = fuse; int sleep_time; fuse_set_thread_name("fuse_prune_nodes"); while(1) { sleep_time = fuse_clean_cache(f); sleep(sleep_time); } return NULL; } int fuse_start_cleanup_thread(struct fuse *f) { if (lru_enabled(f)) return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f); return 0; } void fuse_stop_cleanup_thread(struct fuse *f) { if (lru_enabled(f)) { pthread_mutex_lock(&f->lock); pthread_cancel(f->prune_thread); pthread_mutex_unlock(&f->lock); pthread_join(f->prune_thread, NULL); } } /* * Not supposed to be called directly, but supposed to be called * through the fuse_new macro */ struct fuse *_fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data) { struct fuse *f; struct node *root; struct fuse_fs *fs; struct fuse_lowlevel_ops llop = fuse_path_ops; f = (struct fuse *) calloc(1, sizeof(struct fuse)); if (f == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); goto out; } f->conf.entry_timeout = 1.0; f->conf.attr_timeout = 1.0; f->conf.negative_timeout = 0.0; f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL; /* Parse options */ if (fuse_opt_parse(args, &f->conf, fuse_lib_opts, fuse_lib_opt_proc) == -1) goto out_free; pthread_mutex_lock(&fuse_context_lock); static int builtin_modules_registered = 0; /* Have the builtin modules already been registered? */ if (builtin_modules_registered == 0) { /* If not, register them. */ fuse_register_module("subdir", fuse_module_subdir_factory, NULL); #ifdef HAVE_ICONV fuse_register_module("iconv", fuse_module_iconv_factory, NULL); #endif builtin_modules_registered= 1; } pthread_mutex_unlock(&fuse_context_lock); if (fuse_create_context_key() == -1) goto out_free; fs = fuse_fs_new(op, op_size, user_data); if (!fs) goto out_delete_context_key; f->fs = fs; /* Oh f**k, this is ugly! */ if (!fs->op.lock) { llop.getlk = NULL; llop.setlk = NULL; } f->pagesize = getpagesize(); init_list_head(&f->partial_slabs); init_list_head(&f->full_slabs); init_list_head(&f->lru_table); if (f->conf.modules) { char *module; char *next; for (module = f->conf.modules; module; module = next) { char *p; for (p = module; *p && *p != ':'; p++); next = *p ? p + 1 : NULL; *p = '\0'; if (module[0] && fuse_push_module(f, module, args) == -1) goto out_free_fs; } } if (!f->conf.ac_attr_timeout_set) f->conf.ac_attr_timeout = f->conf.attr_timeout; #if defined(__FreeBSD__) || defined(__NetBSD__) /* * In FreeBSD, we always use these settings as inode numbers * are needed to make getcwd(3) work. */ f->conf.readdir_ino = 1; #endif /* not declared globally, to restrict usage of this function */ struct fuse_session *fuse_session_new_versioned( struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, struct libfuse_version *version, void *userdata); f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version, f); if (f->se == NULL) goto out_free_fs; /* Trace topmost layer by default */ f->fs->debug = f->conf.debug; f->ctr = 0; f->generation = 0; if (node_table_init(&f->name_table) == -1) goto out_free_session; if (node_table_init(&f->id_table) == -1) goto out_free_name_table; pthread_mutex_init(&f->lock, NULL); root = alloc_node(f); if (root == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); goto out_free_id_table; } if (lru_enabled(f)) { struct node_lru *lnode = node_lru(root); init_list_head(&lnode->lru); } strcpy(root->inline_name, "/"); root->name = root->inline_name; root->parent = NULL; root->nodeid = FUSE_ROOT_ID; inc_nlookup(root); hash_id(f, root); return f; out_free_id_table: free(f->id_table.array); out_free_name_table: free(f->name_table.array); out_free_session: fuse_session_destroy(f->se); out_free_fs: free(f->fs); free(f->conf.modules); out_delete_context_key: fuse_delete_context_key(); out_free: free(f); out: return NULL; } /* Emulates 3.0-style fuse_new(), which processes --help */ FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0") struct fuse *_fuse_new_30(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data) { struct fuse_config conf = {0}; const struct fuse_opt opts[] = { FUSE_LIB_OPT("-h", show_help, 1), FUSE_LIB_OPT("--help", show_help, 1), FUSE_OPT_END }; if (fuse_opt_parse(args, &conf, opts, fuse_lib_opt_proc) == -1) return NULL; if (conf.show_help) { fuse_lib_help(args); return NULL; } else return _fuse_new_31(args, op, op_size, version, user_data); } /* ABI compat version */ struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data); FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1") struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data) { /* unknown version */ struct libfuse_version version = { 0 }; return _fuse_new_31(args, op, op_size, &version, user_data); } /* * ABI compat version * Emulates 3.0-style fuse_new(), which processes --help */ struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data); FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0") struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data) { struct fuse_config conf = {0}; const struct fuse_opt opts[] = { FUSE_LIB_OPT("-h", show_help, 1), FUSE_LIB_OPT("--help", show_help, 1), FUSE_OPT_END }; if (fuse_opt_parse(args, &conf, opts, fuse_lib_opt_proc) == -1) return NULL; if (conf.show_help) { fuse_lib_help(args); return NULL; } else return fuse_new_31(args, op, op_size, user_data); } void fuse_destroy(struct fuse *f) { size_t i; if (f->conf.intr && f->intr_installed) fuse_restore_intr_signal(f->conf.intr_signal); if (f->fs) { fuse_create_context(f); for (i = 0; i < f->id_table.size; i++) { struct node *node; for (node = f->id_table.array[i]; node != NULL; node = node->id_next) { if (node->is_hidden) { char *path; if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) { fuse_fs_unlink(f->fs, path); free(path); } } } } } for (i = 0; i < f->id_table.size; i++) { struct node *node; struct node *next; for (node = f->id_table.array[i]; node != NULL; node = next) { next = node->id_next; free_node(f, node); f->id_table.use--; } } assert(list_empty(&f->partial_slabs)); assert(list_empty(&f->full_slabs)); while (fuse_modules) { fuse_put_module(fuse_modules); } free(f->id_table.array); 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(); } int fuse_mount(struct fuse *f, const char *mountpoint) { return fuse_session_mount(fuse_get_session(f), mountpoint); } void fuse_unmount(struct fuse *f) { fuse_session_unmount(fuse_get_session(f)); } int fuse_version(void) { return FUSE_VERSION; } const char *fuse_pkgversion(void) { return PACKAGE_VERSION; } fuse-3.18.2/lib/fuse_i.h0000644000175000017500000001536215156613252013723 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #ifndef LIB_FUSE_I_H_ #define LIB_FUSE_I_H_ #include "fuse.h" #include "fuse_lowlevel.h" #include "util.h" #include #include #include #include #include #include #define MIN(a, b) \ ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a < _b ? _a : _b; \ }) struct mount_opts; struct fuse_ring_pool; struct fuse_req { struct fuse_session *se; uint64_t unique; _Atomic int ref_cnt; pthread_mutex_t lock; struct fuse_ctx ctx; struct fuse_chan *ch; int interrupted; struct { unsigned int ioctl_64bit : 1; unsigned int is_uring : 1; unsigned int is_copy_file_range_64 : 1; } flags; union { struct { uint64_t unique; } i; struct { fuse_interrupt_func_t func; void *data; } ni; } u; struct fuse_req *next; struct fuse_req *prev; }; struct fuse_notify_req { uint64_t unique; void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, const void *, const struct fuse_buf *); struct fuse_notify_req *next; struct fuse_notify_req *prev; }; struct fuse_session_uring { bool enable; unsigned int q_depth; struct fuse_ring_pool *pool; }; struct fuse_session { _Atomic(char *)mountpoint; int fd; struct fuse_custom_io *io; struct mount_opts *mo; int debug; int deny_others; struct fuse_lowlevel_ops op; int got_init; struct cuse_data *cuse_data; void *userdata; uid_t owner; struct fuse_conn_info conn; struct fuse_req list; struct fuse_req interrupts; pthread_mutex_t lock; int got_destroy; pthread_key_t pipe_key; int broken_splice_nonblock; uint64_t notify_ctr; struct fuse_notify_req notify_list; _Atomic size_t bufsize; int error; /* * This is useful if any kind of ABI incompatibility is found at * a later version, to 'fix' it at run time. */ struct libfuse_version version; /* thread synchronization */ _Atomic bool mt_exited; pthread_mutex_t mt_lock; sem_t mt_finish; /* true if reading requests from /dev/fuse are handled internally */ bool buf_reallocable; /* io_uring */ struct fuse_session_uring uring; /* * conn->want and conn_want_ext options set by libfuse , needed * to correctly convert want to want_ext */ uint32_t conn_want; uint64_t conn_want_ext; }; struct fuse_chan { pthread_mutex_t lock; int ctr; int fd; }; /** * Filesystem module * * Filesystem modules are registered with the FUSE_REGISTER_MODULE() * macro. * */ struct fuse_module { char *name; fuse_module_factory_t factory; struct fuse_module *next; struct fusemod_so *so; 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) * * ----------------------------------------------------------- */ /** * Obtain counted reference to the channel * * @param ch the channel * @return the channel */ struct fuse_chan *fuse_chan_get(struct fuse_chan *ch); /** * Drop counted reference to a channel * * @param ch the channel */ void fuse_chan_put(struct fuse_chan *ch); struct mount_opts *parse_mount_opts(struct fuse_args *args); void destroy_mount_opts(struct mount_opts *mo); void fuse_mount_version(void); unsigned get_max_read(struct mount_opts *o); void fuse_kern_unmount(const char *mountpoint, int fd); int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo); int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, int count); void fuse_free_req(fuse_req_t req); void list_init_req(struct fuse_req *req); void _cuse_lowlevel_init(fuse_req_t req, const fuse_ino_t nodeid, const void *req_header, const void *req_payload); void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg); int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg); void fuse_buf_free(struct fuse_buf *buf); int fuse_session_receive_buf_internal(struct fuse_session *se, struct fuse_buf *buf, struct fuse_chan *ch); void fuse_session_process_buf_internal(struct fuse_session *se, const struct fuse_buf *buf, struct fuse_chan *ch); 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_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); /* * This can be changed dynamically on recent kernels through the * /proc/sys/fs/fuse/max_pages_limit interface. * * Older kernels will always use the default value. */ #define FUSE_DEFAULT_MAX_PAGES_LIMIT 256 #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32 /* room needed in buffer to accommodate header */ #define FUSE_BUFFER_HEADER_SIZE 0x1000 #endif /* LIB_FUSE_I_H_*/ fuse-3.18.2/lib/fuse_log.c0000644000175000017500000000176115156613252014245 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2019 Red Hat, Inc. Logging API. This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #include "fuse_log.h" #include #include #include #include #define MAX_SYSLOG_LINE_LEN 512 static bool to_syslog = false; static void default_log_func(enum fuse_log_level level, const char *fmt, va_list ap) { if (to_syslog) vsyslog(level, fmt, ap); else vfprintf(stderr, fmt, ap); } static fuse_log_func_t log_func = default_log_func; void fuse_set_log_func(fuse_log_func_t func) { if (!func) func = default_log_func; log_func = func; } void fuse_log(enum fuse_log_level level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_func(level, fmt, ap); va_end(ap); } void fuse_log_enable_syslog(const char *ident, int option, int facility) { to_syslog = true; openlog(ident, option, facility); } void fuse_log_close_syslog(void) { closelog(); } fuse-3.18.2/lib/fuse_loop.c0000644000175000017500000000165615156613252014440 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Implementation of the single-threaded FUSE session loop. This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #include "fuse_config.h" #include "fuse_lowlevel.h" #include "fuse_i.h" #include "fuse_uring_i.h" #include #include #include int fuse_session_loop(struct fuse_session *se) { int res = 0; struct fuse_buf fbuf = { .mem = NULL, }; while (!fuse_session_exited(se)) { res = fuse_session_receive_buf_internal(se, &fbuf, NULL); if (res == -EINTR) continue; if (res <= 0) break; fuse_session_process_buf(se, &fbuf); } fuse_buf_free(&fbuf); if(res > 0) /* No error, just the length of the most recently read request */ res = 0; if(se->error != 0) res = se->error; if (se->uring.pool) fuse_uring_stop(se); return res; } fuse-3.18.2/lib/fuse_loop_mt.c0000644000175000017500000003035115156613252015132 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Implementation of the multi-threaded FUSE session loop. This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ #define _GNU_SOURCE #include "fuse_config.h" #include "fuse_lowlevel.h" #include "fuse_misc.h" #include "fuse_kernel.h" #include "fuse_i.h" #include "fuse_uring_i.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include /* 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; pthread_t thread_id; // We need to include fuse_buf so that we can properly free // it when a thread is terminated by pthread_cancel(). struct fuse_buf fbuf; struct fuse_chan *ch; struct fuse_mt *mt; }; /* synchronization via se->mt_lock */ struct fuse_mt { int numworker; int numavail; struct fuse_session *se; struct fuse_worker main; int error; int clone_fd; int max_idle; int max_threads; }; static struct fuse_chan *fuse_chan_new(int fd) { struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch)); if (ch == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate channel\n"); return NULL; } memset(ch, 0, sizeof(*ch)); ch->fd = fd; ch->ctr = 1; pthread_mutex_init(&ch->lock, NULL); return ch; } struct fuse_chan *fuse_chan_get(struct fuse_chan *ch) { assert(ch->ctr > 0); pthread_mutex_lock(&ch->lock); ch->ctr++; pthread_mutex_unlock(&ch->lock); return ch; } void fuse_chan_put(struct fuse_chan *ch) { if (ch == NULL) return; pthread_mutex_lock(&ch->lock); ch->ctr--; if (!ch->ctr) { pthread_mutex_unlock(&ch->lock); close(ch->fd); pthread_mutex_destroy(&ch->lock); free(ch); } else pthread_mutex_unlock(&ch->lock); } static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next) { struct fuse_worker *prev = next->prev; w->next = next; w->prev = prev; prev->next = w; next->prev = w; } static void list_del_worker(struct fuse_worker *w) { struct fuse_worker *prev = w->prev; struct fuse_worker *next = w->next; prev->next = next; next->prev = prev; } static int fuse_loop_start_thread(struct fuse_mt *mt); static void *fuse_do_work(void *data) { struct fuse_worker *w = (struct fuse_worker *) data; struct fuse_mt *mt = w->mt; struct fuse_session *se = mt->se; fuse_set_thread_name("fuse_worker"); while (!fuse_session_exited(se)) { int isforget = 0; int res; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); res = fuse_session_receive_buf_internal(se, &w->fbuf, w->ch); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); if (res == -EINTR) continue; if (res <= 0) { if (res < 0) { fuse_session_exit(se); mt->error = res; } break; } pthread_mutex_lock(&se->mt_lock); if (fuse_session_exited(se)) { pthread_mutex_unlock(&se->mt_lock); return NULL; } /* * This disgusting hack is needed so that zillions of threads * are not created on a burst of FORGET messages */ if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) { struct fuse_in_header *in = w->fbuf.mem; if (in->opcode == FUSE_FORGET || in->opcode == FUSE_BATCH_FORGET) isforget = 1; } if (!isforget) mt->numavail--; if (mt->numavail == 0 && mt->numworker < mt->max_threads && likely(se->got_init)) fuse_loop_start_thread(mt); pthread_mutex_unlock(&se->mt_lock); fuse_session_process_buf_internal(se, &w->fbuf, w->ch); pthread_mutex_lock(&se->mt_lock); if (!isforget) mt->numavail++; /* 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 (fuse_session_exited(se)) { pthread_mutex_unlock(&se->mt_lock); return NULL; } list_del_worker(w); mt->numavail--; mt->numworker--; pthread_mutex_unlock(&se->mt_lock); pthread_detach(w->thread_id); fuse_buf_free(&w->fbuf); fuse_chan_put(w->ch); free(w); return NULL; } pthread_mutex_unlock(&se->mt_lock); } sem_post(&se->mt_finish); return NULL; } int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg) { sigset_t oldset; sigset_t newset; int res; pthread_attr_t attr; char *stack_size; /* Override default stack size * 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) { long size; res = libfuse_strtol(stack_size, &size); if (res) fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n", stack_size); else if (pthread_attr_setstacksize(&attr, size)) fuse_log(FUSE_LOG_ERR, "fuse: could not set stack size: %ld\n", size); } /* Disallow signal reception in worker threads */ sigemptyset(&newset); sigaddset(&newset, SIGTERM); sigaddset(&newset, SIGINT); sigaddset(&newset, SIGHUP); sigaddset(&newset, SIGQUIT); pthread_sigmask(SIG_BLOCK, &newset, &oldset); res = pthread_create(thread_id, &attr, func, arg); pthread_sigmask(SIG_SETMASK, &oldset, NULL); pthread_attr_destroy(&attr); if (res != 0) { fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n", strerror(res)); return -1; } return 0; } static int fuse_clone_chan_fd_default(struct fuse_session *se) { int res; int clonefd; uint32_t masterfd; const char *devname = "/dev/fuse"; #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif clonefd = open(devname, O_RDWR | O_CLOEXEC); if (clonefd == -1) { fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname, strerror(errno)); return -1; } if (!O_CLOEXEC) { res = fcntl(clonefd, F_SETFD, FD_CLOEXEC); if (res == -1) { fuse_log(FUSE_LOG_ERR, "fuse: failed to set CLOEXEC: %s\n", strerror(errno)); close(clonefd); return -1; } } masterfd = se->fd; res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd); if (res == -1) { fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n", strerror(errno)); close(clonefd); return -1; } return clonefd; } static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt) { int clonefd; struct fuse_session *se = mt->se; struct fuse_chan *newch; if (se->io != NULL) { if (se->io->clone_fd != NULL) clonefd = se->io->clone_fd(se->fd); else return NULL; } else { clonefd = fuse_clone_chan_fd_default(se); } if (clonefd < 0) return NULL; newch = fuse_chan_new(clonefd); if (newch == NULL) close(clonefd); return newch; } static int fuse_loop_start_thread(struct fuse_mt *mt) { int res; struct fuse_worker *w = malloc(sizeof(struct fuse_worker)); if (!w) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n"); return -1; } memset(w, 0, sizeof(struct fuse_worker)); w->fbuf.mem = NULL; w->mt = mt; w->ch = NULL; if (mt->clone_fd) { w->ch = fuse_clone_chan(mt); if(!w->ch) { /* Don't attempt this again */ fuse_log(FUSE_LOG_ERR, "fuse: trying to continue " "without -o clone_fd.\n"); mt->clone_fd = 0; } } res = fuse_start_thread(&w->thread_id, fuse_do_work, w); if (res == -1) { fuse_chan_put(w->ch); free(w); return -1; } list_add_worker(w, &mt->main); mt->numavail ++; mt->numworker ++; return 0; } static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w) { pthread_join(w->thread_id, NULL); pthread_mutex_lock(&mt->se->mt_lock); list_del_worker(w); pthread_mutex_unlock(&mt->se->mt_lock); fuse_buf_free(&w->fbuf); fuse_chan_put(w->ch); free(w); } 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; 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; mt.clone_fd = config->clone_fd; mt.error = 0; 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; pthread_mutex_lock(&se->mt_lock); err = fuse_loop_start_thread(&mt); pthread_mutex_unlock(&se->mt_lock); if (!err) { while (!fuse_session_exited(se)) sem_wait(&se->mt_finish); if (se->debug) fuse_log(FUSE_LOG_DEBUG, "fuse: session exited, terminating workers\n"); pthread_mutex_lock(&se->mt_lock); for (w = mt.main.next; w != &mt.main; w = w->next) pthread_cancel(w->thread_id); pthread_mutex_unlock(&se->mt_lock); while (mt.main.next != &mt.main) fuse_join_worker(&mt, mt.main.next); err = mt.error; if (se->uring.pool) fuse_uring_stop(se); } pthread_mutex_destroy(&se->mt_lock); if(se->error != 0) err = se->error; 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) { int err; struct fuse_loop_config *config = fuse_loop_cfg_create(); if (clone_fd > 0) fuse_loop_cfg_set_clone_fd(config, clone_fd); err = fuse_session_loop_mt_312(se, config); fuse_loop_cfg_destroy(config); return err; } 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; } fuse-3.18.2/lib/fuse_lowlevel.c0000644000175000017500000033720515156613252015322 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Implementation of (most of) the low-level FUSE API. The session loop functions are implemented in separate files. This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #define _GNU_SOURCE #include "fuse_config.h" #include "fuse_i.h" #include "fuse_kernel.h" #include "fuse_opt.h" #include "fuse_misc.h" #include "mount_util.h" #include "util.h" #include "fuse_uring_i.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USDT_ENABLED #include "usdt.h" #endif #ifndef F_LINUX_SPECIFIC_BASE #define F_LINUX_SPECIFIC_BASE 1024 #endif #ifndef F_SETPIPE_SZ #define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) #endif #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) #define OFFSET_MAX 0x7fffffffffffffffLL struct fuse_pollhandle { uint64_t kh; struct fuse_session *se; }; static size_t pagesize; static __attribute__((constructor)) void fuse_ll_init_pagesize(void) { pagesize = getpagesize(); } #ifdef USDT_ENABLED /* tracepoints */ static void trace_request_receive(int err) { USDT(libfuse, request_receive, err); } static void trace_request_process(unsigned int opcode, unsigned int unique) { USDT(libfuse, request_process, opcode, unique); } static void trace_request_reply(uint64_t unique, unsigned int len, int error, int reply_err) { USDT(libfuse, request_reply, unique, len, error, reply_err); } #else static void trace_request_receive(int err) { (void)err; } static void trace_request_process(unsigned int opcode, unsigned int unique) { (void)opcode; (void)unique; } static void trace_request_reply(uint64_t unique, unsigned int len, int error, int reply_err) { (void)unique; (void)len; (void)error; (void)reply_err; } #endif static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) { attr->ino = stbuf->st_ino; attr->mode = stbuf->st_mode; attr->nlink = stbuf->st_nlink; attr->uid = stbuf->st_uid; attr->gid = stbuf->st_gid; attr->rdev = stbuf->st_rdev; attr->size = stbuf->st_size; attr->blksize = stbuf->st_blksize; attr->blocks = stbuf->st_blocks; attr->atime = stbuf->st_atime; attr->mtime = stbuf->st_mtime; attr->ctime = stbuf->st_ctime; attr->atimensec = ST_ATIM_NSEC(stbuf); attr->mtimensec = ST_MTIM_NSEC(stbuf); attr->ctimensec = ST_CTIM_NSEC(stbuf); } static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) { stbuf->st_mode = attr->mode; stbuf->st_uid = attr->uid; stbuf->st_gid = attr->gid; stbuf->st_size = attr->size; stbuf->st_atime = attr->atime; stbuf->st_mtime = attr->mtime; stbuf->st_ctime = attr->ctime; ST_ATIM_NSEC_SET(stbuf, attr->atimensec); ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); ST_CTIM_NSEC_SET(stbuf, attr->ctimensec); } static size_t iov_length(const struct iovec *iov, size_t count) { size_t seg; size_t ret = 0; for (seg = 0; seg < count; seg++) ret += iov[seg].iov_len; return ret; } void list_init_req(struct fuse_req *req) { req->next = req; req->prev = req; } static void list_del_req(struct fuse_req *req) { struct fuse_req *prev = req->prev; struct fuse_req *next = req->next; prev->next = next; next->prev = prev; } static void list_add_req(struct fuse_req *req, struct fuse_req *next) { struct fuse_req *prev = next->prev; req->next = next; req->prev = prev; prev->next = req; next->prev = req; } static void destroy_req(fuse_req_t req) { if (req->flags.is_uring) { fuse_log(FUSE_LOG_ERR, "Refusing to destruct uring req\n"); return; } assert(req->ch == NULL); pthread_mutex_destroy(&req->lock); free(req); } void fuse_free_req(fuse_req_t req) { int ctr; struct fuse_session *se = req->se; /* XXX: for now no support for interrupts with io-uring * It actually might work already, though. But then would add * a lock across ring queues. */ if (se->conn.no_interrupt || req->flags.is_uring) { ctr = --req->ref_cnt; fuse_chan_put(req->ch); req->ch = NULL; } else { pthread_mutex_lock(&se->lock); req->u.ni.func = NULL; req->u.ni.data = NULL; list_del_req(req); ctr = --req->ref_cnt; fuse_chan_put(req->ch); req->ch = NULL; pthread_mutex_unlock(&se->lock); } if (!ctr) destroy_req(req); } static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se) { struct fuse_req *req; req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req)); if (req == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n"); } else { req->se = se; req->ref_cnt = 1; list_init_req(req); pthread_mutex_init(&req->lock, NULL); } return req; } /* * Send data to fuse-kernel using an fd of the fuse device. */ static int fuse_write_msg_dev(struct fuse_session *se, struct fuse_chan *ch, struct iovec *iov, int count) { ssize_t res; int err; 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); if (res == -1) { /* ENOENT means the operation was interrupted */ err = errno; if (!fuse_session_exited(se) && err != ENOENT) perror("fuse: writing device"); return -err; } return 0; } static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, struct iovec *iov, int count, fuse_req_t req) { struct fuse_out_header *out = iov[0].iov_base; int err; bool is_uring = req && req->flags.is_uring ? true : false; if (!is_uring) assert(se != NULL); out->len = iov_length(iov, count); if (se->debug) { if (out->unique == 0) { fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", out->error, out->len); } else if (out->error) { fuse_log(FUSE_LOG_DEBUG, " unique: %llu, error: %i (%s), outsize: %i\n", (unsigned long long) out->unique, out->error, strerror(-out->error), out->len); } else { fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i\n", (unsigned long long) out->unique, out->len); } } if (is_uring) err = fuse_send_msg_uring(req, iov, count); else err = fuse_write_msg_dev(se, ch, iov, count); trace_request_reply(out->unique, out->len, out->error, err); return err; } int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, int count) { 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; } out.unique = req->unique; out.error = error; iov[0].iov_base = &out; iov[0].iov_len = sizeof(struct fuse_out_header); return fuse_send_msg(req->se, req->ch, iov, count, req); } static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, int count) { int res; res = fuse_send_reply_iov_nofree(req, error, iov, count); fuse_free_req(req); return res; } static int send_reply(fuse_req_t req, int error, const void *arg, size_t argsize) { if (req->flags.is_uring) return send_reply_uring(req, error, arg, argsize); struct iovec iov[2]; int count = 1; if (argsize) { iov[1].iov_base = (void *) arg; iov[1].iov_len = argsize; count++; } return send_reply_iov(req, error, iov, count); } int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) { int res; struct iovec *padded_iov; padded_iov = malloc((count + 1) * sizeof(struct iovec)); if (padded_iov == NULL) return fuse_reply_err(req, ENOMEM); memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); count++; res = send_reply_iov(req, 0, padded_iov, count); free(padded_iov); return res; } /* `buf` is allowed to be empty so that the proper size may be allocated by the caller */ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off) { (void)req; size_t namelen; size_t entlen; size_t entlen_padded; struct fuse_dirent *dirent; namelen = strlen(name); entlen = FUSE_NAME_OFFSET + namelen; entlen_padded = FUSE_DIRENT_ALIGN(entlen); if ((buf == NULL) || (entlen_padded > bufsize)) return entlen_padded; dirent = (struct fuse_dirent*) buf; dirent->ino = stbuf->st_ino; dirent->off = off; dirent->namelen = namelen; dirent->type = (stbuf->st_mode & S_IFMT) >> 12; memcpy(dirent->name, name, namelen); memset(dirent->name + namelen, 0, entlen_padded - entlen); return entlen_padded; } static void convert_statfs(const struct statvfs *stbuf, struct fuse_kstatfs *kstatfs) { kstatfs->bsize = stbuf->f_bsize; kstatfs->frsize = stbuf->f_frsize; kstatfs->blocks = stbuf->f_blocks; kstatfs->bfree = stbuf->f_bfree; kstatfs->bavail = stbuf->f_bavail; kstatfs->files = stbuf->f_files; kstatfs->ffree = stbuf->f_ffree; kstatfs->namelen = stbuf->f_namemax; } static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) { return send_reply(req, 0, arg, argsize); } int fuse_reply_err(fuse_req_t req, int err) { return send_reply(req, -err, NULL, 0); } void fuse_reply_none(fuse_req_t req) { fuse_free_req(req); } static unsigned long calc_timeout_sec(double t) { if (t > (double) ULONG_MAX) return ULONG_MAX; else if (t < 0.0) return 0; else return (unsigned long) t; } static unsigned int calc_timeout_nsec(double t) { double f = t - (double) calc_timeout_sec(t); if (f < 0.0) return 0; else if (f >= 0.999999999) return 999999999; else return (unsigned int) (f * 1.0e9); } static void fill_entry(struct fuse_entry_out *arg, const struct fuse_entry_param *e) { arg->nodeid = e->ino; arg->generation = e->generation; arg->entry_valid = calc_timeout_sec(e->entry_timeout); arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); arg->attr_valid = calc_timeout_sec(e->attr_timeout); arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); convert_stat(&e->attr, &arg->attr); } /* `buf` is allowed to be empty so that the proper size may be allocated by the caller */ size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off) { (void)req; size_t namelen; size_t entlen; size_t entlen_padded; namelen = strlen(name); entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen; entlen_padded = FUSE_DIRENT_ALIGN(entlen); if ((buf == NULL) || (entlen_padded > bufsize)) return entlen_padded; struct fuse_direntplus *dp = (struct fuse_direntplus *) buf; memset(&dp->entry_out, 0, sizeof(dp->entry_out)); fill_entry(&dp->entry_out, e); struct fuse_dirent *dirent = &dp->dirent; dirent->ino = e->attr.st_ino; dirent->off = off; dirent->namelen = namelen; dirent->type = (e->attr.st_mode & S_IFMT) >> 12; memcpy(dirent->name, name, namelen); memset(dirent->name + namelen, 0, entlen_padded - entlen); return entlen_padded; } static void fill_open(struct fuse_open_out *arg, const struct fuse_file_info *f) { arg->fh = f->fh; if (f->backing_id > 0) { arg->backing_id = f->backing_id; arg->open_flags |= FOPEN_PASSTHROUGH; } if (f->direct_io) arg->open_flags |= FOPEN_DIRECT_IO; if (f->keep_cache) arg->open_flags |= FOPEN_KEEP_CACHE; if (f->cache_readdir) 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) { struct fuse_entry_out arg; size_t size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg); /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant negative entry */ if (!e->ino && req->se->conn.proto_minor < 4) return fuse_reply_err(req, ENOENT); memset(&arg, 0, sizeof(arg)); fill_entry(&arg, e); return send_reply_ok(req, &arg, size); } int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *f) { alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; size_t entrysize = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out); struct fuse_entry_out *earg = (struct fuse_entry_out *) buf; struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize); memset(buf, 0, sizeof(buf)); fill_entry(earg, e); fill_open(oarg, f); return send_reply_ok(req, buf, entrysize + sizeof(struct fuse_open_out)); } int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout) { struct fuse_attr_out arg; size_t size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); memset(&arg, 0, sizeof(arg)); arg.attr_valid = calc_timeout_sec(attr_timeout); arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); convert_stat(attr, &arg.attr); return send_reply_ok(req, &arg, size); } int fuse_reply_readlink(fuse_req_t req, const char *linkname) { return send_reply_ok(req, linkname, strlen(linkname)); } int fuse_passthrough_open(fuse_req_t req, int fd) { struct fuse_backing_map map = { .fd = fd }; int ret; ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map); if (ret <= 0) { fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno)); return 0; } return ret; } int fuse_passthrough_close(fuse_req_t req, int backing_id) { int ret; ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id); if (ret < 0) fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno)); return ret; } int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) { struct fuse_open_out arg; memset(&arg, 0, sizeof(arg)); fill_open(&arg, f); return send_reply_ok(req, &arg, sizeof(arg)); } static int do_fuse_reply_write(fuse_req_t req, size_t count) { struct fuse_write_out arg; memset(&arg, 0, sizeof(arg)); arg.size = count; return send_reply_ok(req, &arg, sizeof(arg)); } static int do_fuse_reply_copy(fuse_req_t req, size_t count) { struct fuse_copy_file_range_out arg; memset(&arg, 0, sizeof(arg)); arg.bytes_copied = count; return send_reply_ok(req, &arg, sizeof(arg)); } int fuse_reply_write(fuse_req_t req, size_t count) { /* * This function is also used by FUSE_COPY_FILE_RANGE and its 64-bit * variant. */ if (req->flags.is_copy_file_range_64) return do_fuse_reply_copy(req, count); else return do_fuse_reply_write(req, count); } int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) { return send_reply_ok(req, buf, size); } static int fuse_send_data_iov_fallback(struct fuse_session *se, struct fuse_chan *ch, struct iovec *iov, int iov_count, struct fuse_bufvec *buf, size_t len, fuse_req_t req) { struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); void *mbuf; int res; /* Optimize common case */ if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { /* FIXME: also avoid memory copy if there are multiple buffers but none of them contain an fd */ iov[iov_count].iov_base = buf->buf[0].mem; iov[iov_count].iov_len = len; iov_count++; return fuse_send_msg(se, ch, iov, iov_count, req); } res = posix_memalign(&mbuf, pagesize, len); if (res != 0) return res; mem_buf.buf[0].mem = mbuf; res = fuse_buf_copy(&mem_buf, buf, 0); if (res < 0) { free(mbuf); return -res; } len = res; iov[iov_count].iov_base = mbuf; iov[iov_count].iov_len = len; iov_count++; res = fuse_send_msg(se, ch, iov, iov_count, req); free(mbuf); return res; } struct fuse_ll_pipe { size_t size; int can_grow; int pipe[2]; }; static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp) { close(llp->pipe[0]); close(llp->pipe[1]); free(llp); } #ifdef HAVE_SPLICE #if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC) static int fuse_pipe(int fds[2]) { int rv = pipe(fds); if (rv == -1) return rv; if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 || fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 || fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { close(fds[0]); close(fds[1]); rv = -1; } return rv; } #else static int fuse_pipe(int fds[2]) { return pipe2(fds, O_CLOEXEC | O_NONBLOCK); } #endif static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se) { struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); if (llp == NULL) { int res; llp = malloc(sizeof(struct fuse_ll_pipe)); if (llp == NULL) return NULL; res = fuse_pipe(llp->pipe); if (res == -1) { free(llp); return NULL; } /* *the default size is 16 pages on linux */ llp->size = pagesize * 16; llp->can_grow = 1; pthread_setspecific(se->pipe_key, llp); } return llp; } #endif static void fuse_ll_clear_pipe(struct fuse_session *se) { struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); if (llp) { pthread_setspecific(se->pipe_key, NULL); fuse_ll_pipe_free(llp); } } #if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE) static int read_back(int fd, char *buf, size_t len) { int res; res = read(fd, buf, len); if (res == -1) { fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno)); return -EIO; } if (res != len) { fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zd\n", res, len); return -EIO; } return 0; } static int grow_pipe_to_max(int pipefd) { int res; long max; long maxfd; char buf[32]; maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY); if (maxfd < 0) return -errno; res = read(maxfd, buf, sizeof(buf) - 1); if (res < 0) { int saved_errno; saved_errno = errno; close(maxfd); return -saved_errno; } close(maxfd); buf[res] = '\0'; res = libfuse_strtol(buf, &max); if (res) return res; res = fcntl(pipefd, F_SETPIPE_SZ, max); if (res < 0) return -errno; return max; } static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, struct iovec *iov, int iov_count, struct fuse_bufvec *buf, unsigned int flags, fuse_req_t req) { int res; size_t len = fuse_buf_size(buf); struct fuse_out_header *out = iov[0].iov_base; struct fuse_ll_pipe *llp; int splice_flags; size_t pipesize; size_t total_buf_size; size_t idx; size_t headerlen; struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len); if (se->broken_splice_nonblock) goto fallback; if (flags & FUSE_BUF_NO_SPLICE) goto fallback; total_buf_size = 0; for (idx = buf->idx; idx < buf->count; idx++) { total_buf_size += buf->buf[idx].size; if (idx == buf->idx) total_buf_size -= buf->off; } if (total_buf_size < 2 * pagesize) goto fallback; if (se->conn.proto_minor < 14 || !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE)) goto fallback; llp = fuse_ll_get_pipe(se); if (llp == NULL) goto fallback; headerlen = iov_length(iov, iov_count); out->len = headerlen + len; /* * Heuristic for the required pipe size, does not work if the * source contains less than page size fragments */ pipesize = pagesize * (iov_count + buf->count + 1) + out->len; if (llp->size < pipesize) { if (llp->can_grow) { res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize); if (res == -1) { res = grow_pipe_to_max(llp->pipe[0]); if (res > 0) llp->size = res; llp->can_grow = 0; goto fallback; } llp->size = res; } if (llp->size < pipesize) goto fallback; } res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK); if (res == -1) goto fallback; if (res != headerlen) { res = -EIO; fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res, headerlen); goto clear_pipe; } pipe_buf.buf[0].flags = FUSE_BUF_IS_FD; pipe_buf.buf[0].fd = llp->pipe[1]; res = fuse_buf_copy(&pipe_buf, buf, FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK); if (res < 0) { if (res == -EAGAIN || res == -EINVAL) { /* * Should only get EAGAIN on kernels with * broken SPLICE_F_NONBLOCK support (<= * 2.6.35) where this error or a short read is * returned even if the pipe itself is not * full * * EINVAL might mean that splice can't handle * this combination of input and output. */ if (res == -EAGAIN) se->broken_splice_nonblock = 1; pthread_setspecific(se->pipe_key, NULL); fuse_ll_pipe_free(llp); goto fallback; } res = -res; goto clear_pipe; } if (res != 0 && res < len) { struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); void *mbuf; size_t now_len = res; /* * For regular files a short count is either * 1) due to EOF, or * 2) because of broken SPLICE_F_NONBLOCK (see above) * * For other inputs it's possible that we overflowed * the pipe because of small buffer fragments. */ res = posix_memalign(&mbuf, pagesize, len); if (res != 0) goto clear_pipe; mem_buf.buf[0].mem = mbuf; mem_buf.off = now_len; res = fuse_buf_copy(&mem_buf, buf, 0); if (res > 0) { char *tmpbuf; size_t extra_len = res; /* * Trickiest case: got more data. Need to get * back the data from the pipe and then fall * back to regular write. */ tmpbuf = malloc(headerlen); if (tmpbuf == NULL) { free(mbuf); res = ENOMEM; goto clear_pipe; } res = read_back(llp->pipe[0], tmpbuf, headerlen); free(tmpbuf); if (res != 0) { free(mbuf); goto clear_pipe; } res = read_back(llp->pipe[0], mbuf, now_len); if (res != 0) { free(mbuf); goto clear_pipe; } len = now_len + extra_len; iov[iov_count].iov_base = mbuf; iov[iov_count].iov_len = len; iov_count++; res = fuse_send_msg(se, ch, iov, iov_count, req); free(mbuf); return res; } free(mbuf); res = now_len; } len = res; out->len = headerlen + len; if (se->debug) { fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i (splice)\n", (unsigned long long) out->unique, out->len); } splice_flags = 0; if ((flags & FUSE_BUF_SPLICE_MOVE) && (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE)) splice_flags |= SPLICE_F_MOVE; 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"); goto clear_pipe; } if (res != out->len) { res = -EIO; fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n", res, out->len); goto clear_pipe; } return 0; clear_pipe: fuse_ll_clear_pipe(se); return res; fallback: return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len, req); } #else static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, struct iovec *iov, int iov_count, struct fuse_bufvec *req_data, unsigned int flags, fuse_req_t req) { size_t len = fuse_buf_size(req_data); (void) flags; return fuse_send_data_iov_fallback(se, ch, iov, iov_count, req_data, len, req); } #endif int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags) { struct iovec iov[2]; struct fuse_out_header out; int res; if (req->flags.is_uring) return fuse_reply_data_uring(req, bufv, flags); iov[0].iov_base = &out; iov[0].iov_len = sizeof(struct fuse_out_header); out.unique = req->unique; out.error = 0; res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags, req); if (res <= 0) { fuse_free_req(req); return res; } else { return fuse_reply_err(req, res); } } int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) { struct fuse_statfs_out arg; size_t size = req->se->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg); memset(&arg, 0, sizeof(arg)); convert_statfs(stbuf, &arg.st); return send_reply_ok(req, &arg, size); } int fuse_reply_xattr(fuse_req_t req, size_t count) { struct fuse_getxattr_out arg; memset(&arg, 0, sizeof(arg)); arg.size = count; return send_reply_ok(req, &arg, sizeof(arg)); } int fuse_reply_lock(fuse_req_t req, const struct flock *lock) { struct fuse_lk_out arg; memset(&arg, 0, sizeof(arg)); arg.lk.type = lock->l_type; if (lock->l_type != F_UNLCK) { arg.lk.start = lock->l_start; if (lock->l_len == 0) arg.lk.end = OFFSET_MAX; else arg.lk.end = lock->l_start + lock->l_len - 1; } arg.lk.pid = lock->l_pid; return send_reply_ok(req, &arg, sizeof(arg)); } int fuse_reply_bmap(fuse_req_t req, uint64_t idx) { struct fuse_bmap_out arg; memset(&arg, 0, sizeof(arg)); arg.block = idx; return send_reply_ok(req, &arg, sizeof(arg)); } static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov, size_t count) { struct fuse_ioctl_iovec *fiov; size_t i; fiov = malloc(sizeof(fiov[0]) * count); if (!fiov) return NULL; for (i = 0; i < count; i++) { fiov[i].base = (uintptr_t) iov[i].iov_base; fiov[i].len = iov[i].iov_len; } return fiov; } int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, size_t in_count, const struct iovec *out_iov, size_t out_count) { struct fuse_ioctl_out arg; struct fuse_ioctl_iovec *in_fiov = NULL; struct fuse_ioctl_iovec *out_fiov = NULL; struct iovec iov[4]; size_t count = 1; int res; memset(&arg, 0, sizeof(arg)); arg.flags |= FUSE_IOCTL_RETRY; arg.in_iovs = in_count; arg.out_iovs = out_count; iov[count].iov_base = &arg; iov[count].iov_len = sizeof(arg); count++; if (req->se->conn.proto_minor < 16) { if (in_count) { iov[count].iov_base = (void *)in_iov; iov[count].iov_len = sizeof(in_iov[0]) * in_count; count++; } if (out_count) { iov[count].iov_base = (void *)out_iov; iov[count].iov_len = sizeof(out_iov[0]) * out_count; count++; } } else { /* Can't handle non-compat 64bit ioctls on 32bit */ if (sizeof(void *) == 4 && req->flags.ioctl_64bit) { res = fuse_reply_err(req, EINVAL); goto out; } if (in_count) { in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); if (!in_fiov) goto enomem; iov[count].iov_base = (void *)in_fiov; iov[count].iov_len = sizeof(in_fiov[0]) * in_count; count++; } if (out_count) { out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); if (!out_fiov) goto enomem; iov[count].iov_base = (void *)out_fiov; iov[count].iov_len = sizeof(out_fiov[0]) * out_count; count++; } } res = send_reply_iov(req, 0, iov, count); out: free(in_fiov); free(out_fiov); return res; enomem: res = fuse_reply_err(req, ENOMEM); goto out; } int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size) { struct fuse_ioctl_out arg; struct iovec iov[3]; size_t count = 1; memset(&arg, 0, sizeof(arg)); arg.result = result; iov[count].iov_base = &arg; iov[count].iov_len = sizeof(arg); count++; if (size) { iov[count].iov_base = (char *) buf; iov[count].iov_len = size; count++; } return send_reply_iov(req, 0, iov, count); } int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, int count) { struct iovec *padded_iov; struct fuse_ioctl_out arg; int res; padded_iov = malloc((count + 2) * sizeof(struct iovec)); if (padded_iov == NULL) return fuse_reply_err(req, ENOMEM); memset(&arg, 0, sizeof(arg)); arg.result = result; padded_iov[1].iov_base = &arg; padded_iov[1].iov_len = sizeof(arg); memcpy(&padded_iov[2], iov, count * sizeof(struct iovec)); res = send_reply_iov(req, 0, padded_iov, count + 2); free(padded_iov); return res; } int fuse_reply_poll(fuse_req_t req, unsigned revents) { struct fuse_poll_out arg; memset(&arg, 0, sizeof(arg)); arg.revents = revents; return send_reply_ok(req, &arg, sizeof(arg)); } int fuse_reply_lseek(fuse_req_t req, off_t off) { struct fuse_lseek_out arg; memset(&arg, 0, sizeof(arg)); arg.offset = off; return send_reply_ok(req, &arg, sizeof(arg)); } #ifdef HAVE_STATX int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout) { struct fuse_statx_out arg; memset(&arg, 0, sizeof(arg)); arg.flags = flags; arg.attr_valid = calc_timeout_sec(attr_timeout); arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); memcpy(&arg.stat, statx, sizeof(arg.stat)); return send_reply_ok(req, &arg, sizeof(arg)); } #else int fuse_reply_statx(fuse_req_t req, int flags, struct statx *statx, double attr_timeout) { (void)req; (void)flags; (void)statx; (void)attr_timeout; return -ENOSYS; } #endif static void _do_lookup(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)op_in; char *name = (char *)in_payload; if (req->se->op.lookup) req->se->op.lookup(req, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void do_lookup(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_lookup(req, nodeid, NULL, inarg); } static void _do_forget(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; struct fuse_forget_in *arg = (struct fuse_forget_in *)op_in; if (req->se->op.forget) req->se->op.forget(req, nodeid, arg->nlookup); else fuse_reply_none(req); } static void do_forget(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_forget(req, nodeid, inarg, NULL); } static void _do_batch_forget(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)nodeid; unsigned int i; const struct fuse_batch_forget_in *arg = op_in; const struct fuse_forget_one *forgets = in_payload; if (req->se->op.forget_multi) { req->se->op.forget_multi(req, arg->count, (struct fuse_forget_data *)in_payload); } else if (req->se->op.forget) { for (i = 0; i < arg->count; i++) { const struct fuse_forget_one *forget = &forgets[i]; struct fuse_req *dummy_req; dummy_req = fuse_ll_alloc_req(req->se); if (dummy_req == NULL) break; dummy_req->unique = req->unique; dummy_req->ctx = req->ctx; dummy_req->ch = NULL; req->se->op.forget(dummy_req, forget->nodeid, forget->nlookup); } fuse_reply_none(req); } else { fuse_reply_none(req); } } static void do_batch_forget(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { struct fuse_batch_forget_in *arg = (void *)inarg; struct fuse_forget_one *param = (void *)PARAM(arg); _do_batch_forget(req, nodeid, inarg, param); } static void _do_getattr(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { struct fuse_getattr_in *arg = (struct fuse_getattr_in *)op_in; (void)in_payload; struct fuse_file_info *fip = NULL; struct fuse_file_info fi; if (req->se->conn.proto_minor >= 9) { if (arg->getattr_flags & FUSE_GETATTR_FH) { memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; fip = &fi; } } if (req->se->op.getattr) req->se->op.getattr(req, nodeid, fip); else fuse_reply_err(req, ENOSYS); } static void do_getattr(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_getattr(req, nodeid, inarg, NULL); } static void _do_setattr(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_setattr_in *arg = op_in; uint32_t valid = arg->valid; if (req->se->op.setattr) { struct fuse_file_info *fi = NULL; struct fuse_file_info fi_store; struct stat stbuf; memset(&stbuf, 0, sizeof(stbuf)); convert_attr(arg, &stbuf); if (arg->valid & FATTR_FH) { valid &= ~FATTR_FH; memset(&fi_store, 0, sizeof(fi_store)); fi = &fi_store; fi->fh = arg->fh; } valid &= FUSE_SET_ATTR_MODE | FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID | 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; req->se->op.setattr(req, nodeid, &stbuf, valid, fi); } else fuse_reply_err(req, ENOSYS); } static void do_setattr(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_setattr(req, nodeid, inarg, NULL); } static void _do_access(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_access_in *arg = op_in; if (req->se->op.access) req->se->op.access(req, nodeid, arg->mask); else fuse_reply_err(req, ENOSYS); } static void do_access(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_access(req, nodeid, inarg, NULL); } static void _do_readlink(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)op_in; (void)in_payload; if (req->se->op.readlink) req->se->op.readlink(req, nodeid); else fuse_reply_err(req, ENOSYS); } static void do_readlink(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_readlink(req, nodeid, inarg, NULL); } static void _do_mknod(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { const struct fuse_mknod_in *arg = (struct fuse_mknod_in *)op_in; const char *name = in_payload; if (req->se->conn.proto_minor >= 12) req->ctx.umask = arg->umask; if (req->se->op.mknod) req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); else fuse_reply_err(req, ENOSYS); } static void do_mknod(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg; char *name = PARAM(arg); if (req->se->conn.proto_minor < 12) name = (char *)inarg + FUSE_COMPAT_MKNOD_IN_SIZE; _do_mknod(req, nodeid, inarg, name); } static void _do_mkdir(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { const char *name = in_payload; const struct fuse_mkdir_in *arg = op_in; if (req->se->conn.proto_minor >= 12) req->ctx.umask = arg->umask; if (req->se->op.mkdir) req->se->op.mkdir(req, nodeid, name, arg->mode); else fuse_reply_err(req, ENOSYS); } static void do_mkdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { const struct fuse_mkdir_in *arg = inarg; const char *name = PARAM(arg); _do_mkdir(req, nodeid, inarg, name); } static void _do_unlink(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)op_in; const char *name = in_payload; if (req->se->op.unlink) req->se->op.unlink(req, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void do_unlink(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_unlink(req, nodeid, NULL, inarg); } static void _do_rmdir(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)op_in; const char *name = in_payload; if (req->se->op.rmdir) req->se->op.rmdir(req, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void do_rmdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_rmdir(req, nodeid, NULL, inarg); } static void _do_symlink(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)op_in; const char *name = (char *)in_payload; const char *linkname = name + strlen(name) + 1; if (req->se->op.symlink) req->se->op.symlink(req, linkname, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void do_symlink(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_symlink(req, nodeid, NULL, inarg); } static void _do_rename(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { const struct fuse_rename_in *arg = (struct fuse_rename_in *)op_in; const char *oldname = in_payload; const char *newname = oldname + strlen(oldname) + 1; if (req->se->op.rename) req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, 0); else fuse_reply_err(req, ENOSYS); } static void do_rename(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { const struct fuse_rename_in *arg = inarg; const void *payload = PARAM(arg); _do_rename(req, nodeid, arg, payload); } static void _do_rename2(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { const struct fuse_rename2_in *arg = op_in; const char *oldname = in_payload; const char *newname = oldname + strlen(oldname) + 1; if (req->se->op.rename) req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, arg->flags); else fuse_reply_err(req, ENOSYS); } static void do_rename2(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { const struct fuse_rename2_in *arg = inarg; const void *payload = PARAM(arg); _do_rename2(req, nodeid, arg, payload); } static void _do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_create_in *arg = op_in; if (req->se->op.tmpfile) { struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = arg->flags; if (req->se->conn.proto_minor >= 12) req->ctx.umask = arg->umask; req->se->op.tmpfile(req, nodeid, arg->mode, &fi); } else fuse_reply_err(req, ENOSYS); } static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_create_in *arg = (struct fuse_create_in *) inarg; _do_tmpfile(req, nodeid, arg, NULL); } static void _do_link(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { struct fuse_link_in *arg = (struct fuse_link_in *)op_in; if (req->se->op.link) req->se->op.link(req, arg->oldnodeid, nodeid, in_payload); else fuse_reply_err(req, ENOSYS); } static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { const struct fuse_link_in *arg = inarg; const void *name = PARAM(arg); _do_link(req, nodeid, inarg, name); } static void _do_create(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { const struct fuse_create_in *arg = op_in; const char *name = in_payload; if (req->se->op.create) { struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = arg->flags; if (req->se->conn.proto_minor >= 12) req->ctx.umask = arg->umask; /* XXX: fuse_create_in::open_flags */ req->se->op.create(req, nodeid, name, arg->mode, &fi); } else { fuse_reply_err(req, ENOSYS); } } static void do_create(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { const struct fuse_create_in *arg = (struct fuse_create_in *)inarg; void *payload = PARAM(arg); if (req->se->conn.proto_minor < 12) payload = (char *)inarg + sizeof(struct fuse_open_in); _do_create(req, nodeid, arg, payload); } static void _do_open(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; struct fuse_open_in *arg = (struct fuse_open_in *)op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = arg->flags; /* XXX: fuse_open_in::open_flags */ if (req->se->op.open) req->se->op.open(req, nodeid, &fi); else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT) fuse_reply_err(req, ENOSYS); else fuse_reply_open(req, &fi); } static void do_open(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_open(req, nodeid, inarg, NULL); } static void _do_read(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; struct fuse_read_in *arg = (struct fuse_read_in *)op_in; if (req->se->op.read) { struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; if (req->se->conn.proto_minor >= 9) { fi.lock_owner = arg->lock_owner; fi.flags = arg->flags; } req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); } else fuse_reply_err(req, ENOSYS); } static void do_read(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_read(req, nodeid, inarg, NULL); } static void _do_write(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { struct fuse_write_in *arg = (struct fuse_write_in *)op_in; const char *buf = in_payload; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; if (req->se->conn.proto_minor >= 9) { fi.lock_owner = arg->lock_owner; fi.flags = arg->flags; } if (req->se->op.write) req->se->op.write(req, nodeid, buf, arg->size, arg->offset, &fi); else fuse_reply_err(req, ENOSYS); } static void do_write(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { struct fuse_write_in *arg = (struct fuse_write_in *)inarg; const void *payload; if (req->se->conn.proto_minor < 9) payload = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; else payload = PARAM(arg); _do_write(req, nodeid, arg, payload); } static void _do_write_buf(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, struct fuse_bufvec *bufv) { struct fuse_session *se = req->se; struct fuse_write_in *arg = (struct fuse_write_in *)op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; if (se->conn.proto_minor >= 9) { fi.lock_owner = arg->lock_owner; fi.flags = arg->flags; } se->op.write_buf(req, nodeid, bufv, arg->offset, &fi); } static void do_write_buf(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg, const struct fuse_buf *ibuf) { struct fuse_session *se = req->se; struct fuse_bufvec bufv = { .buf[0] = *ibuf, .count = 1, }; struct fuse_write_in *arg = (struct fuse_write_in *)inarg; if (se->conn.proto_minor < 9) { bufv.buf[0].mem = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; bufv.buf[0].size -= sizeof(struct fuse_in_header) + FUSE_COMPAT_WRITE_IN_SIZE; assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); } else { if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) bufv.buf[0].mem = PARAM(arg); bufv.buf[0].size -= sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); } if (bufv.buf[0].size < arg->size) { fuse_log(FUSE_LOG_ERR, "fuse: %s: buffer size too small\n", __func__); fuse_reply_err(req, EIO); goto out; } bufv.buf[0].size = arg->size; _do_write_buf(req, nodeid, inarg, &bufv); out: /* Need to reset the pipe if ->write_buf() didn't consume all data */ if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) fuse_ll_clear_pipe(se); } static void _do_flush(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; struct fuse_flush_in *arg = (struct fuse_flush_in *)op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; fi.flush = 1; if (req->se->conn.proto_minor >= 7) fi.lock_owner = arg->lock_owner; if (req->se->op.flush) req->se->op.flush(req, nodeid, &fi); else fuse_reply_err(req, ENOSYS); } static void do_flush(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_flush(req, nodeid, inarg, NULL); } static void _do_release(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_release_in *arg = op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = arg->flags; fi.fh = arg->fh; if (req->se->conn.proto_minor >= 8) { fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; fi.lock_owner = arg->lock_owner; } if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { fi.flock_release = 1; fi.lock_owner = arg->lock_owner; } if (req->se->op.release) req->se->op.release(req, nodeid, &fi); else fuse_reply_err(req, 0); } static void do_release(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_release(req, nodeid, inarg, NULL); } static void _do_fsync(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_fsync_in *arg = op_in; struct fuse_file_info fi; int datasync = arg->fsync_flags & 1; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; if (req->se->op.fsync) req->se->op.fsync(req, nodeid, datasync, &fi); else fuse_reply_err(req, ENOSYS); } static void do_fsync(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_fsync(req, nodeid, inarg, NULL); } static void _do_opendir(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_open_in *arg = op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = arg->flags; /* XXX: fuse_open_in::open_flags */ if (req->se->op.opendir) req->se->op.opendir(req, nodeid, &fi); else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT) fuse_reply_err(req, ENOSYS); else fuse_reply_open(req, &fi); } static void do_opendir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_opendir(req, nodeid, inarg, NULL); } static void _do_readdir(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; struct fuse_read_in *arg = (struct fuse_read_in *)op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; if (req->se->op.readdir) req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi); else fuse_reply_err(req, ENOSYS); } static void do_readdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_readdir(req, nodeid, inarg, NULL); } static void _do_readdirplus(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; struct fuse_read_in *arg = (struct fuse_read_in *)op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; if (req->se->op.readdirplus) req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi); else fuse_reply_err(req, ENOSYS); } static void do_readdirplus(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_readdirplus(req, nodeid, inarg, NULL); } static void _do_releasedir(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; struct fuse_release_in *arg = (struct fuse_release_in *)op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = arg->flags; fi.fh = arg->fh; if (req->se->op.releasedir) req->se->op.releasedir(req, nodeid, &fi); else fuse_reply_err(req, 0); } static void do_releasedir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_releasedir(req, nodeid, inarg, NULL); } static void _do_fsyncdir(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; struct fuse_fsync_in *arg = (struct fuse_fsync_in *)op_in; struct fuse_file_info fi; int datasync = arg->fsync_flags & 1; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; if (req->se->op.fsyncdir) req->se->op.fsyncdir(req, nodeid, datasync, &fi); else fuse_reply_err(req, ENOSYS); } static void do_fsyncdir(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_fsyncdir(req, nodeid, inarg, NULL); } static void _do_statfs(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void) nodeid; (void)op_in; (void)in_payload; if (req->se->op.statfs) req->se->op.statfs(req, nodeid); else { struct statvfs buf = { .f_namemax = 255, .f_bsize = 512, }; fuse_reply_statfs(req, &buf); } } static void do_statfs(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_statfs(req, nodeid, inarg, NULL); } static void _do_setxattr(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *)op_in; const char *name = in_payload; const 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); else fuse_reply_err(req, ENOSYS); } static void do_setxattr(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { struct fuse_session *se = req->se; unsigned int xattr_ext = !!(se->conn.want & FUSE_CAP_SETXATTR_EXT); const struct fuse_setxattr_in *arg = inarg; char *payload = xattr_ext ? PARAM(arg) : (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE; _do_setxattr(req, nodeid, arg, payload); } static void _do_getxattr(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { const struct fuse_getxattr_in *arg = op_in; if (req->se->op.getxattr) req->se->op.getxattr(req, nodeid, in_payload, arg->size); else fuse_reply_err(req, ENOSYS); } static void do_getxattr(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { const struct fuse_getxattr_in *arg = inarg; const void *payload = PARAM(arg); _do_getxattr(req, nodeid, arg, payload); } static void _do_listxattr(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg, const void *in_payload) { (void)in_payload; const struct fuse_getxattr_in *arg = inarg; if (req->se->op.listxattr) req->se->op.listxattr(req, nodeid, arg->size); else fuse_reply_err(req, ENOSYS); } static void do_listxattr(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_listxattr(req, nodeid, inarg, NULL); } static void _do_removexattr(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg, const void *in_payload) { (void)inarg; const char *name = in_payload; if (req->se->op.removexattr) req->se->op.removexattr(req, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { _do_removexattr(req, nodeid, NULL, inarg); } static void convert_fuse_file_lock(const struct fuse_file_lock *fl, struct flock *flock) { memset(flock, 0, sizeof(struct flock)); flock->l_type = fl->type; flock->l_whence = SEEK_SET; flock->l_start = fl->start; if (fl->end == OFFSET_MAX) flock->l_len = 0; else flock->l_len = fl->end - fl->start + 1; flock->l_pid = fl->pid; } static void _do_getlk(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_lk_in *arg = op_in; struct fuse_file_info fi; struct flock flock; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; fi.lock_owner = arg->owner; convert_fuse_file_lock(&arg->lk, &flock); if (req->se->op.getlk) req->se->op.getlk(req, nodeid, &fi, &flock); else fuse_reply_err(req, ENOSYS); } static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { _do_getlk(req, nodeid, inarg, NULL); } static void do_setlk_common(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, int sleep) { const struct fuse_lk_in *arg = op_in; struct fuse_file_info fi; struct flock flock; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; fi.lock_owner = arg->owner; if (arg->lk_flags & FUSE_LK_FLOCK) { int op = 0; switch (arg->lk.type) { case F_RDLCK: op = LOCK_SH; break; case F_WRLCK: op = LOCK_EX; break; case F_UNLCK: op = LOCK_UN; break; } if (!sleep) op |= LOCK_NB; if (req->se->op.flock) req->se->op.flock(req, nodeid, &fi, op); else fuse_reply_err(req, ENOSYS); } else { convert_fuse_file_lock(&arg->lk, &flock); if (req->se->op.setlk) req->se->op.setlk(req, nodeid, &fi, &flock, sleep); else fuse_reply_err(req, ENOSYS); } } static void _do_setlk(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; do_setlk_common(req, nodeid, op_in, 0); } static void do_setlk(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_setlk(req, nodeid, inarg, NULL); } static void _do_setlkw(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; do_setlk_common(req, nodeid, op_in, 1); } static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { _do_setlkw(req, nodeid, inarg, NULL); } static int find_interrupted(struct fuse_session *se, struct fuse_req *req) { struct fuse_req *curr; for (curr = se->list.next; curr != &se->list; curr = curr->next) { if (curr->unique == req->u.i.unique) { fuse_interrupt_func_t func; void *data; curr->ref_cnt++; pthread_mutex_unlock(&se->lock); /* Ugh, ugly locking */ pthread_mutex_lock(&curr->lock); pthread_mutex_lock(&se->lock); curr->interrupted = 1; func = curr->u.ni.func; data = curr->u.ni.data; pthread_mutex_unlock(&se->lock); if (func) func(curr, data); pthread_mutex_unlock(&curr->lock); pthread_mutex_lock(&se->lock); curr->ref_cnt--; if (!curr->ref_cnt) { destroy_req(curr); } return 1; } } for (curr = se->interrupts.next; curr != &se->interrupts; curr = curr->next) { if (curr->u.i.unique == req->u.i.unique) return 1; } return 0; } static void _do_interrupt(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_interrupt_in *arg = op_in; struct fuse_session *se = req->se; (void) nodeid; if (se->debug) fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", (unsigned long long) arg->unique); req->u.i.unique = arg->unique; pthread_mutex_lock(&se->lock); if (find_interrupted(se, req)) { fuse_chan_put(req->ch); req->ch = NULL; destroy_req(req); } else list_add_req(req, &se->interrupts); pthread_mutex_unlock(&se->lock); } static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { _do_interrupt(req, nodeid, inarg, NULL); } static struct fuse_req *check_interrupt(struct fuse_session *se, struct fuse_req *req) { struct fuse_req *curr; for (curr = se->interrupts.next; curr != &se->interrupts; curr = curr->next) { if (curr->u.i.unique == req->unique) { req->interrupted = 1; list_del_req(curr); fuse_chan_put(curr->ch); curr->ch = NULL; destroy_req(curr); return NULL; } } curr = se->interrupts.next; if (curr != &se->interrupts) { list_del_req(curr); list_init_req(curr); return curr; } else return NULL; } static void _do_bmap(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_bmap_in *arg = op_in; if (req->se->op.bmap) req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); else fuse_reply_err(req, ENOSYS); } static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { _do_bmap(req, nodeid, inarg, NULL); } static void _do_ioctl(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *)op_in; unsigned int flags = arg->flags; const void *in_buf = in_payload; struct fuse_file_info fi; if (flags & FUSE_IOCTL_DIR && !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) { fuse_reply_err(req, ENOTTY); return; } memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 && !(flags & FUSE_IOCTL_32BIT)) { req->flags.ioctl_64bit = 1; } if (req->se->op.ioctl) req->se->op.ioctl(req, nodeid, arg->cmd, (void *)(uintptr_t)arg->arg, &fi, flags, in_buf, arg->in_size, arg->out_size); else fuse_reply_err(req, ENOSYS); } static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { const struct fuse_ioctl_in *arg = inarg; void *in_buf = arg->in_size ? PARAM(arg) : NULL; _do_ioctl(req, nodeid, arg, in_buf); } void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) { free(ph); } static void _do_poll(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; struct fuse_poll_in *arg = (struct fuse_poll_in *)op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; fi.poll_events = arg->events; if (req->se->op.poll) { struct fuse_pollhandle *ph = NULL; if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) { ph = malloc(sizeof(struct fuse_pollhandle)); if (ph == NULL) { fuse_reply_err(req, ENOMEM); return; } ph->kh = arg->kh; ph->se = req->se; } req->se->op.poll(req, nodeid, &fi, ph); } else { fuse_reply_err(req, ENOSYS); } } static void do_poll(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_poll(req, nodeid, inarg, NULL); } static void _do_fallocate(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_fallocate_in *arg = op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; if (req->se->op.fallocate) req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi); else fuse_reply_err(req, ENOSYS); } static void do_fallocate(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_fallocate(req, nodeid, inarg, NULL); } static void copy_file_range_common(fuse_req_t req, const fuse_ino_t nodeid_in, const struct fuse_copy_file_range_in *arg) { struct fuse_file_info fi_in, fi_out; memset(&fi_in, 0, sizeof(fi_in)); fi_in.fh = arg->fh_in; memset(&fi_out, 0, sizeof(fi_out)); fi_out.fh = arg->fh_out; if (req->se->op.copy_file_range) req->se->op.copy_file_range(req, nodeid_in, arg->off_in, &fi_in, arg->nodeid_out, arg->off_out, &fi_out, arg->len, arg->flags); else fuse_reply_err(req, ENOSYS); } static void _do_copy_file_range(fuse_req_t req, const fuse_ino_t nodeid_in, const void *op_in, const void *in_payload) { const struct fuse_copy_file_range_in *arg = op_in; struct fuse_copy_file_range_in arg_tmp; (void) in_payload; /* fuse_write_out can only handle 32bit copy size */ if (arg->len > 0xfffff000) { arg_tmp = *arg; arg_tmp.len = 0xfffff000; arg = &arg_tmp; } copy_file_range_common(req, nodeid_in, arg); } static void do_copy_file_range(fuse_req_t req, const fuse_ino_t nodeid_in, const void *inarg) { _do_copy_file_range(req, nodeid_in, inarg, NULL); } static void _do_copy_file_range_64(fuse_req_t req, const fuse_ino_t nodeid_in, const void *op_in, const void *in_payload) { (void) in_payload; req->flags.is_copy_file_range_64 = 1; /* Limit size on 32bit userspace to avoid conversion overflow */ if (sizeof(size_t) == 4) _do_copy_file_range(req, nodeid_in, op_in, NULL); else copy_file_range_common(req, nodeid_in, op_in); } static void do_copy_file_range_64(fuse_req_t req, const fuse_ino_t nodeid_in, const void *inarg) { _do_copy_file_range_64(req, nodeid_in, inarg, NULL); } /* * Note that the uint64_t offset in struct fuse_lseek_in is derived from * linux kernel loff_t and is therefore signed. */ static void _do_lseek(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_lseek_in *arg = op_in; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; if (req->se->op.lseek) req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi); else fuse_reply_err(req, ENOSYS); } static void do_lseek(fuse_req_t req, const fuse_ino_t nodeid, const void *inarg) { _do_lseek(req, nodeid, inarg, NULL); } #ifdef HAVE_STATX static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_statx_in *arg = op_in; struct fuse_file_info *fip = NULL; struct fuse_file_info fi; if (arg->getattr_flags & FUSE_GETATTR_FH) { memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; fip = &fi; } if (req->se->op.statx) req->se->op.statx(req, nodeid, arg->sx_flags, arg->sx_mask, fip); else fuse_reply_err(req, ENOSYS); } #else static void _do_statx(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; (void)req; (void)nodeid; (void)op_in; } #endif static void do_statx(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { _do_statx(req, nodeid, inarg, NULL); } static bool want_flags_valid(uint64_t capable, uint64_t want) { uint64_t unknown_flags = want & (~capable); if (unknown_flags != 0) { fuse_log(FUSE_LOG_ERR, "fuse: unknown connection 'want' flags: 0x%08llx\n", (unsigned long long)unknown_flags); return false; } return true; } /** * Get the wanted capability flags, converting from old format if necessary */ int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn) { struct fuse_session *se = container_of(conn, struct fuse_session, conn); /* * Convert want to want_ext if necessary. * For the high level interface this function might be called * twice, once from the high level interface and once from the * low level interface. Both, with different want_ext_default and * want_default values. In order to suppress a failure for the * second call, we check if the lower 32 bits of want_ext are * already set to the value of want. */ if (conn->want != se->conn_want && fuse_lower_32_bits(conn->want_ext) != conn->want) { if (conn->want_ext != se->conn_want_ext) { fuse_log(FUSE_LOG_ERR, "%s: Both conn->want_ext and conn->want are set.\n" "want=%x want_ext=%llx, se->want=%x se->want_ext=%llx\n", __func__, conn->want, (unsigned long long)conn->want_ext, se->conn_want, (unsigned long long)se->conn_want_ext); return -EINVAL; } /* high bits from want_ext, low bits from want */ conn->want_ext = fuse_higher_32_bits(conn->want_ext) | conn->want; } /* ensure there won't be a second conversion */ conn->want = fuse_lower_32_bits(conn->want_ext); return 0; } bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag) { struct fuse_session *se = container_of(conn, struct fuse_session, conn); if (conn->capable_ext & flag) { conn->want_ext |= flag; se->conn_want_ext |= flag; conn->want |= flag; se->conn_want |= flag; return true; } return false; } void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag) { struct fuse_session *se = container_of(conn, struct fuse_session, conn); conn->want_ext &= ~flag; se->conn_want_ext &= ~flag; conn->want &= ~flag; se->conn_want &= ~flag; } bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag) { return conn->capable_ext & flag ? true : false; } /* Prevent bogus data races (bogus since "init" is called before * multi-threading becomes relevant */ static __attribute__((no_sanitize("thread"))) void _do_init(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { (void)in_payload; const struct fuse_init_in *arg = op_in; struct fuse_init_out outarg; struct fuse_session *se = req->se; size_t bufsize = se->bufsize; size_t outargsize = sizeof(outarg); uint64_t inargflags = 0; uint64_t outargflags = 0; bool buf_reallocable = se->buf_reallocable; (void) nodeid; bool enable_io_uring = false; if (se->debug) { fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); if (arg->major == 7 && arg->minor >= 6) { fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", arg->max_readahead); } } se->conn.proto_major = arg->major; se->conn.proto_minor = arg->minor; se->conn.capable_ext = 0; se->conn.want_ext = 0; memset(&outarg, 0, sizeof(outarg)); outarg.major = FUSE_KERNEL_VERSION; outarg.minor = FUSE_KERNEL_MINOR_VERSION; if (arg->major < 7) { fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", arg->major, arg->minor); fuse_reply_err(req, EPROTO); return; } if (arg->major > 7) { /* Wait for a second INIT request with a 7.X version */ send_reply_ok(req, &outarg, sizeof(outarg)); return; } if (arg->minor >= 6) { if (arg->max_readahead < se->conn.max_readahead) se->conn.max_readahead = arg->max_readahead; inargflags = arg->flags; if (inargflags & FUSE_INIT_EXT) inargflags = inargflags | (uint64_t) arg->flags2 << 32; if (inargflags & FUSE_ASYNC_READ) se->conn.capable_ext |= FUSE_CAP_ASYNC_READ; if (inargflags & FUSE_POSIX_LOCKS) se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS; if (inargflags & FUSE_ATOMIC_O_TRUNC) se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC; if (inargflags & FUSE_EXPORT_SUPPORT) se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT; if (inargflags & FUSE_DONT_MASK) se->conn.capable_ext |= FUSE_CAP_DONT_MASK; if (inargflags & FUSE_FLOCK_LOCKS) se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS; if (inargflags & FUSE_AUTO_INVAL_DATA) se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA; if (inargflags & FUSE_DO_READDIRPLUS) se->conn.capable_ext |= FUSE_CAP_READDIRPLUS; if (inargflags & FUSE_READDIRPLUS_AUTO) se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO; if (inargflags & FUSE_ASYNC_DIO) se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO; if (inargflags & FUSE_WRITEBACK_CACHE) se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE; if (inargflags & FUSE_NO_OPEN_SUPPORT) se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT; if (inargflags & FUSE_PARALLEL_DIROPS) se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS; if (inargflags & FUSE_POSIX_ACL) se->conn.capable_ext |= FUSE_CAP_POSIX_ACL; if (inargflags & FUSE_HANDLE_KILLPRIV) se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV; if (inargflags & FUSE_HANDLE_KILLPRIV_V2) se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2; if (inargflags & FUSE_CACHE_SYMLINKS) se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS; if (inargflags & FUSE_NO_OPENDIR_SUPPORT) se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT; if (inargflags & FUSE_EXPLICIT_INVAL_DATA) se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA; if (inargflags & FUSE_SETXATTR_EXT) se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT; if (!(inargflags & FUSE_MAX_PAGES)) { size_t max_bufsize = FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() + FUSE_BUFFER_HEADER_SIZE; if (bufsize > max_bufsize) { bufsize = max_bufsize; } buf_reallocable = false; } if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP) se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP; if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY)) se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY; if (inargflags & FUSE_PASSTHROUGH) se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH; if (inargflags & FUSE_NO_EXPORT_SUPPORT) se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT; if (inargflags & FUSE_OVER_IO_URING) se->conn.capable_ext |= FUSE_CAP_OVER_IO_URING; } else { se->conn.max_readahead = 0; } if (se->conn.proto_minor >= 14) { #ifdef HAVE_SPLICE #ifdef HAVE_VMSPLICE if ((se->io == NULL) || (se->io->splice_send != NULL)) { se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; } #endif if ((se->io == NULL) || (se->io->splice_receive != NULL)) { se->conn.capable_ext |= FUSE_CAP_SPLICE_READ; } #endif } if (se->conn.proto_minor >= 18) se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR; /* Default settings for modern filesystems. * * Most of these capabilities were disabled by default in * libfuse2 for backwards compatibility reasons. In libfuse3, * we can finally enable them by default (as long as they're * supported by the kernel). */ #define LL_SET_DEFAULT(cond, cap) \ if ((cond)) \ fuse_set_feature_flag(&se->conn, cap) LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR); LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC); LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ); LL_SET_DEFAULT(se->op.getlk && se->op.setlk, FUSE_CAP_POSIX_LOCKS); LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS); LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS); LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir, FUSE_CAP_READDIRPLUS_AUTO); LL_SET_DEFAULT(1, FUSE_CAP_OVER_IO_URING); /* 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 (se->op.init) { // Apply the first 32 bits of capable_ext to capable se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext); se->op.init(se->userdata, &se->conn); /* * se->conn.want is 32-bit value and deprecated in favour of * se->conn.want_ext * Userspace might still use conn.want - we need to convert it */ fuse_convert_to_conn_want_ext(&se->conn); } if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) { fuse_reply_err(req, EPROTO); se->error = -EPROTO; fuse_session_exit(se); return; } unsigned max_read_mo = get_max_read(se->mo); if (se->conn.max_read != max_read_mo) { fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() " "requested different maximum read size (%u vs %u)\n", se->conn.max_read, max_read_mo); fuse_reply_err(req, EPROTO); se->error = -EPROTO; fuse_session_exit(se); return; } if (bufsize < FUSE_MIN_READ_BUFFER) { fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n", bufsize); bufsize = FUSE_MIN_READ_BUFFER; } if (buf_reallocable) bufsize = UINT_MAX; se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE); se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; if (arg->flags & FUSE_MAX_PAGES) { 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 */ outargflags |= FUSE_BIG_WRITES; if (se->conn.want_ext & FUSE_CAP_ASYNC_READ) outargflags |= FUSE_ASYNC_READ; if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS) outargflags |= FUSE_POSIX_LOCKS; if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC) outargflags |= FUSE_ATOMIC_O_TRUNC; if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT) outargflags |= FUSE_EXPORT_SUPPORT; if (se->conn.want_ext & FUSE_CAP_DONT_MASK) outargflags |= FUSE_DONT_MASK; if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS) outargflags |= FUSE_FLOCK_LOCKS; if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA) outargflags |= FUSE_AUTO_INVAL_DATA; if (se->conn.want_ext & FUSE_CAP_READDIRPLUS) outargflags |= FUSE_DO_READDIRPLUS; if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO) outargflags |= FUSE_READDIRPLUS_AUTO; if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO) outargflags |= FUSE_ASYNC_DIO; if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE) outargflags |= FUSE_WRITEBACK_CACHE; if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS) outargflags |= FUSE_PARALLEL_DIROPS; if (se->conn.want_ext & FUSE_CAP_POSIX_ACL) outargflags |= FUSE_POSIX_ACL; if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV) outargflags |= FUSE_HANDLE_KILLPRIV; if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2) outargflags |= FUSE_HANDLE_KILLPRIV_V2; if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS) outargflags |= FUSE_CACHE_SYMLINKS; if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA) outargflags |= FUSE_EXPLICIT_INVAL_DATA; if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT) outargflags |= FUSE_SETXATTR_EXT; if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP) outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP; if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) { outargflags |= FUSE_PASSTHROUGH; /* * outarg.max_stack_depth includes the fuse stack layer, * so it is one more than max_backing_stack_depth. */ outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1; } if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT) outargflags |= FUSE_NO_EXPORT_SUPPORT; if (se->uring.enable && se->conn.want_ext & FUSE_CAP_OVER_IO_URING) { outargflags |= FUSE_OVER_IO_URING; enable_io_uring = true; } if ((inargflags & FUSE_REQUEST_TIMEOUT) && se->conn.request_timeout) { outargflags |= FUSE_REQUEST_TIMEOUT; outarg.request_timeout = se->conn.request_timeout; } outarg.max_readahead = se->conn.max_readahead; outarg.max_write = se->conn.max_write; if (se->conn.proto_minor >= 13) { if (se->conn.max_background >= (1 << 16)) se->conn.max_background = (1 << 16) - 1; if (se->conn.congestion_threshold > se->conn.max_background) se->conn.congestion_threshold = se->conn.max_background; if (!se->conn.congestion_threshold) { se->conn.congestion_threshold = se->conn.max_background * 3 / 4; } outarg.max_background = se->conn.max_background; outarg.congestion_threshold = se->conn.congestion_threshold; } if (se->conn.proto_minor >= 23) outarg.time_gran = se->conn.time_gran; if (se->debug) { fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", outarg.max_readahead); fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", outarg.max_background); fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", outarg.congestion_threshold); fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n", outarg.max_stack_depth); } if (arg->minor < 5) outargsize = FUSE_COMPAT_INIT_OUT_SIZE; else if (arg->minor < 23) outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE; /* XXX: Add an option to make non-available io-uring fatal */ if (enable_io_uring) { int ring_rc = fuse_uring_start(se); if (ring_rc != 0) { fuse_log(FUSE_LOG_INFO, "fuse: failed to start io-uring: %s\n", strerror(ring_rc)); outargflags &= ~FUSE_OVER_IO_URING; enable_io_uring = false; } } if (inargflags & FUSE_INIT_EXT) { outargflags |= FUSE_INIT_EXT; outarg.flags2 = outargflags >> 32; } outarg.flags = outargflags; /* * Has to be set before replying, as new kernel requests might * immediately arrive and got_init is used for op-code sanity. * Especially with external handlers, where we have no control * over the thread scheduling. */ se->got_init = 1; send_reply_ok(req, &outarg, outargsize); if (enable_io_uring) fuse_uring_wake_ring_threads(se); } static __attribute__((no_sanitize("thread"))) void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { _do_init(req, nodeid, inarg, NULL); } static void _do_destroy(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in, const void *in_payload) { struct fuse_session *se = req->se; char *mountpoint; (void) nodeid; (void)op_in; (void)in_payload; mountpoint = atomic_exchange(&se->mountpoint, NULL); free(mountpoint); se->got_destroy = 1; se->got_init = 0; if (se->op.destroy) se->op.destroy(se->userdata); send_reply_ok(req, NULL, 0); } static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { _do_destroy(req, nodeid, inarg, NULL); } static void list_del_nreq(struct fuse_notify_req *nreq) { struct fuse_notify_req *prev = nreq->prev; struct fuse_notify_req *next = nreq->next; prev->next = next; next->prev = prev; } static void list_add_nreq(struct fuse_notify_req *nreq, struct fuse_notify_req *next) { struct fuse_notify_req *prev = next->prev; nreq->next = next; nreq->prev = prev; prev->next = nreq; next->prev = nreq; } static void list_init_nreq(struct fuse_notify_req *nreq) { nreq->next = nreq; nreq->prev = nreq; } static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, const struct fuse_buf *buf) { struct fuse_session *se = req->se; struct fuse_notify_req *nreq; struct fuse_notify_req *head; pthread_mutex_lock(&se->lock); head = &se->notify_list; for (nreq = head->next; nreq != head; nreq = nreq->next) { if (nreq->unique == req->unique) { list_del_nreq(nreq); break; } } pthread_mutex_unlock(&se->lock); if (nreq != head) nreq->reply(nreq, req, nodeid, inarg, buf); } static int send_notify_iov(struct fuse_session *se, int notify_code, struct iovec *iov, int count) { struct fuse_out_header out; struct fuse_req *req = NULL; if (!se->got_init) return -ENOTCONN; out.unique = 0; out.error = notify_code; iov[0].iov_base = &out; iov[0].iov_len = sizeof(struct fuse_out_header); return fuse_send_msg(se, NULL, iov, count, req); } int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) { if (ph != NULL) { struct fuse_notify_poll_wakeup_out outarg; struct iovec iov[2]; outarg.kh = ph->kh; iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); } else { return 0; } } int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, off_t off, off_t len) { struct fuse_notify_inval_inode_out outarg; struct iovec iov[2]; if (!se) return -EINVAL; if (se->conn.proto_minor < 12) return -ENOSYS; outarg.ino = ino; outarg.off = off; outarg.len = len; iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); } int fuse_lowlevel_notify_increment_epoch(struct fuse_session *se) { struct iovec iov[1]; if (!se) return -EINVAL; if (se->conn.proto_minor < 44) return -ENOSYS; return send_notify_iov(se, FUSE_NOTIFY_INC_EPOCH, iov, 1); } /** * 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.flags = 0; if (flags & FUSE_LL_EXPIRE_ONLY) outarg.flags |= FUSE_EXPIRE_ONLY; iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); iov[2].iov_base = (void *)name; iov[2].iov_len = namelen + 1; 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_ext & 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) { struct fuse_notify_delete_out outarg; struct iovec iov[3]; if (!se) return -EINVAL; if (se->conn.proto_minor < 18) return -ENOSYS; outarg.parent = parent; outarg.child = child; outarg.namelen = namelen; outarg.padding = 0; iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); iov[2].iov_base = (void *)name; iov[2].iov_len = namelen + 1; return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); } int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags) { struct fuse_out_header out; struct fuse_notify_store_out outarg; struct iovec iov[3]; size_t size = fuse_buf_size(bufv); int res; struct fuse_req *req = NULL; if (!se) return -EINVAL; if (se->conn.proto_minor < 15) return -ENOSYS; out.unique = 0; out.error = FUSE_NOTIFY_STORE; outarg.nodeid = ino; outarg.offset = offset; outarg.size = size; outarg.padding = 0; iov[0].iov_base = &out; iov[0].iov_len = sizeof(out); iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags, req); if (res > 0) res = -res; return res; } struct fuse_retrieve_req { struct fuse_notify_req nreq; void *cookie; }; static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, fuse_req_t req, fuse_ino_t ino, const void *inarg, const struct fuse_buf *ibuf) { struct fuse_session *se = req->se; struct fuse_retrieve_req *rreq = container_of(nreq, struct fuse_retrieve_req, nreq); const struct fuse_notify_retrieve_in *arg = inarg; struct fuse_bufvec bufv = { .buf[0] = *ibuf, .count = 1, }; if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) bufv.buf[0].mem = PARAM(arg); bufv.buf[0].size -= sizeof(struct fuse_in_header) + sizeof(struct fuse_notify_retrieve_in); if (bufv.buf[0].size < arg->size) { fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n"); fuse_reply_none(req); goto out; } bufv.buf[0].size = arg->size; if (se->op.retrieve_reply) { se->op.retrieve_reply(req, rreq->cookie, ino, arg->offset, &bufv); } else { fuse_reply_none(req); } out: free(rreq); if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) fuse_ll_clear_pipe(se); } int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie) { struct fuse_notify_retrieve_out outarg; struct iovec iov[2]; struct fuse_retrieve_req *rreq; int err; if (!se) return -EINVAL; if (se->conn.proto_minor < 15) return -ENOSYS; rreq = malloc(sizeof(*rreq)); if (rreq == NULL) return -ENOMEM; pthread_mutex_lock(&se->lock); rreq->cookie = cookie; rreq->nreq.unique = se->notify_ctr++; rreq->nreq.reply = fuse_ll_retrieve_reply; list_add_nreq(&rreq->nreq, &se->notify_list); pthread_mutex_unlock(&se->lock); outarg.notify_unique = rreq->nreq.unique; outarg.nodeid = ino; outarg.offset = offset; outarg.size = size; outarg.padding = 0; iov[1].iov_base = &outarg; iov[1].iov_len = sizeof(outarg); err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); if (err) { pthread_mutex_lock(&se->lock); list_del_nreq(&rreq->nreq); pthread_mutex_unlock(&se->lock); free(rreq); } return err; } void *fuse_req_userdata(fuse_req_t req) { return req->se->userdata; } const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) { return &req->ctx; } void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data) { pthread_mutex_lock(&req->lock); pthread_mutex_lock(&req->se->lock); req->u.ni.func = func; req->u.ni.data = data; pthread_mutex_unlock(&req->se->lock); if (req->interrupted && func) func(req, data); pthread_mutex_unlock(&req->lock); } int fuse_req_interrupted(fuse_req_t req) { int interrupted; pthread_mutex_lock(&req->se->lock); interrupted = req->interrupted; pthread_mutex_unlock(&req->se->lock); return interrupted; } bool fuse_req_is_uring(fuse_req_t req) { return req->flags.is_uring; } #ifndef HAVE_URING int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr) { (void)req; (void)payload; (void)payload_sz; (void)mr; return -ENOTSUP; } #endif static struct { void (*func)(fuse_req_t req, const fuse_ino_t node, const void *arg); const char *name; } fuse_ll_ops[] = { [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, [FUSE_FORGET] = { do_forget, "FORGET" }, [FUSE_GETATTR] = { do_getattr, "GETATTR" }, [FUSE_SETATTR] = { do_setattr, "SETATTR" }, [FUSE_READLINK] = { do_readlink, "READLINK" }, [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, [FUSE_MKNOD] = { do_mknod, "MKNOD" }, [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, [FUSE_UNLINK] = { do_unlink, "UNLINK" }, [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, [FUSE_RENAME] = { do_rename, "RENAME" }, [FUSE_LINK] = { do_link, "LINK" }, [FUSE_OPEN] = { do_open, "OPEN" }, [FUSE_READ] = { do_read, "READ" }, [FUSE_WRITE] = { do_write, "WRITE" }, [FUSE_STATFS] = { do_statfs, "STATFS" }, [FUSE_RELEASE] = { do_release, "RELEASE" }, [FUSE_FSYNC] = { do_fsync, "FSYNC" }, [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, [FUSE_FLUSH] = { do_flush, "FLUSH" }, [FUSE_INIT] = { do_init, "INIT" }, [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, [FUSE_READDIR] = { do_readdir, "READDIR" }, [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, [FUSE_GETLK] = { do_getlk, "GETLK" }, [FUSE_SETLK] = { do_setlk, "SETLK" }, [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, [FUSE_ACCESS] = { do_access, "ACCESS" }, [FUSE_CREATE] = { do_create, "CREATE" }, [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" }, [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, [FUSE_BMAP] = { do_bmap, "BMAP" }, [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, [FUSE_POLL] = { do_poll, "POLL" }, [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, [FUSE_DESTROY] = { do_destroy, "DESTROY" }, [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"}, [FUSE_RENAME2] = { do_rename2, "RENAME2" }, [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, [FUSE_COPY_FILE_RANGE_64] = { do_copy_file_range_64, "COPY_FILE_RANGE_64" }, [FUSE_LSEEK] = { do_lseek, "LSEEK" }, [FUSE_STATX] = { do_statx, "STATX" }, [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" }, }; static struct { void (*func)(fuse_req_t req, const fuse_ino_t ino, const void *op_in, const void *op_payload); const char *name; } fuse_ll_ops2[] __attribute__((unused)) = { [FUSE_LOOKUP] = { _do_lookup, "LOOKUP" }, [FUSE_FORGET] = { _do_forget, "FORGET" }, [FUSE_GETATTR] = { _do_getattr, "GETATTR" }, [FUSE_SETATTR] = { _do_setattr, "SETATTR" }, [FUSE_READLINK] = { _do_readlink, "READLINK" }, [FUSE_SYMLINK] = { _do_symlink, "SYMLINK" }, [FUSE_MKNOD] = { _do_mknod, "MKNOD" }, [FUSE_MKDIR] = { _do_mkdir, "MKDIR" }, [FUSE_UNLINK] = { _do_unlink, "UNLINK" }, [FUSE_RMDIR] = { _do_rmdir, "RMDIR" }, [FUSE_RENAME] = { _do_rename, "RENAME" }, [FUSE_LINK] = { _do_link, "LINK" }, [FUSE_OPEN] = { _do_open, "OPEN" }, [FUSE_READ] = { _do_read, "READ" }, [FUSE_WRITE] = { _do_write, "WRITE" }, [FUSE_STATFS] = { _do_statfs, "STATFS" }, [FUSE_RELEASE] = { _do_release, "RELEASE" }, [FUSE_FSYNC] = { _do_fsync, "FSYNC" }, [FUSE_SETXATTR] = { _do_setxattr, "SETXATTR" }, [FUSE_GETXATTR] = { _do_getxattr, "GETXATTR" }, [FUSE_LISTXATTR] = { _do_listxattr, "LISTXATTR" }, [FUSE_REMOVEXATTR] = { _do_removexattr, "REMOVEXATTR" }, [FUSE_FLUSH] = { _do_flush, "FLUSH" }, [FUSE_INIT] = { _do_init, "INIT" }, [FUSE_OPENDIR] = { _do_opendir, "OPENDIR" }, [FUSE_READDIR] = { _do_readdir, "READDIR" }, [FUSE_RELEASEDIR] = { _do_releasedir, "RELEASEDIR" }, [FUSE_FSYNCDIR] = { _do_fsyncdir, "FSYNCDIR" }, [FUSE_GETLK] = { _do_getlk, "GETLK" }, [FUSE_SETLK] = { _do_setlk, "SETLK" }, [FUSE_SETLKW] = { _do_setlkw, "SETLKW" }, [FUSE_ACCESS] = { _do_access, "ACCESS" }, [FUSE_CREATE] = { _do_create, "CREATE" }, [FUSE_TMPFILE] = { _do_tmpfile, "TMPFILE" }, [FUSE_INTERRUPT] = { _do_interrupt, "INTERRUPT" }, [FUSE_BMAP] = { _do_bmap, "BMAP" }, [FUSE_IOCTL] = { _do_ioctl, "IOCTL" }, [FUSE_POLL] = { _do_poll, "POLL" }, [FUSE_FALLOCATE] = { _do_fallocate, "FALLOCATE" }, [FUSE_DESTROY] = { _do_destroy, "DESTROY" }, [FUSE_NOTIFY_REPLY] = { (void *)1, "NOTIFY_REPLY" }, [FUSE_BATCH_FORGET] = { _do_batch_forget, "BATCH_FORGET" }, [FUSE_READDIRPLUS] = { _do_readdirplus, "READDIRPLUS" }, [FUSE_RENAME2] = { _do_rename2, "RENAME2" }, [FUSE_COPY_FILE_RANGE] = { _do_copy_file_range, "COPY_FILE_RANGE" }, [FUSE_COPY_FILE_RANGE_64] = { _do_copy_file_range_64, "COPY_FILE_RANGE_64" }, [FUSE_LSEEK] = { _do_lseek, "LSEEK" }, [FUSE_STATX] = { _do_statx, "STATX" }, [CUSE_INIT] = { _cuse_lowlevel_init, "CUSE_INIT" }, }; /* * For ABI compatibility we cannot allow higher values than CUSE_INIT. * Without ABI compatibility we could use the size of the array. * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) */ #define FUSE_MAXOP (CUSE_INIT + 1) /** * * @return 0 if sanity is ok, error otherwise */ static inline int fuse_req_opcode_sanity_ok(struct fuse_session *se, enum fuse_opcode in_op) { int err = EIO; if (!se->got_init) { enum fuse_opcode expected; expected = se->cuse_data ? CUSE_INIT : FUSE_INIT; if (in_op != expected) return err; } else if (in_op == FUSE_INIT || in_op == CUSE_INIT) return err; return 0; } static inline void fuse_session_in2req(struct fuse_req *req, struct fuse_in_header *in) { req->unique = in->unique; req->ctx.uid = in->uid; req->ctx.gid = in->gid; req->ctx.pid = in->pid; } /** * Implement -o allow_root */ static inline int fuse_req_check_allow_root(struct fuse_session *se, enum fuse_opcode in_op, uid_t in_uid) { int err = EACCES; if (se->deny_others && in_uid != se->owner && in_uid != 0 && in_op != FUSE_INIT && in_op != FUSE_READ && in_op != FUSE_WRITE && in_op != FUSE_FSYNC && in_op != FUSE_RELEASE && in_op != FUSE_READDIR && in_op != FUSE_FSYNCDIR && in_op != FUSE_RELEASEDIR && in_op != FUSE_NOTIFY_REPLY && in_op != FUSE_READDIRPLUS) return err; return 0; } static const char *opname(enum fuse_opcode opcode) { if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) return "???"; else return fuse_ll_ops[opcode].name; } static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst, struct fuse_bufvec *src) { ssize_t res = fuse_buf_copy(dst, src, 0); if (res < 0) { fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res)); return res; } if ((size_t)res < fuse_buf_size(dst)) { fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); return -1; } return 0; } void fuse_session_process_buf(struct fuse_session *se, const struct fuse_buf *buf) { fuse_session_process_buf_internal(se, buf, NULL); } /* libfuse internal handler */ void fuse_session_process_buf_internal(struct fuse_session *se, const struct fuse_buf *buf, struct fuse_chan *ch) { const size_t write_header_size = sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 }; struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size); struct fuse_in_header *in; const void *inarg; struct fuse_req *req; void *mbuf = NULL; int err; int res; if (buf->flags & FUSE_BUF_IS_FD) { if (buf->size < tmpbuf.buf[0].size) tmpbuf.buf[0].size = buf->size; mbuf = malloc(tmpbuf.buf[0].size); if (mbuf == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n"); goto clear_pipe; } tmpbuf.buf[0].mem = mbuf; res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); if (res < 0) goto clear_pipe; in = mbuf; } else { in = buf->mem; } trace_request_process(in->opcode, in->unique); if (se->debug) { fuse_log(FUSE_LOG_DEBUG, "dev unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n", (unsigned long long) in->unique, opname((enum fuse_opcode) in->opcode), in->opcode, (unsigned long long) in->nodeid, buf->size, in->pid); } req = fuse_ll_alloc_req(se); if (req == NULL) { struct fuse_out_header out = { .unique = in->unique, .error = -ENOMEM, }; struct iovec iov = { .iov_base = &out, .iov_len = sizeof(struct fuse_out_header), }; fuse_send_msg(se, ch, &iov, 1, NULL); goto clear_pipe; } fuse_session_in2req(req, in); req->ch = ch ? fuse_chan_get(ch) : NULL; err = fuse_req_opcode_sanity_ok(se, in->opcode); if (err) goto reply_err; err = fuse_req_check_allow_root(se, in->opcode, in->uid); if (err) goto reply_err; err = ENOSYS; if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) goto reply_err; /* Do not process interrupt request */ if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) { if (se->debug) fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n"); goto reply_err; } if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) { struct fuse_req *intr; pthread_mutex_lock(&se->lock); intr = check_interrupt(se, req); list_add_req(req, &se->list); pthread_mutex_unlock(&se->lock); if (intr) fuse_reply_err(intr, EAGAIN); } if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size && (in->opcode != FUSE_WRITE || !se->op.write_buf) && in->opcode != FUSE_NOTIFY_REPLY) { void *newmbuf; err = ENOMEM; newmbuf = realloc(mbuf, buf->size); if (newmbuf == NULL) goto reply_err; mbuf = newmbuf; tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size); tmpbuf.buf[0].mem = (char *)mbuf + write_header_size; res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); err = -res; if (res < 0) goto reply_err; in = mbuf; } inarg = (void *) &in[1]; if (in->opcode == FUSE_WRITE && se->op.write_buf) do_write_buf(req, in->nodeid, inarg, buf); else if (in->opcode == FUSE_NOTIFY_REPLY) do_notify_reply(req, in->nodeid, inarg, buf); else fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); out_free: free(mbuf); return; reply_err: fuse_reply_err(req, err); clear_pipe: if (buf->flags & FUSE_BUF_IS_FD) fuse_ll_clear_pipe(se); goto out_free; } void fuse_session_process_uring_cqe(struct fuse_session *se, struct fuse_req *req, struct fuse_in_header *in, void *op_in, void *op_payload, size_t payload_len) { int err; fuse_session_in2req(req, in); err = fuse_req_opcode_sanity_ok(se, in->opcode); if (err) goto reply_err; err = fuse_req_check_allow_root(se, in->opcode, in->uid); if (err) goto reply_err; err = ENOSYS; if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) goto reply_err; if (se->debug) { fuse_log( FUSE_LOG_DEBUG, "cqe unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n", (unsigned long long)in->unique, opname((enum fuse_opcode)in->opcode), in->opcode, (unsigned long long)in->nodeid, payload_len, in->pid); } if (in->opcode == FUSE_WRITE && se->op.write_buf) { struct fuse_bufvec bufv = { .buf[0] = { .size = payload_len, .flags = 0, .mem = op_payload }, .count = 1, }; _do_write_buf(req, in->nodeid, op_in, &bufv); } else if (in->opcode == FUSE_NOTIFY_REPLY) { struct fuse_buf buf = { .size = payload_len, .mem = op_payload }; do_notify_reply(req, in->nodeid, op_in, &buf); } else { fuse_ll_ops2[in->opcode].func(req, in->nodeid, op_in, op_payload); } return; reply_err: fuse_reply_err(req, err); } #define LL_OPTION(n,o,v) \ { n, offsetof(struct fuse_session, o), v } static const struct fuse_opt fuse_ll_opts[] = { LL_OPTION("debug", debug, 1), LL_OPTION("-d", debug, 1), LL_OPTION("--debug", debug, 1), LL_OPTION("allow_root", deny_others, 1), LL_OPTION("io_uring", uring.enable, 1), LL_OPTION("io_uring_q_depth=%u", uring.q_depth, -1), FUSE_OPT_END }; void fuse_lowlevel_version(void) { printf("using FUSE kernel interface version %i.%i\n", FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); fuse_mount_version(); } void fuse_lowlevel_help(void) { /* These are not all options, but the ones that are potentially of interest to an end-user */ printf( " -o allow_other allow access by all users\n" " -o allow_root allow access by root\n" " -o auto_unmount auto unmount on process termination\n" " -o io_uring enable io-uring\n" " -o io_uring_q_depth= io-uring queue depth\n" ); } void fuse_session_destroy(struct fuse_session *se) { struct fuse_ll_pipe *llp; if (se->got_init && !se->got_destroy) { if (se->op.destroy) se->op.destroy(se->userdata); } llp = pthread_getspecific(se->pipe_key); if (llp != NULL) fuse_ll_pipe_free(llp); pthread_key_delete(se->pipe_key); sem_destroy(&se->mt_finish); pthread_mutex_destroy(&se->mt_lock); pthread_mutex_destroy(&se->lock); 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); } static void fuse_ll_pipe_destructor(void *data) { struct fuse_ll_pipe *llp = data; fuse_ll_pipe_free(llp); } void fuse_buf_free(struct fuse_buf *buf) { if (buf->mem == NULL) return; size_t write_header_sz = sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); char *ptr = (char *)buf->mem - pagesize + write_header_sz; free(ptr); buf->mem = NULL; } /* * This is used to allocate buffers that hold fuse requests */ static void *buf_alloc(size_t size, bool internal) { /* * For libfuse internal caller add in alignment. That cannot be done * for an external caller, as it is not guaranteed that the external * caller frees the raw pointer. */ if (internal) { size_t write_header_sz = sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); size_t new_size = ROUND_UP(size + write_header_sz, pagesize); char *buf = aligned_alloc(pagesize, new_size); if (buf == NULL) return NULL; buf += pagesize - write_header_sz; return buf; } else { return malloc(size); } } /* *@param internal true if called from libfuse internal code */ static int _fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf, struct fuse_chan *ch, bool internal) { int err; ssize_t res; size_t bufsize; #ifdef HAVE_SPLICE struct fuse_ll_pipe *llp; struct fuse_buf tmpbuf; pipe_retry: bufsize = se->bufsize; if (se->conn.proto_minor < 14 || !(se->conn.want_ext & FUSE_CAP_SPLICE_READ)) goto fallback; llp = fuse_ll_get_pipe(se); if (llp == NULL) goto fallback; if (llp->size < bufsize) { if (llp->can_grow) { res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize); if (res == -1) { llp->can_grow = 0; res = grow_pipe_to_max(llp->pipe[0]); if (res > 0) llp->size = res; goto fallback; } llp->size = res; } if (llp->size < bufsize) goto fallback; } 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; trace_request_receive(err); if (fuse_session_exited(se)) return 0; if (res == -1) { if (err == ENODEV) { /* Filesystem was unmounted, or connection was aborted via /sys/fs/fuse/connections */ fuse_session_exit(se); return 0; } /* FUSE_INIT might have increased the required bufsize */ if (err == EINVAL && bufsize < se->bufsize) { fuse_ll_clear_pipe(se); goto pipe_retry; } if (err != EINTR && err != EAGAIN) perror("fuse: splice from device"); return -err; } if (res < sizeof(struct fuse_in_header)) { fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n"); return -EIO; } tmpbuf = (struct fuse_buf){ .size = res, .flags = FUSE_BUF_IS_FD, .fd = llp->pipe[0], }; /* * Don't bother with zero copy for small requests. * fuse_loop_mt() needs to check for FORGET so this more than * just an optimization. */ if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + pagesize) { struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 }; struct fuse_bufvec dst = { .count = 1 }; if (!buf->mem) { buf->mem = buf_alloc(bufsize, internal); if (!buf->mem) { fuse_log( FUSE_LOG_ERR, "fuse: failed to allocate read buffer\n"); return -ENOMEM; } buf->mem_size = bufsize; } buf->size = bufsize; buf->flags = 0; dst.buf[0] = *buf; res = fuse_buf_copy(&dst, &src, 0); if (res < 0) { fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res)); fuse_ll_clear_pipe(se); return res; } if (res < tmpbuf.size) { fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); fuse_ll_clear_pipe(se); return -EIO; } assert(res == tmpbuf.size); } else { /* Don't overwrite buf->mem, as that would cause a leak */ buf->fd = tmpbuf.fd; buf->flags = tmpbuf.flags; } buf->size = tmpbuf.size; return res; fallback: #endif bufsize = internal ? buf->mem_size : se->bufsize; if (!buf->mem) { bufsize = se->bufsize; /* might have changed */ buf->mem = buf_alloc(bufsize, internal); if (!buf->mem) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate read buffer\n"); return -ENOMEM; } if (internal) buf->mem_size = bufsize; } restart: 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, bufsize, se->userdata); } else { res = read(ch ? ch->fd : se->fd, buf->mem, bufsize); } err = errno; trace_request_receive(err); if (fuse_session_exited(se)) return 0; if (res == -1) { if (err == EINVAL && internal && se->bufsize > bufsize) { /* FUSE_INIT might have increased the required bufsize */ bufsize = se->bufsize; void *newbuf = buf_alloc(bufsize, internal); if (!newbuf) { fuse_log( FUSE_LOG_ERR, "fuse: failed to (re)allocate read buffer\n"); return -ENOMEM; } fuse_buf_free(buf); buf->mem = newbuf; buf->mem_size = bufsize; goto restart; } /* ENOENT means the operation was interrupted, it's safe to restart */ if (err == ENOENT) goto restart; if (err == ENODEV) { /* Filesystem was unmounted, or connection was aborted via /sys/fs/fuse/connections */ fuse_session_exit(se); return 0; } /* Errors occurring during normal operation: EINTR (read interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem umounted) */ if (err != EINTR && err != EAGAIN) perror("fuse: reading device"); return -err; } if ((size_t)res < sizeof(struct fuse_in_header)) { fuse_log(FUSE_LOG_ERR, "short read on fuse device\n"); return -EIO; } buf->size = res; return res; } int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf) { return _fuse_session_receive_buf(se, buf, NULL, false); } /* libfuse internal handler */ int fuse_session_receive_buf_internal(struct fuse_session *se, struct fuse_buf *buf, struct fuse_chan *ch) { /* * if run internally thread buffers are from libfuse - we can * reallocate them */ if (unlikely(!se->got_init) && !se->buf_reallocable) se->buf_reallocable = true; return _fuse_session_receive_buf(se, buf, ch, true); } struct fuse_session * fuse_session_new_versioned(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, struct libfuse_version *version, void *userdata) { int err; struct fuse_session *se; struct mount_opts *mo; if (op == NULL || op_size == 0) { fuse_log(FUSE_LOG_ERR, "fuse: warning: empty op list passed to fuse_session_new()\n"); return NULL; } if (version == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: warning: version not passed to fuse_session_new()\n"); return NULL; } if (sizeof(struct fuse_lowlevel_ops) < op_size) { fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n"); op_size = sizeof(struct fuse_lowlevel_ops); } if (args == NULL || args->argc == 0) { fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n"); return NULL; } se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session)); if (se == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); goto out1; } se->fd = -1; se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize(); se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; se->conn.max_readahead = UINT_MAX; /* * Allow overriding with env, mostly to avoid the need to modify * all tests. I.e. to test with and without io-uring being enabled. */ se->uring.enable = getenv("FUSE_URING_ENABLE") ? atoi(getenv("FUSE_URING_ENABLE")) : SESSION_DEF_URING_ENABLE; se->uring.q_depth = getenv("FUSE_URING_QUEUE_DEPTH") ? atoi(getenv("FUSE_URING_QUEUE_DEPTH")) : SESSION_DEF_URING_Q_DEPTH; /* Parse options */ if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) goto out2; if(se->deny_others) { /* Allowing access only by root is done by instructing * kernel to allow access by everyone, and then restricting * access to root and mountpoint owner in libfuse. */ // We may be adding the option a second time, but // that doesn't hurt. if(fuse_opt_add_arg(args, "-oallow_other") == -1) goto out2; } mo = parse_mount_opts(args); if (mo == NULL) goto out3; if(args->argc == 1 && args->argv[0][0] == '-') { fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but " "will be ignored\n"); } else if (args->argc != 1) { int i; fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `"); for(i = 1; i < args->argc-1; i++) fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]); fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]); goto out4; } if (se->debug) fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION); list_init_req(&se->list); list_init_req(&se->interrupts); list_init_nreq(&se->notify_list); se->notify_ctr = 1; pthread_mutex_init(&se->lock, NULL); sem_init(&se->mt_finish, 0, 0); pthread_mutex_init(&se->mt_lock, NULL); err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor); if (err) { fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n", strerror(err)); goto out5; } memcpy(&se->op, op, op_size); se->owner = getuid(); se->userdata = userdata; se->mo = mo; /* Fuse server application should pass the version it was compiled * against and pass it. If a libfuse version accidentally introduces an * ABI incompatibility, it might be possible to 'fix' that at run time, * by checking the version numbers. */ se->version = *version; return se; out5: sem_destroy(&se->mt_finish); pthread_mutex_destroy(&se->mt_lock); pthread_mutex_destroy(&se->lock); out4: fuse_opt_free_args(args); out3: if (mo != NULL) destroy_mount_opts(mo); out2: free(se); out1: return NULL; } struct fuse_session *fuse_session_new_30(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata); struct fuse_session *fuse_session_new_30(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata) { struct fuse_lowlevel_ops null_ops = { 0 }; /* unknown version */ struct libfuse_version version = { 0 }; /* * This function is the ABI interface function from fuse_session_new in * compat.c. External libraries like "fuser" might call fuse_session_new() * with NULL ops and then pass that session to fuse_session_mount(). * The actual FUSE operations are handled in their own library. */ if (op == NULL) { op = &null_ops; op_size = sizeof(null_ops); } return fuse_session_new_versioned(args, op, op_size, &version, userdata); } FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17") int fuse_session_custom_io_317(struct fuse_session *se, const struct fuse_custom_io *io, size_t op_size, int fd) { if (sizeof(struct fuse_custom_io) < op_size) { fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n"); op_size = sizeof(struct fuse_custom_io); } 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 = calloc(1, 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; memcpy(se->io, io, op_size); return 0; } int fuse_session_custom_io_30(struct fuse_session *se, const struct fuse_custom_io *io, int fd); FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0") int fuse_session_custom_io_30(struct fuse_session *se, const struct fuse_custom_io *io, int fd) { return fuse_session_custom_io_317(se, io, offsetof(struct fuse_custom_io, clone_fd), fd); } int fuse_session_mount(struct fuse_session *se, const char *_mountpoint) { int fd; char *mountpoint; if (_mountpoint == NULL) { fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n"); return -1; } mountpoint = strdup(_mountpoint); if (mountpoint == NULL) { fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for mountpoint. Error: %s\n", strerror(errno)); return -1; } /* * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos * would ensue. */ do { fd = open("/dev/null", O_RDWR); if (fd > 2) close(fd); } while (fd >= 0 && fd <= 2); /* * To allow FUSE daemons to run without privileges, the caller may open * /dev/fuse before launching the file system and pass on the file * descriptor by specifying /dev/fd/N as the mount point. Note that the * parent process takes care of performing the mount in this case. */ fd = fuse_mnt_parse_fuse_fd(mountpoint); if (fd != -1) { if (fcntl(fd, F_GETFD) == -1) { fuse_log(FUSE_LOG_ERR, "fuse: Invalid file descriptor /dev/fd/%u\n", fd); goto error_out; } se->fd = fd; return 0; } /* Open channel */ fd = fuse_kern_mount(mountpoint, se->mo); if (fd == -1) goto error_out; se->fd = fd; /* Save mountpoint */ se->mountpoint = mountpoint; return 0; error_out: free(mountpoint); return -1; } int fuse_session_fd(struct fuse_session *se) { return se->fd; } void fuse_session_unmount(struct fuse_session *se) { if (se->mountpoint != NULL) { char *mountpoint = atomic_exchange(&se->mountpoint, NULL); fuse_kern_unmount(mountpoint, se->fd); se->fd = -1; free(mountpoint); } } #ifdef linux int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) { char *buf; size_t bufsize = 1024; char path[128]; int ret; int fd; unsigned long pid = req->ctx.pid; char *s; sprintf(path, "/proc/%lu/task/%lu/status", pid, pid); retry: buf = malloc(bufsize); if (buf == NULL) return -ENOMEM; ret = -EIO; fd = open(path, O_RDONLY); if (fd == -1) goto out_free; ret = read(fd, buf, bufsize); close(fd); if (ret < 0) { ret = -EIO; goto out_free; } if ((size_t)ret == bufsize) { free(buf); bufsize *= 4; goto retry; } buf[ret] = '\0'; ret = -EIO; s = strstr(buf, "\nGroups:"); if (s == NULL) goto out_free; s += 8; ret = 0; while (1) { char *end; unsigned long val = strtoul(s, &end, 0); if (end == s) break; s = end; if (ret < size) list[ret] = val; ret++; } out_free: free(buf); return ret; } #else /* linux */ /* * This is currently not implemented on other than Linux... */ int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) { (void) req; (void) size; (void) list; return -ENOSYS; } #endif /* Prevent spurious data race warning - we don't care * about races for this flag */ __attribute__((no_sanitize_thread)) void fuse_session_exit(struct fuse_session *se) { atomic_store_explicit(&se->mt_exited, 1, memory_order_relaxed); sem_post(&se->mt_finish); } __attribute__((no_sanitize_thread)) void fuse_session_reset(struct fuse_session *se) { se->mt_exited = false; se->error = 0; } __attribute__((no_sanitize_thread)) int fuse_session_exited(struct fuse_session *se) { bool exited = atomic_load_explicit(&se->mt_exited, memory_order_relaxed); return exited ? 1 : 0; } fuse-3.18.2/lib/fuse_misc.h0000644000175000017500000000344715156613252014427 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #include /* 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. */ #ifdef LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS # if HAVE_SYMVER_ATTRIBUTE # define FUSE_SYMVER(sym1, sym2) __attribute__ ((symver (sym2))) # else # define FUSE_SYMVER(sym1, sym2) __asm__("\t.symver " sym1 "," sym2); # endif #else #define FUSE_SYMVER(sym1, sym2) #endif #ifdef HAVE_STRUCT_STAT_ST_ATIM /* Linux */ #define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec) #define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec) #define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec) #define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val) #define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctim.tv_nsec = (val) #define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val) #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC) /* FreeBSD */ #define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec) #define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec) #define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec) #define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val) #define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctimespec.tv_nsec = (val) #define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val) #else #define ST_ATIM_NSEC(stbuf) 0 #define ST_CTIM_NSEC(stbuf) 0 #define ST_MTIM_NSEC(stbuf) 0 #define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0) #define ST_CTIM_NSEC_SET(stbuf, val) do { } while (0) #define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0) #endif fuse-3.18.2/lib/fuse_opt.c0000644000175000017500000002135515156613252014267 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Implementation of option parsing routines (dealing with `struct fuse_args`). This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #include "fuse_config.h" #include "fuse_i.h" #include "fuse_opt.h" #include "fuse_misc.h" #include #include #include #include struct fuse_opt_context { void *data; const struct fuse_opt *opt; fuse_opt_proc_t proc; int argctr; int argc; char **argv; struct fuse_args outargs; char *opts; int nonopt; }; void fuse_opt_free_args(struct fuse_args *args) { if (args) { if (args->argv && args->allocated) { int i; for (i = 0; i < args->argc; i++) free(args->argv[i]); free(args->argv); } args->argc = 0; args->argv = NULL; args->allocated = 0; } } static int alloc_failed(void) { fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); return -1; } int fuse_opt_add_arg(struct fuse_args *args, const char *arg) { char **newargv; char *newarg; assert(!args->argv || args->allocated); newarg = strdup(arg); if (!newarg) return alloc_failed(); newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); if (!newargv) { free(newarg); return alloc_failed(); } args->argv = newargv; args->allocated = 1; args->argv[args->argc++] = newarg; args->argv[args->argc] = NULL; return 0; } static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos, const char *arg) { assert(pos <= args->argc); if (fuse_opt_add_arg(args, arg) == -1) return -1; if (pos != args->argc - 1) { char *newarg = args->argv[args->argc - 1]; memmove(&args->argv[pos + 1], &args->argv[pos], sizeof(char *) * (args->argc - pos - 1)); args->argv[pos] = newarg; } return 0; } int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) { return fuse_opt_insert_arg_common(args, pos, arg); } static int next_arg(struct fuse_opt_context *ctx, const char *opt) { if (ctx->argctr + 1 >= ctx->argc) { fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt); return -1; } ctx->argctr++; return 0; } static int add_arg(struct fuse_opt_context *ctx, const char *arg) { return fuse_opt_add_arg(&ctx->outargs, arg); } static int add_opt_common(char **opts, const char *opt, int esc) { unsigned oldlen = *opts ? strlen(*opts) : 0; char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1); if (!d) return alloc_failed(); *opts = d; if (oldlen) { d += oldlen; *d++ = ','; } for (; *opt; opt++) { if (esc && (*opt == ',' || *opt == '\\')) *d++ = '\\'; *d++ = *opt; } *d = '\0'; return 0; } int fuse_opt_add_opt(char **opts, const char *opt) { return add_opt_common(opts, opt, 0); } int fuse_opt_add_opt_escaped(char **opts, const char *opt) { return add_opt_common(opts, opt, 1); } static int add_opt(struct fuse_opt_context *ctx, const char *opt) { return add_opt_common(&ctx->opts, opt, 1); } static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, int iso) { if (key == FUSE_OPT_KEY_DISCARD) return 0; if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); if (res == -1 || !res) return res; } if (iso) return add_opt(ctx, arg); else return add_arg(ctx, arg); } static int match_template(const char *t, const char *arg, unsigned *sepp) { int arglen = strlen(arg); const char *sep = strchr(t, '='); sep = sep ? sep : strchr(t, ' '); if (sep && (!sep[1] || sep[1] == '%')) { int tlen = sep - t; if (sep[0] == '=') tlen ++; if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { *sepp = sep - t; return 1; } } if (strcmp(t, arg) == 0) { *sepp = 0; return 1; } return 0; } static const struct fuse_opt *find_opt(const struct fuse_opt *opt, const char *arg, unsigned *sepp) { for (; opt && opt->templ; opt++) if (match_template(opt->templ, arg, sepp)) return opt; return NULL; } int fuse_opt_match(const struct fuse_opt *opts, const char *opt) { unsigned dummy; return find_opt(opts, opt, &dummy) ? 1 : 0; } static int process_opt_param(void *var, const char *format, const char *param, const char *arg) { assert(format[0] == '%'); if (format[1] == 's') { char **s = var; char *copy = strdup(param); if (!copy) return alloc_failed(); free(*s); *s = copy; } else { if (sscanf(param, format, var) != 1) { fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg); return -1; } } return 0; } static int process_opt(struct fuse_opt_context *ctx, const struct fuse_opt *opt, unsigned sep, const char *arg, int iso) { if (opt->offset == -1U) { if (call_proc(ctx, arg, opt->value, iso) == -1) return -1; } else { void *var = (char *)ctx->data + opt->offset; if (sep && opt->templ[sep + 1]) { const char *param = arg + sep; if (opt->templ[sep] == '=') param ++; if (process_opt_param(var, opt->templ + sep + 1, param, arg) == -1) return -1; } else *(int *)var = opt->value; } return 0; } static int process_opt_sep_arg(struct fuse_opt_context *ctx, const struct fuse_opt *opt, unsigned sep, const char *arg, int iso) { int res; char *newarg; char *param; if (next_arg(ctx, arg) == -1) return -1; param = ctx->argv[ctx->argctr]; newarg = malloc(sep + strlen(param) + 1); if (!newarg) return alloc_failed(); memcpy(newarg, arg, sep); strcpy(newarg + sep, param); res = process_opt(ctx, opt, sep, newarg, iso); free(newarg); return res; } static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) { unsigned sep; const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); if (opt) { for (; opt; opt = find_opt(opt + 1, arg, &sep)) { int res; if (sep && opt->templ[sep] == ' ' && !arg[sep]) res = process_opt_sep_arg(ctx, opt, sep, arg, iso); else res = process_opt(ctx, opt, sep, arg, iso); if (res == -1) return -1; } return 0; } else return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); } static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) { char *s = opts; char *d = s; int end = 0; while (!end) { if (*s == '\0') end = 1; if (*s == ',' || end) { int res; *d = '\0'; res = process_gopt(ctx, opts, 1); if (res == -1) return -1; d = opts; } else { if (s[0] == '\\' && s[1] != '\0') { s++; if (s[0] >= '0' && s[0] <= '3' && s[1] >= '0' && s[1] <= '7' && s[2] >= '0' && s[2] <= '7') { *d++ = (s[0] - '0') * 0100 + (s[1] - '0') * 0010 + (s[2] - '0'); s += 2; } else { *d++ = *s; } } else { *d++ = *s; } } s++; } return 0; } static int process_option_group(struct fuse_opt_context *ctx, const char *opts) { int res; char *copy = strdup(opts); if (!copy) { fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); return -1; } res = process_real_option_group(ctx, copy); free(copy); return res; } static int process_one(struct fuse_opt_context *ctx, const char *arg) { if (ctx->nonopt || arg[0] != '-') return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); else if (arg[1] == 'o') { if (arg[2]) return process_option_group(ctx, arg + 2); else { if (next_arg(ctx, arg) == -1) return -1; return process_option_group(ctx, ctx->argv[ctx->argctr]); } } else if (arg[1] == '-' && !arg[2]) { if (add_arg(ctx, arg) == -1) return -1; ctx->nonopt = ctx->outargs.argc; return 0; } else return process_gopt(ctx, arg, 0); } static int opt_parse(struct fuse_opt_context *ctx) { if (ctx->argc) { if (add_arg(ctx, ctx->argv[0]) == -1) return -1; } for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) return -1; if (ctx->opts) { if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) return -1; } /* If option separator ("--") is the last argument, remove it */ if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc && strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) { free(ctx->outargs.argv[ctx->outargs.argc - 1]); ctx->outargs.argv[--ctx->outargs.argc] = NULL; } return 0; } int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc) { int res; struct fuse_opt_context ctx = { .data = data, .opt = opts, .proc = proc, }; if (!args || !args->argv || !args->argc) return 0; ctx.argc = args->argc; ctx.argv = args->argv; res = opt_parse(&ctx); if (res != -1) { struct fuse_args tmp = *args; *args = ctx.outargs; ctx.outargs = tmp; } free(ctx.opts); fuse_opt_free_args(&ctx.outargs); return res; } fuse-3.18.2/lib/fuse_signals.c0000644000175000017500000001166415156613252015127 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Utility functions for setting signal handlers. This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #include "fuse_config.h" #include "fuse_lowlevel.h" #include "fuse_i.h" #include #include #include #include #include #ifdef HAVE_BACKTRACE #include #endif /* * Must not handle SIGCANCEL, as that is used to wake up threads from * syscalls reading requests from /dev/fuse */ static int teardown_sigs[] = { SIGHUP, SIGINT, SIGTERM }; static int ignore_sigs[] = { SIGPIPE}; static int fail_sigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV }; static struct fuse_session *fuse_instance; #ifdef HAVE_BACKTRACE #define BT_STACK_SZ (1024 * 1024) static void *backtrace_buffer[BT_STACK_SZ]; #endif static void dump_stack(void) { #ifdef HAVE_BACKTRACE char **strings; int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ); strings = backtrace_symbols(backtrace_buffer, nptrs); if (strings == NULL) { fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n", strerror(errno)); return; } for (int idx = 0; idx < nptrs; idx++) fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]); free(strings); #endif } static void exit_handler(int sig) { if (fuse_instance == NULL) { fuse_log(FUSE_LOG_ERR, "fuse_instance is NULL\n"); return; } if (fuse_instance->debug) fuse_log(FUSE_LOG_ERR, "exit_handler called with sig %d\n", sig); fuse_session_exit(fuse_instance); if (sig < 0) { fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); dump_stack(); abort(); fuse_instance->error = sig; } fuse_instance->error = sig; } static void exit_backtrace(int sig) { if (fuse_instance == NULL) return; fuse_session_exit(fuse_instance); fuse_remove_signal_handlers(fuse_instance); fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig); dump_stack(); abort(); } static void do_nothing(int sig) { (void) sig; } static int set_one_signal_handler(int sig, void (*handler)(int), int remove) { struct sigaction sa; struct sigaction old_sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = remove ? SIG_DFL : handler; sigemptyset(&(sa.sa_mask)); sa.sa_flags = 0; if (sigaction(sig, NULL, &old_sa) == -1) { perror("fuse: cannot get old signal handler"); return -1; } if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && sigaction(sig, &sa, NULL) == -1) { perror("fuse: cannot set signal handler"); return -1; } return 0; } static int _fuse_set_signal_handlers(int signals[], int nr_signals, void (*handler)(int)) { for (int idx = 0; idx < nr_signals; idx++) { int signal = signals[idx]; /* * If we used SIG_IGN instead of the do_nothing function, * then we would be unable to tell if we set SIG_IGN (and * thus should reset to SIG_DFL in fuse_remove_signal_handlers) * or if it was already set to SIG_IGN (and should be left * untouched. */ if (set_one_signal_handler(signal, handler, 0) == -1) { fuse_log(FUSE_LOG_ERR, "Failed to install signal handler for sig %d\n", signal); return -1; } } return 0; } int fuse_set_signal_handlers(struct fuse_session *se) { size_t nr_signals; int rc; nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]); rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler); if (rc < 0) return rc; nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]); rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing); if (rc < 0) return rc; /* * needs to be set independently if already set, as some applications * may have multiple sessions and might rely on traditional behavior * that the last session is used. */ fuse_instance = se; return 0; } int fuse_set_fail_signal_handlers(struct fuse_session *se) { size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]); int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals, exit_backtrace); if (rc < 0) return rc; /* See fuse_set_signal_handlers, why set unconditionally */ fuse_instance = se; return 0; } static void _fuse_remove_signal_handlers(int signals[], int nr_signals, void (*handler)(int)) { for (int idx = 0; idx < nr_signals; idx++) set_one_signal_handler(signals[idx], handler, 1); } void fuse_remove_signal_handlers(struct fuse_session *se) { size_t nr_signals; if (fuse_instance != se) fuse_log(FUSE_LOG_ERR, "fuse: fuse_remove_signal_handlers: unknown session\n"); else fuse_instance = NULL; nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]); _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler); nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]); _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing); nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]); _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace); } fuse-3.18.2/lib/fuse_uring.c0000644000175000017500000005605415156613252014615 0ustar berndbernd/* * FUSE: Filesystem in Userspace * Copyright (C) 2025 Bernd Schubert * * Implementation of (most of) FUSE-over-io-uring. * * This program can be distributed under the terms of the GNU LGPLv2. * See the file LGPL2.txt */ #define _GNU_SOURCE #include "fuse_i.h" #include "fuse_kernel.h" #include "fuse_uring_i.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Size of command data area in SQE when IORING_SETUP_SQE128 is used */ #define FUSE_URING_MAX_SQE128_CMD_DATA 80 struct fuse_ring_ent { struct fuse_ring_queue *ring_queue; /* back pointer */ struct fuse_req req; struct fuse_uring_req_header *req_header; void *op_payload; size_t req_payload_sz; /* commit id of a fuse request */ uint64_t req_commit_id; enum fuse_uring_cmd last_cmd; /* header and payload */ struct iovec iov[2]; }; struct fuse_ring_queue { /* back pointer */ struct fuse_ring_pool *ring_pool; int qid; int numa_node; pthread_t tid; int eventfd; size_t req_header_sz; struct io_uring ring; pthread_mutex_t ring_lock; bool cqe_processing; /* size depends on queue depth */ struct fuse_ring_ent ent[]; }; /** * Main fuse_ring structure, holds all fuse-ring data */ struct fuse_ring_pool { struct fuse_session *se; /* number of queues */ size_t nr_queues; /* number of per queue entries */ size_t queue_depth; /* max payload size for fuse requests*/ size_t max_req_payload_sz; /* size of a single queue */ size_t queue_mem_size; unsigned int started_threads; unsigned int failed_threads; /* Avoid sending queue entries before FUSE_INIT reply*/ sem_t init_sem; pthread_cond_t thread_start_cond; pthread_mutex_t thread_start_mutex; /* pointer to the first queue */ struct fuse_ring_queue *queues; }; static size_t fuse_ring_queue_size(const size_t q_depth) { const size_t req_size = sizeof(struct fuse_ring_ent) * q_depth; return sizeof(struct fuse_ring_queue) + req_size; } static struct fuse_ring_queue * fuse_uring_get_queue(struct fuse_ring_pool *fuse_ring, int qid) { void *ptr = ((char *)fuse_ring->queues) + (qid * fuse_ring->queue_mem_size); return ptr; } /** * return a pointer to the 80B area */ static void *fuse_uring_get_sqe_cmd(struct io_uring_sqe *sqe) { return (void *)&sqe->cmd[0]; } static void fuse_uring_sqe_set_req_data(struct fuse_uring_cmd_req *req, const unsigned int qid, const uint64_t commit_id) { req->qid = qid; req->commit_id = commit_id; req->flags = 0; } static void fuse_uring_sqe_prepare(struct io_uring_sqe *sqe, struct fuse_ring_ent *req, __u32 cmd_op) { /* These fields should be written once, never change */ sqe->opcode = IORING_OP_URING_CMD; /* * IOSQE_FIXED_FILE: fd is the index to the fd *array* * given to io_uring_register_files() */ sqe->flags = IOSQE_FIXED_FILE; sqe->fd = 0; sqe->rw_flags = 0; sqe->ioprio = 0; sqe->off = 0; io_uring_sqe_set_data(sqe, req); sqe->cmd_op = cmd_op; sqe->__pad1 = 0; } static int fuse_uring_commit_sqe(struct fuse_ring_pool *ring_pool, struct fuse_ring_queue *queue, struct fuse_ring_ent *ring_ent) { bool locked = false; struct fuse_session *se = ring_pool->se; struct fuse_uring_req_header *rrh = ring_ent->req_header; struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out; struct fuse_uring_ent_in_out *ent_in_out = (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out; struct io_uring_sqe *sqe; if (pthread_self() != queue->tid) { pthread_mutex_lock(&queue->ring_lock); locked = true; } sqe = io_uring_get_sqe(&queue->ring); if (sqe == NULL) { /* This is an impossible condition, unless there is a bug. * The kernel sent back an SQEs, which is assigned to a request. * There is no way to get out of SQEs, as the number of * SQEs matches the number tof requests. */ se->error = -EIO; fuse_log(FUSE_LOG_ERR, "Failed to get a ring SQEs\n"); return -EIO; } ring_ent->last_cmd = FUSE_IO_URING_CMD_COMMIT_AND_FETCH; fuse_uring_sqe_prepare(sqe, ring_ent, ring_ent->last_cmd); fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid, ring_ent->req_commit_id); if (se->debug) { fuse_log(FUSE_LOG_DEBUG, " unique: %" PRIu64 ", result=%d\n", out->unique, ent_in_out->payload_sz); } if (!queue->cqe_processing) io_uring_submit(&queue->ring); if (locked) pthread_mutex_unlock(&queue->ring_lock); return 0; } int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz, void **mr) { struct fuse_ring_ent *ring_ent; /* Not possible without io-uring interface */ if (!req->flags.is_uring) return -EINVAL; ring_ent = container_of(req, struct fuse_ring_ent, req); *payload = ring_ent->op_payload; *payload_sz = ring_ent->req_payload_sz; /* * For now unused, but will be used later when the application can * allocate the buffers itself and register them for rdma. */ if (mr) *mr = NULL; return 0; } int send_reply_uring(fuse_req_t req, int error, const void *arg, size_t argsize) { int res; struct fuse_ring_ent *ring_ent = container_of(req, struct fuse_ring_ent, req); struct fuse_uring_req_header *rrh = ring_ent->req_header; struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out; struct fuse_uring_ent_in_out *ent_in_out = (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out; struct fuse_ring_queue *queue = ring_ent->ring_queue; struct fuse_ring_pool *ring_pool = queue->ring_pool; size_t max_payload_sz = ring_pool->max_req_payload_sz; if (argsize > max_payload_sz) { fuse_log(FUSE_LOG_ERR, "argsize %zu exceeds buffer size %zu", argsize, max_payload_sz); error = -EINVAL; } else if (argsize) { if (arg != ring_ent->op_payload) memcpy(ring_ent->op_payload, arg, argsize); } ent_in_out->payload_sz = argsize; out->error = error; out->unique = req->unique; res = fuse_uring_commit_sqe(ring_pool, queue, ring_ent); fuse_free_req(req); return res; } int fuse_reply_data_uring(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags) { struct fuse_ring_ent *ring_ent = container_of(req, struct fuse_ring_ent, req); struct fuse_ring_queue *queue = ring_ent->ring_queue; struct fuse_ring_pool *ring_pool = queue->ring_pool; struct fuse_uring_req_header *rrh = ring_ent->req_header; struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out; struct fuse_uring_ent_in_out *ent_in_out = (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out; size_t max_payload_sz = ring_ent->req_payload_sz; struct fuse_bufvec dest_vec = FUSE_BUFVEC_INIT(max_payload_sz); int res; dest_vec.buf[0].mem = ring_ent->op_payload; dest_vec.buf[0].size = max_payload_sz; res = fuse_buf_copy(&dest_vec, bufv, flags); out->error = res < 0 ? res : 0; out->unique = req->unique; ent_in_out->payload_sz = res > 0 ? res : 0; res = fuse_uring_commit_sqe(ring_pool, queue, ring_ent); fuse_free_req(req); return res; } /** * Copy the iov into the ring buffer and submit and commit/fetch sqe */ int fuse_send_msg_uring(fuse_req_t req, struct iovec *iov, int count) { struct fuse_ring_ent *ring_ent = container_of(req, struct fuse_ring_ent, req); struct fuse_ring_queue *queue = ring_ent->ring_queue; struct fuse_ring_pool *ring_pool = queue->ring_pool; struct fuse_uring_req_header *rrh = ring_ent->req_header; struct fuse_out_header *out = (struct fuse_out_header *)&rrh->in_out; struct fuse_uring_ent_in_out *ent_in_out = (struct fuse_uring_ent_in_out *)&rrh->ring_ent_in_out; size_t max_buf = ring_pool->max_req_payload_sz; size_t len = 0; int res = 0; /* copy iov into the payload, idx=0 is the header section */ for (int idx = 1; idx < count; idx++) { struct iovec *cur = &iov[idx]; if (len + cur->iov_len > max_buf) { fuse_log(FUSE_LOG_ERR, "iov[%d] exceeds buffer size %zu", idx, max_buf); res = -EINVAL; /* Gracefully handle this? */ break; } memcpy(ring_ent->op_payload + len, cur->iov_base, cur->iov_len); len += cur->iov_len; } ent_in_out->payload_sz = len; out->error = res; out->unique = req->unique; out->len = len; return fuse_uring_commit_sqe(ring_pool, queue, ring_ent); } static int fuse_queue_setup_io_uring(struct io_uring *ring, size_t qid, size_t depth, int fd, int evfd) { int rc; struct io_uring_params params = {0}; int files[2] = { fd, evfd }; depth += 1; /* for the eventfd poll SQE */ params.flags = IORING_SETUP_SQE128; /* Avoid cq overflow */ params.flags |= IORING_SETUP_CQSIZE; params.cq_entries = depth * 2; /* These flags should help to increase performance, but actually * make it a bit slower - reason should get investigated. */ if (0) { /* Has the main slow down effect */ params.flags |= IORING_SETUP_SINGLE_ISSUER; // params.flags |= IORING_SETUP_DEFER_TASKRUN; params.flags |= IORING_SETUP_TASKRUN_FLAG; /* Second main effect to make it slower */ params.flags |= IORING_SETUP_COOP_TASKRUN; } rc = io_uring_queue_init_params(depth, ring, ¶ms); if (rc != 0) { fuse_log(FUSE_LOG_ERR, "Failed to setup qid %zu: %d (%s)\n", qid, rc, strerror(-rc)); return rc; } rc = io_uring_register_files(ring, files, 1); if (rc != 0) { rc = -errno; fuse_log(FUSE_LOG_ERR, "Failed to register files for ring idx %zu: %s", qid, strerror(errno)); return rc; } return 0; } static void fuse_session_destruct_uring(struct fuse_ring_pool *fuse_ring) { for (size_t qid = 0; qid < fuse_ring->nr_queues; qid++) { struct fuse_ring_queue *queue = fuse_uring_get_queue(fuse_ring, qid); if (queue->tid != 0) { uint64_t value = 1ULL; int rc; rc = write(queue->eventfd, &value, sizeof(value)); if (rc != sizeof(value)) fprintf(stderr, "Wrote to eventfd=%d err=%s: rc=%d\n", queue->eventfd, strerror(errno), rc); pthread_cancel(queue->tid); pthread_join(queue->tid, NULL); queue->tid = 0; } if (queue->eventfd >= 0) { close(queue->eventfd); queue->eventfd = -1; } if (queue->ring.ring_fd != -1) io_uring_queue_exit(&queue->ring); for (size_t idx = 0; idx < fuse_ring->queue_depth; idx++) { struct fuse_ring_ent *ent = &queue->ent[idx]; numa_free(ent->op_payload, ent->req_payload_sz); numa_free(ent->req_header, queue->req_header_sz); } pthread_mutex_destroy(&queue->ring_lock); } free(fuse_ring->queues); pthread_cond_destroy(&fuse_ring->thread_start_cond); pthread_mutex_destroy(&fuse_ring->thread_start_mutex); free(fuse_ring); } static int fuse_uring_register_ent(struct fuse_ring_queue *queue, struct fuse_ring_ent *ent) { struct io_uring_sqe *sqe; sqe = io_uring_get_sqe(&queue->ring); if (sqe == NULL) { /* * All SQEs are idle here - no good reason this * could fail */ fuse_log(FUSE_LOG_ERR, "Failed to get all ring SQEs"); return -EIO; } ent->last_cmd = FUSE_IO_URING_CMD_REGISTER; fuse_uring_sqe_prepare(sqe, ent, ent->last_cmd); /* only needed for fetch */ ent->iov[0].iov_base = ent->req_header; ent->iov[0].iov_len = queue->req_header_sz; ent->iov[1].iov_base = ent->op_payload; ent->iov[1].iov_len = ent->req_payload_sz; sqe->addr = (uint64_t)(ent->iov); sqe->len = 2; /* this is a fetch, kernel does not read commit id */ fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid, 0); return 0; } static int fuse_uring_register_queue(struct fuse_ring_queue *queue) { struct fuse_ring_pool *ring_pool = queue->ring_pool; unsigned int sq_ready; struct io_uring_sqe *sqe; int res; for (size_t idx = 0; idx < ring_pool->queue_depth; idx++) { struct fuse_ring_ent *ent = &queue->ent[idx]; res = fuse_uring_register_ent(queue, ent); if (res != 0) return res; } sq_ready = io_uring_sq_ready(&queue->ring); if (sq_ready != ring_pool->queue_depth) { fuse_log(FUSE_LOG_ERR, "SQE ready mismatch, expected %zu got %u\n", ring_pool->queue_depth, sq_ready); return -EINVAL; } /* Poll SQE for the eventfd to wake up on teardown */ sqe = io_uring_get_sqe(&queue->ring); if (sqe == NULL) { fuse_log(FUSE_LOG_ERR, "Failed to get eventfd SQE"); return -EIO; } io_uring_prep_poll_add(sqe, queue->eventfd, POLLIN); io_uring_sqe_set_data(sqe, (void *)(uintptr_t)queue->eventfd); /* Only preparation until here, no submission yet */ return 0; } static struct fuse_ring_pool *fuse_create_ring(struct fuse_session *se) { struct fuse_ring_pool *fuse_ring = NULL; const size_t nr_queues = get_nprocs_conf(); size_t payload_sz = se->bufsize - FUSE_BUFFER_HEADER_SIZE; size_t queue_sz; if (se->debug) fuse_log(FUSE_LOG_DEBUG, "starting io-uring q-depth=%d\n", se->uring.q_depth); fuse_ring = calloc(1, sizeof(*fuse_ring)); if (fuse_ring == NULL) { fuse_log(FUSE_LOG_ERR, "Allocating the ring failed\n"); goto err; } queue_sz = fuse_ring_queue_size(se->uring.q_depth); fuse_ring->queues = calloc(1, queue_sz * nr_queues); if (fuse_ring->queues == NULL) { fuse_log(FUSE_LOG_ERR, "Allocating the queues failed\n"); goto err; } fuse_ring->se = se; fuse_ring->nr_queues = nr_queues; fuse_ring->queue_depth = se->uring.q_depth; fuse_ring->max_req_payload_sz = payload_sz; fuse_ring->queue_mem_size = queue_sz; /* * very basic queue initialization, that cannot fail and will * allow easy cleanup if something (like mmap) fails in the middle * below */ for (size_t qid = 0; qid < nr_queues; qid++) { struct fuse_ring_queue *queue = fuse_uring_get_queue(fuse_ring, qid); queue->ring.ring_fd = -1; queue->numa_node = numa_node_of_cpu(qid); queue->qid = qid; queue->ring_pool = fuse_ring; queue->eventfd = -1; pthread_mutex_init(&queue->ring_lock, NULL); } pthread_cond_init(&fuse_ring->thread_start_cond, NULL); pthread_mutex_init(&fuse_ring->thread_start_mutex, NULL); sem_init(&fuse_ring->init_sem, 0, 0); return fuse_ring; err: if (fuse_ring) fuse_session_destruct_uring(fuse_ring); return NULL; } static void fuse_uring_resubmit(struct fuse_ring_queue *queue, struct fuse_ring_ent *ent) { struct io_uring_sqe *sqe; sqe = io_uring_get_sqe(&queue->ring); if (sqe == NULL) { /* This is an impossible condition, unless there is a bug. * The kernel sent back an SQEs, which is assigned to a request. * There is no way to get out of SQEs, as the number of * SQEs matches the number tof requests. */ queue->ring_pool->se->error = -EIO; fuse_log(FUSE_LOG_ERR, "Failed to get a ring SQEs\n"); return; } fuse_uring_sqe_prepare(sqe, ent, ent->last_cmd); switch (ent->last_cmd) { case FUSE_IO_URING_CMD_REGISTER: sqe->addr = (uint64_t)(ent->iov); sqe->len = 2; fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid, 0); break; case FUSE_IO_URING_CMD_COMMIT_AND_FETCH: fuse_uring_sqe_set_req_data(fuse_uring_get_sqe_cmd(sqe), queue->qid, ent->req_commit_id); break; default: fuse_log(FUSE_LOG_ERR, "Unknown command type: %d\n", ent->last_cmd); queue->ring_pool->se->error = -EINVAL; break; } /* caller submits */ } static void fuse_uring_handle_cqe(struct fuse_ring_queue *queue, struct io_uring_cqe *cqe) { struct fuse_ring_ent *ent = io_uring_cqe_get_data(cqe); if (!ent) { fuse_log(FUSE_LOG_ERR, "cqe=%p io_uring_cqe_get_data returned NULL\n", cqe); return; } struct fuse_req *req = &ent->req; struct fuse_ring_pool *fuse_ring = queue->ring_pool; struct fuse_uring_req_header *rrh = ent->req_header; struct fuse_in_header *in = (struct fuse_in_header *)&rrh->in_out; struct fuse_uring_ent_in_out *ent_in_out = &rrh->ring_ent_in_out; ent->req_commit_id = ent_in_out->commit_id; if (unlikely(ent->req_commit_id == 0)) { /* * If this happens kernel will not find the response - it will * be stuck forever - better to abort immediately. */ fuse_log(FUSE_LOG_ERR, "Received invalid commit_id=0\n"); abort(); } memset(&req->flags, 0, sizeof(req->flags)); memset(&req->u, 0, sizeof(req->u)); req->flags.is_uring = 1; req->ref_cnt++; req->ch = NULL; /* not needed for uring */ req->interrupted = 0; list_init_req(req); fuse_session_process_uring_cqe(fuse_ring->se, req, in, &rrh->op_in, ent->op_payload, ent_in_out->payload_sz); } static int fuse_uring_queue_handle_cqes(struct fuse_ring_queue *queue) { struct fuse_ring_pool *ring_pool = queue->ring_pool; struct fuse_session *se = ring_pool->se; size_t num_completed = 0; struct io_uring_cqe *cqe; unsigned int head; struct fuse_ring_ent *ent; int ret = 0; io_uring_for_each_cqe(&queue->ring, head, cqe) { int err = 0; num_completed++; err = cqe->res; if (unlikely(err != 0)) { if (err > 0 && ((uintptr_t)io_uring_cqe_get_data(cqe) == (unsigned int)queue->eventfd)) { /* teardown from eventfd */ return -ENOTCONN; } switch (err) { case -EAGAIN: fallthrough; case -EINTR: ent = io_uring_cqe_get_data(cqe); fuse_uring_resubmit(queue, ent); continue; default: break; } /* -ENOTCONN is ok on umount */ if (err != -ENOTCONN) { se->error = cqe->res; /* return first error */ if (ret == 0) ret = err; } } else { fuse_uring_handle_cqe(queue, cqe); } } if (num_completed) io_uring_cq_advance(&queue->ring, num_completed); return ret == 0 ? 0 : num_completed; } /** * In per-core-queue configuration we have thread per core - the thread * to that core */ static void fuse_uring_set_thread_core(int qid) { cpu_set_t mask; int rc; CPU_ZERO(&mask); CPU_SET(qid, &mask); rc = sched_setaffinity(0, sizeof(cpu_set_t), &mask); if (rc != 0) fuse_log(FUSE_LOG_ERR, "Failed to bind qid=%d to its core: %s\n", qid, strerror(errno)); if (0) { const int policy = SCHED_IDLE; const struct sched_param param = { .sched_priority = sched_get_priority_min(policy), }; /* Set the lowest possible priority, so that the application * submitting requests is not moved away from the current core. */ rc = sched_setscheduler(0, policy, ¶m); if (rc != 0) fuse_log(FUSE_LOG_ERR, "Failed to set scheduler: %s\n", strerror(errno)); } } /* * @return negative error code or io-uring file descriptor */ static int fuse_uring_init_queue(struct fuse_ring_queue *queue) { struct fuse_ring_pool *ring = queue->ring_pool; struct fuse_session *se = ring->se; int res; size_t page_sz = sysconf(_SC_PAGESIZE); queue->eventfd = eventfd(0, EFD_CLOEXEC); if (queue->eventfd < 0) { res = -errno; fuse_log(FUSE_LOG_ERR, "Failed to create eventfd for qid %d: %s\n", queue->qid, strerror(errno)); return res; } res = fuse_queue_setup_io_uring(&queue->ring, queue->qid, ring->queue_depth, se->fd, queue->eventfd); if (res != 0) { fuse_log(FUSE_LOG_ERR, "qid=%d io_uring init failed\n", queue->qid); return res; } queue->req_header_sz = ROUND_UP(sizeof(struct fuse_ring_ent), page_sz); for (size_t idx = 0; idx < ring->queue_depth; idx++) { struct fuse_ring_ent *ring_ent = &queue->ent[idx]; struct fuse_req *req = &ring_ent->req; ring_ent->ring_queue = queue; /* * Also allocate the header to have it page aligned, which * is a requirement for page pinning */ ring_ent->req_header = numa_alloc_local(queue->req_header_sz); if (!ring_ent->req_header) return -ENOMEM; ring_ent->req_payload_sz = ring->max_req_payload_sz; ring_ent->op_payload = numa_alloc_local(ring_ent->req_payload_sz); if (!ring_ent->op_payload) return -ENOMEM; req->se = se; pthread_mutex_init(&req->lock, NULL); req->flags.is_uring = 1; req->ref_cnt = 1; /* extra ref to avoid destruction */ list_init_req(req); } res = fuse_uring_register_queue(queue); if (res != 0) { fuse_log( FUSE_LOG_ERR, "Grave fuse-uring error on preparing SQEs, aborting\n"); se->error = -EIO; fuse_session_exit(se); return res; } return queue->ring.ring_fd; } static void *fuse_uring_thread(void *arg) { struct fuse_ring_queue *queue = arg; struct fuse_ring_pool *ring_pool = queue->ring_pool; struct fuse_session *se = ring_pool->se; int err; char thread_name[16] = { 0 }; snprintf(thread_name, 16, "fuse-ring-%d", queue->qid); thread_name[15] = '\0'; fuse_set_thread_name(thread_name); fuse_uring_set_thread_core(queue->qid); err = fuse_uring_init_queue(queue); pthread_mutex_lock(&ring_pool->thread_start_mutex); if (err < 0) ring_pool->failed_threads++; ring_pool->started_threads++; pthread_cond_broadcast(&ring_pool->thread_start_cond); pthread_mutex_unlock(&ring_pool->thread_start_mutex); if (err < 0) { fuse_log(FUSE_LOG_ERR, "qid=%d queue setup failed\n", queue->qid); goto err_non_fatal; } sem_wait(&ring_pool->init_sem); /* Not using fuse_session_exited(se), as that cannot be inlined */ while (!atomic_load_explicit(&se->mt_exited, memory_order_relaxed)) { io_uring_submit_and_wait(&queue->ring, 1); pthread_mutex_lock(&queue->ring_lock); queue->cqe_processing = true; err = fuse_uring_queue_handle_cqes(queue); queue->cqe_processing = false; pthread_mutex_unlock(&queue->ring_lock); if (err < 0) goto err; } return NULL; err: fuse_session_exit(se); err_non_fatal: return NULL; } static int fuse_uring_start_ring_threads(struct fuse_ring_pool *ring) { int rc = 0; for (size_t qid = 0; qid < ring->nr_queues; qid++) { struct fuse_ring_queue *queue = fuse_uring_get_queue(ring, qid); rc = pthread_create(&queue->tid, NULL, fuse_uring_thread, queue); if (rc != 0) break; } return rc; } static int fuse_uring_sanity_check(struct fuse_session *se) { if (se->uring.q_depth == 0) { fuse_log(FUSE_LOG_ERR, "io-uring queue depth must be > 0\n"); return -EINVAL; } _Static_assert(sizeof(struct fuse_uring_cmd_req) <= FUSE_URING_MAX_SQE128_CMD_DATA, "SQE128_CMD_DATA has 80B cmd data"); return 0; } int fuse_uring_start(struct fuse_session *se) { int err = 0; struct fuse_ring_pool *fuse_ring; fuse_uring_sanity_check(se); fuse_ring = fuse_create_ring(se); if (fuse_ring == NULL) { err = -EADDRNOTAVAIL; goto err; } se->uring.pool = fuse_ring; /* Hold off threads from send fuse ring entries (SQEs) */ sem_init(&fuse_ring->init_sem, 0, 0); pthread_cond_init(&fuse_ring->thread_start_cond, NULL); pthread_mutex_init(&fuse_ring->thread_start_mutex, NULL); err = fuse_uring_start_ring_threads(fuse_ring); if (err) goto err; /* * Wait for all threads to start or to fail */ pthread_mutex_lock(&fuse_ring->thread_start_mutex); while (fuse_ring->started_threads < fuse_ring->nr_queues) pthread_cond_wait(&fuse_ring->thread_start_cond, &fuse_ring->thread_start_mutex); if (fuse_ring->failed_threads != 0) err = -EADDRNOTAVAIL; pthread_mutex_unlock(&fuse_ring->thread_start_mutex); err: if (err) { /* Note all threads need to have been started */ if (fuse_ring) fuse_session_destruct_uring(fuse_ring); se->uring.pool = NULL; } return err; } int fuse_uring_stop(struct fuse_session *se) { struct fuse_ring_pool *ring = se->uring.pool; if (ring == NULL) return 0; fuse_session_destruct_uring(ring); return 0; } void fuse_uring_wake_ring_threads(struct fuse_session *se) { struct fuse_ring_pool *ring = se->uring.pool; /* Wake up the threads to let them send SQEs */ for (size_t qid = 0; qid < ring->nr_queues; qid++) sem_post(&ring->init_sem); } fuse-3.18.2/lib/fuse_uring_i.h0000644000175000017500000000407315156613252015124 0ustar berndbernd/* * FUSE: Filesystem in Userspace * Copyright (C) 2025 Bernd Schubert * This program can be distributed under the terms of the GNU LGPLv2. * See the file LGPL2.txt */ #ifndef FUSE_URING_I_H_ #define FUSE_URING_I_H_ #include "fuse_config.h" #include "fuse_lowlevel.h" #include "fuse_kernel.h" #ifndef HAVE_URING #include "util.h" #endif #include // IWYU pragma: keep /* io-uring defaults */ #define SESSION_DEF_URING_ENABLE (0) #define SESSION_DEF_URING_Q_DEPTH (8) void fuse_session_process_uring_cqe(struct fuse_session *se, struct fuse_req *req, struct fuse_in_header *in, void *in_header, void *in_payload, size_t payload_len); #ifdef HAVE_URING struct fuse_in_header; int fuse_uring_start(struct fuse_session *se); void fuse_uring_wake_ring_threads(struct fuse_session *se); int fuse_uring_stop(struct fuse_session *se); int send_reply_uring(fuse_req_t req, int error, const void *arg, size_t argsize); int fuse_reply_data_uring(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags); int fuse_send_msg_uring(fuse_req_t req, struct iovec *iov, int count); #else // HAVE_URING static inline int fuse_uring_start(struct fuse_session *se FUSE_VAR_UNUSED) { return -ENOTSUP; } static inline void fuse_uring_wake_ring_threads(struct fuse_session *se FUSE_VAR_UNUSED) { } static inline int fuse_uring_stop(struct fuse_session *se FUSE_VAR_UNUSED) { return -ENOTSUP; } static inline int send_reply_uring(fuse_req_t req FUSE_VAR_UNUSED, int error FUSE_VAR_UNUSED, const void *arg FUSE_VAR_UNUSED, size_t argsize FUSE_VAR_UNUSED) { return -ENOTSUP; } static inline int fuse_reply_data_uring(fuse_req_t req FUSE_VAR_UNUSED, struct fuse_bufvec *bufv FUSE_VAR_UNUSED, enum fuse_buf_copy_flags flags FUSE_VAR_UNUSED) { return -ENOTSUP; } static inline int fuse_send_msg_uring(fuse_req_t req FUSE_VAR_UNUSED, struct iovec *iov FUSE_VAR_UNUSED, int count FUSE_VAR_UNUSED) { return -ENOTSUP; } #endif // HAVE_URING #endif // FUSE_URING_I_H_ fuse-3.18.2/lib/fuse_versionscript0000644000175000017500000001033015156613252016145 0ustar berndberndFUSE_3.0 { global: fuse_destroy; fuse_exit; fuse_loop; fuse_loop_mt; fuse_reply_attr; fuse_reply_buf; fuse_reply_entry; fuse_reply_err; fuse_reply_none; fuse_reply_readlink; fuse_reply_write; fuse_reply_xattr; fuse_req_userdata; fuse_session_destroy; fuse_session_exit; fuse_session_exited; fuse_session_loop; fuse_session_loop_mt; fuse_session_reset; fuse_session_fd; fuse_opt_parse; fuse_opt_add_opt; fuse_opt_add_arg; fuse_opt_free_args; fuse_opt_match; fuse_parse_cmdline; fuse_remove_signal_handlers; fuse_reply_create; fuse_reply_open; fuse_reply_statfs; fuse_set_signal_handlers; fuse_add_direntry; fuse_add_direntry_plus; fuse_daemonize; fuse_get_session; fuse_interrupted; fuse_session_new; fuse_main_real; fuse_mount; fuse_session_custom_io; fuse_session_mount; fuse_new; fuse_opt_insert_arg; fuse_reply_lock; fuse_req_interrupt_func; fuse_req_interrupted; fuse_unmount; fuse_session_unmount; fuse_fs_access; fuse_fs_bmap; fuse_fs_chmod; fuse_fs_chown; fuse_fs_create; fuse_fs_destroy; fuse_fs_flush; fuse_fs_fsync; fuse_fs_fsyncdir; fuse_fs_getattr; fuse_fs_getxattr; fuse_fs_init; fuse_fs_link; fuse_fs_listxattr; fuse_fs_lock; fuse_fs_mkdir; fuse_fs_mknod; fuse_fs_new; fuse_fs_open; fuse_fs_opendir; fuse_fs_read; fuse_fs_readdir; fuse_fs_readlink; fuse_fs_release; fuse_fs_releasedir; fuse_fs_removexattr; fuse_fs_rename; fuse_fs_rmdir; fuse_fs_setxattr; fuse_fs_statfs; fuse_fs_symlink; fuse_fs_truncate; fuse_fs_unlink; fuse_fs_utimens; fuse_fs_write; fuse_reply_iov; fuse_version; fuse_pkgversion; fuse_reply_bmap; cuse_lowlevel_new; cuse_lowlevel_main; cuse_lowlevel_setup; cuse_lowlevel_teardown; fuse_fs_ioctl; fuse_fs_poll; fuse_get_context; fuse_getgroups; fuse_lowlevel_notify_inval_entry; fuse_lowlevel_notify_inval_inode; fuse_lowlevel_notify_poll; fuse_notify_poll; fuse_opt_add_opt_escaped; fuse_pollhandle_destroy; fuse_reply_ioctl; fuse_reply_ioctl_iov; fuse_reply_ioctl_retry; fuse_reply_poll; fuse_req_ctx; fuse_req_getgroups; fuse_buf_copy; fuse_buf_size; fuse_fs_read_buf; fuse_fs_write_buf; fuse_lowlevel_notify_retrieve; fuse_lowlevel_notify_store; fuse_reply_data; fuse_session_process_buf; fuse_session_receive_buf; fuse_start_cleanup_thread; fuse_stop_cleanup_thread; fuse_clean_cache; fuse_lowlevel_notify_delete; fuse_fs_flock; fuse_fs_fallocate; fuse_lowlevel_help; fuse_lowlevel_version; fuse_cmdline_help; fuse_apply_conn_info_opts; fuse_parse_conn_info_opts; fuse_fs_lseek; fuse_reply_lseek; local: *; }; FUSE_3.1 { global: fuse_lib_help; fuse_invalidate_path; fuse_new_30; fuse_new_31; fuse_new; } FUSE_3.0; FUSE_3.2 { global: fuse_session_loop_mt; fuse_session_loop_mt_31; fuse_session_loop_mt_32; fuse_loop_mt; fuse_loop_mt_31; } FUSE_3.1; FUSE_3.3 { global: fuse_open_channel; } FUSE_3.2; FUSE_3.4 { global: fuse_fs_copy_file_range; } FUSE_3.3; FUSE_3.7 { global: fuse_set_log_func; 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; FUSE_3.17 { global: fuse_main_real_versioned; fuse_session_new_versioned; _fuse_new_30; _fuse_new_31; fuse_passthrough_open; fuse_passthrough_close; fuse_session_custom_io_30; fuse_session_custom_io_317; fuse_set_fail_signal_handlers; fuse_log_enable_syslog; fuse_log_close_syslog; } FUSE_3.12; FUSE_3.17.3 { global: fuse_set_feature_flag; fuse_unset_feature_flag; fuse_get_feature_flag; # Not part of public API, for internal testing only fuse_convert_to_conn_want_ext; } FUSE_3.17; FUSE_3.18 { global: fuse_req_is_uring; fuse_req_get_payload; fuse_lowlevel_notify_increment_epoch; fuse_reply_statx; fuse_fs_statx; } FUSE_3.17; # Local Variables: # indent-tabs-mode: t # End: fuse-3.18.2/lib/helper.c0000644000175000017500000003345115156613252013722 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi 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 LGPL2.txt. */ #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 #include #include #include #include #include #include #include #define FUSE_HELPER_OPT(t, p) \ { t, offsetof(struct fuse_cmdline_opts, p), 1 } static const struct fuse_opt fuse_helper_opts[] = { FUSE_HELPER_OPT("-h", show_help), FUSE_HELPER_OPT("--help", show_help), FUSE_HELPER_OPT("-V", show_version), FUSE_HELPER_OPT("--version", show_version), FUSE_HELPER_OPT("-d", debug), FUSE_HELPER_OPT("debug", debug), FUSE_HELPER_OPT("-d", foreground), FUSE_HELPER_OPT("debug", foreground), FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), FUSE_HELPER_OPT("-f", foreground), FUSE_HELPER_OPT("-s", singlethread), FUSE_HELPER_OPT("fsname=", nodefault_subtype), FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), #ifndef __FreeBSD__ FUSE_HELPER_OPT("subtype=", nodefault_subtype), FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), #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 }; struct fuse_conn_info_opts { int atomic_o_trunc; int no_remote_posix_lock; int no_remote_flock; int splice_write; int splice_move; int splice_read; int no_splice_write; int no_splice_move; int no_splice_read; int auto_inval_data; int no_auto_inval_data; int no_readdirplus; int no_readdirplus_auto; int async_dio; int no_async_dio; int writeback_cache; int no_writeback_cache; int async_read; int sync_read; unsigned max_write; unsigned max_readahead; unsigned max_background; unsigned congestion_threshold; unsigned time_gran; int set_max_write; int set_max_readahead; int set_max_background; int set_congestion_threshold; int set_time_gran; }; #define CONN_OPTION(t, p, v) \ { t, offsetof(struct fuse_conn_info_opts, p), v } static const struct fuse_opt conn_info_opt_spec[] = { CONN_OPTION("max_write=%u", max_write, 0), CONN_OPTION("max_write=", set_max_write, 1), CONN_OPTION("max_readahead=%u", max_readahead, 0), CONN_OPTION("max_readahead=", set_max_readahead, 1), CONN_OPTION("max_background=%u", max_background, 0), CONN_OPTION("max_background=", set_max_background, 1), CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1), CONN_OPTION("sync_read", sync_read, 1), CONN_OPTION("async_read", async_read, 1), CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), CONN_OPTION("no_remote_lock", no_remote_flock, 1), CONN_OPTION("no_remote_flock", no_remote_flock, 1), CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), CONN_OPTION("splice_write", splice_write, 1), CONN_OPTION("no_splice_write", no_splice_write, 1), CONN_OPTION("splice_move", splice_move, 1), CONN_OPTION("no_splice_move", no_splice_move, 1), CONN_OPTION("splice_read", splice_read, 1), CONN_OPTION("no_splice_read", no_splice_read, 1), CONN_OPTION("auto_inval_data", auto_inval_data, 1), CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), CONN_OPTION("readdirplus=no", no_readdirplus, 1), CONN_OPTION("readdirplus=yes", no_readdirplus, 0), CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), CONN_OPTION("readdirplus=auto", no_readdirplus, 0), CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), CONN_OPTION("async_dio", async_dio, 1), CONN_OPTION("no_async_dio", no_async_dio, 1), CONN_OPTION("writeback_cache", writeback_cache, 1), CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), CONN_OPTION("time_gran=%u", time_gran, 0), CONN_OPTION("time_gran=", set_time_gran, 1), FUSE_OPT_END }; void fuse_cmdline_help(void) { printf(" -h --help print help\n" " -V --version print version\n" " -d -o debug enable debug output (implies -f)\n" " -f foreground operation\n" " -s disable multi-threaded operation\n" " -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"); } static int fuse_helper_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { (void) outargs; struct fuse_cmdline_opts *opts = data; switch (key) { case FUSE_OPT_KEY_NONOPT: if (!opts->mountpoint) { if (fuse_mnt_parse_fuse_fd(arg) != -1) { return fuse_opt_add_opt(&opts->mountpoint, arg); } char mountpoint[PATH_MAX] = ""; if (realpath(arg, mountpoint) == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: bad mount point `%s': %s\n", arg, strerror(errno)); return -1; } return fuse_opt_add_opt(&opts->mountpoint, mountpoint); } else { fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); return -1; } default: /* Pass through unknown options */ return 1; } } /* Under FreeBSD, there is no subtype option so this function actually sets the fsname */ static int add_default_subtype(const char *progname, struct fuse_args *args) { int res; char *subtype_opt; const char *basename = strrchr(progname, '/'); if (basename == NULL) basename = progname; else if (basename[1] != '\0') basename++; subtype_opt = (char *) malloc(strlen(basename) + 64); if (subtype_opt == NULL) { fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); return -1; } #ifdef __FreeBSD__ sprintf(subtype_opt, "-ofsname=%s", basename); #else sprintf(subtype_opt, "-osubtype=%s", basename); #endif res = fuse_opt_add_arg(args, subtype_opt); free(subtype_opt); return res; } 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 = 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) return -1; /* *Linux*: if neither -o subtype nor -o fsname are specified, set subtype to program's basename. *FreeBSD*: if fsname is not specified, set to program's basename. */ if (!opts->nodefault_subtype) if (add_default_subtype(args->argv[0], args) == -1) return -1; 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) { if (!foreground) { int nullfd; int waiter[2]; char completed; if (pipe(waiter)) { perror("fuse_daemonize: pipe"); return -1; } /* * demonize current process by forking it and killing the * parent. This makes current process as a child of 'init'. */ switch(fork()) { case -1: perror("fuse_daemonize: fork"); return -1; case 0: break; default: (void) read(waiter[0], &completed, sizeof(completed)); _exit(0); } if (setsid() == -1) { perror("fuse_daemonize: setsid"); return -1; } (void) chdir("/"); nullfd = open("/dev/null", O_RDWR, 0); if (nullfd != -1) { (void) dup2(nullfd, 0); (void) dup2(nullfd, 1); (void) dup2(nullfd, 2); if (nullfd > 2) close(nullfd); } /* Propagate completion of daemon initialization */ completed = 1; (void) write(waiter[1], &completed, sizeof(completed)); close(waiter[0]); close(waiter[1]); } else { (void) chdir("/"); } return 0; } int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 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; if (opts.show_version) { printf("FUSE library version %s\n", PACKAGE_VERSION); fuse_lowlevel_version(); res = 0; goto out1; } if (opts.show_help) { if(args.argv[0][0] != '\0') printf("usage: %s [options] \n\n", args.argv[0]); printf("FUSE options:\n"); fuse_cmdline_help(); fuse_lib_help(&args); res = 0; goto out1; } if (!opts.show_help && !opts.mountpoint) { fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n"); res = 2; goto out1; } struct fuse *_fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data); fuse = _fuse_new_31(&args, op, op_size, version, user_data); if (fuse == NULL) { res = 3; goto out1; } if (fuse_mount(fuse,opts.mountpoint) != 0) { res = 4; goto out2; } if (fuse_daemonize(opts.foreground) != 0) { res = 5; goto out3; } struct fuse_session *se = fuse_get_session(fuse); if (fuse_set_signal_handlers(se) != 0) { res = 6; goto out3; } if (opts.singlethread) res = fuse_loop(fuse); else { 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 = 8; fuse_remove_signal_handlers(se); out3: fuse_unmount(fuse); out2: fuse_destroy(fuse); out1: fuse_loop_cfg_destroy(loop_config); free(opts.mountpoint); fuse_opt_free_args(&args); return res; } /* Not symboled, as not part of the official API */ int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data); int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data) { struct libfuse_version version = { 0 }; return fuse_main_real_versioned(argc, argv, op, op_size, &version, user_data); } void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn) { if(opts->set_max_write) conn->max_write = opts->max_write; if(opts->set_max_background) conn->max_background = opts->max_background; if(opts->set_congestion_threshold) conn->congestion_threshold = opts->congestion_threshold; if(opts->set_time_gran) conn->time_gran = opts->time_gran; if(opts->set_max_readahead) conn->max_readahead = opts->max_readahead; #define LL_ENABLE(cond, cap) \ do { \ if (cond) \ fuse_set_feature_flag(conn, cap); \ } while (0) #define LL_DISABLE(cond, cap) \ do { \ if (cond) \ fuse_unset_feature_flag(conn, cap); \ } while (0) LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); } struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args) { struct fuse_conn_info_opts *opts; opts = calloc(1, sizeof(struct fuse_conn_info_opts)); if(opts == NULL) { fuse_log(FUSE_LOG_ERR, "calloc failed\n"); return NULL; } if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { free(opts); return NULL; } return opts; } int fuse_open_channel(const char *mountpoint, const char* options) { struct mount_opts *opts = NULL; int fd = -1; const char *argv[] = { "", "-o", options }; int argc = sizeof(argv) / sizeof(argv[0]); struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv); opts = parse_mount_opts(&args); if (opts == NULL) return -1; fd = fuse_kern_mount(mountpoint, opts); destroy_mount_opts(opts); return fd; } fuse-3.18.2/lib/meson.build0000644000175000017500000000431215156613252014433 0ustar berndberndlibfuse_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', 'compat.c', 'util.c', 'util.h' ] if host_machine.system().startswith('linux') libfuse_sources += [ 'mount.c' ] else libfuse_sources += [ 'mount_bsd.c' ] endif deps = [ thread_dep ] if private_cfg.get('HAVE_ICONV') libfuse_sources += [ 'modules/iconv.c' ] libiconv = cc.find_library('iconv', required: false) if libiconv.found() deps += [ libiconv ] endif endif if private_cfg.get('HAVE_URING', false) libfuse_sources += [ 'fuse_uring.c' ] deps += [ dependency('liburing') ] deps += [ dependency('numa') ] endif libdl = cc.find_library('dl', required: false) if libdl.found() deps += [ libdl ] endif if host_machine.system().startswith('netbsd') deps += [ cc.find_library('perfuse'), cc.find_library('puffs') ] else # Required for clock_gettime before glibc 2.17 deps += cc.find_library('rt') endif fusermount_path = join_paths(get_option('prefix'), get_option('bindir')) libfuse = library('fuse3', libfuse_sources, version: base_version, soversion: '4', include_directories: include_dirs, dependencies: deps, install: true, link_depends: 'fuse_versionscript', c_args: [ '-DFUSE_USE_VERSION=317', '-DFUSERMOUNT_DIR="@0@"'.format(fusermount_path) ], link_args: ['-Wl,--version-script,' + meson.current_source_dir() + '/fuse_versionscript' ]) pkg = import('pkgconfig') pkg.generate(libraries: [ libfuse, '-lpthread' ], libraries_private: '-ldl', version: meson.project_version(), name: 'fuse3', description: 'Filesystem in Userspace', subdirs: 'fuse3') libfuse_dep = declare_dependency(include_directories: include_dirs, link_with: libfuse, dependencies: deps) fuse-3.18.2/lib/modules/0000755000175000017500000000000015156613252013741 5ustar berndberndfuse-3.18.2/lib/modules/iconv.c0000644000175000017500000004054715156613252015235 0ustar berndbernd/* fuse iconv module: file name charset conversion Copyright (C) 2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #include #include #include #include #include #include #include #include #include #include #include struct iconv { struct fuse_fs *next; pthread_mutex_t lock; char *from_code; char *to_code; iconv_t tofs; iconv_t fromfs; }; struct iconv_dh { struct iconv *ic; void *prev_buf; fuse_fill_dir_t prev_filler; }; static struct iconv *iconv_get(void) { return fuse_get_context()->private_data; } static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp, int fromfs) { size_t pathlen; size_t newpathlen; char *newpath; size_t plen; char *p; size_t res; int err; if (path == NULL) { *newpathp = NULL; return 0; } pathlen = strlen(path); newpathlen = pathlen * 4; newpath = malloc(newpathlen + 1); if (!newpath) return -ENOMEM; plen = newpathlen; p = newpath; pthread_mutex_lock(&ic->lock); do { res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path, &pathlen, &p, &plen); if (res == (size_t) -1) { char *tmp; size_t inc; err = -EILSEQ; if (errno != E2BIG) goto err; inc = (pathlen + 1) * 4; newpathlen += inc; int dp = p - newpath; tmp = realloc(newpath, newpathlen + 1); err = -ENOMEM; if (!tmp) goto err; p = tmp + dp; plen += inc; newpath = tmp; } } while (res == (size_t) -1); pthread_mutex_unlock(&ic->lock); *p = '\0'; *newpathp = newpath; return 0; err: iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL); pthread_mutex_unlock(&ic->lock); free(newpath); return err; } static int iconv_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_getattr(ic->next, newpath, stbuf, fi); free(newpath); } return err; } static int iconv_access(const char *path, int mask) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_access(ic->next, newpath, mask); free(newpath); } return err; } static int iconv_readlink(const char *path, char *buf, size_t size) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_readlink(ic->next, newpath, buf, size); if (!err) { char *newlink; err = iconv_convpath(ic, buf, &newlink, 1); if (!err) { strncpy(buf, newlink, size - 1); buf[size - 1] = '\0'; free(newlink); } } free(newpath); } return err; } static int iconv_opendir(const char *path, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_opendir(ic->next, newpath, fi); free(newpath); } return err; } static int iconv_dir_fill(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags) { struct iconv_dh *dh = buf; char *newname; int res = 0; if (iconv_convpath(dh->ic, name, &newname, 1) == 0) { res = dh->prev_filler(dh->prev_buf, newname, stbuf, off, flags); free(newname); } return res; } static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { struct iconv_dh dh; dh.ic = ic; dh.prev_buf = buf; dh.prev_filler = filler; err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill, offset, fi, flags); free(newpath); } return err; } static int iconv_releasedir(const char *path, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_releasedir(ic->next, newpath, fi); free(newpath); } return err; } static int iconv_mknod(const char *path, mode_t mode, dev_t rdev) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_mknod(ic->next, newpath, mode, rdev); free(newpath); } return err; } static int iconv_mkdir(const char *path, mode_t mode) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_mkdir(ic->next, newpath, mode); free(newpath); } return err; } static int iconv_unlink(const char *path) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_unlink(ic->next, newpath); free(newpath); } return err; } static int iconv_rmdir(const char *path) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_rmdir(ic->next, newpath); free(newpath); } return err; } static int iconv_symlink(const char *from, const char *to) { struct iconv *ic = iconv_get(); char *newfrom; char *newto; int err = iconv_convpath(ic, from, &newfrom, 0); if (!err) { err = iconv_convpath(ic, to, &newto, 0); if (!err) { err = fuse_fs_symlink(ic->next, newfrom, newto); free(newto); } free(newfrom); } return err; } static int iconv_rename(const char *from, const char *to, unsigned int flags) { struct iconv *ic = iconv_get(); char *newfrom; char *newto; int err = iconv_convpath(ic, from, &newfrom, 0); if (!err) { err = iconv_convpath(ic, to, &newto, 0); if (!err) { err = fuse_fs_rename(ic->next, newfrom, newto, flags); free(newto); } free(newfrom); } return err; } static int iconv_link(const char *from, const char *to) { struct iconv *ic = iconv_get(); char *newfrom; char *newto; int err = iconv_convpath(ic, from, &newfrom, 0); if (!err) { err = iconv_convpath(ic, to, &newto, 0); if (!err) { err = fuse_fs_link(ic->next, newfrom, newto); free(newto); } free(newfrom); } return err; } static int iconv_chmod(const char *path, mode_t mode, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_chmod(ic->next, newpath, mode, fi); free(newpath); } return err; } static int iconv_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_chown(ic->next, newpath, uid, gid, fi); free(newpath); } return err; } static int iconv_truncate(const char *path, off_t size, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_truncate(ic->next, newpath, size, fi); free(newpath); } return err; } static int iconv_utimens(const char *path, const struct timespec ts[2], struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_utimens(ic->next, newpath, ts, fi); free(newpath); } return err; } static int iconv_create(const char *path, mode_t mode, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_create(ic->next, newpath, mode, fi); free(newpath); } return err; } static int iconv_open_file(const char *path, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_open(ic->next, newpath, fi); free(newpath); } return err; } static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp, size_t size, off_t offset, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi); free(newpath); } return err; } static int iconv_write_buf(const char *path, struct fuse_bufvec *buf, off_t offset, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_write_buf(ic->next, newpath, buf, offset, fi); free(newpath); } return err; } static int iconv_statfs(const char *path, struct statvfs *stbuf) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_statfs(ic->next, newpath, stbuf); free(newpath); } return err; } static int iconv_flush(const char *path, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_flush(ic->next, newpath, fi); free(newpath); } return err; } static int iconv_release(const char *path, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_release(ic->next, newpath, fi); free(newpath); } return err; } static int iconv_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi); free(newpath); } return err; } static int iconv_fsyncdir(const char *path, int isdatasync, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi); free(newpath); } return err; } static int iconv_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_setxattr(ic->next, newpath, name, value, size, flags); free(newpath); } return err; } static int iconv_getxattr(const char *path, const char *name, char *value, size_t size) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_getxattr(ic->next, newpath, name, value, size); free(newpath); } return err; } static int iconv_listxattr(const char *path, char *list, size_t size) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_listxattr(ic->next, newpath, list, size); free(newpath); } return err; } static int iconv_removexattr(const char *path, const char *name) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_removexattr(ic->next, newpath, name); free(newpath); } return err; } static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd, struct flock *lock) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock); free(newpath); } return err; } static int iconv_flock(const char *path, struct fuse_file_info *fi, int op) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_flock(ic->next, newpath, fi, op); free(newpath); } return err; } static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx) { struct iconv *ic = iconv_get(); char *newpath; int err = iconv_convpath(ic, path, &newpath, 0); if (!err) { err = fuse_fs_bmap(ic->next, newpath, blocksize, idx); free(newpath); } return err; } static off_t iconv_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int res = iconv_convpath(ic, path, &newpath, 0); if (!res) { res = fuse_fs_lseek(ic->next, newpath, off, whence, fi); free(newpath); } return res; } #ifdef HAVE_STATX static int iconv_statx(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi) { struct iconv *ic = iconv_get(); char *newpath; int res = iconv_convpath(ic, path, &newpath, 0); if (!res) { res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi); free(newpath); } return res; } #endif static void *iconv_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { struct iconv *ic = iconv_get(); fuse_fs_init(ic->next, conn, cfg); /* Don't touch cfg->nullpath_ok, we can work with either */ return ic; } static void iconv_destroy(void *data) { struct iconv *ic = data; fuse_fs_destroy(ic->next); iconv_close(ic->tofs); iconv_close(ic->fromfs); pthread_mutex_destroy(&ic->lock); free(ic->from_code); free(ic->to_code); free(ic); } static const struct fuse_operations iconv_oper = { .destroy = iconv_destroy, .init = iconv_init, .getattr = iconv_getattr, .access = iconv_access, .readlink = iconv_readlink, .opendir = iconv_opendir, .readdir = iconv_readdir, .releasedir = iconv_releasedir, .mknod = iconv_mknod, .mkdir = iconv_mkdir, .symlink = iconv_symlink, .unlink = iconv_unlink, .rmdir = iconv_rmdir, .rename = iconv_rename, .link = iconv_link, .chmod = iconv_chmod, .chown = iconv_chown, .truncate = iconv_truncate, .utimens = iconv_utimens, .create = iconv_create, .open = iconv_open_file, .read_buf = iconv_read_buf, .write_buf = iconv_write_buf, .statfs = iconv_statfs, .flush = iconv_flush, .release = iconv_release, .fsync = iconv_fsync, .fsyncdir = iconv_fsyncdir, .setxattr = iconv_setxattr, .getxattr = iconv_getxattr, .listxattr = iconv_listxattr, .removexattr = iconv_removexattr, .lock = iconv_lock, .flock = iconv_flock, .bmap = iconv_bmap, .lseek = iconv_lseek, #ifdef HAVE_STATX .statx = iconv_statx, #endif }; static const struct fuse_opt iconv_opts[] = { FUSE_OPT_KEY("-h", 0), FUSE_OPT_KEY("--help", 0), { "from_code=%s", offsetof(struct iconv, from_code), 0 }, { "to_code=%s", offsetof(struct iconv, to_code), 1 }, FUSE_OPT_END }; static void iconv_help(void) { char *charmap; const char *old = setlocale(LC_CTYPE, ""); charmap = strdup(nl_langinfo(CODESET)); if (old) setlocale(LC_CTYPE, old); else perror("setlocale"); printf( " -o from_code=CHARSET original encoding of file names (default: UTF-8)\n" " -o to_code=CHARSET new encoding of the file names (default: %s)\n", charmap); free(charmap); } static int iconv_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { (void) data; (void) arg; (void) outargs; if (!key) { iconv_help(); return -1; } return 1; } static struct fuse_fs *iconv_new(struct fuse_args *args, struct fuse_fs *next[]) { struct fuse_fs *fs; struct iconv *ic; const char *old = NULL; const char *from; const char *to; ic = calloc(1, sizeof(struct iconv)); if (ic == NULL) { fuse_log(FUSE_LOG_ERR, "fuse-iconv: memory allocation failed\n"); return NULL; } if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1) goto out_free; if (!next[0] || next[1]) { fuse_log(FUSE_LOG_ERR, "fuse-iconv: exactly one next filesystem required\n"); goto out_free; } from = ic->from_code ? ic->from_code : "UTF-8"; to = ic->to_code ? ic->to_code : ""; /* FIXME: detect charset equivalence? */ if (!to[0]) old = setlocale(LC_CTYPE, ""); ic->tofs = iconv_open(from, to); if (ic->tofs == (iconv_t) -1) { fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n", to, from); goto out_free; } ic->fromfs = iconv_open(to, from); if (ic->tofs == (iconv_t) -1) { fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n", from, to); goto out_iconv_close_to; } if (old) { setlocale(LC_CTYPE, old); old = NULL; } ic->next = next[0]; fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic); if (!fs) goto out_iconv_close_from; return fs; out_iconv_close_from: iconv_close(ic->fromfs); out_iconv_close_to: iconv_close(ic->tofs); out_free: free(ic->from_code); free(ic->to_code); free(ic); if (old) { setlocale(LC_CTYPE, old); } return NULL; } FUSE_REGISTER_MODULE(iconv, iconv_new); fuse-3.18.2/lib/modules/subdir.c0000644000175000017500000003626715156613252015413 0ustar berndbernd/* fuse subdir module: offset paths with a base directory Copyright (C) 2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt */ #include #include #include #include #include #include #include struct subdir { char *base; size_t baselen; int rellinks; struct fuse_fs *next; }; static struct subdir *subdir_get(void) { return fuse_get_context()->private_data; } static int subdir_addpath(struct subdir *d, const char *path, char **newpathp) { char *newpath = NULL; if (path != NULL) { unsigned newlen = d->baselen + strlen(path); newpath = malloc(newlen + 2); if (!newpath) return -ENOMEM; if (path[0] == '/') path++; strcpy(newpath, d->base); strcpy(newpath + d->baselen, path); if (!newpath[0]) strcpy(newpath, "."); } *newpathp = newpath; return 0; } static int subdir_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_getattr(d->next, newpath, stbuf, fi); free(newpath); } return err; } static int subdir_access(const char *path, int mask) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_access(d->next, newpath, mask); free(newpath); } return err; } static int count_components(const char *p) { int ctr; for (; *p == '/'; p++); for (ctr = 0; *p; ctr++) { for (; *p && *p != '/'; p++); for (; *p == '/'; p++); } return ctr; } static void strip_common(const char **sp, const char **tp) { const char *s = *sp; const char *t = *tp; do { for (; *s == '/'; s++); for (; *t == '/'; t++); *tp = t; *sp = s; for (; *s == *t && *s && *s != '/'; s++, t++); } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t)); } static void transform_symlink(struct subdir *d, const char *path, char *buf, size_t size) { const char *l = buf; size_t llen; char *s; int dotdots; int i; if (l[0] != '/' || d->base[0] != '/') return; strip_common(&l, &path); if (l - buf < (long) d->baselen) return; dotdots = count_components(path); if (!dotdots) return; dotdots--; llen = strlen(l); if (dotdots * 3 + llen + 2 > size) return; s = buf + dotdots * 3; if (llen) memmove(s, l, llen + 1); else if (!dotdots) strcpy(s, "."); else *s = '\0'; for (s = buf, i = 0; i < dotdots; i++, s += 3) memcpy(s, "../", 3); } static int subdir_readlink(const char *path, char *buf, size_t size) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_readlink(d->next, newpath, buf, size); if (!err && d->rellinks) transform_symlink(d, newpath, buf, size); free(newpath); } return err; } static int subdir_opendir(const char *path, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_opendir(d->next, newpath, fi); free(newpath); } return err; } static int subdir_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_readdir(d->next, newpath, buf, filler, offset, fi, flags); free(newpath); } return err; } static int subdir_releasedir(const char *path, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_releasedir(d->next, newpath, fi); free(newpath); } return err; } static int subdir_mknod(const char *path, mode_t mode, dev_t rdev) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_mknod(d->next, newpath, mode, rdev); free(newpath); } return err; } static int subdir_mkdir(const char *path, mode_t mode) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_mkdir(d->next, newpath, mode); free(newpath); } return err; } static int subdir_unlink(const char *path) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_unlink(d->next, newpath); free(newpath); } return err; } static int subdir_rmdir(const char *path) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_rmdir(d->next, newpath); free(newpath); } return err; } static int subdir_symlink(const char *from, const char *path) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_symlink(d->next, from, newpath); free(newpath); } return err; } static int subdir_rename(const char *from, const char *to, unsigned int flags) { struct subdir *d = subdir_get(); char *newfrom; char *newto; int err = subdir_addpath(d, from, &newfrom); if (!err) { err = subdir_addpath(d, to, &newto); if (!err) { err = fuse_fs_rename(d->next, newfrom, newto, flags); free(newto); } free(newfrom); } return err; } static int subdir_link(const char *from, const char *to) { struct subdir *d = subdir_get(); char *newfrom; char *newto; int err = subdir_addpath(d, from, &newfrom); if (!err) { err = subdir_addpath(d, to, &newto); if (!err) { err = fuse_fs_link(d->next, newfrom, newto); free(newto); } free(newfrom); } return err; } static int subdir_chmod(const char *path, mode_t mode, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_chmod(d->next, newpath, mode, fi); free(newpath); } return err; } static int subdir_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_chown(d->next, newpath, uid, gid, fi); free(newpath); } return err; } static int subdir_truncate(const char *path, off_t size, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_truncate(d->next, newpath, size, fi); free(newpath); } return err; } static int subdir_utimens(const char *path, const struct timespec ts[2], struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_utimens(d->next, newpath, ts, fi); free(newpath); } return err; } static int subdir_create(const char *path, mode_t mode, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_create(d->next, newpath, mode, fi); free(newpath); } return err; } static int subdir_open(const char *path, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_open(d->next, newpath, fi); free(newpath); } return err; } static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp, size_t size, off_t offset, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi); free(newpath); } return err; } static int subdir_write_buf(const char *path, struct fuse_bufvec *buf, off_t offset, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi); free(newpath); } return err; } static int subdir_statfs(const char *path, struct statvfs *stbuf) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_statfs(d->next, newpath, stbuf); free(newpath); } return err; } static int subdir_flush(const char *path, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_flush(d->next, newpath, fi); free(newpath); } return err; } static int subdir_release(const char *path, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_release(d->next, newpath, fi); free(newpath); } return err; } static int subdir_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_fsync(d->next, newpath, isdatasync, fi); free(newpath); } return err; } static int subdir_fsyncdir(const char *path, int isdatasync, struct fuse_file_info *fi) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi); free(newpath); } return err; } static int subdir_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_setxattr(d->next, newpath, name, value, size, flags); free(newpath); } return err; } static int subdir_getxattr(const char *path, const char *name, char *value, size_t size) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_getxattr(d->next, newpath, name, value, size); free(newpath); } return err; } static int subdir_listxattr(const char *path, char *list, size_t size) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_listxattr(d->next, newpath, list, size); free(newpath); } return err; } static int subdir_removexattr(const char *path, const char *name) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_removexattr(d->next, newpath, name); free(newpath); } return err; } static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd, struct flock *lock) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_lock(d->next, newpath, fi, cmd, lock); free(newpath); } return err; } static int subdir_flock(const char *path, struct fuse_file_info *fi, int op) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_flock(d->next, newpath, fi, op); free(newpath); } return err; } static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx) { struct subdir *d = subdir_get(); char *newpath; int err = subdir_addpath(d, path, &newpath); if (!err) { err = fuse_fs_bmap(d->next, newpath, blocksize, idx); free(newpath); } return err; } static off_t subdir_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi) { struct subdir *ic = subdir_get(); char *newpath; int res = subdir_addpath(ic, path, &newpath); if (!res) { res = fuse_fs_lseek(ic->next, newpath, off, whence, fi); free(newpath); } return res; } #ifdef HAVE_STATX static int subdir_statx(const char *path, int flags, int mask, struct statx *stxbuf, struct fuse_file_info *fi) { struct subdir *ic = subdir_get(); char *newpath; int res = subdir_addpath(ic, path, &newpath); if (!res) { res = fuse_fs_statx(ic->next, newpath, flags, mask, stxbuf, fi); free(newpath); } return res; } #endif static void *subdir_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { struct subdir *d = subdir_get(); fuse_fs_init(d->next, conn, cfg); /* Don't touch cfg->nullpath_ok, we can work with either */ return d; } static void subdir_destroy(void *data) { struct subdir *d = data; fuse_fs_destroy(d->next); free(d->base); free(d); } static const struct fuse_operations subdir_oper = { .destroy = subdir_destroy, .init = subdir_init, .getattr = subdir_getattr, .access = subdir_access, .readlink = subdir_readlink, .opendir = subdir_opendir, .readdir = subdir_readdir, .releasedir = subdir_releasedir, .mknod = subdir_mknod, .mkdir = subdir_mkdir, .symlink = subdir_symlink, .unlink = subdir_unlink, .rmdir = subdir_rmdir, .rename = subdir_rename, .link = subdir_link, .chmod = subdir_chmod, .chown = subdir_chown, .truncate = subdir_truncate, .utimens = subdir_utimens, .create = subdir_create, .open = subdir_open, .read_buf = subdir_read_buf, .write_buf = subdir_write_buf, .statfs = subdir_statfs, .flush = subdir_flush, .release = subdir_release, .fsync = subdir_fsync, .fsyncdir = subdir_fsyncdir, .setxattr = subdir_setxattr, .getxattr = subdir_getxattr, .listxattr = subdir_listxattr, .removexattr = subdir_removexattr, .lock = subdir_lock, .flock = subdir_flock, .bmap = subdir_bmap, .lseek = subdir_lseek, #ifdef HAVE_STATX .statx = subdir_statx, #endif }; static const struct fuse_opt subdir_opts[] = { FUSE_OPT_KEY("-h", 0), FUSE_OPT_KEY("--help", 0), { "subdir=%s", offsetof(struct subdir, base), 0 }, { "rellinks", offsetof(struct subdir, rellinks), 1 }, { "norellinks", offsetof(struct subdir, rellinks), 0 }, FUSE_OPT_END }; static void subdir_help(void) { printf( " -o subdir=DIR prepend this directory to all paths (mandatory)\n" " -o [no]rellinks transform absolute symlinks to relative\n"); } static int subdir_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { (void) data; (void) arg; (void) outargs; if (!key) { subdir_help(); return -1; } return 1; } static struct fuse_fs *subdir_new(struct fuse_args *args, struct fuse_fs *next[]) { struct fuse_fs *fs; struct subdir *d; d = calloc(1, sizeof(struct subdir)); if (d == NULL) { fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n"); return NULL; } if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1) goto out_free; if (!next[0] || next[1]) { fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n"); goto out_free; } if (!d->base) { fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n"); goto out_free; } if (d->base[0] && d->base[strlen(d->base)-1] != '/') { char *tmp = realloc(d->base, strlen(d->base) + 2); if (!tmp) { fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n"); goto out_free; } d->base = tmp; strcat(d->base, "/"); } d->baselen = strlen(d->base); d->next = next[0]; fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d); if (!fs) goto out_free; return fs; out_free: free(d->base); free(d); return NULL; } FUSE_REGISTER_MODULE(subdir, subdir_new); fuse-3.18.2/lib/mount.c0000644000175000017500000004250215156613252013602 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Architecture specific file system mounting (Linux). This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ /* For environ */ #define _GNU_SOURCE #include "fuse_config.h" #include "fuse_i.h" #include "fuse_misc.h" #include "fuse_opt.h" #include "mount_util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "fuse_mount_compat.h" #ifdef __NetBSD__ #include #define MS_RDONLY MNT_RDONLY #define MS_NOSUID MNT_NOSUID #define MS_NODEV MNT_NODEV #define MS_NOEXEC MNT_NOEXEC #define MS_SYNCHRONOUS MNT_SYNCHRONOUS #define MS_NOATIME MNT_NOATIME #define MS_NOSYMFOLLOW MNT_NOSYMFOLLOW #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0) #endif #define FUSERMOUNT_PROG "fusermount3" #define FUSE_COMMFD_ENV "_FUSE_COMMFD" #define FUSE_COMMFD2_ENV "_FUSE_COMMFD2" #define FUSE_KERN_DEVICE_ENV "FUSE_KERN_DEVICE" #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 #endif enum { KEY_KERN_FLAG, KEY_KERN_OPT, KEY_FUSERMOUNT_OPT, KEY_SUBTYPE_OPT, KEY_MTAB_OPT, KEY_ALLOW_OTHER, KEY_RO, }; struct mount_opts { int allow_other; int flags; int auto_unmount; int blkdev; char *fsname; char *subtype; char *subtype_opt; char *mtab_opts; char *fusermount_opts; char *kernel_opts; unsigned max_read; }; #define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 } static const struct fuse_opt fuse_mount_opts[] = { FUSE_MOUNT_OPT("allow_other", allow_other), FUSE_MOUNT_OPT("blkdev", blkdev), FUSE_MOUNT_OPT("auto_unmount", auto_unmount), FUSE_MOUNT_OPT("fsname=%s", fsname), FUSE_MOUNT_OPT("max_read=%u", max_read), FUSE_MOUNT_OPT("subtype=%s", subtype), FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT), FUSE_OPT_KEY("blksize=", KEY_KERN_OPT), FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT), FUSE_OPT_KEY("context=", KEY_KERN_OPT), FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT), FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT), 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), FUSE_OPT_KEY("suid", KEY_KERN_FLAG), FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG), FUSE_OPT_KEY("dev", KEY_KERN_FLAG), FUSE_OPT_KEY("nodev", KEY_KERN_FLAG), FUSE_OPT_KEY("exec", KEY_KERN_FLAG), FUSE_OPT_KEY("noexec", KEY_KERN_FLAG), FUSE_OPT_KEY("async", KEY_KERN_FLAG), FUSE_OPT_KEY("sync", KEY_KERN_FLAG), FUSE_OPT_KEY("dirsync", 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_KEY("symfollow", KEY_KERN_FLAG), FUSE_OPT_KEY("nosymfollow", KEY_KERN_FLAG), FUSE_OPT_END }; /* * Running fusermount by calling 'posix_spawn' * * @param out_pid might be NULL */ static int fusermount_posix_spawn(posix_spawn_file_actions_t *action, char const * const argv[], pid_t *out_pid) { const char *full_path = FUSERMOUNT_DIR "/" FUSERMOUNT_PROG; pid_t pid; /* See man 7 environ for the global environ pointer */ /* first try the install path */ int status = posix_spawn(&pid, full_path, action, NULL, (char * const *) argv, environ); if (status != 0) { /* if that fails, try a system install */ status = posix_spawnp(&pid, FUSERMOUNT_PROG, action, NULL, (char * const *) argv, environ); } if (status != 0) { fuse_log(FUSE_LOG_ERR, "Failed to call '%s': %s\n", FUSERMOUNT_PROG, strerror(status)); return -status; } if (out_pid) *out_pid = pid; else waitpid(pid, NULL, 0); /* FIXME: check exit code and return error if any */ return 0; } void fuse_mount_version(void) { char const *const argv[] = {FUSERMOUNT_PROG, "--version", NULL}; int status = fusermount_posix_spawn(NULL, argv, NULL); if(status != 0) fuse_log(FUSE_LOG_ERR, "Running '%s --version' failed", FUSERMOUNT_PROG); } struct mount_flags { const char *opt; unsigned long flag; int on; }; static const struct mount_flags mount_flags[] = { {"rw", MS_RDONLY, 0}, {"ro", MS_RDONLY, 1}, {"suid", MS_NOSUID, 0}, {"nosuid", MS_NOSUID, 1}, {"dev", MS_NODEV, 0}, {"nodev", MS_NODEV, 1}, {"exec", MS_NOEXEC, 0}, {"noexec", MS_NOEXEC, 1}, {"async", MS_SYNCHRONOUS, 0}, {"sync", MS_SYNCHRONOUS, 1}, {"noatime", MS_NOATIME, 1}, {"nodiratime", MS_NODIRATIME, 1}, {"norelatime", MS_RELATIME, 0}, {"nostrictatime", MS_STRICTATIME, 0}, {"symfollow", MS_NOSYMFOLLOW, 0}, {"nosymfollow", MS_NOSYMFOLLOW, 1}, #ifndef __NetBSD__ {"dirsync", MS_DIRSYNC, 1}, #endif {NULL, 0, 0} }; unsigned get_max_read(struct mount_opts *o) { return o->max_read; } static void set_mount_flag(const char *s, int *flags) { int i; for (i = 0; mount_flags[i].opt != NULL; i++) { const char *opt = mount_flags[i].opt; if (strcmp(opt, s) == 0) { if (mount_flags[i].on) *flags |= mount_flags[i].flag; else *flags &= ~mount_flags[i].flag; return; } } fuse_log(FUSE_LOG_ERR, "fuse: internal error, can't find mount flag\n"); abort(); } static int fuse_mount_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { (void) outargs; struct mount_opts *mo = data; switch (key) { case KEY_RO: arg = "ro"; /* fall through */ case KEY_KERN_FLAG: set_mount_flag(arg, &mo->flags); return 0; case KEY_KERN_OPT: return fuse_opt_add_opt(&mo->kernel_opts, arg); case KEY_FUSERMOUNT_OPT: return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg); case KEY_SUBTYPE_OPT: return fuse_opt_add_opt(&mo->subtype_opt, arg); case KEY_MTAB_OPT: return fuse_opt_add_opt(&mo->mtab_opts, arg); /* Third party options like 'x-gvfs-notrash' */ case FUSE_OPT_KEY_OPT: return (strncmp("x-", arg, 2) == 0) ? fuse_opt_add_opt(&mo->mtab_opts, arg) : 1; } /* Pass through unknown options */ return 1; } /* return value: * >= 0 => fd * -1 => error */ static int receive_fd(int fd) { struct msghdr msg; struct iovec iov; char buf[1]; int rv; size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)]; struct cmsghdr *cmsg; iov.iov_base = buf; iov.iov_len = 1; memset(&msg, 0, sizeof(msg)); msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; /* old BSD implementations should use msg_accrights instead of * msg_control; the interface is different. */ msg.msg_control = ccmsg; msg.msg_controllen = sizeof(ccmsg); while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR); if (rv == -1) { fuse_log(FUSE_LOG_ERR, "recvmsg failed: %s", strerror(errno)); return -1; } if(!rv) { /* EOF */ return -1; } cmsg = CMSG_FIRSTHDR(&msg); if (cmsg->cmsg_type != SCM_RIGHTS) { fuse_log(FUSE_LOG_ERR, "got control message of unknown type %d\n", cmsg->cmsg_type); return -1; } return *(int*)CMSG_DATA(cmsg); } void fuse_kern_unmount(const char *mountpoint, int fd) { int res; if (fd != -1) { struct pollfd pfd; pfd.fd = fd; pfd.events = 0; res = poll(&pfd, 1, 0); /* Need to close file descriptor, otherwise synchronous umount would recurse into filesystem, and deadlock. Caller expects fuse_kern_unmount to close the fd, so close it anyway. */ close(fd); /* If file poll returns POLLERR on the device file descriptor, then the filesystem is already unmounted or the connection was severed via /sys/fs/fuse/connections/NNN/abort */ if (res == 1 && (pfd.revents & POLLERR)) return; } if (geteuid() == 0) { fuse_mnt_umount("fuse", mountpoint, mountpoint, 1); return; } res = umount2(mountpoint, 2); if (res == 0) return; char const * const argv[] = { FUSERMOUNT_PROG, "--unmount", "--quiet", "--lazy", "--", mountpoint, NULL }; int status = fusermount_posix_spawn(NULL, argv, NULL); if(status != 0) { fuse_log(FUSE_LOG_ERR, "Spawning %s to unmount failed: %s", FUSERMOUNT_PROG, strerror(-status)); return; } } static int setup_auto_unmount(const char *mountpoint, int quiet) { int fds[2]; pid_t 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) { fuse_log(FUSE_LOG_ERR, "Setting up auto-unmount socketpair() failed: %s\n", strerror(errno)); return -1; } char arg_fd_entry[30]; snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]); setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1); /* * This helps to identify the FD hold by parent process. * In auto-unmount case, parent process can close this FD explicitly to do unmount. * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV). * One potential use case is to satisfy FD-Leak checks. */ snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]); setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1); char const *const argv[] = { FUSERMOUNT_PROG, "--auto-unmount", "--", mountpoint, NULL, }; // TODO: add error handling for all manipulations of action. posix_spawn_file_actions_t action; posix_spawn_file_actions_init(&action); if (quiet) { posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0); posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0); } posix_spawn_file_actions_addclose(&action, fds[1]); /* * auto-umount runs in the background - it is not waiting for the * process */ int status = fusermount_posix_spawn(&action, argv, &pid); posix_spawn_file_actions_destroy(&action); if(status != 0) { close(fds[0]); close(fds[1]); fuse_log(FUSE_LOG_ERR, "fuse: Setting up auto-unmount failed (spawn): %s", strerror(-status)); return -1; } // passed to child now, so can close here. close(fds[0]); // Now fusermount3 will only exit when fds[1] closes automatically when our // process exits. return 0; // Note: fds[1] is leakend and doesn't get FD_CLOEXEC } static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo, const char *opts, int quiet) { int fds[2]; pid_t 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) { fuse_log(FUSE_LOG_ERR, "Running %s: socketpair() failed: %s\n", FUSERMOUNT_PROG, strerror(errno)); return -1; } char arg_fd_entry[30]; snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]); setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1); /* * This helps to identify the FD hold by parent process. * In auto-unmount case, parent process can close this FD explicitly to do unmount. * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV). * One potential use case is to satisfy FD-Leak checks. */ snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]); setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1); char const *const argv[] = { FUSERMOUNT_PROG, "-o", opts ? opts : "", "--", mountpoint, NULL, }; posix_spawn_file_actions_t action; posix_spawn_file_actions_init(&action); if (quiet) { posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0); posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0); } posix_spawn_file_actions_addclose(&action, fds[1]); int status = fusermount_posix_spawn(&action, argv, &pid); posix_spawn_file_actions_destroy(&action); if(status != 0) { close(fds[0]); close(fds[1]); fuse_log(FUSE_LOG_ERR, "posix_spawn(p)() for %s failed: %s", FUSERMOUNT_PROG, strerror(-status)); return -1; } // passed to child now, so can close here. close(fds[0]); int fd = receive_fd(fds[1]); if (!mo->auto_unmount) { /* with auto_unmount option fusermount3 will not exit until this socket is closed */ close(fds[1]); waitpid(pid, NULL, 0); /* bury zombie */ } if (fd >= 0) fcntl(fd, F_SETFD, FD_CLOEXEC); return fd; } #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, const char *mnt_opts) { char tmp[128]; const char *devname = getenv(FUSE_KERN_DEVICE_ENV) ?: "/dev/fuse"; char *source = NULL; char *type = NULL; struct stat stbuf; int fd; int res; if (!mnt) { fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n"); return -1; } res = stat(mnt, &stbuf); if (res == -1) { fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n", mnt, strerror(errno)); return -1; } fd = open(devname, O_RDWR | O_CLOEXEC); if (fd == -1) { if (errno == ENODEV || errno == ENOENT) fuse_log(FUSE_LOG_ERR, "fuse: device %s not found. Kernel module not loaded?\n", devname); else fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname, strerror(errno)); return -1; } if (!O_CLOEXEC) fcntl(fd, F_SETFD, FD_CLOEXEC); snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u", fd, stbuf.st_mode & S_IFMT, getuid(), getgid()); res = fuse_opt_add_opt(&mo->kernel_opts, tmp); if (res == -1) goto out_close; source = malloc((mo->fsname ? strlen(mo->fsname) : 0) + (mo->subtype ? strlen(mo->subtype) : 0) + strlen(devname) + 32); type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32); if (!type || !source) { fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n"); goto out_close; } strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); if (mo->subtype) { strcat(type, "."); strcat(type, mo->subtype); } strcpy(source, mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname)); res = mount(source, mnt, type, mo->flags, mo->kernel_opts); if (res == -1 && errno == ENODEV && mo->subtype) { /* Probably missing subtype support */ strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); if (mo->fsname) { if (!mo->blkdev) sprintf(source, "%s#%s", mo->subtype, mo->fsname); } else { strcpy(source, type); } res = mount(source, mnt, type, mo->flags, mo->kernel_opts); } if (res == -1) { /* * Maybe kernel doesn't support unprivileged mounts, in this * case try falling back to fusermount3 */ if (errno == EPERM) { res = -2; } else { int errno_save = errno; if (mo->blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) fuse_log(FUSE_LOG_ERR, "fuse: 'fuseblk' support missing\n"); else fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n", strerror(errno_save)); } goto out_close; } #ifndef IGNORE_MTAB if (geteuid() == 0) { char *newmnt = fuse_mnt_resolve_path("fuse", mnt); res = -1; if (!newmnt) goto out_umount; res = fuse_mnt_add_mount("fuse", source, newmnt, type, mnt_opts); free(newmnt); if (res == -1) goto out_umount; } #endif /* IGNORE_MTAB */ free(type); free(source); return fd; out_umount: umount2(mnt, 2); /* lazy umount */ out_close: free(type); free(source); close(fd); return res; } static int get_mnt_flag_opts(char **mnt_optsp, int flags) { int i; if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1) return -1; for (i = 0; mount_flags[i].opt != NULL; i++) { if (mount_flags[i].on && (flags & mount_flags[i].flag) && fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1) return -1; } return 0; } struct mount_opts *parse_mount_opts(struct fuse_args *args) { struct mount_opts *mo; mo = (struct mount_opts*) malloc(sizeof(struct mount_opts)); if (mo == NULL) return NULL; memset(mo, 0, sizeof(struct mount_opts)); mo->flags = MS_NOSUID | MS_NODEV; if (args && fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) goto err_out; return mo; err_out: destroy_mount_opts(mo); return NULL; } void destroy_mount_opts(struct mount_opts *mo) { free(mo->fsname); free(mo->subtype); free(mo->fusermount_opts); free(mo->subtype_opt); free(mo->kernel_opts); free(mo->mtab_opts); free(mo); } int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo) { int res = -1; char *mnt_opts = NULL; res = -1; if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1) goto out; if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1) goto out; if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1) goto out; res = fuse_mount_sys(mountpoint, mo, mnt_opts); 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; if (mo->subtype) { char *tmp_opts = NULL; res = -1; if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 || fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) { free(tmp_opts); goto out; } res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1); free(tmp_opts); if (res == -1) res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0); } else { res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0); } } out: free(mnt_opts); return res; } fuse-3.18.2/lib/mount_bsd.c0000644000175000017500000001374315156613252014437 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2005-2008 Csaba Henk Architecture specific file system mounting (FreeBSD). This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ #include "fuse_config.h" #include "fuse_i.h" #include "fuse_misc.h" #include "fuse_opt.h" #include "util.h" #include #include "fuse_mount_compat.h" #include #include #include #include #include #include #include #include #define FUSERMOUNT_PROG "mount_fusefs" #define FUSE_DEV_TRUNK "/dev/fuse" enum { KEY_RO, KEY_KERN }; struct mount_opts { int allow_other; char *kernel_opts; unsigned max_read; }; #define FUSE_DUAL_OPT_KEY(templ, key) \ FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key) static const struct fuse_opt fuse_mount_opts[] = { { "allow_other", offsetof(struct mount_opts, allow_other), 1 }, { "max_read=%u", offsetof(struct mount_opts, max_read), 1 }, FUSE_OPT_KEY("-r", KEY_RO), /* standard FreeBSD mount options */ FUSE_DUAL_OPT_KEY("dev", KEY_KERN), FUSE_DUAL_OPT_KEY("async", KEY_KERN), FUSE_DUAL_OPT_KEY("atime", KEY_KERN), FUSE_DUAL_OPT_KEY("dev", KEY_KERN), FUSE_DUAL_OPT_KEY("exec", KEY_KERN), FUSE_DUAL_OPT_KEY("suid", KEY_KERN), FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN), FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN), FUSE_DUAL_OPT_KEY("sync", KEY_KERN), FUSE_DUAL_OPT_KEY("union", KEY_KERN), FUSE_DUAL_OPT_KEY("userquota", KEY_KERN), FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN), FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN), FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN), FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN), FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN), FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN), FUSE_DUAL_OPT_KEY("acls", KEY_KERN), FUSE_DUAL_OPT_KEY("force", KEY_KERN), FUSE_DUAL_OPT_KEY("update", KEY_KERN), FUSE_DUAL_OPT_KEY("ro", KEY_KERN), FUSE_DUAL_OPT_KEY("rw", KEY_KERN), FUSE_DUAL_OPT_KEY("auto", KEY_KERN), FUSE_DUAL_OPT_KEY("automounted", KEY_KERN), /* options supported under both Linux and FBSD */ FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN), FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN), FUSE_OPT_KEY("max_read=", KEY_KERN), FUSE_OPT_KEY("subtype=", KEY_KERN), /* FBSD FUSE specific mount options */ FUSE_DUAL_OPT_KEY("private", KEY_KERN), FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN), FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN), #if __FreeBSD_version >= 1200519 FUSE_DUAL_OPT_KEY("intr", KEY_KERN), #endif /* stock FBSD mountopt parsing routine lets anything be negated... */ /* * Linux specific mount options, but let just the mount util * handle them */ FUSE_OPT_KEY("fsname=", KEY_KERN), FUSE_OPT_END }; void fuse_mount_version(void) { system(FUSERMOUNT_PROG " --version"); } unsigned get_max_read(struct mount_opts *o) { return o->max_read; } static int fuse_mount_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { (void) outargs; struct mount_opts *mo = data; switch (key) { case KEY_RO: arg = "ro"; /* fall through */ case KEY_KERN: return fuse_opt_add_opt(&mo->kernel_opts, arg); } /* Pass through unknown options */ return 1; } void fuse_kern_unmount(const char *mountpoint, int fd) { if (close(fd) < 0) fuse_log(FUSE_LOG_ERR, "closing FD %d failed: %s", fd, strerror(errno)); if (unmount(mountpoint, MNT_FORCE) < 0) fuse_log(FUSE_LOG_ERR, "unmounting %s failed: %s", mountpoint, strerror(errno)); } static int fuse_mount_core(const char *mountpoint, const char *opts) { const char *mountprog = FUSERMOUNT_PROG; long fd; char *fdnam, *dev; pid_t pid, cpid; int status; int err; fdnam = getenv("FUSE_DEV_FD"); if (fdnam) { err = libfuse_strtol(fdnam, &fd); if (err || fd < 0) { fuse_log(FUSE_LOG_ERR, "invalid value given in FUSE_DEV_FD\n"); return -1; } goto mount; } dev = getenv("FUSE_DEV_NAME"); if (! dev) dev = (char *)FUSE_DEV_TRUNK; if ((fd = open(dev, O_RDWR)) < 0) { perror("fuse: failed to open fuse device"); return -1; } mount: if (getenv("FUSE_NO_MOUNT") || ! mountpoint) goto out; pid = fork(); cpid = pid; if (pid == -1) { perror("fuse: fork() failed"); close(fd); return -1; } if (pid == 0) { pid = fork(); if (pid == -1) { perror("fuse: fork() failed"); close(fd); _exit(EXIT_FAILURE); } if (pid == 0) { const char *argv[32]; int a = 0; int ret = -1; if (! fdnam) { ret = asprintf(&fdnam, "%ld", fd); if(ret == -1) { perror("fuse: failed to assemble mount arguments"); close(fd); _exit(EXIT_FAILURE); } } argv[a++] = mountprog; if (opts) { argv[a++] = "-o"; argv[a++] = opts; } argv[a++] = fdnam; argv[a++] = mountpoint; argv[a++] = NULL; execvp(mountprog, (char **) argv); perror("fuse: failed to exec mount program"); free(fdnam); _exit(EXIT_FAILURE); } waitpid(pid, &status, 0); if (!WIFEXITED(status)) _exit(EXIT_FAILURE); _exit(WEXITSTATUS(status)); } if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) { perror("fuse: failed to mount file system"); if (close(fd) < 0) perror("fuse: closing FD"); return -1; } out: return fd; } struct mount_opts *parse_mount_opts(struct fuse_args *args) { struct mount_opts *mo; mo = (struct mount_opts*) malloc(sizeof(struct mount_opts)); if (mo == NULL) return NULL; memset(mo, 0, sizeof(struct mount_opts)); if (args && fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) goto err_out; return mo; err_out: destroy_mount_opts(mo); return NULL; } void destroy_mount_opts(struct mount_opts *mo) { free(mo->kernel_opts); free(mo); } int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo) { /* mount util should not try to spawn the daemon */ setenv("MOUNT_FUSEFS_SAFE", "1", 1); /* to notify the mount util it's called from lib */ setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1); return fuse_mount_core(mountpoint, mo->kernel_opts); } fuse-3.18.2/lib/mount_util.c0000644000175000017500000001760215156613252014642 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi Architecture-independent mounting code. This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ #include "fuse_config.h" #include "mount_util.h" #include #include #include #include #include #include #include #include #include #include #if !defined( __NetBSD__) && !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__) #include #else #define IGNORE_MTAB #endif #include #include #include "fuse_mount_compat.h" #include #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #define umount2(mnt, flags) unmount(mnt, ((flags) == 2) ? MNT_FORCE : 0) #endif #ifdef IGNORE_MTAB #define mtab_needs_update(mnt) 0 #else static int mtab_needs_update(const char *mnt) { int res; struct stat stbuf; /* If mtab is within new mount, don't touch it */ if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 && _PATH_MOUNTED[strlen(mnt)] == '/') return 0; /* * Skip mtab update if /etc/mtab: * * - doesn't exist, * - is on a read-only filesystem. */ res = lstat(_PATH_MOUNTED, &stbuf); if (res == -1) { if (errno == ENOENT) return 0; } else { uid_t ruid; int err; ruid = getuid(); if (ruid != 0) setreuid(0, -1); res = access(_PATH_MOUNTED, W_OK); err = (res == -1) ? errno : 0; if (ruid != 0) setreuid(ruid, -1); if (err == EROFS) return 0; res = access("/run/mount/utab", F_OK); if (res == -1) return 0; } return 1; } #endif /* IGNORE_MTAB */ static int add_mount(const char *progname, const char *fsname, const char *mnt, const char *type, const char *opts) { int res; int status; sigset_t blockmask; sigset_t oldmask; sigemptyset(&blockmask); sigaddset(&blockmask, SIGCHLD); res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask); if (res == -1) { fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno)); return -1; } res = fork(); if (res == -1) { fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); goto out_restore; } if (res == 0) { char *env = NULL; sigprocmask(SIG_SETMASK, &oldmask, NULL); if(setuid(geteuid()) == -1) { fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno)); res = -1; goto out_restore; } execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i", "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env); fprintf(stderr, "%s: failed to execute /bin/mount: %s\n", progname, strerror(errno)); exit(1); } res = waitpid(res, &status, 0); if (res == -1) fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); if (status != 0) res = -1; out_restore: sigprocmask(SIG_SETMASK, &oldmask, NULL); return res; } int fuse_mnt_add_mount(const char *progname, const char *fsname, const char *mnt, const char *type, const char *opts) { if (!mtab_needs_update(mnt)) return 0; return add_mount(progname, fsname, mnt, type, opts); } static int exec_umount(const char *progname, const char *rel_mnt, int lazy) { int res; int status; sigset_t blockmask; sigset_t oldmask; sigemptyset(&blockmask); sigaddset(&blockmask, SIGCHLD); res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask); if (res == -1) { fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno)); return -1; } res = fork(); if (res == -1) { fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); goto out_restore; } if (res == 0) { char *env = NULL; sigprocmask(SIG_SETMASK, &oldmask, NULL); if(setuid(geteuid()) == -1) { fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno)); res = -1; goto out_restore; } if (lazy) { execle("/bin/umount", "/bin/umount", "-i", rel_mnt, "-l", NULL, &env); } else { execle("/bin/umount", "/bin/umount", "-i", rel_mnt, NULL, &env); } fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", progname, strerror(errno)); exit(1); } res = waitpid(res, &status, 0); if (res == -1) fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); if (status != 0) { res = -1; } out_restore: sigprocmask(SIG_SETMASK, &oldmask, NULL); return res; } int fuse_mnt_umount(const char *progname, const char *abs_mnt, const char *rel_mnt, int lazy) { int res; if (!mtab_needs_update(abs_mnt)) { res = umount2(rel_mnt, lazy ? 2 : 0); if (res == -1) fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, abs_mnt, strerror(errno)); return res; } return exec_umount(progname, rel_mnt, lazy); } static int remove_mount(const char *progname, const char *mnt) { int res; int status; sigset_t blockmask; sigset_t oldmask; sigemptyset(&blockmask); sigaddset(&blockmask, SIGCHLD); res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask); if (res == -1) { fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno)); return -1; } res = fork(); if (res == -1) { fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); goto out_restore; } if (res == 0) { char *env = NULL; sigprocmask(SIG_SETMASK, &oldmask, NULL); if(setuid(geteuid()) == -1) { fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno)); res = -1; goto out_restore; } execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i", "--fake", mnt, NULL, &env); fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", progname, strerror(errno)); exit(1); } res = waitpid(res, &status, 0); if (res == -1) fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); if (status != 0) res = -1; out_restore: sigprocmask(SIG_SETMASK, &oldmask, NULL); return res; } int fuse_mnt_remove_mount(const char *progname, const char *mnt) { if (!mtab_needs_update(mnt)) return 0; return remove_mount(progname, mnt); } char *fuse_mnt_resolve_path(const char *progname, const char *orig) { char buf[PATH_MAX]; char *copy; char *dst; char *end; char *lastcomp; const char *toresolv; if (!orig[0]) { fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig); return NULL; } copy = strdup(orig); if (copy == NULL) { fprintf(stderr, "%s: failed to allocate memory\n", progname); return NULL; } toresolv = copy; lastcomp = NULL; for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --); if (end[0] != '/') { char *tmp; end[1] = '\0'; tmp = strrchr(copy, '/'); if (tmp == NULL) { lastcomp = copy; toresolv = "."; } else { lastcomp = tmp + 1; if (tmp == copy) toresolv = "/"; } if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) { lastcomp = NULL; toresolv = copy; } else if (tmp) tmp[0] = '\0'; } if (realpath(toresolv, buf) == NULL) { fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig, strerror(errno)); free(copy); return NULL; } if (lastcomp == NULL) dst = strdup(buf); else { dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1); if (dst) { unsigned buflen = strlen(buf); if (buflen && buf[buflen-1] == '/') sprintf(dst, "%s%s", buf, lastcomp); else sprintf(dst, "%s/%s", buf, lastcomp); } } free(copy); if (dst == NULL) fprintf(stderr, "%s: failed to allocate memory\n", progname); return dst; } int fuse_mnt_check_fuseblk(void) { char buf[256]; FILE *f = fopen("/proc/filesystems", "r"); if (!f) return 1; while (fgets(buf, sizeof(buf), f)) if (strstr(buf, "fuseblk\n")) { fclose(f); return 1; } fclose(f); return 0; } int fuse_mnt_parse_fuse_fd(const char *mountpoint) { int fd = -1; int len = 0; if (mountpoint == NULL) { fprintf(stderr, "Invalid null-ptr mount-point!\n"); return -1; } if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 && len == strlen(mountpoint)) { return fd; } return -1; } fuse-3.18.2/lib/mount_util.h0000644000175000017500000000123715156613252014644 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file LGPL2.txt. */ #include int fuse_mnt_add_mount(const char *progname, const char *fsname, const char *mnt, const char *type, const char *opts); int fuse_mnt_remove_mount(const char *progname, const char *mnt); int fuse_mnt_umount(const char *progname, const char *abs_mnt, const char *rel_mnt, int lazy); char *fuse_mnt_resolve_path(const char *progname, const char *orig); int fuse_mnt_check_fuseblk(void); int fuse_mnt_parse_fuse_fd(const char *mountpoint); fuse-3.18.2/lib/usdt.h0000644000175000017500000005761415156613252013436 0ustar berndbernd/* * Copied from https://github.com/libbpf/usdt/ */ // SPDX-License-Identifier: BSD-2-Clause /* * This single-header library defines a collection of variadic macros for * defining and triggering USDTs (User Statically-Defined Tracepoints): * * - For USDTs without associated semaphore: * USDT(group, name, args...) * * - For USDTs with implicit (transparent to the user) semaphore: * USDT_WITH_SEMA(group, name, args...) * USDT_IS_ACTIVE(group, name) * * - For USDTs with explicit (user-defined and provided) semaphore: * USDT_WITH_EXPLICIT_SEMA(sema, group, name, args...) * USDT_SEMA_IS_ACTIVE(sema) * * all of which emit a NOP instruction into the instruction stream, and so * have *zero* overhead for the surrounding code. USDTs are identified by * a combination of `group` and `name` identifiers, which is used by external * tracing tooling (tracers) for identifying exact USDTs of interest. * * USDTs can have an associated (2-byte) activity counter (USDT semaphore), * automatically maintained by Linux kernel whenever any correctly written * BPF-based tracer is attached to the USDT. This USDT semaphore can be used * to check whether there is a need to do any extra data collection and * processing for a given USDT (if necessary), and otherwise avoid extra work * for a common case of USDT not being traced ("active"). * * See documentation for USDT_WITH_SEMA()/USDT_IS_ACTIVE() or * USDT_WITH_EXPLICIT_SEMA()/USDT_SEMA_IS_ACTIVE() APIs below for details on * working with USDTs with implicitly or explicitly associated * USDT semaphores, respectively. * * There is also some additional data recorded into an auxiliary note * section. The data in the note section describes the operands, in terms of * size and location, used by tracing tooling to know where to find USDT * arguments. Each location is encoded as an assembler operand string. * Tracing tools (bpftrace and BPF-based tracers, systemtap, etc) insert * breakpoints on top of the nop, and decode the location operand-strings, * like an assembler, to find the values being passed. * * The operand strings are selected by the compiler for each operand. * They are constrained by inline-assembler codes.The default is: * * #define USDT_ARG_CONSTRAINT nor * * This is a good default if the operands tend to be integral and * moderate in number (smaller than number of registers). In other * cases, the compiler may report "'asm' requires impossible reload" or * similar. In this case, consider simplifying the macro call (fewer * and simpler operands), reduce optimization, or override the default * constraints string via: * * #define USDT_ARG_CONSTRAINT g * #include * * For some historical description of USDT v3 format (the one used by this * library and generally recognized and assumed by BPF-based tracing tools) * see [0]. The more formal specification can be found at [1]. Additional * argument constraints information can be found at [2]. * * Original SystemTap's sys/sdt.h implementation ([3]) was used as a base for * this USDT library implementation. Current implementation differs *a lot* in * terms of exposed user API and general usability, which was the main goal * and focus of the reimplementation work. Nevertheless, underlying recorded * USDT definitions are fully binary compatible and any USDT-based tooling * should work equally well with USDTs defined by either SystemTap's or this * library's USDT implementation. * * [0] https://ecos.sourceware.org/ml/systemtap/2010-q3/msg00145.html * [1] https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation * [2] https://gcc.gnu.org/onlinedocs/gcc/Constraints.html * [3] https://sourceware.org/git/?p=systemtap.git;a=blob;f=includes/sys/sdt.h */ #ifndef __USDT_H #define __USDT_H /* * Changelog: * * 0.1.0 * ----- * - Initial release */ #define USDT_MAJOR_VERSION 0 #define USDT_MINOR_VERSION 1 #define USDT_PATCH_VERSION 0 /* C++20 and C23 added __VA_OPT__ as a standard replacement for non-standard `##__VA_ARGS__` extension */ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L) || (defined(__cplusplus) && __cplusplus > 201703L) #define __usdt_va_opt 1 #define __usdt_va_args(...) __VA_OPT__(,) __VA_ARGS__ #else #define __usdt_va_args(...) , ##__VA_ARGS__ #endif /* * Trigger USDT with `group`:`name` identifier and pass through `args` as its * arguments. Zero arguments are acceptable as well. No USDT semaphore is * associated with this USDT. * * Such "semaphoreless" USDTs are commonly used when there is no extra data * collection or processing needed to collect and prepare USDT arguments and * they are just available in the surrounding code. USDT() macro will just * record their locations in CPU registers or in memory for tracing tooling to * be able to access them, if necessary. */ #ifdef __usdt_va_opt #define USDT(group, name, ...) \ __usdt_probe(group, name, __usdt_sema_none, 0 __VA_OPT__(,) __VA_ARGS__) #else #define USDT(group, name, ...) \ __usdt_probe(group, name, __usdt_sema_none, 0, ##__VA_ARGS__) #endif /* * Trigger USDT with `group`:`name` identifier and pass through `args` as its * arguments. Zero arguments are acceptable as well. USDT also get an * implicitly-defined associated USDT semaphore, which will be "activated" by * tracing tooling and can be used to check whether USDT is being actively * observed. * * USDTs with semaphore are commonly used when there is a need to perform * additional data collection and processing to prepare USDT arguments, which * otherwise might not be necessary for the rest of application logic. In such * case, USDT semaphore can be used to avoid unnecessary extra work. If USDT * is not traced (which is presumed to be a common situation), the associated * USDT semaphore is "inactive", and so there is no need to waste resources to * prepare USDT arguments. Use USDT_IS_ACTIVE(group, name) to check whether * USDT is "active". * * N.B. There is an inherent (albeit short) gap between checking whether USDT * is active and triggering corresponding USDT, in which external tracer can * be attached to an USDT and activate USDT semaphore after the activity check. * If such a race occurs, tracers might miss one USDT execution. Tracers are * expected to accommodate such possibility and this is expected to not be * a problem for applications and tracers. * * N.B. Implicit USDT semaphore defined by USDT_WITH_SEMA() is contained * within a single executable or shared library and is not shared outside * them. I.e., if you use USDT_WITH_SEMA() with the same USDT group and name * identifier across executable and shared library, it will work and won't * conflict, per se, but will define independent USDT semaphores, one for each * shared library/executable in which USDT_WITH_SEMA(group, name) is used. * That is, if you attach to this USDT in one shared library (or executable), * then only USDT semaphore within that shared library (or executable) will be * updated by the kernel, while other libraries (or executable) will not see * activated USDT semaphore. In short, it's best to use unique USDT group:name * identifiers across different shared libraries (and, equivalently, between * executable and shared library). This is advanced consideration and is * rarely (if ever) seen in practice, but just to avoid surprises this is * called out here. (Static libraries become a part of final executable, once * linked by linker, so the above considerations don't apply to them.) */ #ifdef __usdt_va_opt #define USDT_WITH_SEMA(group, name, ...) \ __usdt_probe(group, name, \ __usdt_sema_implicit, __usdt_sema_name(group, name) \ __VA_OPT__(,) __VA_ARGS__) #else #define USDT_WITH_SEMA(group, name, ...) \ __usdt_probe(group, name, \ __usdt_sema_implicit, __usdt_sema_name(group, name), \ ##__VA_ARGS__) #endif struct usdt_sema { volatile unsigned short active; }; /* * Check if USDT with `group`:`name` identifier is "active" (i.e., whether it * is attached to by external tracing tooling and is actively observed). * * This macro can be used to decide whether any additional and potentially * expensive data collection or processing should be done to pass extra * information into the given USDT. It is assumed that USDT is triggered with * USDT_WITH_SEMA() macro which will implicitly define associated USDT * semaphore. (If one needs more control over USDT semaphore, see * USDT_DEFINE_SEMA() and USDT_WITH_EXPLICIT_SEMA() macros below.) * * N.B. Such checks are necessarily racy and speculative. Between checking * whether USDT is active and triggering the USDT itself, tracer can be * detached with no notification. This race should be extremely rare and worst * case should result in one-time wasted extra data collection and processing. */ #define USDT_IS_ACTIVE(group, name) ({ \ extern struct usdt_sema __usdt_sema_name(group, name) \ __usdt_asm_name(__usdt_sema_name(group, name)); \ __usdt_sema_implicit(__usdt_sema_name(group, name)); \ __usdt_sema_name(group, name).active > 0; \ }) /* * APIs for working with user-defined explicit USDT semaphores. * * This is a less commonly used advanced API for use cases in which user needs * an explicit control over (potentially shared across multiple USDTs) USDT * semaphore instance. This can be used when there is a group of logically * related USDTs that all need extra data collection and processing whenever * any of a family of related USDTs are "activated" (i.e., traced). In such * a case, all such related USDTs will be associated with the same shared USDT * semaphore defined with USDT_DEFINE_SEMA() and the USDTs themselves will be * triggered with USDT_WITH_EXPLICIT_SEMA() macros, taking an explicit extra * USDT semaphore identifier as an extra parameter. */ /** * Underlying C global variable name for user-defined USDT semaphore with * `sema` identifier. Could be useful for debugging, but normally shouldn't be * used explicitly. */ #define USDT_SEMA(sema) __usdt_sema_##sema /* * Define storage for user-defined USDT semaphore `sema`. * * Should be used only once in non-header source file to let compiler allocate * space for the semaphore variable. Just like with any other global variable. * * This macro can be used anywhere where global variable declaration is * allowed. Just like with global variable definitions, there should be only * one definition of user-defined USDT semaphore with given `sema` identifier, * otherwise compiler or linker will complain about duplicate variable * definition. * * For C++, it is allowed to use USDT_DEFINE_SEMA() both in global namespace * and inside namespaces (including nested namespaces). Just make sure that * USDT_DECLARE_SEMA() is placed within the namespace where this semaphore is * referenced, or any of its parent namespaces, so the C++ language-level * identifier is visible to the code that needs to reference the semaphore. * At the lowest layer, USDT semaphores have global naming and visibility * (they have a corresponding `__usdt_sema_` symbol, which can be linked * against from C or C++ code, if necessary). To keep it simple, putting * USDT_DECLARE_SEMA() declarations into global namespaces is the simplest * no-brainer solution. All these aspects are irrelevant for plain C, because * C doesn't have namespaces and everything is always in the global namespace. * * N.B. Due to USDT metadata being recorded in non-allocatable ELF note * section, it has limitations when it comes to relocations, which, in * practice, means that it's not possible to correctly share USDT semaphores * between main executable and shared libraries, or even between multiple * shared libraries. USDT semaphore has to be contained to individual shared * library or executable to avoid unpleasant surprises with half-working USDT * semaphores. We enforce this by marking semaphore ELF symbols as having * a hidden visibility. This is quite an advanced use case and consideration * and for most users this should have no consequences whatsoever. */ #define USDT_DEFINE_SEMA(sema) \ struct usdt_sema __usdt_sema_sec USDT_SEMA(sema) \ __usdt_asm_name(USDT_SEMA(sema)) \ __attribute__((visibility("hidden"))) = { 0 } /* * Declare extern reference to user-defined USDT semaphore `sema`. * * Refers to a variable defined in another compilation unit by * USDT_DEFINE_SEMA() and allows to use the same USDT semaphore across * multiple compilation units (i.e., .c and .cpp files). * * See USDT_DEFINE_SEMA() notes above for C++ language usage peculiarities. */ #define USDT_DECLARE_SEMA(sema) \ extern struct usdt_sema USDT_SEMA(sema) __usdt_asm_name(USDT_SEMA(sema)) /* * Check if user-defined USDT semaphore `sema` is "active" (i.e., whether it * is attached to by external tracing tooling and is actively observed). * * This macro can be used to decide whether any additional and potentially * expensive data collection or processing should be done to pass extra * information into USDT(s) associated with USDT semaphore `sema`. * * N.B. Such checks are necessarily racy. Between checking the state of USDT * semaphore and triggering associated USDT(s), the active tracer might attach * or detach. This race should be extremely rare and worst case should result * in one-time missed USDT event or wasted extra data collection and * processing. USDT-using tracers should be written with this in mind and is * not a concern of the application defining USDTs with associated semaphore. */ #define USDT_SEMA_IS_ACTIVE(sema) (USDT_SEMA(sema).active > 0) /* * Invoke USDT specified by `group` and `name` identifiers and associate * explicitly user-defined semaphore `sema` with it. Pass through `args` as * USDT arguments. `args` are optional and zero arguments are acceptable. * * Semaphore is defined with the help of USDT_DEFINE_SEMA() macro and can be * checked whether active with USDT_SEMA_IS_ACTIVE(). */ #ifdef __usdt_va_opt #define USDT_WITH_EXPLICIT_SEMA(sema, group, name, ...) \ __usdt_probe(group, name, __usdt_sema_explicit, USDT_SEMA(sema), ##__VA_ARGS__) #else #define USDT_WITH_EXPLICIT_SEMA(sema, group, name, ...) \ __usdt_probe(group, name, __usdt_sema_explicit, USDT_SEMA(sema) __VA_OPT__(,) __VA_ARGS__) #endif /* * Adjustable implementation aspects */ #ifndef USDT_ARG_CONSTRAINT #if defined __powerpc__ #define USDT_ARG_CONSTRAINT nZr #elif defined __arm__ #define USDT_ARG_CONSTRAINT g #elif defined __loongarch__ #define USDT_ARG_CONSTRAINT nmr #else #define USDT_ARG_CONSTRAINT nor #endif #endif /* USDT_ARG_CONSTRAINT */ #ifndef USDT_NOP #if defined(__ia64__) || defined(__s390__) || defined(__s390x__) #define USDT_NOP nop 0 #else #define USDT_NOP nop #endif #endif /* USDT_NOP */ /* * Implementation details */ /* USDT name for implicitly-defined USDT semaphore, derived from group:name */ #define __usdt_sema_name(group, name) __usdt_sema_##group##__##name /* ELF section into which USDT semaphores are put */ #define __usdt_sema_sec __attribute__((section(".probes"))) #define __usdt_concat(a, b) a ## b #define __usdt_apply(fn, n) __usdt_concat(fn, n) #ifndef __usdt_nth #define __usdt_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, N, ...) N #endif #ifndef __usdt_narg #ifdef __usdt_va_opt #define __usdt_narg(...) __usdt_nth(_ __VA_OPT__(,) __VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #else #define __usdt_narg(...) __usdt_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #endif #endif /* __usdt_narg */ #define __usdt_hash # #define __usdt_str_(x) #x #define __usdt_str(x) __usdt_str_(x) #ifndef __usdt_asm_name #define __usdt_asm_name(name) __asm__(__usdt_str(name)) #endif #define __usdt_asm1(a) __usdt_str(a) "\n" #define __usdt_asm2(a,b) __usdt_str(a) "," __usdt_str(b) "\n" #define __usdt_asm3(a,b,c) __usdt_str(a) "," __usdt_str(b) "," __usdt_str(c) "\n" #define __usdt_asm5(a,b,c,d,e) __usdt_str(a) "," __usdt_str(b) "," __usdt_str(c) "," \ __usdt_str(d) "," __usdt_str(e) "\n" #ifdef __LP64__ #define __usdt_asm_addr .8byte #else #define __usdt_asm_addr .4byte #endif #define __usdt_asm_strz_(x) __usdt_asm1(.asciz #x) #define __usdt_asm_strz(x) __usdt_asm_strz_(x) #define __usdt_asm_str_(x) __usdt_asm1(.ascii #x) #define __usdt_asm_str(x) __usdt_asm_str_(x) /* "semaphoreless" USDT case */ #ifndef __usdt_sema_none #define __usdt_sema_none(sema) #endif /* implicitly defined __usdt_sema__group__name semaphore (using weak symbols) */ #ifndef __usdt_sema_implicit #define __usdt_sema_implicit(sema) \ __asm__ __volatile__ ( \ __usdt_asm1(.ifndef sema) \ __usdt_asm3( .pushsection .probes, "aw", "progbits") \ __usdt_asm1( .weak sema) \ __usdt_asm1( .hidden sema) \ __usdt_asm1( .align 2) \ __usdt_asm1(sema:) \ __usdt_asm1( .zero 2) \ __usdt_asm2( .type sema, @object) \ __usdt_asm2( .size sema, 2) \ __usdt_asm1( .popsection) \ __usdt_asm1(.endif) \ ); #endif /* externally defined semaphore using USDT_DEFINE_SEMA() and passed explicitly by user */ #ifndef __usdt_sema_explicit #define __usdt_sema_explicit(sema) \ __asm__ __volatile__ ("" :: "m" (sema)); #endif /* main USDT definition (nop and .note.stapsdt metadata) */ #define __usdt_probe(group, name, sema_def, sema, ...) do { \ sema_def(sema) \ __asm__ __volatile__ ( \ __usdt_asm1(990: USDT_NOP) \ __usdt_asm3( .pushsection .note.stapsdt, "", "note") \ __usdt_asm1( .balign 4) \ __usdt_asm3( .4byte 992f-991f,994f-993f,3) \ __usdt_asm1(991: .asciz "stapsdt") \ __usdt_asm1(992: .balign 4) \ __usdt_asm1(993: __usdt_asm_addr 990b) \ __usdt_asm1( __usdt_asm_addr _.stapsdt.base) \ __usdt_asm1( __usdt_asm_addr sema) \ __usdt_asm_strz(group) \ __usdt_asm_strz(name) \ __usdt_asm_args(__VA_ARGS__) \ __usdt_asm1( .ascii "\0") \ __usdt_asm1(994: .balign 4) \ __usdt_asm1( .popsection) \ __usdt_asm1(.ifndef _.stapsdt.base) \ __usdt_asm5( .pushsection .stapsdt.base,"aG","progbits",.stapsdt.base,comdat)\ __usdt_asm1( .weak _.stapsdt.base) \ __usdt_asm1( .hidden _.stapsdt.base) \ __usdt_asm1(_.stapsdt.base:) \ __usdt_asm1( .space 1) \ __usdt_asm2( .size _.stapsdt.base, 1) \ __usdt_asm1( .popsection) \ __usdt_asm1(.endif) \ :: __usdt_asm_ops(__VA_ARGS__) \ ); \ } while (0) /* * NB: gdb PR24541 highlighted an unspecified corner of the sdt.h * operand note format. * * The named register may be a longer or shorter (!) alias for the * storage where the value in question is found. For example, on * i386, 64-bit value may be put in register pairs, and a register * name stored would identify just one of them. Previously, gcc was * asked to emit the %w[id] (16-bit alias of some registers holding * operands), even when a wider 32-bit value was used. * * Bottom line: the byte-width given before the @ sign governs. If * there is a mismatch between that width and that of the named * register, then a sys/sdt.h note consumer may need to employ * architecture-specific heuristics to figure out where the compiler * has actually put the complete value. */ #if defined(__powerpc__) || defined(__powerpc64__) #define __usdt_argref(id) %I[id]%[id] #elif defined(__i386__) #define __usdt_argref(id) %k[id] /* gcc.gnu.org/PR80115 sourceware.org/PR24541 */ #else #define __usdt_argref(id) %[id] #endif #define __usdt_asm_arg(n) __usdt_asm_str(%c[__usdt_asz##n]) \ __usdt_asm1(.ascii "@") \ __usdt_asm_str(__usdt_argref(__usdt_aval##n)) #define __usdt_asm_args0 /* no arguments */ #define __usdt_asm_args1 __usdt_asm_arg(1) #define __usdt_asm_args2 __usdt_asm_args1 __usdt_asm1(.ascii " ") __usdt_asm_arg(2) #define __usdt_asm_args3 __usdt_asm_args2 __usdt_asm1(.ascii " ") __usdt_asm_arg(3) #define __usdt_asm_args4 __usdt_asm_args3 __usdt_asm1(.ascii " ") __usdt_asm_arg(4) #define __usdt_asm_args5 __usdt_asm_args4 __usdt_asm1(.ascii " ") __usdt_asm_arg(5) #define __usdt_asm_args6 __usdt_asm_args5 __usdt_asm1(.ascii " ") __usdt_asm_arg(6) #define __usdt_asm_args7 __usdt_asm_args6 __usdt_asm1(.ascii " ") __usdt_asm_arg(7) #define __usdt_asm_args8 __usdt_asm_args7 __usdt_asm1(.ascii " ") __usdt_asm_arg(8) #define __usdt_asm_args9 __usdt_asm_args8 __usdt_asm1(.ascii " ") __usdt_asm_arg(9) #define __usdt_asm_args10 __usdt_asm_args9 __usdt_asm1(.ascii " ") __usdt_asm_arg(10) #define __usdt_asm_args11 __usdt_asm_args10 __usdt_asm1(.ascii " ") __usdt_asm_arg(11) #define __usdt_asm_args12 __usdt_asm_args11 __usdt_asm1(.ascii " ") __usdt_asm_arg(12) #define __usdt_asm_args(...) __usdt_apply(__usdt_asm_args, __usdt_narg(__VA_ARGS__)) #define __usdt_is_arr(x) (__builtin_classify_type(x) == 14 || __builtin_classify_type(x) == 5) #define __usdt_arg_size(x) (__usdt_is_arr(x) ? sizeof(void *) : sizeof(x)) /* * We can't use __builtin_choose_expr() in C++, so fall back to table-based * signedness determination for known types, utilizing templates magic. */ #ifdef __cplusplus #define __usdt_is_signed(x) (!__usdt_is_arr(x) && __usdt_t<__typeof(x)>::is_signed) #include template struct __usdt_t { static const bool is_signed = false; }; template struct __usdt_t : public __usdt_t {}; template struct __usdt_t : public __usdt_t {}; #define __usdt_def_signed(T) \ template<> struct __usdt_t { static const bool is_signed = true; }; \ template<> struct __usdt_t { static const bool is_signed = true; }; \ template<> struct __usdt_t { static const bool is_signed = true; }; \ template<> struct __usdt_t { static const bool is_signed = true; } #define __usdt_maybe_signed(T) \ template<> struct __usdt_t { static const bool is_signed = (T)-1 < (T)1; }; \ template<> struct __usdt_t { static const bool is_signed = (T)-1 < (T)1; }; \ template<> struct __usdt_t { static const bool is_signed = (T)-1 < (T)1; }; \ template<> struct __usdt_t { static const bool is_signed = (T)-1 < (T)1; } __usdt_def_signed(signed char); __usdt_def_signed(short); __usdt_def_signed(int); __usdt_def_signed(long); __usdt_def_signed(long long); __usdt_maybe_signed(char); __usdt_maybe_signed(wchar_t); #else /* !__cplusplus */ #define __usdt_is_inttype(x) (__builtin_classify_type(x) >= 1 && __builtin_classify_type(x) <= 4) #define __usdt_inttype(x) __typeof(__builtin_choose_expr(__usdt_is_inttype(x), (x), 0U)) #define __usdt_is_signed(x) ((__usdt_inttype(x))-1 < (__usdt_inttype(x))1) #endif /* __cplusplus */ #define __usdt_asm_op(n, x) \ [__usdt_asz##n] "n" ((__usdt_is_signed(x) ? (int)-1 : 1) * (int)__usdt_arg_size(x)), \ [__usdt_aval##n] __usdt_str(USDT_ARG_CONSTRAINT)(x) #define __usdt_asm_ops0() [__usdt_dummy] "g" (0) #define __usdt_asm_ops1(x) __usdt_asm_op(1, x) #define __usdt_asm_ops2(a,x) __usdt_asm_ops1(a), __usdt_asm_op(2, x) #define __usdt_asm_ops3(a,b,x) __usdt_asm_ops2(a,b), __usdt_asm_op(3, x) #define __usdt_asm_ops4(a,b,c,x) __usdt_asm_ops3(a,b,c), __usdt_asm_op(4, x) #define __usdt_asm_ops5(a,b,c,d,x) __usdt_asm_ops4(a,b,c,d), __usdt_asm_op(5, x) #define __usdt_asm_ops6(a,b,c,d,e,x) __usdt_asm_ops5(a,b,c,d,e), __usdt_asm_op(6, x) #define __usdt_asm_ops7(a,b,c,d,e,f,x) __usdt_asm_ops6(a,b,c,d,e,f), __usdt_asm_op(7, x) #define __usdt_asm_ops8(a,b,c,d,e,f,g,x) __usdt_asm_ops7(a,b,c,d,e,f,g), __usdt_asm_op(8, x) #define __usdt_asm_ops9(a,b,c,d,e,f,g,h,x) __usdt_asm_ops8(a,b,c,d,e,f,g,h), __usdt_asm_op(9, x) #define __usdt_asm_ops10(a,b,c,d,e,f,g,h,i,x) __usdt_asm_ops9(a,b,c,d,e,f,g,h,i), __usdt_asm_op(10, x) #define __usdt_asm_ops11(a,b,c,d,e,f,g,h,i,j,x) __usdt_asm_ops10(a,b,c,d,e,f,g,h,i,j), __usdt_asm_op(11, x) #define __usdt_asm_ops12(a,b,c,d,e,f,g,h,i,j,k,x) __usdt_asm_ops11(a,b,c,d,e,f,g,h,i,j,k), __usdt_asm_op(12, x) #define __usdt_asm_ops(...) __usdt_apply(__usdt_asm_ops, __usdt_narg(__VA_ARGS__))(__VA_ARGS__) #endif /* __USDT_H */ fuse-3.18.2/lib/util.c0000644000175000017500000000141315156613252013411 0ustar berndbernd #include "fuse_config.h" #ifdef HAVE_PTHREAD_SETNAME_NP #define _GNU_SOURCE #include #endif #include #include #include #ifndef FUSE_USE_VERSION #define FUSE_USE_VERSION (FUSE_MAKE_VERSION(3, 18)) #endif #include "util.h" #include "fuse_log.h" #include "fuse_lowlevel.h" #include int libfuse_strtol(const char *str, long *res) { char *endptr; int base = 10; long val; errno = 0; if (!str) return -EINVAL; val = strtol(str, &endptr, base); if (errno) return -errno; if (endptr == str || *endptr != '\0') return -EINVAL; *res = val; return 0; } void fuse_set_thread_name(const char *name) { #ifdef HAVE_PTHREAD_SETNAME_NP pthread_setname_np(pthread_self(), name); #else (void)name; #endif } fuse-3.18.2/lib/util.h0000644000175000017500000000214015156613252013414 0ustar berndbernd#ifndef FUSE_UTIL_H_ #define FUSE_UTIL_H_ #include #include #define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1)) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) struct fuse_conn_info; int libfuse_strtol(const char *str, long *res); void fuse_set_thread_name(const char *name); /** * Return the low bits of a number */ static inline uint32_t fuse_lower_32_bits(uint64_t nr) { return (uint32_t)(nr & 0xffffffff); } /** * Return the high bits of a number */ static inline uint64_t fuse_higher_32_bits(uint64_t nr) { return nr & ~0xffffffffULL; } #ifndef FUSE_VAR_UNUSED #define FUSE_VAR_UNUSED __attribute__((__unused__)) #endif #define container_of(ptr, type, member) \ ({ \ unsigned long __mptr = (unsigned long)(ptr); \ ((type *)(__mptr - offsetof(type, member))); \ }) #if __has_attribute(__fallthrough__) #define fallthrough __attribute__((__fallthrough__)) #else #define fallthrough do {} while (0) #endif #endif /* FUSE_UTIL_H_ */ fuse-3.18.2/meson.build0000644000175000017500000002462315156613252013674 0ustar berndberndproject('libfuse3', ['c'], version: '3.18.2', meson_version: '>= 0.60.0', default_options: [ 'buildtype=debugoptimized', 'c_std=gnu11', 'cpp_std=c++20', 'warning_level=2', ]) # Would be better to create the version string # from integers, i.e. concatenating strings instead # of splitting a string, but 'project' needs to be # the first meson.build keyword... # split version into base and rc version_parts = meson.project_version().split('-') base_version = version_parts[0] version_list = base_version.split('.') FUSE_MAJOR_VERSION = version_list[0] FUSE_MINOR_VERSION = version_list[1] FUSE_HOTFIX_VERSION = version_list[2] FUSE_RC_VERSION = version_parts.length() > 1 ? version_parts[1] : '' platform = host_machine.system() if platform == 'darwin' error('libfuse does not support OS-X.\n' + 'Take a look at http://osxfuse.github.io/ or the more recent\n' + 'https://www.fuse-t.org/ instead') elif platform == 'cygwin' or platform == 'windows' error('libfuse does not support Windows.\n' + 'Take a look at http://www.secfs.net/winfsp/ instead') endif cc = meson.get_compiler('c') # # 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() public_cfg.set('FUSE_MAJOR_VERSION', FUSE_MAJOR_VERSION) public_cfg.set('FUSE_MINOR_VERSION', FUSE_MINOR_VERSION) public_cfg.set('FUSE_HOTFIX_VERSION', FUSE_HOTFIX_VERSION) public_cfg.set('FUSE_RC_VERSION', FUSE_RC_VERSION) # Default includes when checking for presence of functions and # struct members include_default = ''' #include #include #include #include #include #include #include #include /* For pthread_setname_np */ ''' args_default = [ '-D_GNU_SOURCE' ] # # Feature detection, only available at libfuse compilation time, # but not for application linking to libfuse. # private_cfg = configuration_data() 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', 'fspacectl' ] foreach func : test_funcs private_cfg.set('HAVE_' + func.to_upper(), cc.has_function(func, prefix: include_default, args: args_default)) endforeach # Special case checks that need custom code special_funcs = { 'pthread_setname_np': ''' #include int main(void) { pthread_t thread = pthread_self(); pthread_setname_np(thread, "test"); return 0; } ''', 'close_range': ''' #include #include #ifdef linux #include #endif int main(void) { unsigned int flags = CLOSE_RANGE_UNSHARE; return close_range(3, ~0U, flags); } ''', 'listmount': ''' #include #include #include #include int main(int argc, char *argv[]) { struct mnt_id_req req = { .size = sizeof(struct mnt_id_req), .mnt_id = LSMT_ROOT, }; uint64_t mnt_ids[1]; int n = syscall(SYS_listmount, &req, &mnt_ids, 1, 0); if (n == -1) { return -1; } } ''' } foreach name, code : special_funcs private_cfg.set('HAVE_' + name.to_upper(), cc.links(code, args: args_default, name: name + ' check')) endforeach # Headers checks test_headers = [ 'sys/xattr.h', 'linux/limits.h' ] foreach header : test_headers private_cfg.set('HAVE_' + header.underscorify().to_upper(), cc.has_header(header)) endforeach # Regular function checks private_cfg.set('HAVE_SETXATTR', cc.has_function('setxattr', prefix: '#include ')) private_cfg.set('HAVE_ICONV', cc.has_function('iconv', prefix: '#include ')) private_cfg.set('HAVE_BACKTRACE', cc.has_function('backtrace', prefix: '#include ')) private_cfg.set('HAVE_STATX', cc.has_function('statx', prefix : '#define _GNU_SOURCE\n#include ')) # Struct member checks private_cfg.set('HAVE_STRUCT_STAT_ST_ATIM', cc.has_member('struct stat', 'st_atim', prefix: include_default + '#include ', args: args_default)) private_cfg.set('HAVE_STRUCT_STAT_ST_ATIMESPEC', cc.has_member('struct stat', 'st_atimespec', prefix: include_default + '#include ', args: args_default)) private_cfg.set('USDT_ENABLED', get_option('enable-usdt')) # Check for liburing with SQE128 support code = ''' #include #include int main(void) { struct io_uring ring; int ret = io_uring_queue_init(1, &ring, 0); #ifndef IORING_SETUP_SQE128 #error "No SQE128 support" #endif return ret; }''' liburing = dependency('liburing', required: false) libnuma = dependency('numa', required: false) if get_option('enable-io-uring') and liburing.found() and libnuma.found() if cc.links(code, name: 'liburing linking and SQE128 support', dependencies: [liburing]) private_cfg.set('HAVE_URING', true) endif endif # # Compiler configuration # 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_LIBFUSE_PRIVATE_CONFIG_H', '-D_GNU_SOURCE', '-D_FILE_OFFSET_BITS=64', '-Wno-sign-compare', '-Wmissing-declarations', '-Wwrite-strings', '-fno-strict-aliasing', language: 'cpp') # Some (stupid) GCC versions warn about unused return values even when they are # casted to void. This makes -Wunused-result pretty useless, since there is no # way to suppress the warning when we really *want* to ignore the value. code = ''' __attribute__((warn_unused_result)) int get_4() { return 4; } int main(void) { (void) get_4(); return 0; }''' if not cc.compiles(code, args: [ '-O0', '-Werror=unused-result' ]) message('Compiler warns about unused result even when casting to void') add_project_arguments('-Wno-unused-result', language: 'c') endif # 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 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 #include #include #include #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', '.') # Common dependencies thread_dep = dependency('threads') # # Read build files from sub-directories # subdirs = [ 'lib', 'include'] if get_option('utils') and not platform.endswith('bsd') and platform != 'dragonfly' subdirs += [ 'util', 'doc' ] endif if get_option('examples') subdirs += 'example' endif if get_option('tests') subdirs += 'test' endif foreach n : subdirs subdir(n) endforeach fuse-3.18.2/meson_options.txt0000644000175000017500000000236515156613252015166 0ustar berndberndoption('disable-mtab', type : 'boolean', value : false, description: 'Disable and ignore usage of /etc/mtab') 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') option('examples', type : 'boolean', value : true, description: 'Whether or not to build example programs') option('useroot', type : 'boolean', value : true, description: 'Set owner and setuid bits on installed files') 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') option('enable-usdt', type : 'boolean', value : false, description: 'Enable user statically defined tracepoints for extra observability') option('enable-io-uring', type: 'boolean', value: true, description: 'Enable fuse-over-io-uring support') fuse-3.18.2/requirements.txt0000644000175000017500000000024315156613252015006 0ustar berndbernd# Packages required for building and testing libfuse only, # no python code is required when using libfuse. # Build packages: meson ninja # Test packages: pytest fuse-3.18.2/signify/0000755000175000017500000000000015156613444013176 5ustar berndberndfuse-3.18.2/signify/fuse-3.15.pub0000644000175000017500000000013715156613252015232 0ustar berndbernduntrusted comment: signify public key RWRsNRJIAGSHE0C6WuVmusQlvudAPlmOOkCw1LbuOLuu+25DuAmPCpDf fuse-3.18.2/signify/fuse-3.16.pub0000644000175000017500000000013715156613252015233 0ustar berndbernduntrusted comment: signify public key RWQtnc3WSoYwHGAdfvtTTVX8RsAXrNwMb8xqVwlY8lYY2Fxn2QUDiPYK fuse-3.18.2/signify/fuse-3.17.pub0000644000175000017500000000013715156613252015234 0ustar berndbernduntrusted comment: signify public key RWQqzcI/bjQ4/nouPgwUbs0WorZncrBxJHmiCb2N+GrMs9L6YAcGSFY/ fuse-3.18.2/signify/fuse-3.18.pub0000644000175000017500000000013715156613252015235 0ustar berndbernduntrusted comment: signify public key RWS6gMnNrKp/zRSYWv13J+KwXE26vCUsbC/hVZmjQ8PA3xjixGLjodz3 fuse-3.18.2/signify/fuse-3.19.pub0000644000175000017500000000013715156613252015236 0ustar berndbernduntrusted comment: signify public key RWR4cEcMGJhD3Dnd3NOeJck3WiuVt9A7mrkq+nQYwrwwmMdDDAan/YiU fuse-3.18.2/test/0000755000175000017500000000000015156613444012505 5ustar berndberndfuse-3.18.2/test/ci-build.sh0000755000175000017500000001035015156613252014530 0ustar berndbernd#!/bin/bash -x set -e TEST_CMD="pytest -v --maxfail=1 --log-level=INFO --log-cli-level=INFO test/" SAN="-Db_sanitize=address,undefined" # not default export UBSAN_OPTIONS=halt_on_error=1 # Make sure binaries can be accessed when invoked by root. umask 0022 # There are tests that run as root but without CAP_DAC_OVERRIDE. To allow these # to launch built binaries, the directory tree must be accessible to the root # user. Since the source directory isn't necessarily accessible to root, we # build and run tests in a temporary directory that we can set up to be world # readable/executable. SOURCE_DIR="$(readlink -f .)" TEST_DIR="$(mktemp -dt libfuse-build-XXXXXX)" PREFIX_DIR="$(mktemp -dt libfuse-install-XXXXXXX)" chmod 0755 "${TEST_DIR}" cd "${TEST_DIR}" echo "Running in ${TEST_DIR}" cp -v "${SOURCE_DIR}/test/lsan_suppress.txt" . export LSAN_OPTIONS="suppressions=$(pwd)/lsan_suppress.txt" export ASAN_OPTIONS="detect_leaks=1" export CC non_sanitized_build() ( echo "Standard build (without sanitizers)" for CC in gcc gcc-9 gcc-10 clang; do echo "=== Building with ${CC} ===" mkdir build-${CC}; pushd build-${CC} if [ "${CC}" == "clang" ]; then export CXX="clang++" export TEST_WITH_VALGRIND=false else unset CXX export TEST_WITH_VALGRIND=true fi if [ ${CC} == 'gcc-7' ]; then build_opts='-D b_lundef=false' else build_opts='' fi if [ ${CC} == 'gcc-10' ]; then build_opts='-Dc_args=-flto=auto' else build_opts='' fi meson setup -Dprefix=${PREFIX_DIR} -D werror=true ${build_opts} "${SOURCE_DIR}" || (cat meson-logs/meson-log.txt; false) ninja sudo env PATH=$PATH ninja install # libfuse will first try the install path and then system defaults sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 # also needed for some of the tests sudo chown root:root util/fusermount3 sudo chmod 4755 util/fusermount3 ${TEST_CMD} popd rm -fr build-${CC} sudo rm -fr ${PREFIX_DIR} done ) sanitized_build() ( echo "=== Building with clang and sanitizers" mkdir build-san; pushd build-san meson setup -Dprefix=${PREFIX_DIR} -D werror=true\ "${SOURCE_DIR}" \ || (cat meson-logs/meson-log.txt; false) meson configure $SAN # b_lundef=false is required to work around clang # bug, cf. https://groups.google.com/forum/#!topic/mesonbuild/tgEdAXIIdC4 meson configure -D b_lundef=false # additional options if [[ $# -gt 0 ]]; then meson configure "$@" fi # print all options meson configure --no-pager # reconfigure to ensure it uses all additional options meson setup --reconfigure "${SOURCE_DIR}" ninja sudo env PATH=$PATH ninja install sudo chmod 4755 ${PREFIX_DIR}/bin/fusermount3 # also needed for some of the tests sudo chown root:root util/fusermount3 sudo chmod 4755 util/fusermount3 # Test as root and regular user sudo env PATH=$PATH ${TEST_CMD} # Cleanup temporary files (since they are now owned by root) sudo rm -rf test/.pytest_cache/ test/__pycache__ ${TEST_CMD} popd rm -fr build-san sudo rm -fr ${PREFIX_DIR} ) # Sanitized with io-uring export CC=clang export CXX=clang++ export FUSE_URING_ENABLE=1 sanitized_build unset FUSE_URING_ENABLE # 32-bit sanitized build export CC=clang export CXX=clang++ export CFLAGS="-m32" export CXXFLAGS="-m32" export LDFLAGS="-m32" export PKG_CONFIG_PATH="/usr/lib/i386-linux-gnu/pkgconfig" TEST_WITH_VALGRIND=false sanitized_build unset CFLAGS unset CXXFLAGS unset LDFLAGS unset PKG_CONFIG_PATH unset TEST_WITH_VALGRIND unset CC unset CXX # Sanitized build export CC=clang export CXX=clang++ TEST_WITH_VALGRIND=false sanitized_build # Sanitized build without libc versioned symbols export CC=clang export CXX=clang++ sanitized_build "-Ddisable-libc-symbol-version=true" # Sanitized build without fuse-io-uring export CC=clang export CXX=clang++ sanitized_build "-Denable-io-uring=false" # Build without any sanitizer non_sanitized_build # Documentation. (cd "${SOURCE_DIR}"; doxygen doc/Doxyfile) # Clean up. rm -rf "${TEST_DIR}" fuse-3.18.2/test/conftest.py0000644000175000017500000000637615156613252014715 0ustar berndbernd#!/usr/bin/env python3 import sys import pytest import time import re import os import threading # If a test fails, wait a moment before retrieving the captured # stdout/stderr. When using a server process, this makes sure that we capture # any potential output of the server that comes *after* a test has failed. For # 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.hookimpl(hookwrapper=True) def pytest_pyfunc_call(pyfuncitem): outcome = yield failed = outcome.excinfo is not None if failed: time.sleep(1) class OutputChecker: '''Check output data for suspicious patterns. Everything written to check_output.fd will be scanned for suspicious messages and then written to sys.stdout. ''' def __init__(self): (fd_r, fd_w) = os.pipe() self.fd = fd_w self._false_positives = [] self._buf = bytearray() self._thread = threading.Thread(target=self._loop, daemon=True, args=(fd_r,)) self._thread.start() def register_output(self, pattern, count=1, flags=re.MULTILINE): '''Register *pattern* as false positive for output checking This prevents the test from failing because the output otherwise appears suspicious. ''' self._false_positives.append((pattern, flags, count)) def _loop(self, ifd): BUFSIZE = 128*1024 ofd = sys.stdout.fileno() while True: buf = os.read(ifd, BUFSIZE) if not buf: break os.write(ofd, buf) self._buf += buf def _check(self): os.close(self.fd) self._thread.join() buf = self._buf.decode('utf8', errors='replace') # Strip out false positives for (pattern, flags, count) in self._false_positives: cp = re.compile(pattern, flags) (buf, cnt) = cp.subn('', buf, count=count) patterns = [ r'\b{}\b'.format(x) for x in ('exception', 'error', 'warning', 'fatal', 'traceback', 'fault', 'crash(?:ed)?', 'abort(?:ed)', 'uninitiali[zs]ed') ] patterns += ['^==[0-9]+== '] for pattern in patterns: cp = re.compile(pattern, re.IGNORECASE | re.MULTILINE) hit = cp.search(buf) if hit: # Skip FUSE error messages in the format "unique: X, error: -Y (...), outsize: Z" # These are no errors, but just fuse debug messages with the return code if re.search(r'unique: \d+, error: -\d+ \(.*\), outsize: \d+', hit.group(0)): continue raise AssertionError(f'Suspicious output to stderr (matched "{hit.group(0)}")') @pytest.fixture() def output_checker(request): checker = OutputChecker() yield checker checker._check() # Make test outcome available to fixtures # (from https://github.com/pytest-dev/pytest/issues/230) @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_makereport(item, call): outcome = yield rep = outcome.get_result() setattr(item, "rep_" + rep.when, rep) return rep fuse-3.18.2/test/hello.c0000644000175000017500000001036315156613252013754 0ustar berndbernd/* * FUSE: Filesystem in Userspace * Copyright (C) 2001-2007 Miklos Szeredi * * This program can be distributed under the terms of the GNU GPLv2. * See the file GPL2.txt. */ /** @file * * minimal example filesystem using high-level API * * Compile with: * * gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello * * ## Source code ## * \include hello.c */ #define FUSE_USE_VERSION 31 #include #include #include #include #include #include #include /* * Command line options * * We can't set default values for the char* fields here because * fuse_opt_parse would attempt to free() them when the user specifies * different values on the command line. */ static struct options { const char *filename; const char *contents; int show_help; } options; #define OPTION(t, p) { t, offsetof(struct options, p), 1 } static const struct fuse_opt option_spec[] = { OPTION("--name=%s", filename), OPTION("--contents=%s", contents), OPTION("-h", show_help), OPTION("--help", show_help), FUSE_OPT_END }; static void *hello_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { (void)conn; cfg->kernel_cache = 1; /* Test setting flags the old way */ conn->want = FUSE_CAP_ASYNC_READ; conn->want &= ~FUSE_CAP_ASYNC_READ; return NULL; } static int hello_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { (void)fi; int res = 0; memset(stbuf, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; } else if (strcmp(path + 1, options.filename) == 0) { stbuf->st_mode = S_IFREG | 0444; stbuf->st_nlink = 1; stbuf->st_size = strlen(options.contents); } else res = -ENOENT; return res; } static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { (void)offset; (void)fi; (void)flags; if (strcmp(path, "/") != 0) return -ENOENT; filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS); filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS); filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS); return 0; } static int hello_open(const char *path, struct fuse_file_info *fi) { if (strcmp(path + 1, options.filename) != 0) return -ENOENT; if ((fi->flags & O_ACCMODE) != O_RDONLY) return -EACCES; return 0; } static int hello_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { size_t len; (void)fi; if (strcmp(path + 1, options.filename) != 0) return -ENOENT; len = strlen(options.contents); if (offset < len) { if (offset + size > len) size = len - offset; memcpy(buf, options.contents + offset, size); } else size = 0; return size; } static const struct fuse_operations hello_oper = { .init = hello_init, .getattr = hello_getattr, .readdir = hello_readdir, .open = hello_open, .read = hello_read, }; static void show_help(const char *progname) { printf("usage: %s [options] \n\n", progname); printf("File-system specific options:\n" " --name= Name of the \"hello\" file\n" " (default: \"hello\")\n" " --contents= Contents \"hello\" file\n" " (default \"Hello, World!\\n\")\n" "\n"); } int main(int argc, char *argv[]) { int ret; struct fuse_args args = FUSE_ARGS_INIT(argc, argv); /* Set defaults -- we have to use strdup so that * fuse_opt_parse can free the defaults if other * values are specified */ options.filename = strdup("hello"); options.contents = strdup("Hello World!\n"); /* Parse options */ if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) return 1; /* When --help is specified, first print our own file-system * specific help text, then signal fuse_main to show * additional help (by adding `--help` to the options again) * without usage: line (by setting argv[0] to the empty * string) */ if (options.show_help) { show_help(argv[0]); assert(fuse_opt_add_arg(&args, "--help") == 0); args.argv[0][0] = '\0'; } ret = fuse_main(args.argc, args.argv, &hello_oper, NULL); fuse_opt_free_args(&args); return ret; } fuse-3.18.2/test/lsan_suppress.txt0000644000175000017500000000005215156613252016141 0ustar berndbernd# Suppression file for address sanitizer. fuse-3.18.2/test/meson.build0000644000175000017500000000363015156613252014646 0ustar berndbernd# Compile helper programs td = [] foreach prog: [ 'test_write_cache', 'test_setattr', 'hello' ] td += executable(prog, prog + '.c', include_directories: include_dirs, link_with: [ libfuse ], dependencies: thread_dep, install: false) endforeach td += executable('test_syscalls', 'test_syscalls.c', include_directories: include_dirs, install: false) 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) td += executable('test_want_conversion', 'test_want_conversion.c', dependencies: [ libfuse_dep ], install: false) td += executable('test_signals', 'test_signals.c', dependencies: [ libfuse_dep, thread_dep ], install: false) td += executable('test_abi', 'test_abi.c', dependencies: [ libfuse_dep ], install: false) test_scripts = [ 'conftest.py', 'pytest.ini', 'test_examples.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', '@INPUT@', meson.current_build_dir() ]) # Provide something helpful when running 'ninja test' if meson.is_subproject() test('libfuse is a subproject, skipping tests', executable('wrong_command', 'wrong_command.c', install: false, c_args: [ '-DMESON_IS_SUBPROJECT' ])) else test('wrong_command', executable('wrong_command', 'wrong_command.c', install: false)) endif fuse-3.18.2/test/pytest.ini0000644000175000017500000000024315156613252014532 0ustar berndbernd[pytest] addopts = --verbose --assert=rewrite --tb=native -x -r a markers = uses_fuse: Indicates that FUSE is supported. log_cli=true faulthandler_timeout=60 fuse-3.18.2/test/readdir_inode.c0000644000175000017500000000310015156613252015430 0ustar berndbernd/* * Prints each directory entry, its inode and d_type as returned by 'readdir'. * Skips '.' and '..' because readdir is not required to return them and * some of our examples don't. However if they are returned, their d_type * should be valid. */ #include #include #include #include #include int main(int argc, char* argv[]) { DIR* dirp; struct dirent* dent; if (argc != 2) { fprintf(stderr, "Usage: readdir_inode dir\n"); return 1; } dirp = opendir(argv[1]); if (dirp == NULL) { perror("failed to open directory"); return 2; } errno = 0; dent = readdir(dirp); while (dent != NULL) { if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) { printf("%llu %d %s\n", (unsigned long long)dent->d_ino, (int)dent->d_type, dent->d_name); if ((long long)dent->d_ino < 0) fprintf(stderr,"%s : bad d_ino %llu\n", dent->d_name, (unsigned long long)dent->d_ino); if ((dent->d_type < 1) || (dent->d_type > 15)) fprintf(stderr,"%s : bad d_type %d\n", dent->d_name, (int)dent->d_type); } else { if (dent->d_type != DT_DIR) fprintf(stderr,"%s : bad d_type %d\n", dent->d_name, (int)dent->d_type); } dent = readdir(dirp); } if (errno != 0) { perror("failed to read directory entry"); return 3; } closedir(dirp); return 0; } fuse-3.18.2/test/release_unlink_race.c0000644000175000017500000000337415156613252016647 0ustar berndbernd/* This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ #define FUSE_USE_VERSION 31 #define _GNU_SOURCE #include #include #include #include #include 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); } fuse-3.18.2/test/stracedecode.c0000644000175000017500000001215115156613252015273 0ustar berndbernd#include #include #include "fuse_kernel.h" static struct { const char *name; } fuse_ll_ops[] = { [FUSE_LOOKUP] = { "LOOKUP" }, [FUSE_FORGET] = { "FORGET" }, [FUSE_GETATTR] = { "GETATTR" }, [FUSE_SETATTR] = { "SETATTR" }, [FUSE_READLINK] = { "READLINK" }, [FUSE_SYMLINK] = { "SYMLINK" }, [FUSE_MKNOD] = { "MKNOD" }, [FUSE_MKDIR] = { "MKDIR" }, [FUSE_UNLINK] = { "UNLINK" }, [FUSE_RMDIR] = { "RMDIR" }, [FUSE_RENAME] = { "RENAME" }, [FUSE_LINK] = { "LINK" }, [FUSE_OPEN] = { "OPEN" }, [FUSE_READ] = { "READ" }, [FUSE_WRITE] = { "WRITE" }, [FUSE_STATFS] = { "STATFS" }, [FUSE_RELEASE] = { "RELEASE" }, [FUSE_FSYNC] = { "FSYNC" }, [FUSE_SETXATTR] = { "SETXATTR" }, [FUSE_GETXATTR] = { "GETXATTR" }, [FUSE_LISTXATTR] = { "LISTXATTR" }, [FUSE_REMOVEXATTR] = { "REMOVEXATTR" }, [FUSE_FLUSH] = { "FLUSH" }, [FUSE_INIT] = { "INIT" }, [FUSE_OPENDIR] = { "OPENDIR" }, [FUSE_READDIR] = { "READDIR" }, [FUSE_RELEASEDIR] = { "RELEASEDIR" }, [FUSE_FSYNCDIR] = { "FSYNCDIR" }, [FUSE_GETLK] = { "GETLK" }, [FUSE_SETLK] = { "SETLK" }, [FUSE_SETLKW] = { "SETLKW" }, [FUSE_ACCESS] = { "ACCESS" }, [FUSE_CREATE] = { "CREATE" }, [FUSE_TMPFILE] = { "TMPFILE" }, [FUSE_INTERRUPT] = { "INTERRUPT" }, [FUSE_BMAP] = { "BMAP" }, [FUSE_DESTROY] = { "DESTROY" }, [FUSE_READDIRPLUS] = { "READDIRPLUS" }, }; #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) static const char *opname(enum fuse_opcode opcode) { if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) return "???"; else return fuse_ll_ops[opcode].name; } static void process_buf(int dir, char *buf, int len) { static unsigned long long prevuniq = -1; static int prevopcode; if (!dir) { struct fuse_in_header *in = (struct fuse_in_header *) buf; buf += sizeof(struct fuse_in_header); printf("unique: %llu, opcode: %s (%i), nodeid: %lu, len: %i, insize: %i\n", (unsigned long long) in->unique, opname((enum fuse_opcode) in->opcode), in->opcode, (unsigned long) in->nodeid, in->len, len); switch (in->opcode) { case FUSE_READ: { struct fuse_read_in *arg = (struct fuse_read_in *) buf; printf("-READ fh:%llu off:%llu siz:%u rfl:%u own:%llu fl:%u\n", arg->fh, arg->offset, arg->size, arg->read_flags, arg->lock_owner, arg->flags); break; } case FUSE_WRITE: { struct fuse_write_in *arg = (struct fuse_write_in *) buf; printf("-WRITE fh:%llu off:%llu siz:%u wfl:%u own:%llu fl:%u\n", arg->fh, arg->offset, arg->size, arg->write_flags, arg->lock_owner, arg->flags); break; } } prevuniq = in->unique; prevopcode = in->opcode; } else { struct fuse_out_header *out = (struct fuse_out_header *) buf; buf += sizeof(struct fuse_out_header); printf(" unique: %llu, error: %i (%s), len: %i, outsize: %i\n", (unsigned long long) out->unique, out->error, strerror(-out->error), out->len, len); if (out->unique == prevuniq) { switch (prevopcode) { case FUSE_GETATTR: { struct fuse_attr_out *arg = (struct fuse_attr_out *) buf; printf("+ATTR v:%llu.%09u i:%llu s:%llu b:%llu\n", arg->attr_valid, arg->attr_valid_nsec, arg->attr.ino, arg->attr.size, arg->attr.blocks); break; } case FUSE_LOOKUP: { struct fuse_entry_out *arg = (struct fuse_entry_out *) buf; printf("+ENTRY nodeid:%llu v:%llu.%09u i:%llu s:%llu b:%llu\n", arg->nodeid, arg->attr_valid, arg->attr_valid_nsec, arg->attr.ino, arg->attr.size, arg->attr.blocks); break; } } } } } int main(void) { FILE *in = stdin; while (1) { int dir; int res; char buf[1048576]; unsigned len = 0; memset(buf, 0, sizeof(buf)); while (1) { char str[32]; res = fscanf(in, "%30s", str); if (res != 1 && feof(in)) return 0; if (res == 0) continue; if (strncmp(str, "read(", 5) == 0) { dir = 0; break; } else if (strncmp(str, "writev(", 7) == 0) { dir = 1; break; } } while (1) { int c = getc(in); if (c == '"') { while (1) { int val; c = getc(in); if (c == EOF) { fprintf(stderr, "eof in string\n"); break; } if (c == '\n') { fprintf(stderr, "eol in string\n"); break; } if (c == '"') break; if (c != '\\') { val = c; } else { c = getc(in); switch (c) { case 'n': val = '\n'; break; case 'r': val = '\r'; break; case 't': val = '\t'; break; case '"': val = '"'; break; case '\\': val = '\\'; break; case 'x': res = scanf("%x", &val); if (res != 1) { fprintf(stderr, "parse error\n"); continue; } break; default: fprintf(stderr, "unknown sequence: '\\%c'\n", c); continue; } } buf[len++] = val; } } if (c == '\n') break; } process_buf(dir, buf, len); memset(buf, 0, len); len = 0; } } fuse-3.18.2/test/test_abi.c0000644000175000017500000000053015156613252014436 0ustar berndbernd#define FUSE_USE_VERSION 30 #include "fuse.h" #include #include int main(void) { if (sizeof(struct fuse_file_info) != 64) { fprintf(stderr, "struct fuse_file_info size mismatch\n"); exit(1); } if (sizeof(struct fuse_conn_info) != 128) { fprintf(stderr, "struct fuse_conn_info size mismatch\n"); exit(1); } } fuse-3.18.2/test/test_ctests.py0000644000175000017500000001423615156613252015426 0ustar berndbernd#!/usr/bin/env python3 if __name__ == '__main__': import pytest import sys sys.exit(pytest.main([__file__] + sys.argv[1:])) import subprocess import pytest import platform import sys import os import logging from packaging import version from util import (wait_for_mount, umount, cleanup, base_cmdline, safe_sleep, basename, fuse_test_marker, fuse_caps, fuse_proto, create_tmpdir, parse_kernel_version) from os.path import join as pjoin import os.path pytestmark = fuse_test_marker() def test_abi(): cmdline = [ pjoin(basename, 'test', 'test_abi') ] subprocess.check_call(cmdline) @pytest.mark.skipif('FUSE_CAP_WRITEBACK_CACHE' not in fuse_caps, reason='not supported by running kernel') @pytest.mark.parametrize("writeback", (False, True)) def test_write_cache(tmpdir, writeback, output_checker): if writeback and parse_kernel_version(platform.release()) < version.parse('3.14'): pytest.skip('Requires kernel 3.14 or newer') # This test hangs under Valgrind when running close(fd) # test_write_cache.c:test_fs(). Most likely this is because of an internal # deadlock in valgrind, it probably assumes that until close() returns, # control does not come to the program. mnt_dir = str(tmpdir) print("mnt_dir: '" + mnt_dir + "'") create_tmpdir(mnt_dir) cmdline = [ pjoin(basename, 'test', 'test_write_cache'), mnt_dir ] if writeback: cmdline.append('-owriteback_cache') elif parse_kernel_version(platform.release()) >= version.parse('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) names = [ 'notify_inval_inode', 'invalidate_path' ] if fuse_proto >= (7,15): names.append('notify_store_retrieve') @pytest.mark.skipif(fuse_proto < (7,12), reason='not supported by running kernel') @pytest.mark.parametrize("name", names) @pytest.mark.parametrize("notify", (True, False)) def test_notify1(tmpdir, name, notify, output_checker): logger = logging.getLogger(__name__) mnt_dir = str(tmpdir) logger.debug(f"Mount directory: {mnt_dir}") create_tmpdir(mnt_dir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', name), '-f', '--update-interval=1', mnt_dir ] if not notify: cmdline.append('--no-notify') logger.debug(f"Command line: {' '.join(cmdline)}") mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd, stderr=output_checker.fd) try: wait_for_mount(mount_process, mnt_dir) logger.debug("Mount completed") filename = pjoin(mnt_dir, 'current_time') logger.debug(f"Target filename: {filename}") with open(filename, 'r') as fh: read1 = fh.read() logger.debug(f"First read: {read1}") logger.debug("Sleeping for 2 seconds...") safe_sleep(2) logger.debug("Sleep completed") with open(filename, 'r') as fh: read2 = fh.read() logger.debug(f"Second read: {read2}") if notify: logger.debug("Expecting reads to be different") assert read1 != read2 else: logger.debug("Expecting reads to be the same") assert read1 == read2 logger.debug("Test completed successfully") except: logger.error(f"Failure in notify test: '{' '.join(cmdline)}'") logger.exception("Exception details:") cleanup(mount_process, mnt_dir) raise else: logger.debug("Unmounting...") try: umount(mount_process, mnt_dir) logger.debug("Umount disabled") except: logger.error(f"Failure in unmount: '{' '.join(cmdline)}'") cleanup(mount_process, mnt_dir) logger.debug("Unmount completed") @pytest.mark.skipif(fuse_proto < (7,12), reason='not supported by running kernel') @pytest.mark.parametrize("notify", (True, False)) def test_notify_file_size(tmpdir, notify, output_checker): logger = logging.getLogger(__name__) mnt_dir = str(tmpdir) logger.debug(f"Mount directory: {mnt_dir}") create_tmpdir(mnt_dir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'invalidate_path'), '-f', '--update-interval=1', mnt_dir ] if not notify: cmdline.append('--no-notify') logger.debug(f"Command line: {' '.join(cmdline)}") mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd, stderr=output_checker.fd) logger.debug(f"Mount process PID: {mount_process.pid}") try: wait_for_mount(mount_process, mnt_dir) filename = pjoin(mnt_dir, 'growing') size = os.path.getsize(filename) logger.debug(f"Initial file size: {size}") logger.debug("Sleeping for 2 seconds...") safe_sleep(2) logger.debug("Sleep completed") new_size = os.path.getsize(filename) logger.debug(f"New file size: {new_size}") if notify: assert new_size > size else: assert new_size == size logger.debug("Test completed successfully") except: cleanup(mount_process, mnt_dir) raise else: try: umount(mount_process, mnt_dir) except: logger.error(f"Failure in unmount: '{' '.join(cmdline)}'") cleanup(mount_process, mnt_dir) logger.debug("Unmount completed") def test_signals(output_checker): """Test for proper signal handling (issue #1182)""" logger = logging.getLogger(__name__) logger.debug("Testing signal handling") cmdline = [ pjoin(basename, 'test', 'test_signals') ] logger.debug(f"Command line: {' '.join(cmdline)}") subprocess.run(cmdline, stdout=output_checker.fd, \ stderr=output_checker.fd, timeout=10, check=True) logger.debug("Signal handling test completed successfully") fuse-3.18.2/test/test_custom_io.py0000644000175000017500000000422115156613252016113 0ustar berndbernd#!/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 = ' 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") fuse-3.18.2/test/test_examples.py0000755000175000017500000010265115156613252015741 0ustar berndbernd#!/usr/bin/env python3 if __name__ == '__main__': import pytest import sys sys.exit(pytest.main([__file__] + sys.argv[1:])) import subprocess import os import sys import py import pytest import stat import shutil import filecmp import tempfile import time import errno import sys import platform import re from packaging import version from tempfile import NamedTemporaryFile from contextlib import contextmanager from util import (wait_for_mount, umount, cleanup, base_cmdline, safe_sleep, basename, fuse_test_marker, test_printcap, fuse_proto, fuse_caps, powerset, parse_kernel_version) from os.path import join as pjoin import logging from enum import Enum class InodeCheck(Enum): EXACT = 1 NONZERO = 2 pytestmark = fuse_test_marker() TEST_FILE = __file__ with open(TEST_FILE, 'rb') as fh: TEST_DATA = fh.read() def name_generator(__ctr=[0]): __ctr[0] += 1 return 'testfile_%d' % __ctr[0] options = [] if sys.platform == 'linux': options.append('clone_fd') def invoke_directly(mnt_dir, name, options): # Handle test/hello specially since it's not in example/ if name.startswith('test/'): path = pjoin(basename, name) else: path = pjoin(basename, 'example', name) cmdline = base_cmdline + [ path, '-f', mnt_dir, '-o', ','.join(options) ] if name == 'hello_ll': # supports single-threading only cmdline.append('-s') return cmdline def invoke_mount_fuse(mnt_dir, name, options): return base_cmdline + [ pjoin(basename, 'util', 'mount.fuse3'), name, mnt_dir, '-o', ','.join(options) ] def invoke_mount_fuse_drop_privileges(mnt_dir, name, options): if os.getuid() != 0: pytest.skip('drop_privileges requires root, skipping.') return invoke_mount_fuse(mnt_dir, name, options + ('drop_privileges',)) class raii_tmpdir: def __init__(self): self.d = tempfile.mkdtemp() def __str__(self): return str(self.d) def mkdir(self, path): return py.path.local(str(self.d)).mkdir(path) @pytest.fixture def short_tmpdir(): return raii_tmpdir() def readdir_inode(dir): cmd = base_cmdline + [ pjoin(basename, 'test', 'readdir_inode'), dir ] with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) as proc: lines = proc.communicate()[0].splitlines() lines.sort() return lines @pytest.mark.parametrize("cmdline_builder", (invoke_directly, invoke_mount_fuse, invoke_mount_fuse_drop_privileges)) @pytest.mark.parametrize("options", powerset(options)) @pytest.mark.parametrize("name", ('hello', 'hello_ll', 'test/hello')) def test_hello(tmpdir, name, options, cmdline_builder, output_checker): logger = logging.getLogger(__name__) mnt_dir = str(tmpdir) logger.debug(f"Mount directory: {mnt_dir}") cmdline = cmdline_builder(mnt_dir, name, options) logger.debug(f"Command line: {' '.join(cmdline)}") mount_process = subprocess.Popen( cmdline, stdout=output_checker.fd, stderr=output_checker.fd) logger.debug(f"Mount process PID: {mount_process.pid}") try: logger.debug("Waiting for mount...") wait_for_mount(mount_process, mnt_dir) logger.debug("Mount completed") assert os.listdir(mnt_dir) == [ 'hello' ] logger.debug("Verified 'hello' file exists in mount directory") filename = pjoin(mnt_dir, 'hello') with open(filename, 'r') as fh: assert fh.read() == 'Hello World!\n' logger.debug("Verified contents of 'hello' file") with pytest.raises(IOError) as exc_info: open(filename, 'r+') assert exc_info.value.errno == errno.EACCES logger.debug("Verified EACCES error when trying to open file for writing") with pytest.raises(IOError) as exc_info: open(filename + 'does-not-exist', 'r+') assert exc_info.value.errno == errno.ENOENT logger.debug("Verified ENOENT error for non-existent file") if name == 'hello_ll': logger.debug("Testing xattr for hello_ll") tst_xattr(mnt_dir) path = os.path.join(mnt_dir, 'hello') tst_xattr(path) except: logger.error("Exception occurred during test", exc_info=True) cleanup(mount_process, mnt_dir) raise else: logger.debug("Unmounting...") umount(mount_process, mnt_dir) logger.debug("Test completed successfully") @pytest.mark.parametrize("writeback", (False, True)) @pytest.mark.parametrize("name", ('passthrough', 'passthrough_plus', 'passthrough_fh', 'passthrough_ll', 'passthrough_zero_ino')) @pytest.mark.parametrize("debug", (False, True)) def test_passthrough(short_tmpdir, name, debug, output_checker, writeback): # Avoid false positives from libfuse debug messages if debug: output_checker.register_output(r'^ unique: [0-9]+, error: -[0-9]+ .+$', count=0) # test_syscalls prints "No error" under FreeBSD output_checker.register_output(r"^ \d\d \[[^\]]+ message: 'No error: 0'\]", count=0) mnt_dir = str(short_tmpdir.mkdir('mnt')) src_dir = str(short_tmpdir.mkdir('src')) inode_check = InodeCheck.EXACT if name == 'passthrough_plus': cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'passthrough'), '--plus', '-f', mnt_dir ] elif name == 'passthrough_zero_ino': cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'passthrough'), '--plus', '--readdir-zero-inodes', '-f', mnt_dir ] inode_check = InodeCheck.NONZERO elif name == 'passthrough_ll': cmdline = base_cmdline + \ [ pjoin(basename, 'example', name), '-f', mnt_dir, '-o', 'timeout=0' ] else: # passthrough and passthrough_fh cmdline = base_cmdline + \ [ pjoin(basename, 'example', name), '-f', mnt_dir ] # Set all timeouts to 0 for everything except passthrough_ll # (this includes passthrough, passthrough_plus, and passthrough_fh) if name != 'passthrough_ll': cmdline.extend(['-o', 'entry_timeout=0,negative_timeout=0,attr_timeout=0,ac_attr_timeout=0']) if debug: cmdline.append('-d') if writeback: if name != 'passthrough_ll': pytest.skip('example does not support writeback caching') cmdline.append('-o') cmdline.append('writeback') print(f"\nDebug: Command line: {' '.join(cmdline)}") mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd, stderr=output_checker.fd) try: wait_for_mount(mount_process, mnt_dir) work_dir = mnt_dir + src_dir tst_statvfs(work_dir) tst_readdir(src_dir, work_dir, inode_check) tst_readdir_big(src_dir, work_dir, inode_check) tst_open_read(src_dir, work_dir) tst_open_write(src_dir, work_dir) tst_create(work_dir) tst_passthrough(src_dir, work_dir, inode_check) tst_append(src_dir, work_dir) tst_seek(src_dir, work_dir) tst_mkdir(work_dir) tst_rmdir(work_dir, src_dir) tst_unlink(work_dir, src_dir) tst_symlink(work_dir) if os.getuid() == 0: tst_chown(work_dir) # Underlying fs may not have full nanosecond resolution tst_utimens(work_dir, ns_tol=1000) if inode_check == InodeCheck.EXACT: tst_link(work_dir) tst_truncate_path(work_dir) tst_truncate_fd(work_dir) tst_open_unlink(work_dir) syscall_test_cmd = [ os.path.join(basename, 'test', 'test_syscalls'), work_dir, ':' + src_dir ] if writeback: # When writeback caching is enabled, kernel has to open files for # reading even when userspace opens with O_WDONLY. This fails if the # filesystem process doesn't have special permission. syscall_test_cmd.append('-53') subprocess.check_call(syscall_test_cmd) except: cleanup(mount_process, mnt_dir) raise else: umount(mount_process, mnt_dir) @pytest.mark.parametrize("cache", (False, True)) def test_passthrough_hp(short_tmpdir, cache, output_checker): mnt_dir = str(short_tmpdir.mkdir('mnt')) src_dir = str(short_tmpdir.mkdir('src')) cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'passthrough_hp'), src_dir, mnt_dir ] cmdline.append('--foreground') if not cache: cmdline.append('--nocache') mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd, stderr=output_checker.fd) try: wait_for_mount(mount_process, mnt_dir) tst_statvfs(mnt_dir) tst_readdir(src_dir, mnt_dir) tst_readdir_big(src_dir, mnt_dir) tst_open_read(src_dir, mnt_dir) tst_open_write(src_dir, mnt_dir) tst_create(mnt_dir) if not cache: tst_passthrough(src_dir, mnt_dir) tst_append(src_dir, mnt_dir) tst_seek(src_dir, mnt_dir) tst_mkdir(mnt_dir) if cache: # if cache is enabled, no operations should go through # src_dir as the cache will become stale. tst_rmdir(mnt_dir) tst_unlink(mnt_dir) else: tst_rmdir(mnt_dir, src_dir) tst_unlink(mnt_dir, src_dir) tst_symlink(mnt_dir) if os.getuid() == 0: tst_chown(mnt_dir) # Underlying fs may not have full nanosecond resolution tst_utimens(mnt_dir, ns_tol=1000) tst_link(mnt_dir) tst_truncate_path(mnt_dir) tst_truncate_fd(mnt_dir) tst_open_unlink(mnt_dir) # test_syscalls assumes that changes in source directory # will be reflected immediately in mountpoint, so we # can't use it. if not cache: syscall_test_cmd = [ os.path.join(basename, 'test', 'test_syscalls'), mnt_dir, ':' + src_dir ] # unlinked testfiles check fails without kernel fix # "fuse: fix illegal access to inode with reused nodeid" # so opt-in for this test from kernel 5.14 if parse_kernel_version(platform.release()) >= version.parse('5.14'): syscall_test_cmd.append('-u') subprocess.check_call(syscall_test_cmd) except: cleanup(mount_process, mnt_dir) raise else: umount(mount_process, mnt_dir) @pytest.mark.skipif(fuse_proto < (7,11), reason='not supported by running kernel') def test_ioctl(tmpdir, output_checker): progname = pjoin(basename, 'example', 'ioctl') if not os.path.exists(progname): pytest.skip('%s not built' % os.path.basename(progname)) # Check if binary is 32-bit file_output = subprocess.check_output(['file', progname]).decode() if 'ELF 32-bit' in file_output and platform.machine() == 'x86_64': pytest.skip('ioctl test not supported for 32-bit binary on 64-bit system') mnt_dir = str(tmpdir) testfile = pjoin(mnt_dir, 'fioc') cmdline = base_cmdline + [progname, '-f', mnt_dir ] mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd, stderr=output_checker.fd) try: wait_for_mount(mount_process, mnt_dir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'ioctl_client'), testfile ] assert subprocess.check_output(cmdline) == b'0\n' with open(testfile, 'wb') as fh: fh.write(b'foobar') assert subprocess.check_output(cmdline) == b'6\n' subprocess.check_call(cmdline + [ '3' ]) with open(testfile, 'rb') as fh: assert fh.read()== b'foo' except: cleanup(mount_process, mnt_dir) raise else: umount(mount_process, mnt_dir) def test_poll(tmpdir, output_checker): mnt_dir = str(tmpdir) cmdline = base_cmdline + [pjoin(basename, 'example', 'poll'), '-f', mnt_dir ] mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd, stderr=output_checker.fd) try: wait_for_mount(mount_process, mnt_dir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'poll_client') ] subprocess.check_call(cmdline, cwd=mnt_dir) except: cleanup(mount_process, mnt_dir) raise else: umount(mount_process, mnt_dir) def test_null(tmpdir, output_checker): progname = pjoin(basename, 'example', 'null') if not os.path.exists(progname): pytest.skip('%s not built' % os.path.basename(progname)) mnt_file = str(tmpdir) + '/file' with open(mnt_file, 'w') as fh: fh.write('dummy') cmdline = base_cmdline + [ progname, '-f', mnt_file ] mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd, stderr=output_checker.fd) def test_fn(name): return os.stat(name).st_size > 4000 try: wait_for_mount(mount_process, mnt_file, test_fn) with open(mnt_file, 'rb') as fh: assert fh.read(382) == b'\0' * 382 with open(mnt_file, 'wb') as fh: fh.write(b'whatever') except: cleanup(mount_process, mnt_file) raise else: umount(mount_process, mnt_file) @pytest.mark.skipif(fuse_proto < (7,12), reason='not supported by running kernel') @pytest.mark.parametrize("only_expire", ("invalidate_entries", "expire_entries", "inc_epoch")) @pytest.mark.parametrize("notify", (True, False)) def test_notify_inval_entry(tmpdir, only_expire, notify, output_checker): mnt_dir = str(tmpdir) cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'notify_inval_entry'), '-f', '--update-interval=1', '--timeout=5', mnt_dir ] if not notify: cmdline.append('--no-notify') if only_expire == "expire_entries": cmdline.append('--only-expire') if "FUSE_CAP_EXPIRE_ONLY" not in fuse_caps: pytest.skip('only-expire not supported by running kernel') elif only_expire == "inc_epoch": cmdline.append('--inc-epoch') if fuse_proto < (7,44): pytest.skip('inc-epoch not supported by running kernel') mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd, stderr=output_checker.fd) try: wait_for_mount(mount_process, mnt_dir) fname = pjoin(mnt_dir, os.listdir(mnt_dir)[0]) try: os.stat(fname) except FileNotFoundError: # We may have hit a race condition and issued # readdir just before the name changed fname = pjoin(mnt_dir, os.listdir(mnt_dir)[0]) os.stat(fname) safe_sleep(2) if not notify: os.stat(fname) safe_sleep(5) with pytest.raises(FileNotFoundError): os.stat(fname) except: cleanup(mount_process, mnt_dir) raise 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): progname = pjoin(basename, 'example', 'cuse') if not os.path.exists(progname): pytest.skip('%s not built' % os.path.basename(progname)) # Check if binary is 32-bit file_output = subprocess.check_output(['file', progname]).decode() if 'ELF 32-bit' in file_output and platform.machine() == 'x86_64': pytest.skip('cuse test not supported for 32-bit binary on 64-bit system') # Valgrind warns about unknown ioctls, that's ok output_checker.register_output(r'^==([0-9]+).+unhandled ioctl.+\n' r'==\1== \s{3}.+\n' r'==\1== \s{3}.+$', count=0) devname = 'cuse-test-%d' % os.getpid() devpath = '/dev/%s' % devname cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'cuse'), '-f', '--name=%s' % devname ] mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd, stderr=output_checker.fd) cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'cuse_client'), devpath ] try: wait_for_mount(mount_process, devpath, test_fn=os.path.exists) assert subprocess.check_output(cmdline + ['s']) == b'0\n' data = b'some test data' off = 5 proc = subprocess.Popen(cmdline + [ 'w', str(len(data)), str(off) ], stdin=subprocess.PIPE) proc.stdin.write(data) proc.stdin.close() assert proc.wait(timeout=10) == 0 size = str(off + len(data)).encode() + b'\n' assert subprocess.check_output(cmdline + ['s']) == size out = subprocess.check_output( cmdline + [ 'r', str(off + len(data) + 2), '0' ]) assert out == (b'\0' * off) + data 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) try: yield fd finally: os.close(fd) def os_create(name): os.close(os.open(name, os.O_CREAT | os.O_RDWR)) def tst_unlink(mnt_dir, src_dir=None): name = name_generator() fullname = mnt_dir + "/" + name srcname = fullname if src_dir is not None: srcname = pjoin(src_dir, name) with open(srcname, 'wb') as fh: fh.write(b'hello') assert name in os.listdir(mnt_dir) os.unlink(fullname) with pytest.raises(OSError) as exc_info: os.stat(fullname) assert exc_info.value.errno == errno.ENOENT assert name not in os.listdir(mnt_dir) def tst_mkdir(mnt_dir): dirname = name_generator() fullname = mnt_dir + "/" + dirname os.mkdir(fullname) fstat = os.stat(fullname) assert stat.S_ISDIR(fstat.st_mode) assert os.listdir(fullname) == [] # Some filesystem (e.g. BTRFS) don't track st_nlink for directories assert fstat.st_nlink in (1,2) assert dirname in os.listdir(mnt_dir) def tst_rmdir(mnt_dir, src_dir=None): name = name_generator() fullname = mnt_dir + "/" + name srcname = fullname if src_dir is not None: srcname = pjoin(src_dir, name) os.mkdir(srcname) assert name in os.listdir(mnt_dir) os.rmdir(fullname) with pytest.raises(OSError) as exc_info: os.stat(fullname) assert exc_info.value.errno == errno.ENOENT assert name not in os.listdir(mnt_dir) def tst_symlink(mnt_dir): linkname = name_generator() fullname = mnt_dir + "/" + linkname os.symlink("/imaginary/dest", fullname) fstat = os.lstat(fullname) assert stat.S_ISLNK(fstat.st_mode) assert os.readlink(fullname) == "/imaginary/dest" assert fstat.st_nlink == 1 assert linkname in os.listdir(mnt_dir) def tst_create(mnt_dir): name = name_generator() fullname = pjoin(mnt_dir, name) with pytest.raises(OSError) as exc_info: os.stat(fullname) assert exc_info.value.errno == errno.ENOENT assert name not in os.listdir(mnt_dir) fd = os.open(fullname, os.O_CREAT | os.O_RDWR) os.close(fd) assert name in os.listdir(mnt_dir) fstat = os.lstat(fullname) assert stat.S_ISREG(fstat.st_mode) assert fstat.st_nlink == 1 assert fstat.st_size == 0 def tst_chown(mnt_dir): filename = pjoin(mnt_dir, name_generator()) os.mkdir(filename) fstat = os.lstat(filename) uid = fstat.st_uid gid = fstat.st_gid uid_new = uid + 1 os.chown(filename, uid_new, -1) fstat = os.lstat(filename) assert fstat.st_uid == uid_new assert fstat.st_gid == gid gid_new = gid + 1 os.chown(filename, -1, gid_new) fstat = os.lstat(filename) assert fstat.st_uid == uid_new assert fstat.st_gid == gid_new def tst_open_read(src_dir, mnt_dir): name = name_generator() with open(pjoin(src_dir, name), 'wb') as fh_out, \ open(TEST_FILE, 'rb') as fh_in: shutil.copyfileobj(fh_in, fh_out) assert filecmp.cmp(pjoin(mnt_dir, name), TEST_FILE, False) def tst_open_write(src_dir, mnt_dir): name = name_generator() os_create(pjoin(src_dir, name)) fullname = pjoin(mnt_dir, name) with open(fullname, 'wb') as fh_out, \ open(TEST_FILE, 'rb') as fh_in: shutil.copyfileobj(fh_in, fh_out) assert filecmp.cmp(fullname, TEST_FILE, False) def tst_append(src_dir, mnt_dir): name = name_generator() os_create(pjoin(src_dir, name)) fullname = pjoin(mnt_dir, name) with os_open(fullname, os.O_WRONLY) as fd: os.write(fd, b'foo\n') with os_open(fullname, os.O_WRONLY|os.O_APPEND) as fd: os.write(fd, b'bar\n') with open(fullname, 'rb') as fh: assert fh.read() == b'foo\nbar\n' def tst_seek(src_dir, mnt_dir): name = name_generator() os_create(pjoin(src_dir, name)) fullname = pjoin(mnt_dir, name) with os_open(fullname, os.O_WRONLY) as fd: os.lseek(fd, 1, os.SEEK_SET) os.write(fd, b'foobar\n') with os_open(fullname, os.O_WRONLY) as fd: os.lseek(fd, 4, os.SEEK_SET) os.write(fd, b'com') with open(fullname, 'rb') as fh: assert fh.read() == b'\0foocom\n' def tst_open_unlink(mnt_dir): name = pjoin(mnt_dir, name_generator()) data1 = b'foo' data2 = b'bar' fullname = pjoin(mnt_dir, name) with open(fullname, 'wb+', buffering=0) as fh: fh.write(data1) os.unlink(fullname) with pytest.raises(OSError) as exc_info: os.stat(fullname) assert exc_info.value.errno == errno.ENOENT assert name not in os.listdir(mnt_dir) fh.write(data2) fh.seek(0) assert fh.read() == data1+data2 def tst_statvfs(mnt_dir): os.statvfs(mnt_dir) def tst_link(mnt_dir): name1 = pjoin(mnt_dir, name_generator()) name2 = pjoin(mnt_dir, name_generator()) shutil.copyfile(TEST_FILE, name1) assert filecmp.cmp(name1, TEST_FILE, False) fstat1 = os.lstat(name1) assert fstat1.st_nlink == 1 os.link(name1, name2) fstat1 = os.lstat(name1) fstat2 = os.lstat(name2) assert fstat1 == fstat2 assert fstat1.st_nlink == 2 assert os.path.basename(name2) in os.listdir(mnt_dir) assert filecmp.cmp(name1, name2, False) # Since RELEASE requests are asynchronous, it is possible that # libfuse still considers the file to be open at this point # and (since -o hard_remove is not used) renames it instead of # deleting it. In that case, the following lstat() call will # still report an st_nlink value of 2 (cf. issue #157). os.unlink(name2) assert os.path.basename(name2) not in os.listdir(mnt_dir) with pytest.raises(FileNotFoundError): os.lstat(name2) # See above, we may have to wait until RELEASE has been # received before the st_nlink value is correct. maxwait = time.time() + 2 fstat1 = os.lstat(name1) while fstat1.st_nlink == 2 and time.time() < maxwait: fstat1 = os.lstat(name1) time.sleep(0.1) assert fstat1.st_nlink == 1 os.unlink(name1) def tst_inodes_nonzero(lines): inode_nums = [int(line.split()[0]) for line in lines] assert all(i != 0 for i in inode_nums), inode_nums def tst_inode(inode_check, actual, expected): if inode_check == InodeCheck.EXACT: assert expected == actual elif inode_check == InodeCheck.NONZERO: assert actual != 0 def tst_readdir(src_dir, mnt_dir, inode_check=InodeCheck.EXACT): newdir = name_generator() src_newdir = pjoin(src_dir, newdir) mnt_newdir = pjoin(mnt_dir, newdir) file_ = src_newdir + "/" + name_generator() subdir = src_newdir + "/" + name_generator() subfile = subdir + "/" + name_generator() os.mkdir(src_newdir) shutil.copyfile(TEST_FILE, file_) os.mkdir(subdir) shutil.copyfile(TEST_FILE, subfile) listdir_is = os.listdir(mnt_newdir) listdir_is.sort() listdir_should = [ os.path.basename(file_), os.path.basename(subdir) ] listdir_should.sort() assert listdir_is == listdir_should inodes_is = readdir_inode(mnt_newdir) if inode_check == InodeCheck.EXACT: inodes_should = readdir_inode(src_newdir) assert inodes_is == inodes_should elif inode_check == InodeCheck.NONZERO: tst_inodes_nonzero(inodes_is) os.unlink(file_) os.unlink(subfile) os.rmdir(subdir) os.rmdir(src_newdir) def tst_readdir_big(src_dir, mnt_dir, inode_check=InodeCheck.EXACT): # Add enough entries so that readdir needs to be called # multiple times. fnames = [] for i in range(500): fname = ('A rather long filename to make sure that we ' 'fill up the buffer - ' * 3) + str(i) with open(pjoin(src_dir, fname), 'w') as fh: fh.write('File %d' % i) fnames.append(fname) listdir_is = sorted(os.listdir(mnt_dir)) listdir_should = sorted(os.listdir(src_dir)) assert listdir_is == listdir_should inodes_is = readdir_inode(mnt_dir) if inode_check == InodeCheck.EXACT: inodes_should = readdir_inode(src_dir) assert inodes_is == inodes_should elif inode_check == InodeCheck.NONZERO: tst_inodes_nonzero(inodes_is) for fname in fnames: # A comment just to get a diff stat_src = os.stat(pjoin(src_dir, fname)) stat_mnt = os.stat(pjoin(mnt_dir, fname)) tst_inode(inode_check, stat_mnt.st_ino, stat_src.st_ino) assert stat_src.st_mtime == stat_mnt.st_mtime assert stat_src.st_ctime == stat_mnt.st_ctime assert stat_src.st_size == stat_mnt.st_size os.unlink(pjoin(src_dir, fname)) def tst_truncate_path(mnt_dir): assert len(TEST_DATA) > 1024 filename = pjoin(mnt_dir, name_generator()) with open(filename, 'wb') as fh: fh.write(TEST_DATA) fstat = os.stat(filename) size = fstat.st_size assert size == len(TEST_DATA) # Add zeros at the end os.truncate(filename, size + 1024) assert os.stat(filename).st_size == size + 1024 with open(filename, 'rb') as fh: assert fh.read(size) == TEST_DATA assert fh.read(1025) == b'\0' * 1024 # Truncate data os.truncate(filename, size - 1024) assert os.stat(filename).st_size == size - 1024 with open(filename, 'rb') as fh: assert fh.read(size) == TEST_DATA[:size-1024] os.unlink(filename) def tst_truncate_fd(mnt_dir): assert len(TEST_DATA) > 1024 with NamedTemporaryFile('w+b', 0, dir=mnt_dir) as fh: fd = fh.fileno() fh.write(TEST_DATA) fstat = os.fstat(fd) size = fstat.st_size assert size == len(TEST_DATA) # Add zeros at the end os.ftruncate(fd, size + 1024) assert os.fstat(fd).st_size == size + 1024 fh.seek(0) assert fh.read(size) == TEST_DATA assert fh.read(1025) == b'\0' * 1024 # Truncate data os.ftruncate(fd, size - 1024) assert os.fstat(fd).st_size == size - 1024 fh.seek(0) assert fh.read(size) == TEST_DATA[:size-1024] def tst_utimens(mnt_dir, ns_tol=0): filename = pjoin(mnt_dir, name_generator()) os.mkdir(filename) fstat = os.lstat(filename) atime = fstat.st_atime + 42.28 mtime = fstat.st_mtime - 42.23 if sys.version_info < (3,3): os.utime(filename, (atime, mtime)) else: atime_ns = fstat.st_atime_ns + int(42.28*1e9) mtime_ns = fstat.st_mtime_ns - int(42.23*1e9) os.utime(filename, None, ns=(atime_ns, mtime_ns)) fstat = os.lstat(filename) assert abs(fstat.st_atime - atime) < 1 assert abs(fstat.st_mtime - mtime) < 1 if sys.version_info >= (3,3): assert abs(fstat.st_atime_ns - atime_ns) <= ns_tol assert abs(fstat.st_mtime_ns - mtime_ns) <= ns_tol def tst_passthrough(src_dir, mnt_dir, inode_check=InodeCheck.EXACT): name = name_generator() src_name = pjoin(src_dir, name) mnt_name = pjoin(mnt_dir, name) print(f"\nDebug: Creating file {name}") print(f"Debug: src_name={src_name}") print(f"Debug: mnt_name={mnt_name}") # First test: write to source directory assert name not in os.listdir(src_dir) assert name not in os.listdir(mnt_dir) with open(src_name, 'w') as fh: fh.write('Hello, world') print(f"Debug: File written to src_name") start_time = time.time() while time.time() - start_time < 10: # 10 second timeout if name in os.listdir(mnt_dir): break print(f"Debug: Waiting for file to appear... ({time.time() - start_time:.1f}s)") time.sleep(0.1) else: pytest.fail("File did not appear in mount directory within 10 seconds") assert name in os.listdir(src_dir) assert name in os.listdir(mnt_dir) # Compare relevant stat attributes src_stat = os.stat(src_name) mnt_stat = os.stat(mnt_name) assert src_stat.st_mode == mnt_stat.st_mode tst_inode(inode_check, mnt_stat.st_ino, src_stat.st_ino) assert src_stat.st_size == mnt_stat.st_size assert src_stat.st_mtime == mnt_stat.st_mtime # Second test: write to mount directory name = name_generator() src_name = pjoin(src_dir, name) mnt_name = pjoin(mnt_dir, name) assert name not in os.listdir(src_dir) assert name not in os.listdir(mnt_dir) with open(mnt_name, 'w') as fh: fh.write('Hello, world') assert name in os.listdir(src_dir) assert name in os.listdir(mnt_dir) # Compare relevant stat attributes src_stat = os.stat(src_name) mnt_stat = os.stat(mnt_name) assert src_stat.st_mode == mnt_stat.st_mode tst_inode(inode_check, mnt_stat.st_ino, src_stat.st_ino) assert src_stat.st_size == mnt_stat.st_size assert abs(src_stat.st_mtime - mnt_stat.st_mtime) < 0.01 def tst_xattr(path): 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 fuse-3.18.2/test/test_setattr.c0000644000175000017500000001054615156613252015401 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2016 Nikolaus Rath This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ #define FUSE_USE_VERSION 30 /* Not really needed - just to test build with FUSE_USE_VERSION == 30 */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __linux__ #include #else #include #endif #define FILE_INO 2 #define FILE_NAME "truncate_me" static int got_fh; static mode_t file_mode = S_IFREG | 0644; static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) { stbuf->st_ino = ino; if (ino == FUSE_ROOT_ID) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 1; } else if (ino == FILE_INO) { stbuf->st_mode = file_mode; stbuf->st_nlink = 1; stbuf->st_size = 0; } else return -1; return 0; } static void tfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse_entry_param e; memset(&e, 0, sizeof(e)); if (parent != FUSE_ROOT_ID) goto err_out; else if (strcmp(name, FILE_NAME) == 0) e.ino = FILE_INO; else goto err_out; if (tfs_stat(e.ino, &e.attr) != 0) goto err_out; fuse_reply_entry(req, &e); return; err_out: fuse_reply_err(req, ENOENT); } static void tfs_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 (tfs_stat(ino, &stbuf) != 0) fuse_reply_err(req, ENOENT); else fuse_reply_attr(req, &stbuf, 5); } static void tfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { if (ino == FUSE_ROOT_ID) fuse_reply_err(req, EISDIR); else { assert(ino == FILE_INO); fi->fh = FILE_INO; fuse_reply_open(req, fi); } } static void tfs_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi) { if(ino != FILE_INO || !(to_set & FUSE_SET_ATTR_MODE)) { fuse_reply_err(req, EINVAL); return; } if(fi == NULL) fprintf(stderr, "setattr with fi == NULL\n"); else if (fi->fh != FILE_INO) fprintf(stderr, "setattr with wrong fi->fh\n"); else { fprintf(stderr, "setattr ok\n"); got_fh = 1; file_mode = attr->st_mode; } tfs_getattr(req, ino, fi); } static struct fuse_lowlevel_ops tfs_oper = { .lookup = tfs_lookup, .getattr = tfs_getattr, .open = tfs_open, .setattr = tfs_setattr, }; static void* run_fs(void *data) { struct fuse_session *se = (struct fuse_session*) data; assert(fuse_session_loop(se) == 0); return NULL; } static void test_fs(char *mountpoint) { char fname[PATH_MAX]; int fd; assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME, mountpoint) > 0); fd = open(fname, O_WRONLY); if (fd == -1) { perror(fname); assert(0); } assert(fchmod(fd, 0600) == 0); close(fd); } int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_session *se; struct fuse_cmdline_opts fuse_opts; pthread_t fs_thread; assert(fuse_parse_cmdline(&args, &fuse_opts) == 0); #ifndef __FreeBSD__ assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0); #endif se = fuse_session_new(&args, &tfs_oper, sizeof(tfs_oper), NULL); assert (se != NULL); assert(fuse_set_signal_handlers(se) == 0); assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0); /* Start file-system thread */ assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0); /* Do test */ test_fs(fuse_opts.mountpoint); /* Stop file system */ assert(pthread_cancel(fs_thread) == 0); fuse_session_unmount(se); assert(got_fh == 1); fuse_remove_signal_handlers(se); fuse_session_destroy(se); printf("Test completed successfully.\n"); return 0; } /** * Local Variables: * mode: c * indent-tabs-mode: nil * c-basic-offset: 4 * End: */ fuse-3.18.2/test/test_signals.c0000644000175000017500000001121115156613252015341 0ustar berndbernd/* * FUSE: Filesystem in Userspace * Copyright (C) 2025 Bernd Schubert * * Test for signal handling in libfuse. * * This program can be distributed under the terms of the GNU LGPLv2. * See the file GPL2.txt */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17) #include "fuse_config.h" #include "fuse_lowlevel.h" #include "fuse_i.h" #include #include #include #include #include #include #include #include #include #include static void test_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { (void)parent; (void)name; /* Simulate slow lookup to test signal interruption */ sleep(2); fuse_reply_err(req, ENOENT); } static void test_ll_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { (void)ino; (void)fi; /* Simulate slow getattr to test signal interruption */ sleep(2); fuse_reply_err(req, ENOENT); } static const struct fuse_lowlevel_ops test_ll_ops = { .lookup = test_ll_lookup, .getattr = test_ll_getattr, }; static void *signal_sender_thread(void *arg) { (void)arg; usleep(2 * 1000 * 1000); /* Send SIGTERM to the process */ kill(getpid(), SIGTERM); return NULL; } static void fork_child(void) { struct fuse_args args = FUSE_ARGS_INIT(0, NULL); struct fuse_session *se; struct fuse_loop_config *loop_config; pthread_t sig_thread; char *mountpoint = NULL; int ret = -1; /* Add the program name to arg[0] */ if (fuse_opt_add_arg(&args, "test_signals")) { fprintf(stderr, "Failed to add argument\n"); goto out_free_mountpoint; } /* Add debug flag to see more output */ fuse_opt_add_arg(&args, "-d"); /* Create temporary mount point */ mountpoint = strdup("/tmp/fuse_test_XXXXXX"); if (!mountpoint || !mkdtemp(mountpoint)) { fprintf(stderr, "Failed to create temp dir\n"); goto out_free_args; } /* Create session */ se = fuse_session_new(&args, &test_ll_ops, sizeof(test_ll_ops), NULL); if (!se) { fprintf(stderr, "Failed to create FUSE session\n"); goto out_free_mountpoint; } /* Mount filesystem */ if (fuse_session_mount(se, mountpoint)) { fprintf(stderr, "Failed to mount FUSE filesystem\n"); goto out_destroy_session; } /* Create loop config */ loop_config = fuse_loop_cfg_create(); if (!loop_config) { fprintf(stderr, "Failed to create loop config\n"); goto out_unmount; } fuse_loop_cfg_set_clone_fd(loop_config, 0); fuse_loop_cfg_set_max_threads(loop_config, 2); /* Set up signal handlers */ if (fuse_set_signal_handlers(se)) { fprintf(stderr, "Failed to set up signal handlers\n"); goto out_destroy_config; } /* Create thread that will send signals */ if (pthread_create(&sig_thread, NULL, signal_sender_thread, NULL)) { fprintf(stderr, "Failed to create signal sender thread\n"); goto out_remove_handlers; } /* Enter FUSE loop */ ret = fuse_session_loop_mt_312(se, loop_config); printf("Debug: fuse_session_loop_mt_312 returned %d\n", ret); printf("Debug: session exited state: %d\n", fuse_session_exited(se)); printf("Debug: session status: %d\n", se->error); /* Check exit status before cleanup */ int clean_exit = (fuse_session_exited(se) && se->error == SIGTERM); /* Clean up */ pthread_join(sig_thread, NULL); fuse_remove_signal_handlers(se); fuse_session_unmount(se); fuse_session_destroy(se); fuse_loop_cfg_destroy(loop_config); rmdir(mountpoint); free(mountpoint); fuse_opt_free_args(&args); /* Use saved exit status */ if (clean_exit) { printf("Debug: Clean shutdown via SIGTERM\n"); exit(0); } printf("Debug: Exiting with status %d\n", ret != 0); exit(ret != 0); out_remove_handlers: fuse_remove_signal_handlers(se); out_destroy_config: fuse_loop_cfg_destroy(loop_config); out_unmount: fuse_session_unmount(se); out_destroy_session: fuse_session_destroy(se); out_free_mountpoint: rmdir(mountpoint); free(mountpoint); out_free_args: fuse_opt_free_args(&args); exit(1); } static void run_test_in_child(void) { pid_t child; int status; child = fork(); if (child == -1) { perror("fork"); exit(1); } if (child == 0) fork_child(); /* In parent process */ if (waitpid(child, &status, 0) == -1) { perror("waitpid"); exit(1); } /* Check if child exited due to SIGTERM - this is expected */ if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM) { printf("Child process terminated by SIGTERM as expected\n"); exit(0); } /* For any other type of exit, maintain existing behavior */ exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1); } int main(void) { printf("Testing SIGTERM handling in libfuse\n"); run_test_in_child(); printf("SIGTERM handling test passed\n"); return 0; } fuse-3.18.2/test/test_syscalls.c0000644000175000017500000012472115156613252015551 0ustar berndbernd#define _GNU_SOURCE #include "fuse_config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef ALLPERMS # define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)/* 07777 */ #endif static const char *basepath; static const char *basepath_r; static char testfile[1024]; static char testfile2[1024]; static char testdir[1024]; static char testdir2[1024]; static char testsock[1024]; static char subfile[1280]; static char testfile_r[1024]; static char testfile2_r[1024]; static char testdir_r[1024]; static char testdir2_r[1024]; static char subfile_r[1280]; static char testname[256]; static char testdata[] = "abcdefghijklmnopqrstuvwxyz"; static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./"; static const char *testdir_files[] = { "f1", "f2", NULL}; static long seekdir_offsets[4]; static char zerodata[4096]; static int testdatalen = sizeof(testdata) - 1; static int testdata2len = sizeof(testdata2) - 1; static unsigned int testnum = 0; static unsigned int select_test = 0; static unsigned int skip_test = 0; static unsigned int unlinked_test = 0; #define MAX_ENTRIES 1024 #define MAX_TESTS 100 static struct test { int fd; struct stat stat; } tests[MAX_TESTS]; static void test_perror(const char *func, const char *msg) { fprintf(stderr, "%s %s() - %s: %s\n", testname, func, msg, strerror(errno)); } static void test_error(const char *func, const char *msg, ...) __attribute__ ((format (printf, 2, 3))); static void __start_test(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); static void test_error(const char *func, const char *msg, ...) { va_list ap; fprintf(stderr, "%s %s() - ", testname, func); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); fprintf(stderr, "\n"); } static int is_dot_or_dotdot(const char *name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } static void success(void) { fprintf(stderr, "%s OK\n", testname); } #define this_test (&tests[testnum-1]) #define next_test (&tests[testnum]) static void __start_test(const char *fmt, ...) { unsigned int n; va_list ap; n = sprintf(testname, "%3i [", testnum); va_start(ap, fmt); n += vsprintf(testname + n, fmt, ap); va_end(ap); sprintf(testname + n, "]"); // Use dedicated testfile per test sprintf(testfile, "%s/testfile.%d", basepath, testnum); sprintf(testfile_r, "%s/testfile.%d", basepath_r, testnum); if (testnum > MAX_TESTS) { fprintf(stderr, "%s - too many tests\n", testname); exit(1); } this_test->fd = -1; } #define start_test(msg, args...) { \ testnum++; \ if ((select_test && testnum != select_test) || \ (testnum == skip_test)) { \ return 0; \ } \ __start_test(msg, ##args); \ } #define PERROR(msg) test_perror(__FUNCTION__, msg) #define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) static int st_check_size(struct stat *st, int len) { if (st->st_size != len) { ERROR("length %u instead of %u", (int) st->st_size, (int) len); return -1; } return 0; } static int check_size(const char *path, int len) { struct stat stbuf; int res = stat(path, &stbuf); if (res == -1) { PERROR("stat"); return -1; } return st_check_size(&stbuf, len); } static int check_testfile_size(const char *path, int len) { this_test->stat.st_size = len; return check_size(path, len); } static int st_check_type(struct stat *st, mode_t type) { if ((st->st_mode & S_IFMT) != type) { ERROR("type 0%o instead of 0%o", st->st_mode & S_IFMT, type); return -1; } return 0; } static int check_type(const char *path, mode_t type) { struct stat stbuf; int res = lstat(path, &stbuf); if (res == -1) { PERROR("lstat"); return -1; } return st_check_type(&stbuf, type); } static int st_check_mode(struct stat *st, mode_t mode) { if ((st->st_mode & ALLPERMS) != mode) { ERROR("mode 0%o instead of 0%o", st->st_mode & ALLPERMS, mode); return -1; } return 0; } static int check_mode(const char *path, mode_t mode) { struct stat stbuf; int res = lstat(path, &stbuf); if (res == -1) { PERROR("lstat"); return -1; } return st_check_mode(&stbuf, mode); } static int check_testfile_mode(const char *path, mode_t mode) { this_test->stat.st_mode &= ~ALLPERMS; this_test->stat.st_mode |= mode; return check_mode(path, mode); } static int check_times(const char *path, time_t atime, time_t mtime) { int err = 0; struct stat stbuf; int res = lstat(path, &stbuf); if (res == -1) { PERROR("lstat"); return -1; } if (stbuf.st_atime != atime) { ERROR("atime %li instead of %li", stbuf.st_atime, atime); err--; } if (stbuf.st_mtime != mtime) { ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime); err--; } if (err) return -1; return 0; } #if 0 static int fcheck_times(int fd, time_t atime, time_t mtime) { int err = 0; struct stat stbuf; int res = fstat(fd, &stbuf); if (res == -1) { PERROR("fstat"); return -1; } if (stbuf.st_atime != atime) { ERROR("atime %li instead of %li", stbuf.st_atime, atime); err--; } if (stbuf.st_mtime != mtime) { ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime); err--; } if (err) return -1; return 0; } #endif static int st_check_nlink(struct stat *st, nlink_t nlink) { if (st->st_nlink != nlink) { ERROR("nlink %li instead of %li", (long) st->st_nlink, (long) nlink); return -1; } return 0; } static int check_nlink(const char *path, nlink_t nlink) { struct stat stbuf; int res = lstat(path, &stbuf); if (res == -1) { PERROR("lstat"); return -1; } return st_check_nlink(&stbuf, nlink); } static int fcheck_stat(int fd, int flags, struct stat *st) { struct stat stbuf; int res = fstat(fd, &stbuf); if (res == -1) { 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 || errno == EBADF) return 0; } PERROR("fstat"); return -1; } int err = 0; err += st_check_type(&stbuf, st->st_mode & S_IFMT); err += st_check_mode(&stbuf, st->st_mode & ALLPERMS); err += st_check_size(&stbuf, st->st_size); err += st_check_nlink(&stbuf, st->st_nlink); return err; } static int check_nonexist(const char *path) { struct stat stbuf; int res = lstat(path, &stbuf); if (res == 0) { ERROR("file should not exist"); return -1; } if (errno != ENOENT) { ERROR("file should not exist: %s", strerror(errno)); return -1; } return 0; } static int check_buffer(const char *buf, const char *data, unsigned len) { if (memcmp(buf, data, len) != 0) { ERROR("data mismatch"); return -1; } return 0; } static int check_data(const char *path, const char *data, int offset, unsigned len) { char buf[4096]; int res; int fd = open(path, O_RDONLY); if (fd == -1) { PERROR("open"); return -1; } if (lseek(fd, offset, SEEK_SET) == (off_t) -1) { PERROR("lseek"); close(fd); return -1; } while (len) { int rdlen = len < sizeof(buf) ? len : sizeof(buf); res = read(fd, buf, rdlen); if (res == -1) { PERROR("read"); close(fd); return -1; } if (res != rdlen) { ERROR("short read: %u instead of %u", res, rdlen); close(fd); return -1; } if (check_buffer(buf, data, rdlen) != 0) { close(fd); return -1; } data += rdlen; len -= rdlen; } res = close(fd); if (res == -1) { PERROR("close"); return -1; } return 0; } static int fcheck_data(int fd, const char *data, int offset, unsigned len) { char buf[4096]; int res; if (lseek(fd, offset, SEEK_SET) == (off_t) -1) { PERROR("lseek"); return -1; } while (len) { int rdlen = len < sizeof(buf) ? len : sizeof(buf); res = read(fd, buf, rdlen); if (res == -1) { PERROR("read"); return -1; } if (res != rdlen) { ERROR("short read: %u instead of %u", res, rdlen); return -1; } if (check_buffer(buf, data, rdlen) != 0) { return -1; } data += rdlen; len -= rdlen; } return 0; } static int check_dir_contents(const char *path, const char **contents) { int i; int res; int err = 0; int found[MAX_ENTRIES]; const char *cont[MAX_ENTRIES]; DIR *dp; for (i = 0; contents[i]; i++) { assert(i < MAX_ENTRIES - 3); found[i] = 0; cont[i] = contents[i]; } cont[i] = NULL; dp = opendir(path); if (dp == NULL) { PERROR("opendir"); return -1; } memset(found, 0, sizeof(found)); while(1) { struct dirent *de; errno = 0; de = readdir(dp); if (de == NULL) { if (errno) { PERROR("readdir"); closedir(dp); return -1; } break; } if (is_dot_or_dotdot(de->d_name)) continue; for (i = 0; cont[i] != NULL; i++) { assert(i < MAX_ENTRIES); if (strcmp(cont[i], de->d_name) == 0) { if (found[i]) { ERROR("duplicate entry <%s>", de->d_name); err--; } else found[i] = 1; break; } } if (!cont[i]) { ERROR("unexpected entry <%s>", de->d_name); err --; } } for (i = 0; cont[i] != NULL; i++) { if (!found[i]) { ERROR("missing entry <%s>", cont[i]); err--; } } res = closedir(dp); if (res == -1) { PERROR("closedir"); return -1; } if (err) return -1; return 0; } static int create_file(const char *path, const char *data, int len) { int res; int fd; unlink(path); fd = creat(path, 0644); if (fd == -1) { PERROR("creat"); return -1; } if (len) { res = write(fd, data, len); if (res == -1) { PERROR("write"); close(fd); return -1; } if (res != len) { ERROR("write is short: %u instead of %u", res, len); close(fd); return -1; } } res = close(fd); if (res == -1) { PERROR("close"); return -1; } res = check_type(path, S_IFREG); if (res == -1) return -1; res = check_mode(path, 0644); if (res == -1) return -1; res = check_nlink(path, 1); if (res == -1) return -1; res = check_size(path, len); if (res == -1) return -1; if (len) { res = check_data(path, data, 0, len); if (res == -1) return -1; } return 0; } static int create_path_fd(const char *path, const char *data, int len) { int path_fd; int res; res = create_file(path, data, len); if (res == -1) return -1; path_fd = open(path, O_PATH); if (path_fd == -1) PERROR("open(O_PATH)"); return path_fd; } // Can be called once per test static int create_testfile(const char *path, const char *data, int len) { struct test *t = this_test; struct stat *st = &t->stat; int res, fd; if (t->fd > 0) { ERROR("testfile already created"); return -1; } fd = create_path_fd(path, data, len); if (fd == -1) return -1; t->fd = fd; res = fstat(fd, st); if (res == -1) { PERROR("fstat"); return -1; } return 0; } static int check_unlinked_testfile(int fd) { struct stat *st = &this_test->stat; st->st_nlink = 0; return fcheck_stat(fd, O_PATH, st); } // Check recorded testfiles after all tests completed static int check_unlinked_testfiles(void) { int fd; int res, err = 0; int num = testnum; if (!unlinked_test) return 0; testnum = 0; while (testnum < num) { fd = next_test->fd; start_test("check_unlinked_testfile"); if (fd == -1) continue; err += check_unlinked_testfile(fd); res = close(fd); if (res == -1) { PERROR("close(test_fd)"); err--; } } if (err) { fprintf(stderr, "%i unlinked testfile checks failed\n", -err); return 1; } return err; } static int cleanup_dir(const char *path, const char **dir_files, int quiet) { int i; int err = 0; for (i = 0; dir_files[i]; i++) { int res; char fpath[1280]; sprintf(fpath, "%s/%s", path, dir_files[i]); res = unlink(fpath); if (res == -1 && !quiet) { PERROR("unlink"); err --; } } if (err) return -1; return 0; } static int create_dir(const char *path, const char **dir_files) { int res; int i; rmdir(path); res = mkdir(path, 0755); if (res == -1) { PERROR("mkdir"); return -1; } res = check_type(path, S_IFDIR); if (res == -1) return -1; res = check_mode(path, 0755); if (res == -1) return -1; for (i = 0; dir_files[i]; i++) { char fpath[1280]; sprintf(fpath, "%s/%s", path, dir_files[i]); res = create_file(fpath, "", 0); if (res == -1) { cleanup_dir(path, dir_files, 1); return -1; } } res = check_dir_contents(path, dir_files); if (res == -1) { cleanup_dir(path, dir_files, 1); return -1; } return 0; } static int test_truncate(int len) { const char *data = testdata; int datalen = testdatalen; int res; start_test("truncate(%u)", (int) len); res = create_testfile(testfile, data, datalen); if (res == -1) return -1; res = truncate(testfile, len); if (res == -1) { PERROR("truncate"); return -1; } res = check_testfile_size(testfile, len); if (res == -1) return -1; if (len > 0) { if (len <= datalen) { res = check_data(testfile, data, 0, len); if (res == -1) return -1; } else { res = check_data(testfile, data, 0, datalen); if (res == -1) return -1; res = check_data(testfile, zerodata, datalen, len - datalen); if (res == -1) return -1; } } res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; success(); return 0; } static int test_ftruncate(int len, int mode) { const char *data = testdata; int datalen = testdatalen; int res; int fd; start_test("ftruncate(%u) mode: 0%03o", len, mode); res = create_testfile(testfile, data, datalen); if (res == -1) return -1; fd = open(testfile, O_WRONLY); if (fd == -1) { PERROR("open"); return -1; } res = fchmod(fd, mode); if (res == -1) { PERROR("fchmod"); close(fd); return -1; } res = check_testfile_mode(testfile, mode); if (res == -1) { close(fd); return -1; } res = ftruncate(fd, len); if (res == -1) { PERROR("ftruncate"); close(fd); return -1; } close(fd); res = check_testfile_size(testfile, len); if (res == -1) return -1; if (len > 0) { if (len <= datalen) { res = check_data(testfile, data, 0, len); if (res == -1) return -1; } else { res = check_data(testfile, data, 0, datalen); if (res == -1) return -1; res = check_data(testfile, zerodata, datalen, len - datalen); if (res == -1) return -1; } } res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; success(); return 0; } static int test_seekdir(void) { int i; int res; DIR *dp; struct dirent *de = NULL; start_test("seekdir"); res = create_dir(testdir, testdir_files); if (res == -1) return res; dp = opendir(testdir); if (dp == NULL) { PERROR("opendir"); return -1; } /* Remember dir offsets */ for (i = 0; i < ARRAY_SIZE(seekdir_offsets); i++) { seekdir_offsets[i] = telldir(dp); errno = 0; de = readdir(dp); if (de == NULL) { if (errno) { PERROR("readdir"); goto fail; } break; } } /* Walk until the end of directory */ while (de) de = readdir(dp); /* Start from the last valid dir offset and seek backwards */ for (i--; i >= 0; i--) { seekdir(dp, seekdir_offsets[i]); de = readdir(dp); if (de == NULL) { ERROR("Unexpected end of directory after seekdir()"); goto fail; } } closedir(dp); res = cleanup_dir(testdir, testdir_files, 0); if (!res) success(); return res; fail: closedir(dp); cleanup_dir(testdir, testdir_files, 1); return -1; } #ifdef HAVE_COPY_FILE_RANGE static int test_copy_file_range(void) { const char *data = testdata; int datalen = testdatalen; int err = 0; int res; int fd_in, fd_out; off_t pos_in = 0, pos_out = 0; start_test("copy_file_range"); unlink(testfile); fd_in = open(testfile, O_CREAT | O_RDWR, 0644); if (fd_in == -1) { PERROR("creat"); return -1; } res = write(fd_in, data, datalen); if (res == -1) { PERROR("write"); close(fd_in); return -1; } if (res != datalen) { ERROR("write is short: %u instead of %u", res, datalen); close(fd_in); return -1; } unlink(testfile2); fd_out = creat(testfile2, 0644); if (fd_out == -1) { PERROR("creat"); close(fd_in); return -1; } res = copy_file_range(fd_in, &pos_in, fd_out, &pos_out, datalen, 0); if (res == -1) { PERROR("copy_file_range"); close(fd_in); close(fd_out); return -1; } if (res != datalen) { ERROR("copy is short: %u instead of %u", res, datalen); close(fd_in); close(fd_out); return -1; } res = close(fd_in); if (res == -1) { PERROR("close"); close(fd_out); return -1; } res = close(fd_out); if (res == -1) { PERROR("close"); return -1; } err = check_data(testfile2, data, 0, datalen); res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; if (err) return -1; res = unlink(testfile2); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile2); if (res == -1) return -1; if (err) return -1; success(); return 0; } #else static int test_copy_file_range(void) { return 0; } #endif #ifdef HAVE_STATX static int test_statx(void) { struct statx sb; char msg[] = "hi"; size_t msg_size = sizeof(msg); struct timespec tp; int res; memset(&sb, 0, sizeof(sb)); unlink(testfile); start_test("statx"); res = create_testfile(testfile, msg, msg_size); if (res == -1) return -1; res = statx(-1, testfile, AT_EMPTY_PATH, STATX_BASIC_STATS | STATX_BTIME, &sb); if (res == -1) return -1; if (sb.stx_size != msg_size) return -1; clock_gettime(CLOCK_REALTIME, &tp); if (sb.stx_btime.tv_sec > tp.tv_sec) return -1; if (sb.stx_btime.tv_sec == tp.tv_sec && sb.stx_btime.tv_nsec >= tp.tv_nsec) return -1; unlink(testfile); success(); return 0; } #else static int test_statx(void) { return 0; } #endif static int test_utime(void) { struct utimbuf utm; time_t atime = 987631200; time_t mtime = 123116400; int res; start_test("utime"); res = create_testfile(testfile, NULL, 0); if (res == -1) return -1; utm.actime = atime; utm.modtime = mtime; res = utime(testfile, &utm); if (res == -1) { PERROR("utime"); return -1; } res = check_times(testfile, atime, mtime); if (res == -1) { return -1; } res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; success(); return 0; } static int test_create(void) { const char *data = testdata; int datalen = testdatalen; int err = 0; int res; int fd; start_test("create"); unlink(testfile); fd = creat(testfile, 0644); if (fd == -1) { PERROR("creat"); return -1; } res = write(fd, data, datalen); if (res == -1) { PERROR("write"); close(fd); return -1; } if (res != datalen) { ERROR("write is short: %u instead of %u", res, datalen); close(fd); return -1; } res = close(fd); if (res == -1) { PERROR("close"); return -1; } res = check_type(testfile, S_IFREG); if (res == -1) return -1; err += check_mode(testfile, 0644); err += check_nlink(testfile, 1); err += check_size(testfile, datalen); err += check_data(testfile, data, 0, datalen); res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; if (err) return -1; success(); return 0; } static int test_create_unlink(void) { const char *data = testdata; int datalen = testdatalen; int err = 0; int res; int fd; start_test("create+unlink"); unlink(testfile); fd = open(testfile, O_CREAT | O_RDWR | O_TRUNC, 0644); if (fd == -1) { PERROR("creat"); return -1; } res = unlink(testfile); if (res == -1) { PERROR("unlink"); close(fd); return -1; } res = check_nonexist(testfile); if (res == -1) { close(fd); return -1; } res = write(fd, data, datalen); if (res == -1) { PERROR("write"); close(fd); return -1; } if (res != datalen) { ERROR("write is short: %u instead of %u", res, datalen); close(fd); return -1; } struct stat st = { .st_mode = S_IFREG | 0644, .st_size = datalen, }; err = fcheck_stat(fd, O_RDWR, &st); err += fcheck_data(fd, data, 0, datalen); res = close(fd); if (res == -1) { PERROR("close"); err--; } if (err) return -1; success(); return 0; } static int test_mknod(void) { int err = 0; int res; start_test("mknod"); unlink(testfile); res = mknod(testfile, 0644, 0); if (res == -1) { PERROR("mknod"); return -1; } res = check_type(testfile, S_IFREG); if (res == -1) return -1; err += check_mode(testfile, 0644); err += check_nlink(testfile, 1); err += check_size(testfile, 0); res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; if (err) return -1; success(); return 0; } #define test_open(exist, flags, mode) do_test_open(exist, flags, #flags, mode) static int do_test_open(int exist, int flags, const char *flags_str, int mode) { char buf[4096]; const char *data = testdata; int datalen = testdatalen; unsigned currlen = 0; int err = 0; int res; int fd; off_t off; start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode); unlink(testfile); if (exist) { res = create_file(testfile_r, testdata2, testdata2len); if (res == -1) return -1; currlen = testdata2len; } fd = open(testfile, flags, mode); if ((flags & O_CREAT) && (flags & O_EXCL) && exist) { if (fd != -1) { ERROR("open should have failed"); close(fd); return -1; } else if (errno == EEXIST) goto succ; } if (!(flags & O_CREAT) && !exist) { if (fd != -1) { ERROR("open should have failed"); close(fd); return -1; } else if (errno == ENOENT) goto succ; } if (fd == -1) { PERROR("open"); return -1; } if (flags & O_TRUNC) currlen = 0; err += check_type(testfile, S_IFREG); if (exist) err += check_mode(testfile, 0644); else err += check_mode(testfile, mode); err += check_nlink(testfile, 1); err += check_size(testfile, currlen); if (exist && !(flags & O_TRUNC) && (mode & S_IRUSR)) err += check_data(testfile, testdata2, 0, testdata2len); res = write(fd, data, datalen); if ((flags & O_ACCMODE) != O_RDONLY) { if (res == -1) { PERROR("write"); err --; } else if (res != datalen) { ERROR("write is short: %u instead of %u", res, datalen); err --; } else { if (datalen > (int) currlen) currlen = datalen; err += check_size(testfile, currlen); if (mode & S_IRUSR) { err += check_data(testfile, data, 0, datalen); if (exist && !(flags & O_TRUNC) && testdata2len > datalen) err += check_data(testfile, testdata2 + datalen, datalen, testdata2len - datalen); } } } else { if (res != -1) { ERROR("write should have failed"); err --; } else if (errno != EBADF) { PERROR("write"); err --; } } off = lseek(fd, SEEK_SET, 0); if (off == (off_t) -1) { PERROR("lseek"); err--; } else if (off != 0) { ERROR("offset should have returned 0"); err --; } res = read(fd, buf, sizeof(buf)); if ((flags & O_ACCMODE) != O_WRONLY) { if (res == -1) { PERROR("read"); err--; } else { int readsize = currlen < sizeof(buf) ? currlen : sizeof(buf); if (res != readsize) { ERROR("read is short: %i instead of %u", res, readsize); err--; } else { if ((flags & O_ACCMODE) != O_RDONLY) { err += check_buffer(buf, data, datalen); if (exist && !(flags & O_TRUNC) && testdata2len > datalen) err += check_buffer(buf + datalen, testdata2 + datalen, testdata2len - datalen); } else if (exist) err += check_buffer(buf, testdata2, testdata2len); } } } else { if (res != -1) { ERROR("read should have failed"); err --; } else if (errno != EBADF) { PERROR("read"); err --; } } res = close(fd); if (res == -1) { PERROR("close"); return -1; } res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; res = check_nonexist(testfile_r); if (res == -1) return -1; if (err) return -1; succ: success(); return 0; } #define test_open_acc(flags, mode, err) \ do_test_open_acc(flags, #flags, mode, err) static int do_test_open_acc(int flags, const char *flags_str, int mode, int err) { const char *data = testdata; int datalen = testdatalen; int res; int fd; start_test("open_acc(%s) mode: 0%03o message: '%s'", flags_str, mode, strerror(err)); unlink(testfile); res = create_testfile(testfile, data, datalen); if (res == -1) return -1; res = chmod(testfile, mode); if (res == -1) { PERROR("chmod"); return -1; } res = check_testfile_mode(testfile, mode); if (res == -1) return -1; fd = open(testfile, flags); if (fd == -1) { if (err != errno) { PERROR("open"); return -1; } } else { if (err) { ERROR("open should have failed"); close(fd); return -1; } close(fd); } res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; res = check_nonexist(testfile_r); if (res == -1) return -1; success(); return 0; } static int test_symlink(void) { char buf[1024]; const char *data = testdata; int datalen = testdatalen; int linklen = strlen(testfile); int err = 0; int res; start_test("symlink"); res = create_testfile(testfile, data, datalen); if (res == -1) return -1; unlink(testfile2); res = symlink(testfile, testfile2); if (res == -1) { PERROR("symlink"); return -1; } res = check_type(testfile2, S_IFLNK); if (res == -1) return -1; err += check_mode(testfile2, 0777); err += check_nlink(testfile2, 1); res = readlink(testfile2, buf, sizeof(buf)); if (res == -1) { PERROR("readlink"); err--; } if (res != linklen) { ERROR("short readlink: %u instead of %u", res, linklen); err--; } if (memcmp(buf, testfile, linklen) != 0) { ERROR("link mismatch"); err--; } err += check_size(testfile2, datalen); err += check_data(testfile2, data, 0, datalen); res = unlink(testfile2); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile2); if (res == -1) return -1; if (err) return -1; res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; success(); return 0; } static int test_link(void) { const char *data = testdata; int datalen = testdatalen; int err = 0; int res; start_test("link"); res = create_testfile(testfile, data, datalen); if (res == -1) return -1; unlink(testfile2); res = link(testfile, testfile2); if (res == -1) { PERROR("link"); return -1; } res = check_type(testfile2, S_IFREG); if (res == -1) return -1; err += check_mode(testfile2, 0644); err += check_nlink(testfile2, 2); err += check_size(testfile2, datalen); err += check_data(testfile2, data, 0, datalen); res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; err += check_nlink(testfile2, 1); res = unlink(testfile2); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile2); if (res == -1) return -1; if (err) return -1; success(); return 0; } static int test_link2(void) { const char *data = testdata; int datalen = testdatalen; int err = 0; int res; start_test("link-unlink-link"); res = create_testfile(testfile, data, datalen); if (res == -1) return -1; unlink(testfile2); res = link(testfile, testfile2); if (res == -1) { PERROR("link"); return -1; } res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; res = link(testfile2, testfile); if (res == -1) { PERROR("link"); } res = check_type(testfile, S_IFREG); if (res == -1) return -1; err += check_mode(testfile, 0644); err += check_nlink(testfile, 2); err += check_size(testfile, datalen); err += check_data(testfile, data, 0, datalen); res = unlink(testfile2); if (res == -1) { PERROR("unlink"); return -1; } err += check_nlink(testfile, 1); res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; if (err) return -1; success(); return 0; } static int test_rename_file(void) { const char *data = testdata; int datalen = testdatalen; int err = 0; int res; start_test("rename file"); res = create_testfile(testfile, data, datalen); if (res == -1) return -1; unlink(testfile2); res = rename(testfile, testfile2); if (res == -1) { PERROR("rename"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; res = check_type(testfile2, S_IFREG); if (res == -1) return -1; err += check_mode(testfile2, 0644); err += check_nlink(testfile2, 1); err += check_size(testfile2, datalen); err += check_data(testfile2, data, 0, datalen); res = unlink(testfile2); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile2); if (res == -1) return -1; if (err) return -1; success(); return 0; } static int test_rename_dir(void) { int err = 0; int res; start_test("rename dir"); res = create_dir(testdir, testdir_files); if (res == -1) return -1; rmdir(testdir2); res = rename(testdir, testdir2); if (res == -1) { PERROR("rename"); cleanup_dir(testdir, testdir_files, 1); return -1; } res = check_nonexist(testdir); if (res == -1) { cleanup_dir(testdir, testdir_files, 1); return -1; } res = check_type(testdir2, S_IFDIR); if (res == -1) { cleanup_dir(testdir2, testdir_files, 1); return -1; } err += check_mode(testdir2, 0755); err += check_dir_contents(testdir2, testdir_files); err += cleanup_dir(testdir2, testdir_files, 0); res = rmdir(testdir2); if (res == -1) { PERROR("rmdir"); return -1; } res = check_nonexist(testdir2); if (res == -1) return -1; if (err) return -1; success(); return 0; } static int test_rename_dir_loop(void) { #define PATH(p) (snprintf(path, sizeof path, "%s/%s", testdir, p), path) #define PATH2(p) (snprintf(path2, sizeof path2, "%s/%s", testdir, p), path2) char path[1280], path2[1280]; int err = 0; int res; start_test("rename dir loop"); res = create_dir(testdir, testdir_files); if (res == -1) return -1; res = mkdir(PATH("a"), 0755); if (res == -1) { PERROR("mkdir"); goto fail; } res = rename(PATH("a"), PATH2("a")); if (res == -1) { PERROR("rename"); goto fail; } errno = 0; res = rename(PATH("a"), PATH2("a/b")); if (res == 0 || errno != EINVAL) { PERROR("rename"); goto fail; } res = mkdir(PATH("a/b"), 0755); if (res == -1) { PERROR("mkdir"); goto fail; } res = mkdir(PATH("a/b/c"), 0755); if (res == -1) { PERROR("mkdir"); goto fail; } errno = 0; res = rename(PATH("a"), PATH2("a/b/c")); if (res == 0 || errno != EINVAL) { PERROR("rename"); goto fail; } errno = 0; res = rename(PATH("a"), PATH2("a/b/c/a")); if (res == 0 || errno != EINVAL) { PERROR("rename"); goto fail; } errno = 0; res = rename(PATH("a/b/c"), PATH2("a")); if (res == 0 || errno != ENOTEMPTY) { PERROR("rename"); goto fail; } res = open(PATH("a/foo"), O_CREAT, 0644); if (res == -1) { PERROR("open"); goto fail; } close(res); res = rename(PATH("a/foo"), PATH2("a/bar")); if (res == -1) { PERROR("rename"); goto fail; } res = rename(PATH("a/bar"), PATH2("a/foo")); if (res == -1) { PERROR("rename"); goto fail; } res = rename(PATH("a/foo"), PATH2("a/b/bar")); if (res == -1) { PERROR("rename"); goto fail; } res = rename(PATH("a/b/bar"), PATH2("a/foo")); if (res == -1) { PERROR("rename"); goto fail; } res = rename(PATH("a/foo"), PATH2("a/b/c/bar")); if (res == -1) { PERROR("rename"); goto fail; } res = rename(PATH("a/b/c/bar"), PATH2("a/foo")); if (res == -1) { PERROR("rename"); goto fail; } res = open(PATH("a/bar"), O_CREAT, 0644); if (res == -1) { PERROR("open"); goto fail; } close(res); res = rename(PATH("a/foo"), PATH2("a/bar")); if (res == -1) { PERROR("rename"); goto fail; } unlink(PATH("a/bar")); res = rename(PATH("a/b"), PATH2("a/d")); if (res == -1) { PERROR("rename"); goto fail; } res = rename(PATH("a/d"), PATH2("a/b")); if (res == -1) { PERROR("rename"); goto fail; } res = mkdir(PATH("a/d"), 0755); if (res == -1) { PERROR("mkdir"); goto fail; } res = rename(PATH("a/b"), PATH2("a/d")); if (res == -1) { PERROR("rename"); goto fail; } res = rename(PATH("a/d"), PATH2("a/b")); if (res == -1) { PERROR("rename"); goto fail; } res = mkdir(PATH("a/d"), 0755); if (res == -1) { PERROR("mkdir"); goto fail; } res = mkdir(PATH("a/d/e"), 0755); if (res == -1) { PERROR("mkdir"); goto fail; } errno = 0; res = rename(PATH("a/b"), PATH2("a/d")); if (res == 0 || (errno != ENOTEMPTY && errno != EEXIST)) { PERROR("rename"); goto fail; } rmdir(PATH("a/d/e")); rmdir(PATH("a/d")); rmdir(PATH("a/b/c")); rmdir(PATH("a/b")); rmdir(PATH("a")); err += cleanup_dir(testdir, testdir_files, 0); res = rmdir(testdir); if (res == -1) { PERROR("rmdir"); goto fail; } res = check_nonexist(testdir); if (res == -1) return -1; if (err) return -1; success(); return 0; fail: unlink(PATH("a/bar")); rmdir(PATH("a/d/e")); rmdir(PATH("a/d")); rmdir(PATH("a/b/c")); rmdir(PATH("a/b")); rmdir(PATH("a")); cleanup_dir(testdir, testdir_files, 1); rmdir(testdir); return -1; #undef PATH2 #undef PATH } static int test_mkfifo(void) { int res; int err = 0; start_test("mkfifo"); unlink(testfile); res = mkfifo(testfile, 0644); if (res == -1) { PERROR("mkfifo"); return -1; } res = check_type(testfile, S_IFIFO); if (res == -1) return -1; err += check_mode(testfile, 0644); err += check_nlink(testfile, 1); res = unlink(testfile); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testfile); if (res == -1) return -1; if (err) return -1; success(); return 0; } static int test_mkdir(void) { int res; int err = 0; const char *dir_contents[] = {NULL}; start_test("mkdir"); rmdir(testdir); res = mkdir(testdir, 0755); if (res == -1) { PERROR("mkdir"); return -1; } res = check_type(testdir, S_IFDIR); if (res == -1) return -1; err += check_mode(testdir, 0755); /* Some file systems (like btrfs) don't track link count for directories */ //err += check_nlink(testdir, 2); err += check_dir_contents(testdir, dir_contents); res = rmdir(testdir); if (res == -1) { PERROR("rmdir"); return -1; } res = check_nonexist(testdir); if (res == -1) return -1; if (err) return -1; success(); return 0; } static int test_socket(void) { struct sockaddr_un su; int fd; int res; int err = 0; const size_t test_sock_len = strlen(testsock) + 1; start_test("socket"); 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; } unlink(testsock); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { PERROR("socket"); return -1; } su.sun_family = AF_UNIX; 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) { PERROR("bind"); return -1; } res = check_type(testsock, S_IFSOCK); if (res == -1) { close(fd); return -1; } err += check_nlink(testsock, 1); close(fd); res = unlink(testsock); if (res == -1) { PERROR("unlink"); return -1; } res = check_nonexist(testsock); if (res == -1) return -1; if (err) return -1; success(); return 0; } #define test_create_ro_dir(flags) \ do_test_create_ro_dir(flags, #flags) static int do_test_create_ro_dir(int flags, const char *flags_str) { int res; int err = 0; int fd; start_test("open(%s) in read-only directory", flags_str); rmdir(testdir); res = mkdir(testdir, 0555); if (res == -1) { PERROR("mkdir"); return -1; } fd = open(subfile, flags, 0644); if (fd != -1) { close(fd); unlink(subfile); ERROR("open should have failed"); err--; } else { res = check_nonexist(subfile); if (res == -1) err--; } unlink(subfile); res = rmdir(testdir); if (res == -1) { PERROR("rmdir"); return -1; } res = check_nonexist(testdir); if (res == -1) return -1; if (err) return -1; success(); return 0; } #ifndef __FreeBSD__ /* this tests open with O_TMPFILE note that this will only work with the fuse low level api you will get ENOTSUP with the high level api */ static int test_create_tmpfile(void) { rmdir(testdir); int res = mkdir(testdir, 0777); if (res) return -1; start_test("create tmpfile"); int fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); if(fd == -1) { if (errno == ENOTSUP) { /* don't bother if we're working on an old kernel or on the high level API */ return 0; } PERROR("open O_TMPFILE | O_RDWR"); return -1; } close(fd); fd = open(testdir, O_TMPFILE | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); if(fd == -1){ PERROR("open with O_TMPFILE | O_WRONLY | O_EXCL"); return -1; }; close(fd); fd = open(testdir, O_TMPFILE | O_RDONLY, S_IRUSR); if (fd != -1) { ERROR("open with O_TMPFILE | O_RDONLY succeeded"); return -1; } success(); return 0; } static int test_create_and_link_tmpfile(void) { /* skip this test for now since the github runner will fail in the linkat call below */ return 0; rmdir(testdir); unlink(testfile); int res = mkdir(testdir, 0777); if (res) return -1; start_test("create and link tmpfile"); int fd = open(testdir, O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR); if(fd == -1) { if (errno == ENOTSUP) { /* don't bother if we're working on an old kernel or on the high level API */ return 0; } PERROR("open with O_TMPFILE | O_RDWR | O_EXCL"); return -1; } if (!linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) { ERROR("linkat succeeded on a tmpfile opened with O_EXCL"); return -1; } close(fd); fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); if(fd == -1) { PERROR("open O_TMPFILE"); return -1; } if (check_nonexist(testfile)) { return -1; } if (linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) { PERROR("linkat tempfile"); return -1; } close(fd); if (check_nlink(testfile, 1)) { return -1; } unlink(testfile); success(); return 0; } #endif int main(int argc, char *argv[]) { int err = 0; int a; int is_root; umask(0); if (argc < 2 || argc > 4) { fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#] [-u]\n", argv[0]); return 1; } basepath = argv[1]; basepath_r = basepath; for (a = 2; a < argc; a++) { char *endptr; char *arg = argv[a]; if (arg[0] == ':') { basepath_r = arg + 1; } else { if (arg[0] == '-') { arg++; if (arg[0] == 'u') { unlinked_test = 1; endptr = arg + 1; } else { skip_test = strtoul(arg, &endptr, 10); } } else { select_test = strtoul(arg, &endptr, 10); } if (arg[0] == '\0' || *endptr != '\0') { fprintf(stderr, "invalid option: '%s'\n", argv[a]); return 1; } } } assert(strlen(basepath) < 512); assert(strlen(basepath_r) < 512); if (basepath[0] != '/') { fprintf(stderr, "testdir must be an absolute path\n"); return 1; } sprintf(testfile, "%s/testfile", basepath); sprintf(testfile2, "%s/testfile2", basepath); sprintf(testdir, "%s/testdir", basepath); sprintf(testdir2, "%s/testdir2", basepath); sprintf(subfile, "%s/subfile", testdir2); sprintf(testsock, "%s/testsock", basepath); sprintf(testfile_r, "%s/testfile", basepath_r); sprintf(testfile2_r, "%s/testfile2", basepath_r); sprintf(testdir_r, "%s/testdir", basepath_r); sprintf(testdir2_r, "%s/testdir2", basepath_r); sprintf(subfile_r, "%s/subfile", testdir2_r); is_root = (geteuid() == 0); err += test_create(); err += test_create_unlink(); err += test_symlink(); err += test_link(); err += test_link2(); err += test_mknod(); err += test_mkfifo(); err += test_mkdir(); err += test_rename_file(); err += test_rename_dir(); err += test_rename_dir_loop(); err += test_seekdir(); err += test_socket(); err += test_utime(); err += test_truncate(0); err += test_truncate(testdatalen / 2); err += test_truncate(testdatalen); err += test_truncate(testdatalen + 100); err += test_ftruncate(0, 0600); err += test_ftruncate(testdatalen / 2, 0600); err += test_ftruncate(testdatalen, 0600); err += test_ftruncate(testdatalen + 100, 0600); err += test_ftruncate(0, 0400); err += test_ftruncate(0, 0200); err += test_ftruncate(0, 0000); err += test_open(0, O_RDONLY, 0); err += test_open(1, O_RDONLY, 0); err += test_open(1, O_RDWR, 0); err += test_open(1, O_WRONLY, 0); err += test_open(0, O_RDWR | O_CREAT, 0600); err += test_open(1, O_RDWR | O_CREAT, 0600); err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600); err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600); err += test_open(0, O_RDONLY | O_CREAT, 0600); err += test_open(0, O_RDONLY | O_CREAT, 0400); err += test_open(0, O_RDONLY | O_CREAT, 0200); err += test_open(0, O_RDONLY | O_CREAT, 0000); err += test_open(0, O_WRONLY | O_CREAT, 0600); err += test_open(0, O_WRONLY | O_CREAT, 0400); err += test_open(0, O_WRONLY | O_CREAT, 0200); err += test_open(0, O_WRONLY | O_CREAT, 0000); err += test_open(0, O_RDWR | O_CREAT, 0400); err += test_open(0, O_RDWR | O_CREAT, 0200); err += test_open(0, O_RDWR | O_CREAT, 0000); err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600); err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600); err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000); err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000); err += test_open_acc(O_RDONLY, 0600, 0); err += test_open_acc(O_WRONLY, 0600, 0); err += test_open_acc(O_RDWR, 0600, 0); err += test_open_acc(O_RDONLY, 0400, 0); err += test_open_acc(O_WRONLY, 0200, 0); if(!is_root) { err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES); err += test_open_acc(O_WRONLY, 0400, EACCES); err += test_open_acc(O_RDWR, 0400, EACCES); err += test_open_acc(O_RDONLY, 0200, EACCES); err += test_open_acc(O_RDWR, 0200, EACCES); err += test_open_acc(O_RDONLY, 0000, EACCES); err += test_open_acc(O_WRONLY, 0000, EACCES); err += test_open_acc(O_RDWR, 0000, EACCES); } err += test_create_ro_dir(O_CREAT); err += test_create_ro_dir(O_CREAT | O_EXCL); err += test_create_ro_dir(O_CREAT | O_WRONLY); err += test_create_ro_dir(O_CREAT | O_TRUNC); err += test_copy_file_range(); err += test_statx(); #ifndef __FreeBSD__ err += test_create_tmpfile(); err += test_create_and_link_tmpfile(); #endif unlink(testfile2); unlink(testsock); rmdir(testdir); rmdir(testdir2); if (err) { fprintf(stderr, "%i tests failed\n", -err); return 1; } return check_unlinked_testfiles(); } fuse-3.18.2/test/test_want_conversion.c0000644000175000017500000001205515156613252017126 0ustar berndbernd#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17) #include "util.h" #include "fuse_i.h" #include "fuse_lowlevel.h" #include #include #include #include #include static void print_conn_info(const char *prefix, struct fuse_conn_info *conn) { struct fuse_session *se = container_of(conn, struct fuse_session, conn); printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64 " want_default=0x%" PRIx32 " want_ext_default=0x%" PRIx64 "\n", prefix, conn->want, conn->want_ext, se->conn_want, se->conn_want_ext); } static void application_init_old_style(struct fuse_conn_info *conn) { /* Simulate application init the old style */ conn->want |= FUSE_CAP_ASYNC_READ; conn->want &= ~FUSE_CAP_SPLICE_READ; /* * Also use new style API, as that might happen through * fuse_apply_conn_info_opts() */ fuse_set_feature_flag(conn, FUSE_CAP_IOCTL_DIR); } static void application_init_new_style(struct fuse_conn_info *conn) { /* Simulate application init the new style */ fuse_set_feature_flag(conn, FUSE_CAP_ASYNC_READ); fuse_set_feature_flag(conn, FUSE_CAP_IOCTL_DIR); fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ); } static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style) { /* High-level init */ fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT); if (new_style) application_init_new_style(conn); else application_init_old_style(conn); } static void test_do_init(struct fuse_conn_info *conn, bool new_style) { /* Initial setup */ conn->capable_ext = FUSE_CAP_SPLICE_READ | FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE | FUSE_CAP_POSIX_LOCKS | FUSE_CAP_FLOCK_LOCKS | FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_ASYNC_READ | FUSE_CAP_IOCTL_DIR; conn->capable = fuse_lower_32_bits(conn->capable_ext); fuse_set_feature_flag(conn, FUSE_CAP_SPLICE_READ | FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE); print_conn_info("Initial state", conn); int rc; test_fuse_fs_init(conn, new_style); print_conn_info("After init", conn); rc = fuse_convert_to_conn_want_ext(conn); assert(rc == 0); /* Verify all expected flags are set */ assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ)); assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE); assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE); assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT); assert(conn->want_ext & FUSE_CAP_ASYNC_READ); assert(conn->want_ext & FUSE_CAP_IOCTL_DIR); /* Verify no other flags are set */ assert(conn->want_ext == (FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE | FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_ASYNC_READ | FUSE_CAP_IOCTL_DIR)); print_conn_info("After init", conn); } static void test_want_conversion_basic(void) { const struct fuse_lowlevel_ops ops = { 0 }; struct fuse_args args = FUSE_ARGS_INIT(0, NULL); struct fuse_session *se; struct fuse_conn_info *conn; /* Add the program name to arg[0] */ if (fuse_opt_add_arg(&args, "test_signals")) { fprintf(stderr, "Failed to add argument\n"); errx(1, "Failed to add argument"); } se = fuse_session_new(&args, &ops, sizeof(ops), NULL); assert(se); conn = &se->conn; printf("\nTesting basic want conversion, old style:\n"); test_do_init(conn, false); fuse_session_destroy(se); se = fuse_session_new(&args, &ops, sizeof(ops), NULL); assert(se); conn = &se->conn; printf("\nTesting basic want conversion, new style:\n"); test_do_init(conn, true); print_conn_info("After init", conn); fuse_session_destroy(se); fuse_opt_free_args(&args); } static void test_want_conversion_conflict(void) { struct fuse_conn_info conn = { 0 }; int rc; printf("\nTesting want conversion conflict:\n"); /* Test conflicting values */ /* Initialize like fuse_lowlevel.c does */ conn.capable_ext = FUSE_CAP_SPLICE_READ | FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE | FUSE_CAP_POSIX_LOCKS | FUSE_CAP_FLOCK_LOCKS; conn.capable = fuse_lower_32_bits(conn.capable_ext); conn.want_ext = conn.capable_ext; conn.want = fuse_lower_32_bits(conn.want_ext); print_conn_info("Test conflict initial", &conn); /* Simulate application init modifying capabilities */ conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */ conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */ rc = fuse_convert_to_conn_want_ext(&conn); assert(rc == -EINVAL); print_conn_info("Test conflict after", &conn); printf("Want conversion conflict test passed\n"); } static void test_want_conversion_high_bits(void) { struct fuse_conn_info conn = { 0 }; int rc; printf("\nTesting want conversion high bits preservation:\n"); /* Test high bits preservation */ conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ; conn.want = fuse_lower_32_bits(conn.want_ext); print_conn_info("Test high bits initial", &conn); rc = fuse_convert_to_conn_want_ext(&conn); assert(rc == 0); assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ)); print_conn_info("Test high bits after", &conn); printf("Want conversion high bits test passed\n"); } int main(void) { test_want_conversion_basic(); test_want_conversion_conflict(); test_want_conversion_high_bits(); return 0; } fuse-3.18.2/test/test_write_cache.c0000644000175000017500000001632115156613252016165 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2016 Nikolaus Rath This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ #define FUSE_USE_VERSION 30 /* Not really needed - just to test build with FUSE_USE_VERSION == 30 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __linux__ #include #else #include #endif #define FILE_INO 2 #define FILE_NAME "write_me" /* Command line parsing */ struct options { int writeback; int data_size; int delay_ms; } options = { .writeback = 0, .data_size = 2048, .delay_ms = 0, }; #define WRITE_SYSCALLS 64 #define OPTION(t, p) { t, offsetof(struct options, p), 1 } 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; static atomic_int write_cnt; 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; if (options.writeback) { assert(fuse_get_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE)); fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE); } } static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) { stbuf->st_ino = ino; if (ino == FUSE_ROOT_ID) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 1; } else if (ino == FILE_INO) { stbuf->st_mode = S_IFREG | 0222; stbuf->st_nlink = 1; stbuf->st_size = 0; } else return -1; return 0; } static void tfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse_entry_param e; memset(&e, 0, sizeof(e)); if (parent != FUSE_ROOT_ID) goto err_out; else if (strcmp(name, FILE_NAME) == 0) e.ino = FILE_INO; else goto err_out; if (tfs_stat(e.ino, &e.attr) != 0) goto err_out; fuse_reply_entry(req, &e); return; err_out: fuse_reply_err(req, ENOENT); } static void tfs_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 (tfs_stat(ino, &stbuf) != 0) fuse_reply_err(req, ENOENT); else fuse_reply_attr(req, &stbuf, 5); } static void tfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { if (ino == FUSE_ROOT_ID) 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); } } static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) { (void)fi; (void)buf; (void)off; size_t expected; assert(ino == FILE_INO); expected = options.data_size; if (options.writeback) expected *= 2; write_cnt++; if (size != expected && !options.writeback) fprintf(stderr, "ERROR: Expected %zd bytes, got %zd\n!", 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); } static struct fuse_lowlevel_ops tfs_oper = { .init = tfs_init, .lookup = tfs_lookup, .getattr = tfs_getattr, .open = tfs_open, .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); return NULL; } static void test_fs(char *mountpoint) { char fname[PATH_MAX]; char *buf; const size_t iosize = options.data_size; const size_t dsize = options.data_size * WRITE_SYSCALLS; int fd, rofd; pthread_t rofd_thread; off_t off = 0; buf = malloc(dsize); assert(buf != NULL); assert((fd = open("/dev/urandom", O_RDONLY)) != -1); assert(read(fd, buf, dsize) == dsize); close(fd); assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME, mountpoint) > 0); fd = open(fname, O_WRONLY); if (fd == -1) { perror(fname); 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); } for (int cnt = 0; cnt < WRITE_SYSCALLS; cnt++) { assert(pwrite(fd, buf + off, iosize, off) == iosize); off += iosize; assert(off <= 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[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_session *se; struct fuse_cmdline_opts fuse_opts; pthread_t fs_thread; assert(fuse_opt_parse(&args, &options, option_spec, NULL) == 0); assert(fuse_parse_cmdline(&args, &fuse_opts) == 0); #ifndef __FreeBSD__ assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0); #endif se = fuse_session_new(&args, &tfs_oper, sizeof(tfs_oper), NULL); fuse_opt_free_args(&args); assert(se != NULL); assert(fuse_set_signal_handlers(se) == 0); assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0); /* Start file-system thread */ assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0); /* Write test data */ test_fs(fuse_opts.mountpoint); free(fuse_opts.mountpoint); /* Stop file system */ fuse_session_exit(se); fuse_session_unmount(se); assert(pthread_join(fs_thread, NULL) == 0); assert(got_write == 1); /* * when writeback cache is enabled, kernel side can merge requests, but * memory pressure, system 'sync' might trigger data flushes before - flush * might happen in between write syscalls - merging subpage writes into * a single page and pages into large fuse requests might or might not work. * Though we can expect that that at least some (but maybe all) write * system calls can be merged. */ if (options.writeback) assert(write_cnt < WRITE_SYSCALLS); else assert(write_cnt == WRITE_SYSCALLS); fuse_remove_signal_handlers(se); fuse_session_destroy(se); printf("Test completed successfully.\n"); return 0; } /** * Local Variables: * mode: c * indent-tabs-mode: nil * c-basic-offset: 4 * End: */ fuse-3.18.2/test/util.py0000644000175000017500000001524615156613252014041 0ustar berndbernd#!/usr/bin/env python3 import subprocess import pytest import os import stat import time from os.path import join as pjoin import sys import re import itertools from packaging import version import logging basename = pjoin(os.path.dirname(__file__), '..') def parse_kernel_version(release): # Extract the first three numbers from the kernel version string match = re.match(r'^(\d+\.\d+\.\d+)', release) if match: return version.parse(match.group(1)) return version.parse('0') def get_printcap(): cmdline = base_cmdline + [ pjoin(basename, 'example', 'printcap') ] proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, universal_newlines=True) (stdout, _) = proc.communicate(30) assert proc.returncode == 0 proto = None caps = set() for line in stdout.split('\n'): if line.startswith('\t'): caps.add(line.strip()) continue hit = re.match(r'Protocol version: (\d+)\.(\d+)$', line) if hit: proto = (int(hit.group(1)), int(hit.group(2))) return (proto, caps) def test_printcap(): get_printcap() def wait_for_mount(mount_process, mnt_dir, test_fn=os.path.ismount): elapsed = 0 while elapsed < 30: if test_fn(mnt_dir): return True if mount_process.poll() is not None: if test_fn(mnt_dir): return True pytest.fail('file system process terminated prematurely') time.sleep(0.1) elapsed += 0.1 pytest.fail("mountpoint failed to come up") def cleanup(mount_process, mnt_dir): # Don't bother trying Valgrind if things already went wrong if 'bsd' in sys.platform or 'dragonfly' in sys.platform: cmd = [ 'umount', '-f', mnt_dir ] else: cmd = [pjoin(basename, 'util', 'fusermount3'), '-z', '-u', mnt_dir] subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) mount_process.terminate() try: mount_process.wait(1) except subprocess.TimeoutExpired: mount_process.kill() def umount(mount_process, mnt_dir): logger = logging.getLogger(__name__) logger.debug(f"Unmounting {mnt_dir}") if 'bsd' in sys.platform or 'dragonfly' in sys.platform: cmdline = [ 'umount', mnt_dir ] logger.debug("Using BSD-style umount command") else: logger.debug("Using fusermount3 for unmounting") # fusermount3 will be setuid root, so we can only trace it with # valgrind if we're root if os.getuid() == 0: cmdline = base_cmdline logger.debug("Running as root, using valgrind if configured") else: cmdline = [] logger.debug("Not running as root, skipping valgrind for fusermount3") cmdline = cmdline + [ pjoin(basename, 'util', 'fusermount3'), '-z', '-u', mnt_dir ] logger.debug(f"Unmount command: {' '.join(cmdline)}") try: result = subprocess.run(cmdline, capture_output=True, text=True, check=True) if result.stdout: logger.debug(f"Unmount command stdout: {result.stdout}") if result.stderr: logger.debug(f"Unmount command stderr: {result.stderr}") except subprocess.CalledProcessError as e: logger.error(f"Unmount command failed with return code {e.returncode}\nStdout: {e.stdout}\nStderr: {e.stderr}") raise if not os.path.ismount(mnt_dir): logger.debug(f"{mnt_dir} is no longer a mount point") else: logger.warning(f"{mnt_dir} is still a mount point after unmount command") # Give mount process a little while to terminate. Popen.wait(timeout) # was only added in 3.3... elapsed = 0 while elapsed < 30: code = mount_process.poll() if code is not None: if code == 0: return logger.error(f"File system process terminated with code {code}") pytest.fail(f'file system process terminated with code {code}') time.sleep(0.1) elapsed += 0.1 logger.error("Mount process did not terminate within 30 seconds") pytest.fail('mount process did not terminate') def safe_sleep(secs): '''Like time.sleep(), but sleep for at least *secs* `time.sleep` may sleep less than the given period if a signal is received. This function ensures that we sleep for at least the desired time. ''' now = time.time() end = now + secs while now < end: time.sleep(end - now) now = time.time() def fuse_test_marker(): '''Return a pytest.marker that indicates FUSE availability If system/user/environment does not support FUSE, return a `pytest.mark.skip` object with more details. If FUSE is supported, return `pytest.mark.uses_fuse()`. ''' skip = lambda x: pytest.mark.skip(reason=x) if 'bsd' in sys.platform or 'dragonfly' in sys.platform: return pytest.mark.uses_fuse() with subprocess.Popen(['which', 'fusermount3'], stdout=subprocess.PIPE, universal_newlines=True) as which: fusermount_path = which.communicate()[0].strip() if not fusermount_path or which.returncode != 0: return skip("Can't find fusermount executable") if not os.path.exists('/dev/fuse'): return skip("FUSE kernel module does not seem to be loaded") if os.getuid() == 0: return pytest.mark.uses_fuse() mode = os.stat(fusermount_path).st_mode if mode & stat.S_ISUID == 0: return skip('fusermount executable not setuid, and we are not root.') try: fd = os.open('/dev/fuse', os.O_RDWR) except OSError as exc: return skip('Unable to open /dev/fuse: %s' % exc.strerror) else: os.close(fd) return pytest.mark.uses_fuse() def powerset(iterable): s = list(iterable) return itertools.chain.from_iterable( itertools.combinations(s, r) for r in range(len(s)+1)) def create_tmpdir(mnt_dir): if not os.path.exists(mnt_dir): print("makedirs: '" + mnt_dir + "'") os.makedirs(mnt_dir) else: print("mnt_dir exists: '" + mnt_dir + "'") # Use valgrind if requested if os.environ.get('TEST_WITH_VALGRIND', 'no').lower().strip() \ not in ('no', 'false', '0'): base_cmdline = [ 'valgrind', '-q', '--' ] else: base_cmdline = [] # Try to use local fusermount3 os.environ['PATH'] = '%s:%s' % (pjoin(basename, 'util'), os.environ['PATH']) # Put example binaries on PATH os.environ['PATH'] = '%s:%s' % (pjoin(basename, 'example'), os.environ['PATH']) try: (fuse_proto, fuse_caps) = get_printcap() except: # Rely on test to raise error fuse_proto = (0,0) fuse_caps = set() fuse-3.18.2/test/wrong_command.c0000644000175000017500000000100415156613252015473 0ustar berndbernd#include int main(void) { #ifdef MESON_IS_SUBPROJECT fprintf(stderr, "libfuse tests were skipped because it's a meson subproject.\n" "If you wish to run them try:\n" "'cd /subprojects/libfuse && meson . build && cd build && python3 -m pytest test/' instead"); return 77; /* report as a skipped test */ #else fprintf(stderr, "\x1B[31m\e[1m" "This is not the command you are looking for.\n" "You probably want to run 'python3 -m pytest test/' instead" "\e[0m\n"); return 1; #endif } fuse-3.18.2/tsan_suppressions.txt0000644000175000017500000000017115156613252016065 0ustar berndbernd# Use with # TSAN_OPTIONS="suppressions=tsan_suppressions.txt" ./myprogram # False positive race:pthread_setcancelstate fuse-3.18.2/util/0000755000175000017500000000000015156613444012503 5ustar berndberndfuse-3.18.2/util/fuse.conf0000644000175000017500000000132515156613252014312 0ustar berndbernd# The file /etc/fuse.conf allows for the following parameters: # # 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. # It must be typed exactly as shown (with a single space before and after the # equals sign). #mount_max = 1000 fuse-3.18.2/util/fusermount.c0000644000175000017500000012046015156613252015056 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ /* This program does the mounting and unmounting of FUSE filesystems */ #define _GNU_SOURCE /* for clone,strchrnul and close_range */ #include "fuse_config.h" #include "mount_util.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fuse_mount_compat.h" #include #include #include #include #include #include #if defined HAVE_CLOSE_RANGE && defined linux #include #endif #if defined HAVE_LISTMOUNT #include #include #include #endif #define FUSE_COMMFD_ENV "_FUSE_COMMFD" #define FUSE_KERN_DEVICE_ENV "FUSE_KERN_DEVICE" #define FUSE_DEV "/dev/fuse" static const char *progname; static int user_allow_other = 0; 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 /* * Take a ',' separated option string and extract "x-" options */ static int extract_x_options(const char *original, char **non_x_opts, char **x_opts) { size_t orig_len; const char *opt, *opt_end; orig_len = strlen(original) + 1; *non_x_opts = calloc(1, orig_len); *x_opts = calloc(1, orig_len); size_t non_x_opts_len = orig_len; size_t x_opts_len = orig_len; if (*non_x_opts == NULL || *x_opts == NULL) { fprintf(stderr, "%s: Failed to allocate %zuB.\n", __func__, orig_len); return -ENOMEM; } for (opt = original; opt < original + orig_len; opt = opt_end + 1) { char *opt_buf; opt_end = strchr(opt, ','); if (opt_end == NULL) opt_end = original + orig_len; size_t opt_len = opt_end - opt; size_t opt_len_left = orig_len - (opt - original); size_t buf_len; bool is_x_opts; if (strncmp(opt, "x-", MIN(2, opt_len_left)) == 0) { buf_len = x_opts_len; is_x_opts = true; opt_buf = *x_opts; } else { buf_len = non_x_opts_len; is_x_opts = false; opt_buf = *non_x_opts; } if (buf_len < orig_len) { strncat(opt_buf, ",", 2); buf_len -= 1; } /* omits ',' */ if ((ssize_t)(buf_len - opt_len) < 0) { /* This would be a bug */ fprintf(stderr, "%s: no buf space left in copy, orig='%s'\n", __func__, original); return -EIO; } strncat(opt_buf, opt, opt_end - opt); buf_len -= opt_len; if (is_x_opts) x_opts_len = buf_len; else non_x_opts_len = buf_len; } return 0; } static const char *get_user_name(void) { struct passwd *pw = getpwuid(getuid()); if (pw != NULL && pw->pw_name != NULL) return pw->pw_name; else { fprintf(stderr, "%s: could not determine username\n", progname); return NULL; } } static uid_t oldfsuid; static gid_t oldfsgid; static void drop_privs(void) { if (getuid() != 0) { oldfsuid = setfsuid(getuid()); oldfsgid = setfsgid(getgid()); } } static void restore_privs(void) { if (getuid() != 0) { setfsuid(oldfsuid); setfsgid(oldfsgid); } } #ifndef IGNORE_MTAB /* * Make sure that /etc/mtab is checked and updated atomically */ static int lock_umount(void) { const char *mtab_lock = _PATH_MOUNTED ".fuselock"; int mtablock; int res; struct stat mtab_stat; /* /etc/mtab could be a symlink to /proc/mounts */ if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode)) return -1; mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600); if (mtablock == -1) { fprintf(stderr, "%s: unable to open fuse lock file: %s\n", progname, strerror(errno)); return -1; } res = lockf(mtablock, F_LOCK, 0); if (res < 0) { fprintf(stderr, "%s: error getting lock: %s\n", progname, strerror(errno)); close(mtablock); return -1; } return mtablock; } static void unlock_umount(int mtablock) { if (mtablock >= 0) { int res; res = lockf(mtablock, F_ULOCK, 0); if (res < 0) { fprintf(stderr, "%s: error releasing lock: %s\n", progname, strerror(errno)); } close(mtablock); } } static int add_mount(const char *source, const char *mnt, const char *type, const char *opts) { return fuse_mnt_add_mount(progname, source, mnt, type, opts); } static int may_unmount(const char *mnt, int quiet) { struct mntent *entp; FILE *fp; const char *user = NULL; char uidstr[32]; unsigned uidlen = 0; int found; const char *mtab = _PATH_MOUNTED; user = get_user_name(); if (user == NULL) return -1; fp = setmntent(mtab, "r"); if (fp == NULL) { fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, strerror(errno)); return -1; } uidlen = sprintf(uidstr, "%u", getuid()); found = 0; 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 || strncmp(entp->mnt_type, "fuse.", 5) == 0 || strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) { char *p = strstr(entp->mnt_opts, "user="); if (p && (p == entp->mnt_opts || *(p-1) == ',') && strcmp(p + 5, user) == 0) { found = 1; break; } /* /etc/mtab is a link pointing to /proc/mounts: */ else if ((p = strstr(entp->mnt_opts, "user_id=")) && (p == entp->mnt_opts || *(p-1) == ',') && strncmp(p + 8, uidstr, uidlen) == 0 && (*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0')) { found = 1; break; } } } endmntent(fp); if (!found) { if (!quiet) fprintf(stderr, "%s: entry for %s not found in %s\n", progname, mnt, mtab); return -1; } return 0; } #endif /* * Check whether the file specified in "fusermount3 -u" is really a * mountpoint and not a symlink. This is necessary otherwise the user * could move the mountpoint away and replace it with a symlink * pointing to an arbitrary mount, thereby tricking fusermount3 into * unmounting that (umount(2) will follow symlinks). * * This is the child process running in a separate mount namespace, so * we don't mess with the global namespace and if the process is * killed for any reason, mounts are automatically cleaned up. * * First make sure nothing is propagated back into the parent * namespace by marking all mounts "private". * * Then bind mount parent onto a stable base where the user can't move * it around. * * Finally check /proc/mounts for an entry matching the requested * mountpoint. If it's found then we are OK, and the user can't move * it around within the parent directory as rename() will return * EBUSY. Be careful to ignore any mounts that existed before the * bind. */ static int check_is_mount_child(void *p) { const char **a = p; const char *last = a[0]; const char *mnt = a[1]; const char *type = a[2]; int res; const char *procmounts = "/proc/mounts"; int found; FILE *fp; struct mntent *entp; int count; res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL); if (res == -1) { fprintf(stderr, "%s: failed to mark mounts private: %s\n", progname, strerror(errno)); return 1; } fp = setmntent(procmounts, "r"); if (fp == NULL) { fprintf(stderr, "%s: failed to open %s: %s\n", progname, procmounts, strerror(errno)); return 1; } count = 0; while (GETMNTENT(fp) != NULL) count++; endmntent(fp); fp = setmntent(procmounts, "r"); if (fp == NULL) { fprintf(stderr, "%s: failed to open %s: %s\n", progname, procmounts, strerror(errno)); return 1; } res = mount(".", "/", "", MS_BIND | MS_REC, NULL); if (res == -1) { fprintf(stderr, "%s: failed to bind parent to /: %s\n", progname, strerror(errno)); return 1; } found = 0; while ((entp = GETMNTENT(fp)) != NULL) { if (count > 0) { count--; continue; } if (entp->mnt_dir[0] == '/' && strcmp(entp->mnt_dir + 1, last) == 0 && (!type || strcmp(entp->mnt_type, type) == 0)) { found = 1; break; } } endmntent(fp); if (!found) { fprintf(stderr, "%s: %s not mounted\n", progname, mnt); return 1; } return 0; } static pid_t clone_newns(void *a) { char buf[131072]; char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15)); #ifdef __ia64__ extern int __clone2(int (*fn)(void *), void *child_stack_base, size_t stack_size, int flags, void *arg, pid_t *ptid, void *tls, pid_t *ctid); return __clone2(check_is_mount_child, stack, sizeof(buf) / 2, CLONE_NEWNS, a, NULL, NULL, NULL); #else return clone(check_is_mount_child, stack, CLONE_NEWNS, a); #endif } static int check_is_mount(const char *last, const char *mnt, const char *type) { pid_t pid, p; int status; const char *a[3] = { last, mnt, type }; pid = clone_newns((void *) a); if (pid == (pid_t) -1) { fprintf(stderr, "%s: failed to clone namespace: %s\n", progname, strerror(errno)); return -1; } p = waitpid(pid, &status, __WCLONE); if (p == (pid_t) -1) { fprintf(stderr, "%s: waitpid failed: %s\n", progname, strerror(errno)); return -1; } if (!WIFEXITED(status)) { fprintf(stderr, "%s: child terminated abnormally (status %i)\n", progname, status); return -1; } if (WEXITSTATUS(status) != 0) return -1; return 0; } static int chdir_to_parent(char *copy, const char **lastp) { char *tmp; const char *parent; char buf[65536]; int res; tmp = strrchr(copy, '/'); if (tmp == NULL || tmp[1] == '\0') { fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n", progname, copy); return -1; } if (tmp != copy) { *tmp = '\0'; parent = copy; *lastp = tmp + 1; } else if (tmp[1] != '\0') { *lastp = tmp + 1; parent = "/"; } else { *lastp = "."; parent = "/"; } res = chdir(parent); if (res == -1) { fprintf(stderr, "%s: failed to chdir to %s: %s\n", progname, parent, strerror(errno)); return -1; } if (getcwd(buf, sizeof(buf)) == NULL) { fprintf(stderr, "%s: failed to obtain current directory: %s\n", progname, strerror(errno)); return -1; } if (strcmp(buf, parent) != 0) { fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname, parent, buf); return -1; } return 0; } #ifndef IGNORE_MTAB static int unmount_fuse_locked(const char *mnt, int quiet, int lazy) { int res; char *copy; const char *last; int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW; if (getuid() != 0) { res = may_unmount(mnt, quiet); if (res == -1) return -1; } copy = strdup(mnt); if (copy == NULL) { fprintf(stderr, "%s: failed to allocate memory\n", progname); return -1; } drop_privs(); res = chdir_to_parent(copy, &last); if (res == -1) { restore_privs(); goto out; } res = umount2(last, umount_flags); restore_privs(); if (res == -1 && !quiet) { fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, mnt, strerror(errno)); } out: free(copy); if (res == -1) return -1; res = chdir("/"); if (res == -1) { fprintf(stderr, "%s: failed to chdir to '/'\n", progname); return -1; } return fuse_mnt_remove_mount(progname, mnt); } static int unmount_fuse(const char *mnt, int quiet, int lazy) { int res; int mtablock = lock_umount(); res = unmount_fuse_locked(mnt, quiet, lazy); unlock_umount(mtablock); return res; } static int count_fuse_fs_mtab(void) { struct mntent *entp; int count = 0; const char *mtab = _PATH_MOUNTED; FILE *fp = setmntent(mtab, "r"); if (fp == NULL) { fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, strerror(errno)); return -1; } while ((entp = GETMNTENT(fp)) != NULL) { if (strcmp(entp->mnt_type, "fuse") == 0 || strncmp(entp->mnt_type, "fuse.", 5) == 0) count ++; } endmntent(fp); return count; } #ifdef HAVE_LISTMOUNT static int count_fuse_fs_ls_mnt(void) { #define SMBUF_SIZE 1024 #define MNT_ID_LEN 128 int fuse_count = 0; int n_mounts = 0; int ret = 0; uint64_t mnt_ids[MNT_ID_LEN]; unsigned char smbuf[SMBUF_SIZE]; struct mnt_id_req req = { .size = sizeof(struct mnt_id_req), }; struct statmount *sm; for (;;) { req.mnt_id = LSMT_ROOT; n_mounts = syscall(SYS_listmount, &req, &mnt_ids, MNT_ID_LEN, 0); if (n_mounts == -1) { if (errno != ENOSYS) { fprintf(stderr, "%s: failed to list mounts: %s\n", progname, strerror(errno)); } return -1; } for (int i = 0; i < n_mounts; i++) { req.mnt_id = mnt_ids[i]; req.param = STATMOUNT_FS_TYPE; ret = syscall(SYS_statmount, &req, &smbuf, SMBUF_SIZE, 0); if (ret) { if (errno == ENOENT) continue; fprintf(stderr, "%s: failed to stat mount %lld: %s\n", progname, req.mnt_id, strerror(errno)); return -1; } sm = (struct statmount *)smbuf; if (sm->mask & STATMOUNT_FS_TYPE && strcmp(&sm->str[sm->fs_type], "fuse") == 0) fuse_count++; } if (n_mounts < MNT_ID_LEN) break; req.param = mnt_ids[MNT_ID_LEN - 1]; } return fuse_count; } static int count_fuse_fs(void) { int count = count_fuse_fs_ls_mnt(); return count >= 0 ? count : count_fuse_fs_mtab(); } #else static int count_fuse_fs(void) { return count_fuse_fs_mtab(); } #endif #else /* IGNORE_MTAB */ static int count_fuse_fs(void) { return 0; } static int add_mount(const char *source, const char *mnt, const char *type, const char *opts) { (void) source; (void) mnt; (void) type; (void) opts; return 0; } static int unmount_fuse(const char *mnt, int quiet, int lazy) { (void) quiet; return fuse_mnt_umount(progname, mnt, mnt, lazy); } #endif /* IGNORE_MTAB */ static void strip_line(char *line) { char *s = strchr(line, '#'); if (s != NULL) s[0] = '\0'; for (s = line + strlen(line) - 1; s >= line && isspace((unsigned char) *s); s--); s[1] = '\0'; for (s = line; isspace((unsigned char) *s); s++); if (s != line) memmove(line, s, strlen(s)+1); } static void parse_line(char *line, int linenum) { int tmp; if (strcmp(line, "user_allow_other") == 0) user_allow_other = 1; else if (sscanf(line, "mount_max = %i", &tmp) == 1) mount_max = tmp; else if(line[0]) fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n", progname, FUSE_CONF, linenum, line); } static void read_conf(void) { FILE *fp = fopen(FUSE_CONF, "r"); if (fp != NULL) { int linenum = 1; char line[256]; int isnewline = 1; while (fgets(line, sizeof(line), fp) != NULL) { if (isnewline) { if (line[strlen(line)-1] == '\n') { strip_line(line); parse_line(line, linenum); } else { isnewline = 0; } } else if(line[strlen(line)-1] == '\n') { fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum); isnewline = 1; } if (isnewline) linenum ++; } if (!isnewline) { fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF); } if (ferror(fp)) { fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF); exit(1); } fclose(fp); } else if (errno != ENOENT) { bool fatal = (errno != EACCES && errno != ELOOP && errno != ENAMETOOLONG && errno != ENOTDIR && errno != EOVERFLOW); fprintf(stderr, "%s: failed to open %s: %s\n", progname, FUSE_CONF, strerror(errno)); if (fatal) exit(1); } } static int begins_with(const char *s, const char *beg) { if (strncmp(s, beg, strlen(beg)) == 0) return 1; else return 0; } struct mount_flags { const char *opt; unsigned long flag; int on; int safe; }; static struct mount_flags mount_flags[] = { {"rw", MS_RDONLY, 0, 1}, {"ro", MS_RDONLY, 1, 1}, {"suid", MS_NOSUID, 0, 0}, {"nosuid", MS_NOSUID, 1, 1}, {"dev", MS_NODEV, 0, 0}, {"nodev", MS_NODEV, 1, 1}, {"exec", MS_NOEXEC, 0, 1}, {"noexec", MS_NOEXEC, 1, 1}, {"async", MS_SYNCHRONOUS, 0, 1}, {"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}, {"symfollow", MS_NOSYMFOLLOW, 0, 1}, {"nosymfollow", MS_NOSYMFOLLOW, 1, 1}, {NULL, 0, 0, 0} }; static int find_mount_flag(const char *s, unsigned len, int *on, int *flag) { int i; for (i = 0; mount_flags[i].opt != NULL; i++) { const char *opt = mount_flags[i].opt; if (strlen(opt) == len && strncmp(opt, s, len) == 0) { *on = mount_flags[i].on; *flag = mount_flags[i].flag; if (!mount_flags[i].safe && getuid() != 0) { *flag = 0; fprintf(stderr, "%s: unsafe option %s ignored\n", progname, opt); } return 1; } } return 0; } static int add_option(char **optsp, const char *opt, unsigned expand) { char *newopts; if (*optsp == NULL) newopts = strdup(opt); else { unsigned oldsize = strlen(*optsp); unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1; newopts = (char *) realloc(*optsp, newsize); if (newopts) sprintf(newopts + oldsize, ",%s", opt); } if (newopts == NULL) { fprintf(stderr, "%s: failed to allocate memory\n", progname); return -1; } *optsp = newopts; return 0; } static int get_mnt_opts(int flags, char *opts, char **mnt_optsp) { int i; int l; if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1) return -1; for (i = 0; mount_flags[i].opt != NULL; i++) { if (mount_flags[i].on && (flags & mount_flags[i].flag) && add_option(mnt_optsp, mount_flags[i].opt, 0) == -1) return -1; } if (add_option(mnt_optsp, opts, 0) == -1) return -1; /* remove comma from end of opts*/ l = strlen(*mnt_optsp); if ((*mnt_optsp)[l-1] == ',') (*mnt_optsp)[l-1] = '\0'; if (getuid() != 0) { const char *user = get_user_name(); if (user == NULL) return -1; if (add_option(mnt_optsp, "user=", strlen(user)) == -1) return -1; strcat(*mnt_optsp, user); } return 0; } static int opt_eq(const char *s, unsigned len, const char *opt) { if(strlen(opt) == len && strncmp(s, opt, len) == 0) return 1; else return 0; } static int get_string_opt(const char *s, unsigned len, const char *opt, char **val) { int i; unsigned opt_len = strlen(opt); char *d; if (*val) free(*val); *val = (char *) malloc(len - opt_len + 1); if (!*val) { fprintf(stderr, "%s: failed to allocate memory\n", progname); return 0; } d = *val; s += opt_len; len -= opt_len; for (i = 0; i < len; i++) { if (s[i] == '\\' && i + 1 < len) i++; *d++ = s[i]; } *d = '\0'; return 1; } /* The kernel silently truncates the "data" argument to PAGE_SIZE-1 characters. * This can be dangerous if it e.g. truncates the option "group_id=1000" to * "group_id=1". * This wrapper detects this case and bails out with an error. */ static int mount_notrunc(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const char *data) { if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) { fprintf(stderr, "%s: mount options too long\n", progname); errno = EINVAL; return -1; } return mount(source, target, filesystemtype, mountflags, data); } static int do_mount(const char *mnt, const char **typep, mode_t rootmode, int fd, const char *opts, const char *dev, char **sourcep, char **mnt_optsp) { int res; int flags = MS_NOSUID | MS_NODEV; char *optbuf; char *mnt_opts = NULL; const char *s; char *d; char *fsname = NULL; char *subtype = NULL; char *source = NULL; char *type = NULL; int blkdev = 0; optbuf = (char *) malloc(strlen(opts) + 128); if (!optbuf) { fprintf(stderr, "%s: failed to allocate memory\n", progname); return -1; } for (s = opts, d = optbuf; *s;) { unsigned len; const char *fsname_str = "fsname="; const char *subtype_str = "subtype="; bool escape_ok = begins_with(s, fsname_str) || begins_with(s, subtype_str); for (len = 0; s[len]; len++) { if (escape_ok && s[len] == '\\' && s[len + 1]) len++; else if (s[len] == ',') break; } if (begins_with(s, fsname_str)) { if (!get_string_opt(s, len, fsname_str, &fsname)) goto err; } else if (begins_with(s, subtype_str)) { if (!get_string_opt(s, len, subtype_str, &subtype)) goto err; } else if (opt_eq(s, len, "blkdev")) { if (getuid() != 0) { fprintf(stderr, "%s: option blkdev is privileged\n", progname); goto err; } blkdev = 1; } else if (opt_eq(s, len, "auto_unmount")) { auto_unmount = 1; } else if (!opt_eq(s, len, "nonempty") && !begins_with(s, "fd=") && !begins_with(s, "rootmode=") && !begins_with(s, "user_id=") && !begins_with(s, "group_id=")) { int on; int flag; int skip_option = 0; if (opt_eq(s, len, "large_read")) { struct utsname utsname; unsigned kmaj, kmin; res = uname(&utsname); if (res == 0 && sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 && (kmaj > 2 || (kmaj == 2 && kmin > 4))) { fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin); skip_option = 1; } } if (getuid() != 0 && !user_allow_other && (opt_eq(s, len, "allow_other") || opt_eq(s, len, "allow_root"))) { fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF); goto err; } if (!skip_option) { if (find_mount_flag(s, len, &on, &flag)) { if (on) flags |= flag; else flags &= ~flag; } else if (opt_eq(s, len, "default_permissions") || opt_eq(s, len, "allow_other") || begins_with(s, "max_read=") || begins_with(s, "blksize=")) { memcpy(d, s, len); d += len; *d++ = ','; } else { fprintf(stderr, "%s: unknown option '%.*s'\n", progname, len, s); exit(1); } } } s += len; if (*s) s++; } *d = '\0'; res = get_mnt_opts(flags, optbuf, &mnt_opts); if (res == -1) goto err; sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u", fd, rootmode, getuid(), getgid()); source = malloc((fsname ? strlen(fsname) : 0) + (subtype ? strlen(subtype) : 0) + strlen(dev) + 32); type = malloc((subtype ? strlen(subtype) : 0) + 32); if (!type || !source) { fprintf(stderr, "%s: failed to allocate memory\n", progname); goto err; } if (subtype) sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype); else strcpy(type, blkdev ? "fuseblk" : "fuse"); if (fsname) strcpy(source, fsname); else strcpy(source, subtype ? subtype : dev); res = mount_notrunc(source, mnt, type, flags, optbuf); if (res == -1 && errno == ENODEV && subtype) { /* Probably missing subtype support */ strcpy(type, blkdev ? "fuseblk" : "fuse"); if (fsname) { if (!blkdev) sprintf(source, "%s#%s", subtype, fsname); } else { strcpy(source, type); } res = mount_notrunc(source, mnt, type, flags, optbuf); } if (res == -1 && errno == EINVAL) { /* It could be an old version not supporting group_id */ sprintf(d, "fd=%i,rootmode=%o,user_id=%u", fd, rootmode, getuid()); res = mount_notrunc(source, mnt, type, flags, optbuf); } if (res == -1) { int errno_save = errno; if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) fprintf(stderr, "%s: 'fuseblk' support missing\n", progname); else fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save)); goto err; } *sourcep = source; *typep = type; *mnt_optsp = mnt_opts; free(fsname); free(optbuf); return 0; err: free(fsname); free(subtype); free(source); free(type); free(mnt_opts); free(optbuf); return -1; } static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd) { int res; const char *mnt = *mntp; const char *origmnt = mnt; struct statfs fs_buf; size_t i; res = lstat(mnt, stbuf); if (res == -1) { fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", progname, mnt, strerror(errno)); return -1; } /* No permission checking is done for root */ if (getuid() == 0) return 0; if (S_ISDIR(stbuf->st_mode)) { res = chdir(mnt); if (res == -1) { fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n", progname, strerror(errno)); return -1; } mnt = *mntp = "."; res = lstat(mnt, stbuf); if (res == -1) { fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", progname, origmnt, strerror(errno)); return -1; } if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) { fprintf(stderr, "%s: mountpoint %s not owned by user\n", progname, origmnt); return -1; } res = access(mnt, W_OK); if (res == -1) { fprintf(stderr, "%s: user has no write access to mountpoint %s\n", progname, origmnt); return -1; } } else if (S_ISREG(stbuf->st_mode)) { static char procfile[256]; *mountpoint_fd = open(mnt, O_WRONLY); if (*mountpoint_fd == -1) { fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt, strerror(errno)); return -1; } res = fstat(*mountpoint_fd, stbuf); if (res == -1) { fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", progname, mnt, strerror(errno)); return -1; } if (!S_ISREG(stbuf->st_mode)) { fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n", progname, mnt); return -1; } sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd); *mntp = procfile; } else { fprintf(stderr, "%s: mountpoint %s is not a directory or a regular file\n", progname, mnt); return -1; } /* Do not permit mounting over anything in procfs - it has a couple * places to which we have "write access" without being supposed to be * able to just put anything we want there. * Luckily, without allow_other, we can't get other users to actually * use any fake information we try to put there anyway. * Use a whitelist to be safe. */ if (statfs(*mntp, &fs_buf)) { fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", progname, mnt, strerror(errno)); return -1; } /* Define permitted filesystems for the mount target. This was * 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 * overlaid. */ typeof(fs_buf.f_type) f_type_whitelist[] = { 0x61756673 /* AUFS_SUPER_MAGIC */, 0x00000187 /* AUTOFS_SUPER_MAGIC */, 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */, 0x9123683E /* BTRFS_SUPER_MAGIC */, 0x00C36400 /* CEPH_SUPER_MAGIC */, 0xFF534D42 /* CIFS_MAGIC_NUMBER */, 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */, 0X2011BAB0 /* EXFAT_SUPER_MAGIC */, 0x0000EF53 /* EXT[234]_SUPER_MAGIC */, 0xF2F52010 /* F2FS_SUPER_MAGIC */, 0x65735546 /* FUSE_SUPER_MAGIC */, 0x01161970 /* GFS2_MAGIC */, 0x47504653 /* GPFS_SUPER_MAGIC */, 0x0000482b /* HFSPLUS_SUPER_MAGIC */, 0x000072B6 /* JFFS2_SUPER_MAGIC */, 0x3153464A /* JFS_SUPER_MAGIC */, 0x0BD00BD0 /* LL_SUPER_MAGIC */, 0X00004D44 /* MSDOS_SUPER_MAGIC */, 0x0000564C /* NCP_SUPER_MAGIC */, 0x00006969 /* NFS_SUPER_MAGIC */, 0x00003434 /* NILFS_SUPER_MAGIC */, 0x5346544E /* NTFS_SB_MAGIC */, 0x7366746E /* NTFS3_SUPER_MAGIC */, 0x5346414f /* OPENAFS_SUPER_MAGIC */, 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */, 0xAAD7AAEA /* PANFS_SUPER_MAGIC */, 0x52654973 /* REISERFS_SUPER_MAGIC */, 0xFE534D42 /* SMB2_SUPER_MAGIC */, 0x73717368 /* SQUASHFS_MAGIC */, 0x01021994 /* TMPFS_MAGIC */, 0x24051905 /* UBIFS_SUPER_MAGIC */, 0x18031977 /* WEKAFS_SUPER_MAGIC */, #if __SIZEOF_LONG__ > 4 0x736675005346544e /* UFSD */, #endif 0x58465342 /* XFS_SB_MAGIC */, 0x2FC12FC1 /* ZFS_SUPER_MAGIC */, 0x858458f6 /* RAMFS_MAGIC */, }; for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) { if (f_type_whitelist[i] == fs_buf.f_type) return 0; } fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n", progname, (unsigned long)fs_buf.f_type); return -1; } static int open_fuse_device(const char *dev) { int fd; drop_privs(); fd = open(dev, O_RDWR); if (fd == -1) { if (errno == ENODEV || errno == ENOENT)/* check for ENOENT too, for the udev case */ fprintf(stderr, "%s: fuse device %s not found. Kernel module not loaded?\n", progname, dev); else fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev, strerror(errno)); } restore_privs(); return fd; } static int mount_fuse(const char *mnt, const char *opts, const char **type) { int res; int fd; const char *dev = getenv(FUSE_KERN_DEVICE_ENV) ?: FUSE_DEV; struct stat stbuf; char *source = NULL; char *mnt_opts = NULL; const char *real_mnt = mnt; int mountpoint_fd = -1; char *do_mount_opts = NULL; char *x_opts = NULL; fd = open_fuse_device(dev); if (fd == -1) return -1; drop_privs(); read_conf(); if (getuid() != 0 && mount_max != -1) { int mount_count = count_fuse_fs(); if (mount_count >= mount_max) { fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF); goto fail_close_fd; } } // Extract any options starting with "x-" res= extract_x_options(opts, &do_mount_opts, &x_opts); if (res) goto fail_close_fd; res = check_perm(&real_mnt, &stbuf, &mountpoint_fd); restore_privs(); if (res != -1) res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT, fd, do_mount_opts, dev, &source, &mnt_opts); if (mountpoint_fd != -1) close(mountpoint_fd); if (res == -1) goto fail_close_fd; res = chdir("/"); if (res == -1) { fprintf(stderr, "%s: failed to chdir to '/'\n", progname); goto fail_close_fd; } if (geteuid() == 0) { if (x_opts && strlen(x_opts) > 0) { /* * Add back the options starting with "x-" to opts from * do_mount. +2 for ',' and '\0' */ size_t mnt_opts_len = strlen(mnt_opts); size_t x_mnt_opts_len = mnt_opts_len+ strlen(x_opts) + 2; char *x_mnt_opts = calloc(1, x_mnt_opts_len); if (mnt_opts_len) { strcpy(x_mnt_opts, mnt_opts); strncat(x_mnt_opts, ",", 2); } strncat(x_mnt_opts, x_opts, x_mnt_opts_len - mnt_opts_len - 2); free(mnt_opts); mnt_opts = x_mnt_opts; } res = add_mount(source, mnt, *type, mnt_opts); if (res == -1) { /* Can't clean up mount in a non-racy way */ goto fail_close_fd; } } out_free: free(source); free(mnt_opts); free(x_opts); free(do_mount_opts); return fd; fail_close_fd: close(fd); fd = -1; goto out_free; } static int send_fd(int sock_fd, int fd) { int retval; struct msghdr msg; struct cmsghdr *p_cmsg; struct iovec vec; size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)]; int *p_fds; char sendchar = 0; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); p_cmsg = CMSG_FIRSTHDR(&msg); p_cmsg->cmsg_level = SOL_SOCKET; p_cmsg->cmsg_type = SCM_RIGHTS; p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); p_fds = (int *) CMSG_DATA(p_cmsg); *p_fds = fd; msg.msg_controllen = p_cmsg->cmsg_len; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &vec; msg.msg_iovlen = 1; msg.msg_flags = 0; /* "To pass file descriptors or credentials you need to send/read at * least one byte" (man 7 unix) */ vec.iov_base = &sendchar; vec.iov_len = sizeof(sendchar); while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR); if (retval != 1) { perror("sending file descriptor"); return -1; } 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 * has already been unmounted. If we simply unmount again we can * cause problems with stacked mounts (e.g. autofs). * * So we unmount here only in abnormal case where fuse process has * died without unmount happening. To detect this, we first look in * the mount table to make sure the mountpoint is still mounted and * has proper type. If so, we then see if opening the mount dir is * returning 'Transport endpoint is not connected'. * * The order of these is important, because if autofs is in use, * opening the dir to check for ENOTCONN will cause a new mount * in the normal case where filesystem has been unmounted cleanly. */ static int should_auto_unmount(const char *mnt, const char *type) { char *copy; const char *last; int result = 0; int fd; copy = strdup(mnt); if (copy == NULL) { fprintf(stderr, "%s: failed to allocate memory\n", progname); return 0; } if (chdir_to_parent(copy, &last) == -1) goto out; if (check_is_mount(last, mnt, type) == -1) goto out; fd = open(mnt, O_RDONLY); if (fd != -1) { close(fd); } else { switch(errno) { case ENOTCONN: result = 1; break; case EACCES: result = recheck_ENOTCONN_as_owner(mnt); break; default: result = 0; break; } } out: free(copy); return result; } static void usage(void) { printf("%s: [options] mountpoint\n" "Options:\n" " -h print help\n" " -V print version\n" " -o opt[,opt...] mount options\n" " -u unmount\n" " -q quiet\n" " -z lazy unmount\n", progname); exit(1); } static void show_version(void) { printf("fusermount3 version: %s\n", PACKAGE_VERSION); exit(0); } static void close_range_loop(int min_fd, int max_fd, int cfd) { for (int fd = min_fd; fd <= max_fd; fd++) if (fd != cfd) close(fd); } /* * Close all inherited fds that are not needed * Ideally these wouldn't come up at all, applications should better * use FD_CLOEXEC / O_CLOEXEC */ static int close_inherited_fds(int cfd) { int rc = -1; int nullfd; /* We can't even report an error */ if (cfd <= STDERR_FILENO) return -EINVAL; #ifdef HAVE_CLOSE_RANGE if (cfd < STDERR_FILENO + 2) { close_range_loop(STDERR_FILENO + 1, cfd - 1, cfd); } else { rc = close_range(STDERR_FILENO + 1, cfd - 1, 0); if (rc < 0) goto fallback; } /* Close high range */ rc = close_range(cfd + 1, ~0U, 0); #else goto fallback; /* make use of fallback to avoid compiler warnings */ #endif fallback: if (rc < 0) { int max_fd = sysconf(_SC_OPEN_MAX) - 1; close_range_loop(STDERR_FILENO + 1, max_fd, cfd); } nullfd = open("/dev/null", O_RDWR); if (nullfd < 0) { perror("fusermount: cannot open /dev/null"); return -errno; } /* Redirect stdin, stdout, stderr to /dev/null */ dup2(nullfd, STDIN_FILENO); dup2(nullfd, STDOUT_FILENO); dup2(nullfd, STDERR_FILENO); if (nullfd > STDERR_FILENO) close(nullfd); return 0; } int main(int argc, char *argv[]) { sigset_t sigset; int ch; int fd; int res; char *origmnt; char *mnt; static int unmount = 0; static int lazy = 0; static int quiet = 0; char *commfd = NULL; long 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'}, {"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'}, // Note: auto-unmount and comm-fd don't have short versions. // They'ne meant for internal use by mount.c {"auto-unmount", no_argument, NULL, 'U'}, {"comm-fd", required_argument, NULL, 'c'}, {0, 0, 0, 0}}; progname = strdup(argc > 0 ? argv[0] : "fusermount"); if (progname == NULL) { fprintf(stderr, "%s: failed to allocate memory\n", argv[0]); exit(1); } while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts, NULL)) != -1) { switch (ch) { case 'h': usage(); break; case 'V': show_version(); break; case 'o': opts = optarg; break; case 'u': unmount = 1; break; case 'U': unmount = 1; auto_unmount = 1; setup_auto_unmount_only = 1; break; case 'c': commfd = optarg; break; case 'z': lazy = 1; break; case 'q': quiet = 1; break; default: exit(1); } } if (lazy && !unmount) { fprintf(stderr, "%s: -z can only be used with -u\n", progname); exit(1); } if (optind >= argc) { fprintf(stderr, "%s: missing mountpoint argument\n", progname); exit(1); } else if (argc > optind + 1) { fprintf(stderr, "%s: extra arguments after the mountpoint\n", progname); exit(1); } origmnt = argv[optind]; drop_privs(); mnt = fuse_mnt_resolve_path(progname, origmnt); if (mnt != NULL) { res = chdir("/"); if (res == -1) { fprintf(stderr, "%s: failed to chdir to '/'\n", progname); goto err_out; } } restore_privs(); if (mnt == NULL) exit(1); umask(033); if (!setup_auto_unmount_only && unmount) goto do_unmount; if(commfd == NULL) commfd = getenv(FUSE_COMMFD_ENV); if (commfd == NULL) { fprintf(stderr, "%s: old style mounting not supported\n", progname); goto err_out; } res = libfuse_strtol(commfd, &cfd); if (res) { fprintf(stderr, "%s: invalid _FUSE_COMMFD: %s\n", progname, commfd); goto err_out; } { struct stat statbuf; fstat(cfd, &statbuf); if(!S_ISSOCK(statbuf.st_mode)) { fprintf(stderr, "%s: file descriptor %li 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; res = send_fd(cfd, fd); 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 it forks and messes with the file descriptors. */ res = close_inherited_fds(cfd); if (res < 0) exit(EXIT_FAILURE); setsid(); res = chdir("/"); if (res == -1) { fprintf(stderr, "%s: failed to chdir to '/'\n", progname); goto err_out; } sigfillset(&sigset); sigprocmask(SIG_BLOCK, &sigset, NULL); lazy = 1; quiet = 1; while (1) { unsigned char buf[16]; int n = recv(cfd, buf, sizeof(buf), 0); if (!n) break; if (n < 0) { if (errno == EINTR) continue; break; } } if (!should_auto_unmount(mnt, type)) { goto success_out; } do_unmount: if (geteuid() == 0) res = unmount_fuse(mnt, quiet, lazy); else { res = umount2(mnt, lazy ? UMOUNT_DETACH : 0); if (res == -1 && !quiet) fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, mnt, strerror(errno)); } if (res == -1) goto err_out; success_out: free((void*) type); free(mnt); return 0; err_out: free((void*) type); free(mnt); exit(1); } fuse-3.18.2/util/init_script0000755000175000017500000000363615156613252014765 0ustar berndbernd#!/bin/sh ### BEGIN INIT INFO # Provides: fuse # Required-Start: # Should-Start: udev # Required-Stop: # Default-Start: S # Default-Stop: # Short-Description: Start and stop fuse. # Description: Load the fuse module and mount the fuse control # filesystem. ### END INIT INFO set -e PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin MOUNTPOINT=/sys/fs/fuse/connections # Gracefully exit if the package has been removed. which fusermount3 &>/dev/null || exit 5 # Define LSB log_* functions. . /lib/lsb/init-functions case "$1" in start|restart|force-reload) if ! grep -qw fuse /proc/filesystems; then echo -n "Loading fuse module" if ! modprobe fuse >/dev/null 2>&1; then echo " failed!" exit 1 else echo "." fi else echo "Fuse filesystem already available." fi if grep -qw fusectl /proc/filesystems && \ ! grep -qw $MOUNTPOINT /proc/mounts; then echo -n "Mounting fuse control filesystem" if ! mount -t fusectl fusectl $MOUNTPOINT >/dev/null 2>&1; then echo " failed!" exit 1 else echo "." fi else echo "Fuse control filesystem already available." fi ;; stop) if ! grep -qw fuse /proc/filesystems; then echo "Fuse filesystem not loaded." exit 7 fi if grep -qw $MOUNTPOINT /proc/mounts; then echo -n "Unmounting fuse control filesystem" if ! umount $MOUNTPOINT >/dev/null 2>&1; then echo " failed!" else echo "." fi else echo "Fuse control filesystem not mounted." fi if grep -qw "^fuse" /proc/modules; then echo -n "Unloading fuse module" if ! rmmod fuse >/dev/null 2>&1; then echo " failed!" else echo "." fi else echo "Fuse module not loaded." fi ;; status) echo -n "Checking fuse filesystem" if ! grep -qw fuse /proc/filesystems; then echo " not available." exit 3 else echo " ok." fi ;; *) echo "Usage: $0 {start|stop|restart|force-reload|status}" exit 1 ;; esac exit 0 fuse-3.18.2/util/install_helper.sh0000755000175000017500000000277315156613252016055 0ustar berndbernd#!/bin/sh # # Don't call this script. It is used internally by the Meson # build system. Thank you for your cooperation. # set -e 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 # to DESTDIR (if specified). if [ -z "${DESTDIR}" ]; then # Prevent warnings about uninitialized variable DESTDIR="" else # Get rid of duplicate slash DESTDIR="${DESTDIR%/}" fi install -D -m 644 "${MESON_SOURCE_ROOT}/util/fuse.conf" \ "${DESTDIR}${sysconfdir}/fuse.conf" if $useroot; then chown root:root "${DESTDIR}${bindir}/fusermount3" chmod u+s "${DESTDIR}${bindir}/fusermount3" if test ! -e "${DESTDIR}/dev/fuse"; then mkdir -p "${DESTDIR}/dev" mknod "${DESTDIR}/dev/fuse" -m 0666 c 10 229 fi fi if [ "${udevrulesdir}" != "" ]; then install -D -m 644 "${MESON_SOURCE_ROOT}/util/udev.rules" \ "${DESTDIR}${udevrulesdir}/99-fuse3.rules" 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 fuse-3.18.2/util/meson.build0000644000175000017500000000241715156613252014646 0ustar berndberndfuseconf_path = join_paths(get_option('prefix'), get_option('sysconfdir'), 'fuse.conf') executable('fusermount3', ['fusermount.c', '../lib/mount_util.c', '../lib/util.c'], include_directories: include_dirs, install: true, install_dir: get_option('bindir'), c_args: '-DFUSE_CONF="@0@"'.format(fuseconf_path)) executable('mount.fuse3', ['mount.fuse.c'], include_directories: include_dirs, link_with: [ libfuse ], install: true, install_dir: get_option('sbindir'), c_args: '-DFUSE_USE_VERSION=317') udevrulesdir = get_option('udevrulesdir') if udevrulesdir == '' 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')), get_option('initscriptdir')) fuse-3.18.2/util/mount.fuse.c0000644000175000017500000002500215156613252014746 0ustar berndbernd/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU GPLv2. See the file GPL2.txt. */ #include "fuse_config.h" #include #include #include #include #include #include #include #include #include #ifdef linux #include #include #include #include /* for 2.6 kernels */ #if !defined(SECBIT_KEEP_CAPS) && defined(SECURE_KEEP_CAPS) #define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS)) #endif #if !defined(SECBIT_KEEP_CAPS_LOCKED) && defined(SECURE_KEEP_CAPS_LOCKED) #define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED)) #endif #if !defined(SECBIT_NO_SETUID_FIXUP) && defined(SECURE_NO_SETUID_FIXUP) #define SECBIT_NO_SETUID_FIXUP (issecure_mask(SECURE_NO_SETUID_FIXUP)) #endif #if !defined(SECBIT_NO_SETUID_FIXUP_LOCKED) && defined(SECURE_NO_SETUID_FIXUP_LOCKED) #define SECBIT_NO_SETUID_FIXUP_LOCKED (issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED)) #endif #if !defined(SECBIT_NOROOT) && defined(SECURE_NOROOT) #define SECBIT_NOROOT (issecure_mask(SECURE_NOROOT)) #endif #if !defined(SECBIT_NOROOT_LOCKED) && defined(SECURE_NOROOT_LOCKED) #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" static char *progname; static char *xstrdup(const char *s) { char *t = strdup(s); if (!t) { fprintf(stderr, "%s: failed to allocate memory\n", progname); exit(1); } return t; } static void *xrealloc(void *oldptr, size_t size) { void *ptr = realloc(oldptr, size); if (!ptr) { fprintf(stderr, "%s: failed to allocate memory\n", progname); exit(1); } return ptr; } static void add_arg(char **cmdp, const char *opt) { size_t optlen = strlen(opt); size_t cmdlen = *cmdp ? strlen(*cmdp) : 0; if (optlen >= (SIZE_MAX - cmdlen - 4)/4) { fprintf(stderr, "%s: argument too long\n", progname); exit(1); } char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4); char *s; s = cmd + cmdlen; if (*cmdp) *s++ = ' '; *s++ = '\''; for (; *opt; opt++) { if (*opt == '\'') { *s++ = '\''; *s++ = '\\'; *s++ = '\''; *s++ = '\''; } else *s++ = *opt; } *s++ = '\''; *s = '\0'; *cmdp = cmd; } static char *add_option(const char *opt, char *options) { int oldlen = options ? strlen(options) : 0; options = xrealloc(options, oldlen + 1 + strlen(opt) + 1); if (!oldlen) strcpy(options, opt); else { strcat(options, ","); strcat(options, opt); } return options; } static int prepare_fuse_fd(const char *mountpoint, const char* subtype, const char *options) { int fuse_fd = -1; int flags = -1; int subtype_len = strlen(subtype) + 9; char* options_copy = xrealloc(NULL, subtype_len); snprintf(options_copy, subtype_len, "subtype=%s", subtype); options_copy = add_option(options, options_copy); fuse_fd = fuse_open_channel(mountpoint, options_copy); if (fuse_fd == -1) { exit(1); } flags = fcntl(fuse_fd, F_GETFD); if (flags == -1 || fcntl(fuse_fd, F_SETFD, flags & ~FD_CLOEXEC) == 1) { fprintf(stderr, "%s: Failed to clear CLOEXEC: %s\n", progname, strerror(errno)); exit(1); } return fuse_fd; } #ifdef linux static uint64_t get_capabilities(void) { /* * This invokes the capset syscall directly to avoid the libcap * dependency, which isn't really justified just for this. */ struct __user_cap_header_struct header = { .version = _LINUX_CAPABILITY_VERSION_3, .pid = 0, }; struct __user_cap_data_struct data[2]; memset(data, 0, sizeof(data)); if (syscall(SYS_capget, &header, data) == -1) { fprintf(stderr, "%s: Failed to get capabilities: %s\n", progname, strerror(errno)); exit(1); } return data[0].effective | ((uint64_t) data[1].effective << 32); } static void set_capabilities(uint64_t caps) { /* * This invokes the capset syscall directly to avoid the libcap * dependency, which isn't really justified just for this. */ struct __user_cap_header_struct header = { .version = _LINUX_CAPABILITY_VERSION_3, .pid = 0, }; struct __user_cap_data_struct data[2]; memset(data, 0, sizeof(data)); data[0].effective = data[0].permitted = caps; data[1].effective = data[1].permitted = caps >> 32; if (syscall(SYS_capset, &header, data) == -1) { fprintf(stderr, "%s: Failed to set capabilities: %s\n", progname, strerror(errno)); exit(1); } } static void drop_and_lock_capabilities(void) { /* Set and lock securebits. */ if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS_LOCKED | SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED | SECBIT_NOROOT | SECBIT_NOROOT_LOCKED) == -1) { fprintf(stderr, "%s: Failed to set securebits %s\n", progname, strerror(errno)); exit(1); } /* Clear the capability bounding set. */ int cap; for (cap = 0; ; cap++) { int cap_status = prctl(PR_CAPBSET_READ, cap); if (cap_status == 0) { continue; } if (cap_status == -1 && errno == EINVAL) { break; } if (cap_status != 1) { fprintf(stderr, "%s: Failed to get capability %u: %s\n", progname, cap, strerror(errno)); exit(1); } if (prctl(PR_CAPBSET_DROP, cap) == -1) { fprintf(stderr, "%s: Failed to drop capability %u: %s\n", progname, cap, strerror(errno)); } } /* Drop capabilities. */ set_capabilities(0); /* Prevent re-acquisition of privileges. */ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { fprintf(stderr, "%s: Failed to set no_new_privs: %s\n", progname, strerror(errno)); exit(1); } } #endif int main(int argc, char *argv[]) { char *type = NULL; char *source; char *dup_source = NULL; const char *mountpoint; char *basename; char *options = NULL; char *command = NULL; char *setuid_name = NULL; int i; int dev = 1; int suid = 1; int pass_fuse_fd = 0; int fuse_fd = 0; int drop_privileges = 0; char *dev_fd_mountpoint = NULL; progname = argv[0]; basename = strrchr(argv[0], '/'); if (basename) basename++; else basename = argv[0]; if (strncmp(basename, "mount.fuse.", 11) == 0) type = basename + 11; if (strncmp(basename, "mount.fuseblk.", 14) == 0) type = basename + 14; if (type && !type[0]) type = NULL; if (argc < 3) { fprintf(stderr, "usage: %s %s destination [-t type] [-o opt[,opts...]]\n", progname, type ? "source" : "type#[source]"); exit(1); } source = argv[1]; if (!source[0]) source = NULL; mountpoint = argv[2]; for (i = 3; i < argc; i++) { if (strcmp(argv[i], "-v") == 0) { continue; } else if (strcmp(argv[i], "-t") == 0) { i++; if (i == argc) { fprintf(stderr, "%s: missing argument to option '-t'\n", progname); exit(1); } type = argv[i]; if (strncmp(type, "fuse.", 5) == 0) type += 5; else if (strncmp(type, "fuseblk.", 8) == 0) type += 8; if (!type[0]) { fprintf(stderr, "%s: empty type given as argument to option '-t'\n", progname); exit(1); } } else if (strcmp(argv[i], "-o") == 0) { char *opts; char *opt; i++; if (i == argc) break; opts = xstrdup(argv[i]); opt = strtok(opts, ","); while (opt) { int j; int ignore = 0; const char *ignore_opts[] = { "", "user", "nofail", "nouser", "users", "auto", "noauto", "_netdev", NULL}; if (strncmp(opt, "setuid=", 7) == 0) { setuid_name = xstrdup(opt + 7); ignore = 1; } else if (strcmp(opt, "drop_privileges") == 0) { pass_fuse_fd = 1; drop_privileges = 1; ignore = 1; } for (j = 0; ignore_opts[j]; j++) if (strcmp(opt, ignore_opts[j]) == 0) ignore = 1; if (!ignore) { if (strcmp(opt, "nodev") == 0) dev = 0; else if (strcmp(opt, "nosuid") == 0) suid = 0; options = add_option(opt, options); } opt = strtok(NULL, ","); } free(opts); } } if (drop_privileges) { uint64_t required_caps = CAP_TO_MASK(CAP_SETPCAP) | CAP_TO_MASK(CAP_SYS_ADMIN); if ((get_capabilities() & required_caps) != required_caps) { fprintf(stderr, "%s: drop_privileges was requested, which launches the FUSE file system fully unprivileged. In order to do so %s must be run with privileges, please invoke with CAP_SYS_ADMIN and CAP_SETPCAP (e.g. as root).\n", progname, progname); exit(1); } } if (dev) options = add_option("dev", options); if (suid) options = add_option("suid", options); if (!type) { if (source) { dup_source = xstrdup(source); type = dup_source; source = strchr(type, '#'); if (source) *source++ = '\0'; if (!type[0]) { fprintf(stderr, "%s: empty filesystem type\n", progname); exit(1); } } else { fprintf(stderr, "%s: empty source\n", progname); exit(1); } } if (setuid_name && setuid_name[0]) { #ifdef linux if (drop_privileges) { /* * Make securebits more permissive before calling * setuid(). Specifically, if SECBIT_KEEP_CAPS and * SECBIT_NO_SETUID_FIXUP weren't set, setuid() would * have the side effect of dropping all capabilities, * and we need to retain CAP_SETPCAP in order to drop * all privileges before exec(). */ if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_NO_SETUID_FIXUP) == -1) { fprintf(stderr, "%s: Failed to set securebits %s\n", progname, strerror(errno)); exit(1); } } #endif struct passwd *pwd = getpwnam(setuid_name); if (!pwd || setgid(pwd->pw_gid) == -1 || setuid(pwd->pw_uid) == -1) { fprintf(stderr, "%s: Failed to setuid to %s: %s\n", progname, setuid_name, strerror(errno)); exit(1); } } else if (!getenv("HOME")) { /* Hack to make filesystems work in the boot environment */ setenv("HOME", "/root", 0); } if (pass_fuse_fd) { fuse_fd = prepare_fuse_fd(mountpoint, type, options); dev_fd_mountpoint = xrealloc(NULL, 20); snprintf(dev_fd_mountpoint, 20, "/dev/fd/%u", fuse_fd); mountpoint = dev_fd_mountpoint; } #ifdef linux if (drop_privileges) { drop_and_lock_capabilities(); } #endif add_arg(&command, type); if (source) add_arg(&command, source); add_arg(&command, mountpoint); if (options) { add_arg(&command, "-o"); add_arg(&command, options); } free(options); free(dev_fd_mountpoint); free(dup_source); free(setuid_name); execl("/bin/sh", "/bin/sh", "-c", command, NULL); fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname, strerror(errno)); if (pass_fuse_fd) close(fuse_fd); free(command); return 1; } fuse-3.18.2/util/parse-backtrace.sh0000755000175000017500000000535215156613252016073 0ustar berndbernd#!/bin/bash tmpfile=`mktemp backtrace.XXX` PROGRAM_PATH='' TRACE_TYPE=glibc print_help() { echo "Usage: " echo " " `basename $0`" [-s] [-p ] -t <\"full trace\">" echo "Options: " echo " -t '' - The entire trace should be put into quotes" echo " for this option" echo " -p path/to/filename - optional path to the binary, typically" echo " autodetected" echo exit 1 } if [ -z "$1" -o "$1" = "-h" -o "$1" = "--help" ]; then rm -f $tmpfile print_help fi while getopts "hf:t:p:" opt; do case $opt in h) print_help ;; f) PROGRAM_PATH="$OPTARG" ;; t) TRACE="$OPTARG" ;; p) PROGRAM_PATH="$OPTARG" ;; *) print_help ;; esac done # use addr2line parse_glibc_trace() { local trace="$1" local filename="$2" local tmpname="" local symbol="" IFS=$'\n' for line in ${trace}; do #echo "Line: '$line" # remove C2 A0 (non breaking space, as inserted by windows) line=$(echo $line | sed 's/\xC2\xA0/ /g') line=`echo $line | egrep "\[" | egrep "\]"` [ -n "$line" ] || continue # cut off additional syslog part - beginning of line to ':' line=$(echo $line line | sed -e 's/.*://') # parse lines like # /usr/lib/libfuse3.so.3(+0x1c0ef) [0x7fca6061c0ef] filename=$(echo $line | awk '{print $1}' | sed -e 's/(.*$//') if [ -z "${filename}" ]; then echo "Failed to get filename path for line: \"$line\"" return fi if [[ $filename != /* ]]; then if [ -n "${PROGRAM_PATH}" ]; then filename="${PROGRAM_PATH}" else tmpname="$(which $filename)" if [ $? -ne 0 ]; then echo "Failed to get path for '$filename'" continue fi filename="${tmpname}" fi fi # for plain glibc backtrace_symbols the symbol is also in column1, # within the brackets () symbol=$(echo $line | awk '{print $1}' | sed -e 's/^.*(//' | sed -e 's/).*//') if [ -z "${symbol}" ]; then echo "Failed to get symbol for line: \"$line\"" continue fi addr2line -a -p -s -C -f -i -e ${filename} ${symbol} done } if [ -z "$TRACE" ]; then echo "Missing backtrace option!" echo print_help fi # For now only glibc backtrace_symbols traces are supported if [ $TRACE_TYPE = "glibc" ]; then parse_glibc_trace "$TRACE" "${PROGRAM_PATH}" else echo "Unknown tracetype: '${TRACE_TYPE}'" fi rm -f $tmpfile fuse-3.18.2/util/udev.rules0000644000175000017500000000003415156613252014514 0ustar berndberndKERNEL=="fuse", MODE="0666" fuse-3.18.2/xfstests/0000755000175000017500000000000015156613252013406 5ustar berndberndfuse-3.18.2/xfstests/README.md0000644000175000017500000000070215156613252014664 0ustar berndberndTo 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/ fuse-3.18.2/xfstests/local.config0000644000175000017500000000131515156613252015667 0ustar berndberndexport TEST_DEV=source:/mnt/src/test export TEST_DIR=/mnt/test export SCRATCH_DEV=source:/mnt/src/scratch export SCRATCH_MNT=/mnt/scratch export FSTYP=fuse export FUSE_SUBTYP=.passthrough export MOUNT_OPTIONS="" export TEST_FS_MOUNT_OPTS="" # extra binary options, such as '--direct-io' or '--nopassthrough', etc export EXTRA_BIN_OPTIONS="" # If PASSTHROUGH_PATH is unset, the mount helper is going to look # for the binary '${FUSE_SUBTYP}', though omitting the leading dot '.'. # Example: # with FUSE_SUBTYP=".passthrough", the mount helper is called # 'mount.fuse.passthrough' and that would try to run # 'passthrough'. # export PASSTHROUGH_PATH=/example/passthrough_hp fuse-3.18.2/xfstests/mount.fuse.passthrough0000755000175000017500000000211315156613252020002 0ustar berndbernd#!/bin/bash ulimit -n 1048576 dev="$1" shift mnt="$1" shift # -o shift mntopts="$1" shift # source can be provided as NFS style device (e.g. TEST_DEV=source:/${TEST_SOURCE}) # and/or it can already be inside mount options (passthrough_ll style) if ( echo "$mntopts" | grep -q "source=" ) ; then # Don't pass source as position argument source= elif [[ "$dev" == "source:"* ]]; then source="${dev#"source:"}" else >&2 echo "passthrough source is undefined, aborting!" fi if ( echo "$mntopts" | grep -q remount ) ; then exec mount -i "$dev" "$mnt" -o "$mntopts" fi # set default to SUBTYPE (extracted from this script name) # example: # Copy or link this script to /sbin/mount.fuse.passthrough_hp # If xfstests local.config does not set PASSTHROUGH_PATH, # PASSTHROUGH_PATH will be set to 'passthrough_hp' and exec below # will look that up from $PATH [ -n "$PASSTHROUGH_PATH" ] || PASSTHROUGH_PATH=${0#*mount.fuse.} #echo "EXTRA_BIN_OPTIONS='${EXTRA_BIN_OPTIONS}'" exec "$PASSTHROUGH_PATH" ${EXTRA_BIN_OPTIONS} -o fsname=$dev,allow_other,dev $source "$mnt" -o "$mntopts" "$@"